mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-23 00:35:51 +00:00
fix: 修复子代理 token 消耗在主 spinner 中始终显示为 0
Spinner.tsx 的 token 聚合循环仅统计 in_process_teammate 类型任务, 漏掉了 local_agent(后台代理/verification agent)类型。当后台代理 运行时,主界面 spinner 一直显示 "↓ 0 tokens",因为 background agent 的 token 消耗未被纳入 teammateTokens 聚合。 同时在 inProcessRunner.ts 中,进程内队友完成时计算并设置 result (含 totalTokens/totalToolUseCount/content/usage),使详情弹窗可以 正确展示累计 token 消耗,不再仅依赖 progress.tokenCount 间歇更新。 Co-Authored-By: deepseek-v4-pro[1m] <deepseek-ai@claude-code-best.win>
This commit is contained in:
@@ -23,6 +23,7 @@ import { getDefaultCharacters, type SpinnerMode } from './Spinner/index.js';
|
|||||||
import { SpinnerAnimationRow } from './Spinner/SpinnerAnimationRow.js';
|
import { SpinnerAnimationRow } from './Spinner/SpinnerAnimationRow.js';
|
||||||
import { useSettings } from '../hooks/useSettings.js';
|
import { useSettings } from '../hooks/useSettings.js';
|
||||||
import { isInProcessTeammateTask } from '../tasks/InProcessTeammateTask/types.js';
|
import { isInProcessTeammateTask } from '../tasks/InProcessTeammateTask/types.js';
|
||||||
|
import { isLocalAgentTask } from '../tasks/LocalAgentTask/LocalAgentTask.js';
|
||||||
import { isBackgroundTask } from '../tasks/types.js';
|
import { isBackgroundTask } from '../tasks/types.js';
|
||||||
import { getAllInProcessTeammateTasks } from '../tasks/InProcessTeammateTask/InProcessTeammateTask.js';
|
import { getAllInProcessTeammateTasks } from '../tasks/InProcessTeammateTask/InProcessTeammateTask.js';
|
||||||
import { getEffortSuffix } from '../utils/effort.js';
|
import { getEffortSuffix } from '../utils/effort.js';
|
||||||
@@ -214,7 +215,7 @@ function SpinnerWithVerbInner({
|
|||||||
let teammateTokens = 0;
|
let teammateTokens = 0;
|
||||||
if (!showSpinnerTree) {
|
if (!showSpinnerTree) {
|
||||||
for (const task of Object.values(tasks)) {
|
for (const task of Object.values(tasks)) {
|
||||||
if (isInProcessTeammateTask(task) && task.status === 'running') {
|
if (task.status === 'running' && (isInProcessTeammateTask(task) || isLocalAgentTask(task))) {
|
||||||
if (task.progress?.tokenCount) {
|
if (task.progress?.tokenCount) {
|
||||||
teammateTokens += task.progress.tokenCount;
|
teammateTokens += task.progress.tokenCount;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ import {
|
|||||||
import type { CustomAgentDefinition } from '@claude-code-best/builtin-tools/tools/AgentTool/loadAgentsDir.js'
|
import type { CustomAgentDefinition } from '@claude-code-best/builtin-tools/tools/AgentTool/loadAgentsDir.js'
|
||||||
import { runAgent } from '@claude-code-best/builtin-tools/tools/AgentTool/runAgent.js'
|
import { runAgent } from '@claude-code-best/builtin-tools/tools/AgentTool/runAgent.js'
|
||||||
import { awaitClassifierAutoApproval } from '@claude-code-best/builtin-tools/tools/BashTool/bashPermissions.js'
|
import { awaitClassifierAutoApproval } from '@claude-code-best/builtin-tools/tools/BashTool/bashPermissions.js'
|
||||||
|
import type { AgentToolResult } from '@claude-code-best/builtin-tools/tools/AgentTool/agentToolUtils.js'
|
||||||
import { BASH_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/BashTool/toolName.js'
|
import { BASH_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/BashTool/toolName.js'
|
||||||
import { SEND_MESSAGE_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/SendMessageTool/constants.js'
|
import { SEND_MESSAGE_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/SendMessageTool/constants.js'
|
||||||
import { TASK_CREATE_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/TaskCreateTool/constants.js'
|
import { TASK_CREATE_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/TaskCreateTool/constants.js'
|
||||||
@@ -63,7 +64,10 @@ import {
|
|||||||
} from '../../utils/messages.js'
|
} from '../../utils/messages.js'
|
||||||
import { evictTaskOutput } from '../../utils/task/diskOutput.js'
|
import { evictTaskOutput } from '../../utils/task/diskOutput.js'
|
||||||
import { evictTerminalTask } from '../../utils/task/framework.js'
|
import { evictTerminalTask } from '../../utils/task/framework.js'
|
||||||
import { tokenCountWithEstimation } from '../../utils/tokens.js'
|
import {
|
||||||
|
tokenCountWithEstimation,
|
||||||
|
getTokenCountFromUsage,
|
||||||
|
} from '../../utils/tokens.js'
|
||||||
import { createAbortController } from '../abortController.js'
|
import { createAbortController } from '../abortController.js'
|
||||||
import { type AgentContext, runWithAgentContext } from '../agentContext.js'
|
import { type AgentContext, runWithAgentContext } from '../agentContext.js'
|
||||||
import {
|
import {
|
||||||
@@ -915,6 +919,7 @@ export async function runInProcessTeammate(
|
|||||||
invokingRequestId,
|
invokingRequestId,
|
||||||
} = config
|
} = config
|
||||||
const { setAppState } = toolUseContext
|
const { setAppState } = toolUseContext
|
||||||
|
const startTime = Date.now()
|
||||||
|
|
||||||
logForDebugging(
|
logForDebugging(
|
||||||
`[inProcessRunner] Starting agent loop for ${identity.agentId}`,
|
`[inProcessRunner] Starting agent loop for ${identity.agentId}`,
|
||||||
@@ -1463,6 +1468,48 @@ export async function runInProcessTeammate(
|
|||||||
// Mark as completed when exiting the loop
|
// Mark as completed when exiting the loop
|
||||||
let alreadyTerminal = false
|
let alreadyTerminal = false
|
||||||
let toolUseId: string | undefined
|
let toolUseId: string | undefined
|
||||||
|
|
||||||
|
// Compute result so the detail dialog can show token usage.
|
||||||
|
// Walk backwards for the last API usage (cumulative input_tokens from the
|
||||||
|
// Anthropic API already includes all prior context).
|
||||||
|
let completionTokens = 0
|
||||||
|
let completionToolUseCount = 0
|
||||||
|
let lastAssistantContent: AgentToolResult['content'] = []
|
||||||
|
let lastUsage: AgentToolResult['usage'] | undefined
|
||||||
|
for (let i = allMessages.length - 1; i >= 0; i--) {
|
||||||
|
const m = allMessages[i]!
|
||||||
|
if (m.type === 'assistant') {
|
||||||
|
const blocks = (m.message?.content ?? []) as any[]
|
||||||
|
for (const b of blocks) {
|
||||||
|
if (b?.type === 'tool_use') completionToolUseCount++
|
||||||
|
}
|
||||||
|
const textBlocks = blocks.filter((b: any) => b?.type === 'text')
|
||||||
|
if (textBlocks.length > 0 && lastAssistantContent.length === 0) {
|
||||||
|
lastAssistantContent = textBlocks.map((b: any) => ({
|
||||||
|
type: 'text' as const,
|
||||||
|
text: b.text,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
if (!lastUsage && m.message?.usage) {
|
||||||
|
lastUsage = m.message.usage as AgentToolResult['usage']
|
||||||
|
completionTokens = getTokenCountFromUsage(
|
||||||
|
m.message.usage as Parameters<typeof getTokenCountFromUsage>[0],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (completionTokens > 0 && lastAssistantContent.length > 0) break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const teammateResult: AgentToolResult = {
|
||||||
|
agentId: identity.agentId,
|
||||||
|
agentType: 'teammate',
|
||||||
|
content: lastAssistantContent,
|
||||||
|
totalToolUseCount: completionToolUseCount,
|
||||||
|
totalDurationMs: Date.now() - startTime,
|
||||||
|
totalTokens: completionTokens,
|
||||||
|
usage: lastUsage as AgentToolResult['usage'],
|
||||||
|
} as unknown as AgentToolResult
|
||||||
|
|
||||||
updateTaskState(
|
updateTaskState(
|
||||||
taskId,
|
taskId,
|
||||||
task => {
|
task => {
|
||||||
@@ -1481,6 +1528,7 @@ export async function runInProcessTeammate(
|
|||||||
status: 'completed' as const,
|
status: 'completed' as const,
|
||||||
notified: true,
|
notified: true,
|
||||||
endTime: Date.now(),
|
endTime: Date.now(),
|
||||||
|
result: teammateResult,
|
||||||
messages: task.messages?.length ? [task.messages.at(-1)!] : undefined,
|
messages: task.messages?.length ? [task.messages.at(-1)!] : undefined,
|
||||||
pendingUserMessages: [],
|
pendingUserMessages: [],
|
||||||
inProgressToolUseIDs: undefined,
|
inProgressToolUseIDs: undefined,
|
||||||
|
|||||||
Reference in New Issue
Block a user