-
Notifications
You must be signed in to change notification settings - Fork 15.8k
fix: 修复子代理 token 显示为 0 + cacheWarningStateBySource Map 内存泄漏 #1225
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -47,6 +47,7 @@ import { | |
| 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 { 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 { 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' | ||
|
|
@@ -63,7 +64,10 @@ import { | |
| } from '../../utils/messages.js' | ||
| import { evictTaskOutput } from '../../utils/task/diskOutput.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 { type AgentContext, runWithAgentContext } from '../agentContext.js' | ||
| import { | ||
|
|
@@ -915,6 +919,7 @@ export async function runInProcessTeammate( | |
| invokingRequestId, | ||
| } = config | ||
| const { setAppState } = toolUseContext | ||
| const startTime = Date.now() | ||
|
|
||
| logForDebugging( | ||
| `[inProcessRunner] Starting agent loop for ${identity.agentId}`, | ||
|
|
@@ -1463,6 +1468,48 @@ export async function runInProcessTeammate( | |
| // Mark as completed when exiting the loop | ||
| let alreadyTerminal = false | ||
| 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 | ||
| } | ||
| } | ||
|
Comment on lines
+1479
to
+1501
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tool-use count is undercounted: loop breaks before scanning earlier assistant turns. The early-exit condition If the intent is a true total, count tool uses in a separate pass (or don't break) and only short-circuit the content/usage lookups. 🛠️ Suggested fix- 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
- }
- }
+ for (let i = allMessages.length - 1; i >= 0; i--) {
+ const m = allMessages[i]!
+ if (m.type !== 'assistant') continue
+ const content = m.message?.content
+ const blocks = Array.isArray(content) ? content : []
+ for (const b of blocks) {
+ if (typeof b === 'object' && b !== null && 'type' in b && b.type === 'tool_use') {
+ completionToolUseCount++
+ }
+ }
+ if (lastAssistantContent.length === 0) {
+ const textBlocks = blocks.filter(
+ (b): b is { type: 'text'; text: string } =>
+ typeof b === 'object' && b !== null && 'type' in b && b.type === 'text',
+ )
+ if (textBlocks.length > 0) {
+ lastAssistantContent = textBlocks.map(b => ({ 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],
+ )
+ }
+ // Do NOT break — keep counting tool_use blocks across the full history.
+ }🤖 Prompt for AI Agents |
||
|
|
||
| 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 | ||
|
Comment on lines
+1503
to
+1511
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Inspect the AgentToolResult definition and its agentType literal/required fields.
fd -t f 'agentToolUtils.(ts|tsx)' | head -20
rg -nP -C2 'export\s+(type|interface)\s+AgentToolResult\b'
rg -nP -C2 "agentType\s*:\s*['\"]teammate['\"]"Repository: claude-code-best/claude-code Length of output: 2097 🏁 Script executed: sed -n '240,280p' packages/builtin-tools/src/tools/AgentTool/agentToolUtils.ts | cat -nRepository: claude-code-best/claude-code Length of output: 1471 🏁 Script executed: sed -n '200,265p' packages/builtin-tools/src/tools/AgentTool/agentToolUtils.ts | cat -nRepository: claude-code-best/claude-code Length of output: 2460 🏁 Script executed: sed -n '1470,1520p' src/utils/swarm/inProcessRunner.ts | cat -nRepository: claude-code-best/claude-code Length of output: 2488 Remove the double-cast The 🤖 Prompt for AI Agents |
||
|
|
||
| updateTaskState( | ||
| taskId, | ||
| task => { | ||
|
|
@@ -1481,6 +1528,7 @@ export async function runInProcessTeammate( | |
| status: 'completed' as const, | ||
| notified: true, | ||
| endTime: Date.now(), | ||
| result: teammateResult, | ||
| messages: task.messages?.length ? [task.messages.at(-1)!] : undefined, | ||
| pendingUserMessages: [], | ||
| inProgressToolUseIDs: undefined, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace
as anycasts with proper typing.Per the repository's coding guidelines, production
src/**/*.{ts,tsx}code must not useas any; preferRecord<string, unknown>, type guards, oras unknown as SpecificTypedouble assertions. The three sites here —(m.message?.content ?? []) as any[],blocks.filter((b: any) => …), andtextBlocks.map((b: any) => …)— can all be expressed with a narrow content-block type guard.ContentBlockParamis already imported at the top of this file.As per coding guidelines: "Do not use
as anyin production code; useas unknown as SpecificTypedouble assertion or add proper interface definitions instead" and "Use type guards to narrow union types rather than force type conversion".📝 Committable suggestion
🤖 Prompt for AI Agents