@zhin.js/agent 1.1.39 → 1.1.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/assistant/job-storage.d.ts +238 -0
- package/lib/assistant/job-storage.js +143 -0
- package/lib/assistant/job-storage.js.map +1 -0
- package/lib/assistant/job-store.d.ts +7 -0
- package/lib/assistant/job-store.js +41 -3
- package/lib/assistant/job-store.js.map +1 -1
- package/lib/config/perception-config.d.ts +9 -0
- package/lib/config/perception-config.js +27 -0
- package/lib/config/perception-config.js.map +1 -1
- package/lib/context/context-system.d.ts +1 -0
- package/lib/context/context-system.js +8 -8
- package/lib/context/context-system.js.map +1 -1
- package/lib/context/im-turn-context-adapter.js +2 -0
- package/lib/context/im-turn-context-adapter.js.map +1 -1
- package/lib/context/skill-context.d.ts +3 -0
- package/lib/context/skill-context.js +6 -0
- package/lib/context/skill-context.js.map +1 -0
- package/lib/context/turn-envelope.d.ts +4 -0
- package/lib/context/turn-envelope.js +24 -7
- package/lib/context/turn-envelope.js.map +1 -1
- package/lib/context/turn-user-message.d.ts +3 -1
- package/lib/context/turn-user-message.js +14 -1
- package/lib/context/turn-user-message.js.map +1 -1
- package/lib/core/agent-core-run.d.ts +1 -0
- package/lib/core/agent-core-run.js +47 -20
- package/lib/core/agent-core-run.js.map +1 -1
- package/lib/core/structured-reply-recovery.d.ts +6 -2
- package/lib/core/structured-reply-recovery.js +22 -8
- package/lib/core/structured-reply-recovery.js.map +1 -1
- package/lib/core/turn-completion-usage.d.ts +2 -1
- package/lib/core/turn-completion-usage.js +14 -5
- package/lib/core/turn-completion-usage.js.map +1 -1
- package/lib/core/turn-event-mapper.d.ts +1 -1
- package/lib/core/turn-event-mapper.js +8 -5
- package/lib/core/turn-event-mapper.js.map +1 -1
- package/lib/decision/config.d.ts +1 -1
- package/lib/decision/config.js +1 -1
- package/lib/decision/config.js.map +1 -1
- package/lib/decision/types.d.ts +1 -1
- package/lib/decision/types.js.map +1 -1
- package/lib/event/turn-event.d.ts +7 -1
- package/lib/event/turn-event.js.map +1 -1
- package/lib/index.d.ts +4 -1
- package/lib/index.js +2 -0
- package/lib/index.js.map +1 -1
- package/lib/media/perception.d.ts +3 -4
- package/lib/media/perception.js +59 -37
- package/lib/media/perception.js.map +1 -1
- package/lib/memory/compaction-runtime.d.ts +3 -2
- package/lib/memory/compaction-runtime.js +22 -14
- package/lib/memory/compaction-runtime.js.map +1 -1
- package/lib/outbound/agent-output.js +2 -0
- package/lib/outbound/agent-output.js.map +1 -1
- package/lib/outbound/chat-failure.d.ts +27 -0
- package/lib/outbound/chat-failure.js +53 -0
- package/lib/outbound/chat-failure.js.map +1 -0
- package/lib/outbound/structured-reply.d.ts +7 -0
- package/lib/outbound/structured-reply.js +52 -16
- package/lib/outbound/structured-reply.js.map +1 -1
- package/lib/plugin-runtime/agent-runtime.js +49 -8
- package/lib/plugin-runtime/agent-runtime.js.map +1 -1
- package/lib/plugin-runtime/ai-completion.d.ts +4 -3
- package/lib/plugin-runtime/ai-completion.js +64 -10
- package/lib/plugin-runtime/ai-completion.js.map +1 -1
- package/lib/plugin-runtime/capability-ingress.js +3 -1
- package/lib/plugin-runtime/capability-ingress.js.map +1 -1
- package/lib/plugin-runtime/decision-runtime.d.ts +1 -0
- package/lib/plugin-runtime/deferred-capability-plan.d.ts +4 -1
- package/lib/plugin-runtime/deferred-capability-plan.js +25 -9
- package/lib/plugin-runtime/deferred-capability-plan.js.map +1 -1
- package/lib/plugin-runtime/full-agent-turn-engine.js +1 -0
- package/lib/plugin-runtime/full-agent-turn-engine.js.map +1 -1
- package/lib/plugin-runtime/index.d.ts +2 -0
- package/lib/plugin-runtime/index.js +2 -0
- package/lib/plugin-runtime/index.js.map +1 -1
- package/lib/prompt/assembly.js +0 -9
- package/lib/prompt/assembly.js.map +1 -1
- package/lib/prompt/git-context.js +1 -1
- package/lib/prompt/git-context.js.map +1 -1
- package/lib/prompt/rules/chat-rules.js +8 -9
- package/lib/prompt/rules/chat-rules.js.map +1 -1
- package/lib/prompt/rules/critical-rules.js +10 -10
- package/lib/prompt/rules/critical-rules.js.map +1 -1
- package/lib/prompt/system-prompt.js +4 -5
- package/lib/prompt/system-prompt.js.map +1 -1
- package/lib/security/file-role-policy.js +6 -6
- package/lib/security/file-role-policy.js.map +1 -1
- package/lib/security/policy-facade.js +1 -1
- package/lib/security/policy-facade.js.map +1 -1
- package/lib/security/usage-quota.d.ts +4 -0
- package/lib/security/usage-quota.js +23 -2
- package/lib/security/usage-quota.js.map +1 -1
- package/lib/session/approval-port.d.ts +2 -0
- package/lib/session/approval-port.js.map +1 -1
- package/lib/session/approval-review-agent.d.ts +1 -1
- package/lib/session/approval-review-agent.js +19 -4
- package/lib/session/approval-review-agent.js.map +1 -1
- package/lib/session/turn-ingress-session.js +7 -1
- package/lib/session/turn-ingress-session.js.map +1 -1
- package/lib/tool/tool-approval-gate.js +4 -2
- package/lib/tool/tool-approval-gate.js.map +1 -1
- package/lib/tool/turn-tool-runtime.js +24 -5
- package/lib/tool/turn-tool-runtime.js.map +1 -1
- package/lib/tool-catalog/deferred-turn-controller.js +1 -4
- package/lib/tool-catalog/deferred-turn-controller.js.map +1 -1
- package/lib/trace/context-budget.d.ts +4 -3
- package/lib/trace/context-budget.js +28 -8
- package/lib/trace/context-budget.js.map +1 -1
- package/lib/trace/turn-trace-format.js +4 -1
- package/lib/trace/turn-trace-format.js.map +1 -1
- package/lib/trace/turn-trace.d.ts +6 -0
- package/lib/trace/turn-trace.js +8 -2
- package/lib/trace/turn-trace.js.map +1 -1
- package/lib/trace/turn-usage-log.d.ts +14 -0
- package/lib/trace/turn-usage-log.js +83 -0
- package/lib/trace/turn-usage-log.js.map +1 -0
- package/lib/usage/usage-store.d.ts +199 -0
- package/lib/usage/usage-store.js +121 -0
- package/lib/usage/usage-store.js.map +1 -0
- package/package.json +12 -12
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deferred-turn-controller.js","sourceRoot":"","sources":["../../src/tool-catalog/deferred-turn-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n addSkillToSnapshot,\n getLoadedToolNamesFromSnapshot,\n touchToolInSnapshot,\n touchToolsInSnapshot,\n type AgentTool,\n type DeferredToolSessionSnapshot,\n} from '@zhin.js/ai';\nimport type { Tool, ToolResult } from '@zhin.js/core';\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport type { SkillRegistry } from '../resource-hub/skill-registry.js';\nimport type { SkillInstructionSource } from '../skill/skill-instruction-reader.js';\nimport { catalogToolByName, discoverInCatalog, type DiscoverKind } from './tool-catalog.js';\nimport type { ToolCatalogItem } from './types.js';\n\nexport const TOOLS_MUTATED_MARKER = '__zhin_tools_mutated__';\n\nexport interface DeferredTurnControllerOptions {\n readonly sessionId: string;\n readonly platform?: string;\n readonly catalog: readonly ToolCatalogItem[];\n readonly skillRegistry: SkillRegistry | null;\n readonly snapshot: DeferredToolSessionSnapshot;\n readonly maxLoadedPerSession: number;\n /** Always-loaded tools: never recorded in the session snapshot, so they never count against or get evicted by `maxLoadedPerSession`. */\n readonly pinnedTools?: ReadonlySet<string>;\n readonly discoverTopK: number;\n readonly persistSnapshot: (snapshot: DeferredToolSessionSnapshot) => Promise<void>;\n readonly onSkillLoaded?: (name: string, instructions: string, toolNames: string[]) => void;\n readonly skillInstructions: SkillInstructionSource;\n}\n\nexport interface DeferredTurnController {\n readonly tools: readonly Tool[];\n snapshot(): DeferredToolSessionSnapshot;\n loadedToolNames(): string[];\n tool(name: string): AgentTool | undefined;\n /** Load a catalog tool into the session. Returns false when the name is not in this turn's catalog. */\n load(name: string): Promise<boolean>;\n fork(): DeferredTurnController;\n}\n\nconst activeController = new AsyncLocalStorage<DeferredTurnController>();\n\nexport function runWithDeferredTurnController<TResult>(\n controller: DeferredTurnController,\n operation: () => Promise<TResult>,\n): Promise<TResult> {\n return activeController.run(controller, operation);\n}\n\nexport function getActiveDeferredTurnController(): DeferredTurnController | undefined {\n return activeController.getStore();\n}\n\nconst MEMORY_HINT_KEYWORDS = ['memory', 'remember', 'recall', 'store', '记忆', '记住', '记下', '回忆', '备忘'];\nconst MEMORY_FALLBACK_HINT =\n 'No matches. 语义记忆工具未启用(需配置 ai.memory.semantic.enabled: true)。'\n + '可使用 write_file 将信息写入 data/memory/sessions/…/MEMORY.md 作为文件记忆替代。';\n\nfunction formatDiscoverResults(items: ReturnType<typeof discoverInCatalog>, query?: string): string {\n if (items.length === 0) {\n if (query && MEMORY_HINT_KEYWORDS.some(keyword => query.toLowerCase().includes(keyword))) {\n return MEMORY_FALLBACK_HINT;\n }\n return 'No matches.';\n }\n return items.map(item => `- [${item.kind}] ${item.name}: ${item.brief}`).join('\\n');\n}\n\nfunction schemaSummary(item: ToolCatalogItem): string {\n const params = item.fullTool.parameters;\n if (!params) return `Loaded tool \"${item.name}\".`;\n return `Loaded tool \"${item.name}\".\\nParameters schema:\\n${JSON.stringify(params, null, 2)}`;\n}\n\nexport function createDeferredTurnController(\n options: DeferredTurnControllerOptions,\n): DeferredTurnController {\n let currentSnapshot = structuredClone(options.snapshot);\n const catalog = [...options.catalog];\n const byName = catalogToolByName(catalog);\n\n const persist = async (next: DeferredToolSessionSnapshot): Promise<void> => {\n currentSnapshot = next;\n await options.persistSnapshot(currentSnapshot);\n };\n\n const isPinned = (name: string): boolean => options.pinnedTools?.has(name) ?? false;\n\n const loadTool = async (name: string): Promise<ToolCatalogItem | undefined> => {\n const item = byName.get(name);\n if (!item) return undefined;\n if (!isPinned(name)) {\n await persist(touchToolInSnapshot(currentSnapshot, name, options.maxLoadedPerSession));\n }\n return item;\n };\n\n const tools: Tool[] = [\n {\n name: 'discover',\n description: 'Search deferred tools and skills by query. kind: tool|skill|all (default all). Empty query returns platform-matched skills.',\n parameters: {\n type: 'object',\n properties: {\n query: { type: 'string', description: 'Search query (user intent keywords)' },\n kind: { type: 'string', enum: ['tool', 'skill', 'all'], description: 'What to search (default all)' },\n mcp_server: { type: 'string', description: 'Filter tools by MCP server name' },\n },\n },\n source: 'builtin:agent',\n execute: async (args): Promise<ToolResult> => {\n const input = args as Record<string, unknown>;\n const kind = (typeof input.kind === 'string' ? input.kind : 'all') as DiscoverKind;\n const query = typeof input.query === 'string' ? input.query : '';\n const mcpServer = typeof input.mcp_server === 'string' ? input.mcp_server : undefined;\n return formatDiscoverResults(discoverInCatalog({\n query,\n kind,\n topK: options.discoverTopK,\n platform: options.platform,\n mcpServer,\n skillRegistry: options.skillRegistry,\n catalog,\n }), query);\n },\n },\n {\n name: 'load_tool',\n description: 'Load a deferred tool schema into this turn so it can be called.',\n parameters: {\n type: 'object',\n properties: { name: { type: 'string', description: 'Tool name from discover results' } },\n required: ['name'],\n },\n source: 'builtin:agent',\n execute: async (args): Promise<ToolResult> => {\n const name = String((args as Record<string, unknown>).name);\n const item = await loadTool(name);\n if (!item) return `Tool \"${name}\" not found in catalog.`;\n return `${schemaSummary(item)}\\n${TOOLS_MUTATED_MARKER}`;\n },\n },\n {\n name: 'load_skill',\n description: 'Load full skill instructions and unlock associated tools for this turn.',\n parameters: {\n type: 'object',\n properties: { name: { type: 'string', description: 'Skill name from discover results' } },\n required: ['name'],\n },\n source: 'builtin:agent',\n execute: async (args): Promise<ToolResult> => {\n const name = String((args as Record<string, unknown>).name);\n const result = await options.skillInstructions.read(name);\n if (result.status === 'missing') return `Skill '${result.name}' not found. Check skills/ directory.`;\n const { instructions } = result;\n let next = addSkillToSnapshot(currentSnapshot, name);\n const toolNames = options.skillRegistry?.getByName(name)?.tools?.map(tool => tool.name) ?? [];\n next = touchToolsInSnapshot(\n next,\n toolNames.filter(toolName => !isPinned(toolName)),\n options.maxLoadedPerSession,\n );\n await persist(next);\n options.onSkillLoaded?.(name, instructions, toolNames);\n const unlock = toolNames.length > 0 ? `\\nUnlocked tools: ${toolNames.join(', ')}` : '';\n return `${instructions}${unlock}\\n${TOOLS_MUTATED_MARKER}`;\n },\n },\n ];\n\n return Object.freeze({\n tools: Object.freeze(tools),\n snapshot: () => currentSnapshot,\n loadedToolNames: () => getLoadedToolNamesFromSnapshot(currentSnapshot),\n tool: (name: string) => byName.get(name)?.fullTool,\n load: async (name: string) => (await loadTool(name)) !== undefined,\n fork: () => createDeferredTurnController({\n ...options,\n snapshot: currentSnapshot,\n persistSnapshot: async () => undefined,\n }),\n });\n}\n\nexport function isToolsMutatedResult(result: ToolResult): boolean {\n const text = typeof result === 'string' ? result : JSON.stringify(result);\n return text.includes(TOOLS_MUTATED_MARKER);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"deferred-turn-controller.js","sourceRoot":"","sources":["../../src/tool-catalog/deferred-turn-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n addSkillToSnapshot,\n getLoadedToolNamesFromSnapshot,\n touchToolInSnapshot,\n touchToolsInSnapshot,\n type AgentTool,\n type DeferredToolSessionSnapshot,\n} from '@zhin.js/ai';\nimport type { Tool, ToolResult } from '@zhin.js/core';\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport type { SkillRegistry } from '../resource-hub/skill-registry.js';\nimport type { SkillInstructionSource } from '../skill/skill-instruction-reader.js';\nimport { catalogToolByName, discoverInCatalog, type DiscoverKind } from './tool-catalog.js';\nimport type { ToolCatalogItem } from './types.js';\n\nexport const TOOLS_MUTATED_MARKER = '__zhin_tools_mutated__';\n\nexport interface DeferredTurnControllerOptions {\n readonly sessionId: string;\n readonly platform?: string;\n readonly catalog: readonly ToolCatalogItem[];\n readonly skillRegistry: SkillRegistry | null;\n readonly snapshot: DeferredToolSessionSnapshot;\n readonly maxLoadedPerSession: number;\n /** Always-loaded tools: never recorded in the session snapshot, so they never count against or get evicted by `maxLoadedPerSession`. */\n readonly pinnedTools?: ReadonlySet<string>;\n readonly discoverTopK: number;\n readonly persistSnapshot: (snapshot: DeferredToolSessionSnapshot) => Promise<void>;\n readonly onSkillLoaded?: (name: string, instructions: string, toolNames: string[]) => void;\n readonly skillInstructions: SkillInstructionSource;\n}\n\nexport interface DeferredTurnController {\n readonly tools: readonly Tool[];\n snapshot(): DeferredToolSessionSnapshot;\n loadedToolNames(): string[];\n tool(name: string): AgentTool | undefined;\n /** Load a catalog tool into the session. Returns false when the name is not in this turn's catalog. */\n load(name: string): Promise<boolean>;\n fork(): DeferredTurnController;\n}\n\nconst activeController = new AsyncLocalStorage<DeferredTurnController>();\n\nexport function runWithDeferredTurnController<TResult>(\n controller: DeferredTurnController,\n operation: () => Promise<TResult>,\n): Promise<TResult> {\n return activeController.run(controller, operation);\n}\n\nexport function getActiveDeferredTurnController(): DeferredTurnController | undefined {\n return activeController.getStore();\n}\n\nconst MEMORY_HINT_KEYWORDS = ['memory', 'remember', 'recall', 'store', '记忆', '记住', '记下', '回忆', '备忘'];\nconst MEMORY_FALLBACK_HINT =\n 'No matches. 语义记忆工具未启用(需配置 ai.memory.semantic.enabled: true)。'\n + '可使用 write_file 将信息写入 data/memory/sessions/…/MEMORY.md 作为文件记忆替代。';\n\nfunction formatDiscoverResults(items: ReturnType<typeof discoverInCatalog>, query?: string): string {\n if (items.length === 0) {\n if (query && MEMORY_HINT_KEYWORDS.some(keyword => query.toLowerCase().includes(keyword))) {\n return MEMORY_FALLBACK_HINT;\n }\n return 'No matches.';\n }\n return items.map(item => `- [${item.kind}] ${item.name}: ${item.brief}`).join('\\n');\n}\n\nfunction schemaSummary(item: ToolCatalogItem): string {\n return `Loaded tool \"${item.name}\". Use its tool definition for arguments.`;\n}\n\nexport function createDeferredTurnController(\n options: DeferredTurnControllerOptions,\n): DeferredTurnController {\n let currentSnapshot = structuredClone(options.snapshot);\n const catalog = [...options.catalog];\n const byName = catalogToolByName(catalog);\n\n const persist = async (next: DeferredToolSessionSnapshot): Promise<void> => {\n currentSnapshot = next;\n await options.persistSnapshot(currentSnapshot);\n };\n\n const isPinned = (name: string): boolean => options.pinnedTools?.has(name) ?? false;\n\n const loadTool = async (name: string): Promise<ToolCatalogItem | undefined> => {\n const item = byName.get(name);\n if (!item) return undefined;\n if (!isPinned(name)) {\n await persist(touchToolInSnapshot(currentSnapshot, name, options.maxLoadedPerSession));\n }\n return item;\n };\n\n const tools: Tool[] = [\n {\n name: 'discover',\n description: 'Search deferred tools and skills by query. kind: tool|skill|all (default all). Empty query returns platform-matched skills.',\n parameters: {\n type: 'object',\n properties: {\n query: { type: 'string', description: 'Search query (user intent keywords)' },\n kind: { type: 'string', enum: ['tool', 'skill', 'all'], description: 'What to search (default all)' },\n mcp_server: { type: 'string', description: 'Filter tools by MCP server name' },\n },\n },\n source: 'builtin:agent',\n execute: async (args): Promise<ToolResult> => {\n const input = args as Record<string, unknown>;\n const kind = (typeof input.kind === 'string' ? input.kind : 'all') as DiscoverKind;\n const query = typeof input.query === 'string' ? input.query : '';\n const mcpServer = typeof input.mcp_server === 'string' ? input.mcp_server : undefined;\n return formatDiscoverResults(discoverInCatalog({\n query,\n kind,\n topK: options.discoverTopK,\n platform: options.platform,\n mcpServer,\n skillRegistry: options.skillRegistry,\n catalog,\n }), query);\n },\n },\n {\n name: 'load_tool',\n description: 'Load a deferred tool schema into this turn so it can be called.',\n parameters: {\n type: 'object',\n properties: { name: { type: 'string', description: 'Tool name from discover results' } },\n required: ['name'],\n },\n source: 'builtin:agent',\n execute: async (args): Promise<ToolResult> => {\n const name = String((args as Record<string, unknown>).name);\n const item = await loadTool(name);\n if (!item) return `Tool \"${name}\" not found in catalog.`;\n return `${schemaSummary(item)}\\n${TOOLS_MUTATED_MARKER}`;\n },\n },\n {\n name: 'load_skill',\n description: 'Load full skill instructions and unlock associated tools for this turn.',\n parameters: {\n type: 'object',\n properties: { name: { type: 'string', description: 'Skill name from discover results' } },\n required: ['name'],\n },\n source: 'builtin:agent',\n execute: async (args): Promise<ToolResult> => {\n const name = String((args as Record<string, unknown>).name);\n const result = await options.skillInstructions.read(name);\n if (result.status === 'missing') return `Skill '${result.name}' not found. Check skills/ directory.`;\n const { instructions } = result;\n let next = addSkillToSnapshot(currentSnapshot, name);\n const toolNames = options.skillRegistry?.getByName(name)?.tools?.map(tool => tool.name) ?? [];\n next = touchToolsInSnapshot(\n next,\n toolNames.filter(toolName => !isPinned(toolName)),\n options.maxLoadedPerSession,\n );\n await persist(next);\n options.onSkillLoaded?.(name, instructions, toolNames);\n const unlock = toolNames.length > 0 ? `\\nUnlocked tools: ${toolNames.join(', ')}` : '';\n return `${instructions}${unlock}\\n${TOOLS_MUTATED_MARKER}`;\n },\n },\n ];\n\n return Object.freeze({\n tools: Object.freeze(tools),\n snapshot: () => currentSnapshot,\n loadedToolNames: () => getLoadedToolNamesFromSnapshot(currentSnapshot),\n tool: (name: string) => byName.get(name)?.fullTool,\n load: async (name: string) => (await loadTool(name)) !== undefined,\n fork: () => createDeferredTurnController({\n ...options,\n snapshot: currentSnapshot,\n persistSnapshot: async () => undefined,\n }),\n });\n}\n\nexport function isToolsMutatedResult(result: ToolResult): boolean {\n const text = typeof result === 'string' ? result : JSON.stringify(result);\n return text.includes(TOOLS_MUTATED_MARKER);\n}\n"]}
|
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
import { type AgentMessage, type AgentTool } from '@zhin.js/ai';
|
|
11
11
|
import { estimatePromptTokens } from '../prompt/estimate-tokens.js';
|
|
12
12
|
/** Prompt segments, in the order the report lists them. */
|
|
13
|
-
export declare const CONTEXT_BUDGET_SEGMENTS: readonly ["system", "tool_schemas", "tool_index", "skill_catalog", "agent_catalog", "active_skills", "memory", "bootstrap", "history", "user"];
|
|
13
|
+
export declare const CONTEXT_BUDGET_SEGMENTS: readonly ["system", "tool_schemas", "output_schema", "tool_index", "skill_catalog", "agent_catalog", "active_skills", "memory", "bootstrap", "history", "user"];
|
|
14
14
|
export type ContextBudgetSegment = (typeof CONTEXT_BUDGET_SEGMENTS)[number];
|
|
15
|
-
/**
|
|
15
|
+
/** Fixed-cost categories; a catalog carried in turn data is deducted separately. */
|
|
16
16
|
export declare const FIXED_CONTEXT_SEGMENTS: ReadonlySet<ContextBudgetSegment>;
|
|
17
17
|
export { estimatePromptTokens };
|
|
18
18
|
export interface ContextBudgetSection {
|
|
@@ -22,7 +22,7 @@ export interface ContextBudgetSection {
|
|
|
22
22
|
/** Snake_case: persisted inside the TurnTrace JSON. */
|
|
23
23
|
export interface ContextBudgetReport {
|
|
24
24
|
readonly total_tokens: number;
|
|
25
|
-
/**
|
|
25
|
+
/** Fixed segments, excluding the current user-context tool catalog. */
|
|
26
26
|
readonly fixed_tokens: number;
|
|
27
27
|
readonly segments: Readonly<Record<ContextBudgetSegment, number>>;
|
|
28
28
|
/** System prompt sections, largest first. */
|
|
@@ -35,6 +35,7 @@ export interface ContextBudgetReport {
|
|
|
35
35
|
export interface ContextBudgetInput {
|
|
36
36
|
/** The final system prompt sent to the model. */
|
|
37
37
|
readonly systemPrompt: string;
|
|
38
|
+
readonly outputSchema?: Record<string, unknown>;
|
|
38
39
|
/** Per-section text sizes from the prompt assembly (ids such as `§8_skills`). */
|
|
39
40
|
readonly sections?: readonly Readonly<{
|
|
40
41
|
id: string;
|
|
@@ -7,13 +7,14 @@
|
|
|
7
7
|
* provider's real `prompt_tokens` stays in the trace's model calls for
|
|
8
8
|
* comparison. Only sizes are kept; prompt text never enters the report.
|
|
9
9
|
*/
|
|
10
|
-
import { estimateAgentMessagesTokens } from '@zhin.js/ai';
|
|
10
|
+
import { projectPromptContext, estimateAgentMessagesTokens } from '@zhin.js/ai';
|
|
11
11
|
import { estimatePromptTokens } from '../prompt/estimate-tokens.js';
|
|
12
12
|
import { AGENT_CATALOG_HEADING, DEFERRED_TOOL_INDEX_HEADING, SKILL_CATALOG_HEADING } from '../skill/capability-catalog.js';
|
|
13
13
|
/** Prompt segments, in the order the report lists them. */
|
|
14
14
|
export const CONTEXT_BUDGET_SEGMENTS = [
|
|
15
15
|
'system',
|
|
16
16
|
'tool_schemas',
|
|
17
|
+
'output_schema',
|
|
17
18
|
'tool_index',
|
|
18
19
|
'skill_catalog',
|
|
19
20
|
'agent_catalog',
|
|
@@ -23,10 +24,11 @@ export const CONTEXT_BUDGET_SEGMENTS = [
|
|
|
23
24
|
'history',
|
|
24
25
|
'user',
|
|
25
26
|
];
|
|
26
|
-
/**
|
|
27
|
+
/** Fixed-cost categories; a catalog carried in turn data is deducted separately. */
|
|
27
28
|
export const FIXED_CONTEXT_SEGMENTS = new Set([
|
|
28
29
|
'system',
|
|
29
30
|
'tool_schemas',
|
|
31
|
+
'output_schema',
|
|
30
32
|
'tool_index',
|
|
31
33
|
'skill_catalog',
|
|
32
34
|
'agent_catalog',
|
|
@@ -78,22 +80,40 @@ export function buildContextBudgetReport(input) {
|
|
|
78
80
|
segments.agent_catalog += attributed - Math.min(skillCatalog, attributed);
|
|
79
81
|
segments.bootstrap -= attributed;
|
|
80
82
|
}
|
|
81
|
-
//
|
|
83
|
+
// Legacy callers may still carry the tool index in system; current turns mark it in user context.
|
|
82
84
|
segments.tool_index = estimatePromptTokens(headedBlock(input.systemPrompt, DEFERRED_TOOL_INDEX_HEADING));
|
|
83
85
|
const attributedSystem = segments.tool_index + segments.skill_catalog + segments.agent_catalog + segments.memory + segments.bootstrap;
|
|
84
86
|
segments.system = Math.max(0, systemTokens - attributedSystem);
|
|
85
87
|
segments.tool_schemas = toolSchemaTokens(input.loadedTools);
|
|
86
|
-
segments.
|
|
87
|
-
const
|
|
88
|
-
segments.
|
|
89
|
-
|
|
88
|
+
segments.output_schema = input.outputSchema ? estimatePromptTokens(JSON.stringify(input.outputSchema)) : 0;
|
|
89
|
+
const projected = projectPromptContext([...input.historyMessages, ...input.userMessages]);
|
|
90
|
+
segments.history = estimateAgentMessagesTokens(projected.slice(0, input.historyMessages.length));
|
|
91
|
+
const current = projected.slice(input.historyMessages.length);
|
|
92
|
+
const contextTokens = (key) => input.userMessages.reduce((total, message, index) => {
|
|
93
|
+
const renderedContent = current[index]?.content;
|
|
94
|
+
if (!Array.isArray(message.content) || !Array.isArray(renderedContent))
|
|
95
|
+
return total;
|
|
96
|
+
return total + message.content.reduce((sum, block, blockIndex) => {
|
|
97
|
+
const rendered = renderedContent[blockIndex];
|
|
98
|
+
return sum + (typeof block.contextRef?.key === 'string' && key(block.contextRef.key) && rendered?.type === 'text'
|
|
99
|
+
? estimatePromptTokens(rendered.text) : 0);
|
|
100
|
+
}, 0);
|
|
101
|
+
}, 0);
|
|
102
|
+
const dynamicIndexTokens = contextTokens(key => key === 'deferred-tools');
|
|
103
|
+
segments.tool_index += dynamicIndexTokens;
|
|
104
|
+
const userTokens = estimateAgentMessagesTokens(current);
|
|
105
|
+
const skillTokens = contextTokens(key => key.startsWith('skill:'));
|
|
106
|
+
const hasMarkedSkills = input.userMessages.some(message => Array.isArray(message.content)
|
|
107
|
+
&& message.content.some(block => typeof block.contextRef?.key === 'string' && block.contextRef.key.startsWith('skill:')));
|
|
108
|
+
segments.active_skills = Math.min(hasMarkedSkills ? skillTokens : estimatePromptTokens(input.activeSkillsContext ?? ''), userTokens);
|
|
109
|
+
segments.user = Math.max(0, userTokens - segments.active_skills - dynamicIndexTokens);
|
|
90
110
|
const total = CONTEXT_BUDGET_SEGMENTS.reduce((sum, segment) => sum + segments[segment], 0);
|
|
91
111
|
const fixed = CONTEXT_BUDGET_SEGMENTS
|
|
92
112
|
.filter((segment) => FIXED_CONTEXT_SEGMENTS.has(segment))
|
|
93
113
|
.reduce((sum, segment) => sum + segments[segment], 0);
|
|
94
114
|
return Object.freeze({
|
|
95
115
|
total_tokens: total,
|
|
96
|
-
fixed_tokens: fixed,
|
|
116
|
+
fixed_tokens: fixed - dynamicIndexTokens,
|
|
97
117
|
segments: Object.freeze(segments),
|
|
98
118
|
sections: Object.freeze(sections),
|
|
99
119
|
tools: Object.freeze({ loaded: input.loadedTools.length, deferred: input.deferredToolCount }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-budget.js","sourceRoot":"","sources":["../../src/trace/context-budget.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Context budget report (#729, M2.1): how many prompt tokens each part of a\n * turn's first model request costs, so context work is driven by data.\n *\n * Numbers are estimates (CJK ≈ 1 token per character, other text ≈ 4\n * characters per token), computed from what the harness actually sends. The\n * provider's real `prompt_tokens` stays in the trace's model calls for\n * comparison. Only sizes are kept; prompt text never enters the report.\n */\nimport { estimateAgentMessagesTokens, type AgentMessage, type AgentTool } from '@zhin.js/ai';\nimport { estimatePromptTokens } from '../prompt/estimate-tokens.js';\nimport { AGENT_CATALOG_HEADING, DEFERRED_TOOL_INDEX_HEADING, SKILL_CATALOG_HEADING } from '../skill/capability-catalog.js';\n\n/** Prompt segments, in the order the report lists them. */\nexport const CONTEXT_BUDGET_SEGMENTS = [\n 'system',\n 'tool_schemas',\n 'tool_index',\n 'skill_catalog',\n 'agent_catalog',\n 'active_skills',\n 'memory',\n 'bootstrap',\n 'history',\n 'user',\n] as const;\n\nexport type ContextBudgetSegment = (typeof CONTEXT_BUDGET_SEGMENTS)[number];\n\n/** Segments that are paid on every request regardless of the conversation. */\nexport const FIXED_CONTEXT_SEGMENTS: ReadonlySet<ContextBudgetSegment> = new Set([\n 'system',\n 'tool_schemas',\n 'tool_index',\n 'skill_catalog',\n 'agent_catalog',\n 'memory',\n 'bootstrap',\n]);\n\nexport { estimatePromptTokens };\n\nexport interface ContextBudgetSection {\n readonly id: string;\n readonly tokens: number;\n}\n\n/** Snake_case: persisted inside the TurnTrace JSON. */\nexport interface ContextBudgetReport {\n readonly total_tokens: number;\n /** Sum of the segments in FIXED_CONTEXT_SEGMENTS. */\n readonly fixed_tokens: number;\n readonly segments: Readonly<Record<ContextBudgetSegment, number>>;\n /** System prompt sections, largest first. */\n readonly sections: readonly ContextBudgetSection[];\n readonly tools: Readonly<{ loaded: number; deferred: number }>;\n}\n\nexport interface ContextBudgetInput {\n /** The final system prompt sent to the model. */\n readonly systemPrompt: string;\n /** Per-section text sizes from the prompt assembly (ids such as `§8_skills`). */\n readonly sections?: readonly Readonly<{ id: string; approxChars: number; approxTokens?: number }>[];\n /** Bootstrap text that went into the system prompt; capability catalogs are attributed out of it. */\n readonly bootstrapContext?: string;\n /** Loaded skill instructions; the turn envelope carries them inside the user message. */\n readonly activeSkillsContext?: string;\n /** Tools whose full schema is sent on the request. */\n readonly loadedTools: readonly AgentTool[];\n /** Number of catalog tools reachable only via discover / load_tool. */\n readonly deferredToolCount: number;\n readonly historyMessages: readonly AgentMessage[];\n readonly userMessages: readonly AgentMessage[];\n}\n\nconst SECTION_SEGMENT: Readonly<Record<string, ContextBudgetSegment>> = {\n '§8_skills': 'skill_catalog',\n '§5e_memory_instructions': 'memory',\n '§11_bootstrap': 'bootstrap',\n};\n\nfunction toolSchemaTokens(tools: readonly AgentTool[]): number {\n return tools.reduce((sum, tool) => sum + estimatePromptTokens(JSON.stringify({\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters,\n })), 0);\n}\n\n/** Text of the block that starts at `heading` and runs to the next `## ` heading. */\nfunction headedBlock(text: string, heading: string): string {\n const start = text.indexOf(heading);\n if (start < 0) return '';\n const next = text.indexOf('\\n## ', start + heading.length);\n return text.slice(start, next < 0 ? text.length : next);\n}\n\nfunction sectionTokens(section: Readonly<{ approxChars: number; approxTokens?: number }>): number {\n return section.approxTokens ?? Math.ceil(section.approxChars / 4);\n}\n\nexport function buildContextBudgetReport(input: ContextBudgetInput): ContextBudgetReport {\n const systemTokens = estimatePromptTokens(input.systemPrompt);\n const sections = (input.sections ?? [])\n .map((section) => Object.freeze({ id: section.id, tokens: sectionTokens(section) }))\n .sort((left, right) => right.tokens - left.tokens);\n\n const segments: Record<ContextBudgetSegment, number> = Object.fromEntries(\n CONTEXT_BUDGET_SEGMENTS.map((segment) => [segment, 0]),\n ) as Record<ContextBudgetSegment, number>;\n\n for (const section of sections) {\n const segment = SECTION_SEGMENT[section.id];\n if (segment) segments[segment] += section.tokens;\n }\n\n // The plugin runtime renders skill/agent catalogs into the bootstrap block.\n const bootstrap = input.bootstrapContext ?? '';\n if (bootstrap && segments.bootstrap > 0) {\n const skillCatalog = estimatePromptTokens(headedBlock(bootstrap, SKILL_CATALOG_HEADING));\n const agentCatalog = estimatePromptTokens(headedBlock(bootstrap, AGENT_CATALOG_HEADING));\n const attributed = Math.min(segments.bootstrap, skillCatalog + agentCatalog);\n segments.skill_catalog += Math.min(skillCatalog, attributed);\n segments.agent_catalog += attributed - Math.min(skillCatalog, attributed);\n segments.bootstrap -= attributed;\n }\n\n // The deferred tool index is appended to the system prompt after assembly.\n segments.tool_index = estimatePromptTokens(headedBlock(input.systemPrompt, DEFERRED_TOOL_INDEX_HEADING));\n\n const attributedSystem = segments.tool_index + segments.skill_catalog + segments.agent_catalog + segments.memory + segments.bootstrap;\n segments.system = Math.max(0, systemTokens - attributedSystem);\n segments.tool_schemas = toolSchemaTokens(input.loadedTools);\n segments.history = estimateAgentMessagesTokens([...input.historyMessages]);\n\n const userTokens = estimateAgentMessagesTokens([...input.userMessages]);\n segments.active_skills = Math.min(estimatePromptTokens(input.activeSkillsContext ?? ''), userTokens);\n segments.user = userTokens - segments.active_skills;\n\n const total = CONTEXT_BUDGET_SEGMENTS.reduce((sum, segment) => sum + segments[segment], 0);\n const fixed = CONTEXT_BUDGET_SEGMENTS\n .filter((segment) => FIXED_CONTEXT_SEGMENTS.has(segment))\n .reduce((sum, segment) => sum + segments[segment], 0);\n\n return Object.freeze({\n total_tokens: total,\n fixed_tokens: fixed,\n segments: Object.freeze(segments),\n sections: Object.freeze(sections),\n tools: Object.freeze({ loaded: input.loadedTools.length, deferred: input.deferredToolCount }),\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"context-budget.js","sourceRoot":"","sources":["../../src/trace/context-budget.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Context budget report (#729, M2.1): how many prompt tokens each part of a\n * turn's first model request costs, so context work is driven by data.\n *\n * Numbers are estimates (CJK ≈ 1 token per character, other text ≈ 4\n * characters per token), computed from what the harness actually sends. The\n * provider's real `prompt_tokens` stays in the trace's model calls for\n * comparison. Only sizes are kept; prompt text never enters the report.\n */\nimport { projectPromptContext, estimateAgentMessagesTokens, type AgentMessage, type AgentTool } from '@zhin.js/ai';\nimport { estimatePromptTokens } from '../prompt/estimate-tokens.js';\nimport { AGENT_CATALOG_HEADING, DEFERRED_TOOL_INDEX_HEADING, SKILL_CATALOG_HEADING } from '../skill/capability-catalog.js';\n\n/** Prompt segments, in the order the report lists them. */\nexport const CONTEXT_BUDGET_SEGMENTS = [\n 'system',\n 'tool_schemas',\n 'output_schema',\n 'tool_index',\n 'skill_catalog',\n 'agent_catalog',\n 'active_skills',\n 'memory',\n 'bootstrap',\n 'history',\n 'user',\n] as const;\n\nexport type ContextBudgetSegment = (typeof CONTEXT_BUDGET_SEGMENTS)[number];\n\n/** Fixed-cost categories; a catalog carried in turn data is deducted separately. */\nexport const FIXED_CONTEXT_SEGMENTS: ReadonlySet<ContextBudgetSegment> = new Set([\n 'system',\n 'tool_schemas',\n 'output_schema',\n 'tool_index',\n 'skill_catalog',\n 'agent_catalog',\n 'memory',\n 'bootstrap',\n]);\n\nexport { estimatePromptTokens };\n\nexport interface ContextBudgetSection {\n readonly id: string;\n readonly tokens: number;\n}\n\n/** Snake_case: persisted inside the TurnTrace JSON. */\nexport interface ContextBudgetReport {\n readonly total_tokens: number;\n /** Fixed segments, excluding the current user-context tool catalog. */\n readonly fixed_tokens: number;\n readonly segments: Readonly<Record<ContextBudgetSegment, number>>;\n /** System prompt sections, largest first. */\n readonly sections: readonly ContextBudgetSection[];\n readonly tools: Readonly<{ loaded: number; deferred: number }>;\n}\n\nexport interface ContextBudgetInput {\n /** The final system prompt sent to the model. */\n readonly systemPrompt: string;\n readonly outputSchema?: Record<string, unknown>;\n /** Per-section text sizes from the prompt assembly (ids such as `§8_skills`). */\n readonly sections?: readonly Readonly<{ id: string; approxChars: number; approxTokens?: number }>[];\n /** Bootstrap text that went into the system prompt; capability catalogs are attributed out of it. */\n readonly bootstrapContext?: string;\n /** Loaded skill instructions; the turn envelope carries them inside the user message. */\n readonly activeSkillsContext?: string;\n /** Tools whose full schema is sent on the request. */\n readonly loadedTools: readonly AgentTool[];\n /** Number of catalog tools reachable only via discover / load_tool. */\n readonly deferredToolCount: number;\n readonly historyMessages: readonly AgentMessage[];\n readonly userMessages: readonly AgentMessage[];\n}\n\nconst SECTION_SEGMENT: Readonly<Record<string, ContextBudgetSegment>> = {\n '§8_skills': 'skill_catalog',\n '§5e_memory_instructions': 'memory',\n '§11_bootstrap': 'bootstrap',\n};\n\nfunction toolSchemaTokens(tools: readonly AgentTool[]): number {\n return tools.reduce((sum, tool) => sum + estimatePromptTokens(JSON.stringify({\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters,\n })), 0);\n}\n\n/** Text of the block that starts at `heading` and runs to the next `## ` heading. */\nfunction headedBlock(text: string, heading: string): string {\n const start = text.indexOf(heading);\n if (start < 0) return '';\n const next = text.indexOf('\\n## ', start + heading.length);\n return text.slice(start, next < 0 ? text.length : next);\n}\n\nfunction sectionTokens(section: Readonly<{ approxChars: number; approxTokens?: number }>): number {\n return section.approxTokens ?? Math.ceil(section.approxChars / 4);\n}\n\nexport function buildContextBudgetReport(input: ContextBudgetInput): ContextBudgetReport {\n const systemTokens = estimatePromptTokens(input.systemPrompt);\n const sections = (input.sections ?? [])\n .map((section) => Object.freeze({ id: section.id, tokens: sectionTokens(section) }))\n .sort((left, right) => right.tokens - left.tokens);\n\n const segments: Record<ContextBudgetSegment, number> = Object.fromEntries(\n CONTEXT_BUDGET_SEGMENTS.map((segment) => [segment, 0]),\n ) as Record<ContextBudgetSegment, number>;\n\n for (const section of sections) {\n const segment = SECTION_SEGMENT[section.id];\n if (segment) segments[segment] += section.tokens;\n }\n\n // The plugin runtime renders skill/agent catalogs into the bootstrap block.\n const bootstrap = input.bootstrapContext ?? '';\n if (bootstrap && segments.bootstrap > 0) {\n const skillCatalog = estimatePromptTokens(headedBlock(bootstrap, SKILL_CATALOG_HEADING));\n const agentCatalog = estimatePromptTokens(headedBlock(bootstrap, AGENT_CATALOG_HEADING));\n const attributed = Math.min(segments.bootstrap, skillCatalog + agentCatalog);\n segments.skill_catalog += Math.min(skillCatalog, attributed);\n segments.agent_catalog += attributed - Math.min(skillCatalog, attributed);\n segments.bootstrap -= attributed;\n }\n\n // Legacy callers may still carry the tool index in system; current turns mark it in user context.\n segments.tool_index = estimatePromptTokens(headedBlock(input.systemPrompt, DEFERRED_TOOL_INDEX_HEADING));\n\n const attributedSystem = segments.tool_index + segments.skill_catalog + segments.agent_catalog + segments.memory + segments.bootstrap;\n segments.system = Math.max(0, systemTokens - attributedSystem);\n segments.tool_schemas = toolSchemaTokens(input.loadedTools);\n segments.output_schema = input.outputSchema ? estimatePromptTokens(JSON.stringify(input.outputSchema)) : 0;\n const projected = projectPromptContext([...input.historyMessages, ...input.userMessages]);\n segments.history = estimateAgentMessagesTokens(projected.slice(0, input.historyMessages.length));\n const current = projected.slice(input.historyMessages.length);\n const contextTokens = (key: (key: string) => boolean) => input.userMessages.reduce((total, message, index) => {\n const renderedContent = current[index]?.content;\n if (!Array.isArray(message.content) || !Array.isArray(renderedContent)) return total;\n return total + message.content.reduce((sum: number, block: { contextRef?: { key: string } }, blockIndex: number) => {\n const rendered = renderedContent[blockIndex];\n return sum + (typeof block.contextRef?.key === 'string' && key(block.contextRef.key) && rendered?.type === 'text'\n ? estimatePromptTokens(rendered.text) : 0);\n }, 0);\n }, 0);\n const dynamicIndexTokens = contextTokens(key => key === 'deferred-tools');\n segments.tool_index += dynamicIndexTokens;\n const userTokens = estimateAgentMessagesTokens(current);\n const skillTokens = contextTokens(key => key.startsWith('skill:'));\n const hasMarkedSkills = input.userMessages.some(message => Array.isArray(message.content)\n && message.content.some(block => typeof block.contextRef?.key === 'string' && block.contextRef.key.startsWith('skill:')));\n segments.active_skills = Math.min(hasMarkedSkills ? skillTokens : estimatePromptTokens(input.activeSkillsContext ?? ''), userTokens);\n segments.user = Math.max(0, userTokens - segments.active_skills - dynamicIndexTokens);\n\n const total = CONTEXT_BUDGET_SEGMENTS.reduce((sum, segment) => sum + segments[segment], 0);\n const fixed = CONTEXT_BUDGET_SEGMENTS\n .filter((segment) => FIXED_CONTEXT_SEGMENTS.has(segment))\n .reduce((sum, segment) => sum + segments[segment], 0);\n\n return Object.freeze({\n total_tokens: total,\n fixed_tokens: fixed - dynamicIndexTokens,\n segments: Object.freeze(segments),\n sections: Object.freeze(sections),\n tools: Object.freeze({ loaded: input.loadedTools.length, deferred: input.deferredToolCount }),\n });\n}\n"]}
|
|
@@ -56,7 +56,9 @@ function renderTrace(trace, indent, withTimeline) {
|
|
|
56
56
|
for (const call of trace.model_calls) {
|
|
57
57
|
const cache = call.cache_read_tokens ? ` cache=${call.cache_read_tokens}` : '';
|
|
58
58
|
const reasoning = call.reasoning_tokens ? ` reasoning=${call.reasoning_tokens}` : '';
|
|
59
|
-
|
|
59
|
+
const cacheWrite = call.cache_write_tokens !== undefined ? ` cache_write=${call.cache_write_tokens}` : '';
|
|
60
|
+
const model = call.model ? ` model=${call.provider ?? '?'}/${call.model}` : '';
|
|
61
|
+
lines.push(`${indent} #${call.index} in=${call.prompt_tokens} out=${call.completion_tokens}${cache}${reasoning}${cacheWrite}${model}`);
|
|
60
62
|
}
|
|
61
63
|
if (trace.context_budget)
|
|
62
64
|
lines.push(...renderContextBudget(trace.context_budget, indent));
|
|
@@ -95,6 +97,7 @@ function renderTrace(trace, indent, withTimeline) {
|
|
|
95
97
|
const SEGMENT_LABEL = {
|
|
96
98
|
system: '系统提示',
|
|
97
99
|
tool_schemas: '工具 schema',
|
|
100
|
+
output_schema: '输出 schema',
|
|
98
101
|
tool_index: '按需工具索引',
|
|
99
102
|
skill_catalog: '技能目录',
|
|
100
103
|
agent_catalog: '专家 agent 目录',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"turn-trace-format.js","sourceRoot":"","sources":["../../src/trace/turn-trace-format.ts"],"names":[],"mappings":"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","sourcesContent":["/** Human-readable terminal rendering of a TurnTrace for `zhin trace`. */\nimport type { TurnTrace, TurnTraceToolCall } from './turn-trace.js';\nimport { CONTEXT_BUDGET_SEGMENTS, type ContextBudgetReport, type ContextBudgetSegment } from './context-budget.js';\n\nconst STATUS_LABEL: Record<TurnTrace['termination']['status'], string> = {\n running: '进行中',\n completed: '完成',\n failed: '失败',\n cancelled: '已取消',\n budget_exceeded: '超出预算',\n unknown: '未知',\n};\n\nconst TOOL_MARK: Record<TurnTraceToolCall['status'], string> = {\n running: '…',\n ok: '✓',\n failed: '✗',\n denied: '⊘',\n cancelled: '-',\n};\n\nexport interface FormatTurnTraceOptions {\n /** Include the chronological timeline (default true). */\n readonly timeline?: boolean;\n}\n\nexport function formatTurnTrace(trace: TurnTrace, options: FormatTurnTraceOptions = {}): string {\n return renderTrace(trace, '', options.timeline ?? true).join('\\n');\n}\n\n/** One line per trace, newest last. */\nexport function formatTurnTraceList(traces: readonly TurnTrace[]): string {\n if (traces.length === 0) return '还没有记录到任何回合。';\n return traces.map(trace => [\n new Date(trace.started_at).toLocaleString(),\n STATUS_LABEL[trace.termination.status],\n trace.turn_id,\n trace.session_key,\n `${trace.tool_calls.length} tools`,\n `${trace.usage.total_tokens} tokens`,\n ].join(' ')).join('\\n');\n}\n\nfunction renderTrace(trace: TurnTrace, indent: string, withTimeline: boolean): string[] {\n const lines: string[] = [];\n const termination = trace.termination;\n const failingTool = trace.tool_calls.find(tool => tool.status === 'failed' || tool.status === 'denied');\n lines.push(`${indent}回合 ${trace.turn_id} [${STATUS_LABEL[termination.status]}]`);\n lines.push(`${indent} 会话: ${trace.session_key}`);\n if (trace.principal) {\n const name = trace.principal.display_name ? ` (${trace.principal.display_name})` : '';\n lines.push(`${indent} 发起人: ${trace.principal.subject_id}${name} 角色: ${trace.principal.roles.join(', ') || '-'}`\n + (trace.principal.trust_level ? ` 信任级别: ${trace.principal.trust_level}` : ''));\n }\n lines.push(`${indent} 开始: ${new Date(trace.started_at).toLocaleString()} 耗时: ${formatDuration(trace.duration_ms)}`);\n if (termination.code || termination.reason) {\n lines.push(`${indent} 结束原因: ${[termination.code, termination.reason].filter(Boolean).join(' — ')}`);\n }\n if (failingTool) {\n lines.push(`${indent} 首个失败步骤: ${failingTool.tool_name} ${failingTool.status}${failingTool.error ? ` — ${failingTool.error}` : ''}`);\n }\n\n lines.push(`${indent} 模型调用 ${trace.model_calls.length} 次,tokens 输入 ${trace.usage.prompt_tokens} / 输出 ${trace.usage.completion_tokens} / 合计 ${trace.usage.total_tokens}`);\n const cached = trace.model_calls.reduce((sum, call) => sum + (call.cache_read_tokens ?? 0), 0);\n if (cached > 0 && trace.usage.prompt_tokens > 0) {\n lines.push(`${indent} 提示缓存命中 ${cached} tokens(占输入 ${Math.round((cached / trace.usage.prompt_tokens) * 100)}%)`);\n }\n for (const call of trace.model_calls) {\n const cache = call.cache_read_tokens ? ` cache=${call.cache_read_tokens}` : '';\n const reasoning = call.reasoning_tokens ? ` reasoning=${call.reasoning_tokens}` : '';\n lines.push(`${indent} #${call.index} in=${call.prompt_tokens} out=${call.completion_tokens}${cache}${reasoning}`);\n }\n\n if (trace.context_budget) lines.push(...renderContextBudget(trace.context_budget, indent));\n\n lines.push(`${indent} 工具调用 ${trace.tool_calls.length} 次`);\n for (const tool of trace.tool_calls) {\n const details = [\n tool.duration_ms !== undefined ? `${tool.duration_ms}ms` : undefined,\n tool.result_size !== undefined ? `${tool.result_size} 字符` : undefined,\n tool.policy ? `策略 ${tool.policy}` : undefined,\n ].filter(Boolean).join(', ');\n lines.push(`${indent} ${TOOL_MARK[tool.status]} ${tool.tool_name}${details ? ` (${details})` : ''} ${tool.args_summary}`.trimEnd());\n if (tool.error) lines.push(`${indent} ${tool.error}`);\n }\n\n if (trace.budget.extensions.length > 0 || trace.budget.continuations > 0) {\n const extensions = trace.budget.extensions.map(item => `${item.from}→${item.to}`).join(', ');\n lines.push(`${indent} 预算: 延长 ${trace.budget.extensions.length} 次${extensions ? ` (${extensions})` : ''},自动续跑 ${trace.budget.continuations} 次`);\n }\n\n for (const task of trace.subagent_tasks) {\n lines.push(`${indent} 子任务 ${task.task_id} [${task.status}] ${task.description}`);\n }\n for (const child of trace.subagents) {\n lines.push(`${indent} 子 agent:`);\n lines.push(...renderTrace(child, `${indent} `, false));\n }\n\n if (withTimeline && trace.timeline.length > 0) {\n lines.push(`${indent} 时间线:`);\n for (const entry of trace.timeline) {\n lines.push(`${indent} +${formatDuration(entry.at_ms).padStart(7)} ${entry.kind.padEnd(14)} ${entry.detail}`.trimEnd());\n }\n }\n if (trace.dropped > 0) lines.push(`${indent} (有 ${trace.dropped} 条记录因超出上限被省略)`);\n return lines;\n}\n\nconst SEGMENT_LABEL: Record<ContextBudgetSegment, string> = {\n system: '系统提示',\n tool_schemas: '工具 schema',\n tool_index: '按需工具索引',\n skill_catalog: '技能目录',\n agent_catalog: '专家 agent 目录',\n active_skills: '已加载技能',\n memory: '记忆',\n bootstrap: 'bootstrap',\n history: '历史消息',\n user: '本轮输入',\n};\n\nconst TOP_SECTIONS = 5;\n\n/** Estimated prompt cost of the first model request, by segment. */\nfunction renderContextBudget(budget: ContextBudgetReport, indent: string): string[] {\n const parts = CONTEXT_BUDGET_SEGMENTS\n .filter(segment => budget.segments[segment] > 0)\n .map(segment => `${SEGMENT_LABEL[segment]} ${budget.segments[segment]}`);\n const lines = [\n `${indent} 上下文估算 ~${budget.total_tokens} tokens(固定开销 ~${budget.fixed_tokens};工具 ${budget.tools.loaded} 个常驻 / ${budget.tools.deferred} 个按需)`,\n ];\n if (parts.length > 0) lines.push(`${indent} ${parts.join(' · ')}`);\n const top = budget.sections.slice(0, TOP_SECTIONS).filter(section => section.tokens > 0);\n if (top.length > 0) lines.push(`${indent} 最大的提示段: ${top.map(section => `${section.id} ${section.tokens}`).join(', ')}`);\n return lines;\n}\n\nfunction formatDuration(ms: number | undefined): string {\n if (ms === undefined) return '-';\n if (ms < 1000) return `${Math.round(ms)}ms`;\n if (ms < 60_000) return `${(ms / 1000).toFixed(1)}s`;\n return `${Math.floor(ms / 60_000)}m${Math.round((ms % 60_000) / 1000)}s`;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"turn-trace-format.js","sourceRoot":"","sources":["../../src/trace/turn-trace-format.ts"],"names":[],"mappings":"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","sourcesContent":["/** Human-readable terminal rendering of a TurnTrace for `zhin trace`. */\nimport type { TurnTrace, TurnTraceToolCall } from './turn-trace.js';\nimport { CONTEXT_BUDGET_SEGMENTS, type ContextBudgetReport, type ContextBudgetSegment } from './context-budget.js';\n\nconst STATUS_LABEL: Record<TurnTrace['termination']['status'], string> = {\n running: '进行中',\n completed: '完成',\n failed: '失败',\n cancelled: '已取消',\n budget_exceeded: '超出预算',\n unknown: '未知',\n};\n\nconst TOOL_MARK: Record<TurnTraceToolCall['status'], string> = {\n running: '…',\n ok: '✓',\n failed: '✗',\n denied: '⊘',\n cancelled: '-',\n};\n\nexport interface FormatTurnTraceOptions {\n /** Include the chronological timeline (default true). */\n readonly timeline?: boolean;\n}\n\nexport function formatTurnTrace(trace: TurnTrace, options: FormatTurnTraceOptions = {}): string {\n return renderTrace(trace, '', options.timeline ?? true).join('\\n');\n}\n\n/** One line per trace, newest last. */\nexport function formatTurnTraceList(traces: readonly TurnTrace[]): string {\n if (traces.length === 0) return '还没有记录到任何回合。';\n return traces.map(trace => [\n new Date(trace.started_at).toLocaleString(),\n STATUS_LABEL[trace.termination.status],\n trace.turn_id,\n trace.session_key,\n `${trace.tool_calls.length} tools`,\n `${trace.usage.total_tokens} tokens`,\n ].join(' ')).join('\\n');\n}\n\nfunction renderTrace(trace: TurnTrace, indent: string, withTimeline: boolean): string[] {\n const lines: string[] = [];\n const termination = trace.termination;\n const failingTool = trace.tool_calls.find(tool => tool.status === 'failed' || tool.status === 'denied');\n lines.push(`${indent}回合 ${trace.turn_id} [${STATUS_LABEL[termination.status]}]`);\n lines.push(`${indent} 会话: ${trace.session_key}`);\n if (trace.principal) {\n const name = trace.principal.display_name ? ` (${trace.principal.display_name})` : '';\n lines.push(`${indent} 发起人: ${trace.principal.subject_id}${name} 角色: ${trace.principal.roles.join(', ') || '-'}`\n + (trace.principal.trust_level ? ` 信任级别: ${trace.principal.trust_level}` : ''));\n }\n lines.push(`${indent} 开始: ${new Date(trace.started_at).toLocaleString()} 耗时: ${formatDuration(trace.duration_ms)}`);\n if (termination.code || termination.reason) {\n lines.push(`${indent} 结束原因: ${[termination.code, termination.reason].filter(Boolean).join(' — ')}`);\n }\n if (failingTool) {\n lines.push(`${indent} 首个失败步骤: ${failingTool.tool_name} ${failingTool.status}${failingTool.error ? ` — ${failingTool.error}` : ''}`);\n }\n\n lines.push(`${indent} 模型调用 ${trace.model_calls.length} 次,tokens 输入 ${trace.usage.prompt_tokens} / 输出 ${trace.usage.completion_tokens} / 合计 ${trace.usage.total_tokens}`);\n const cached = trace.model_calls.reduce((sum, call) => sum + (call.cache_read_tokens ?? 0), 0);\n if (cached > 0 && trace.usage.prompt_tokens > 0) {\n lines.push(`${indent} 提示缓存命中 ${cached} tokens(占输入 ${Math.round((cached / trace.usage.prompt_tokens) * 100)}%)`);\n }\n for (const call of trace.model_calls) {\n const cache = call.cache_read_tokens ? ` cache=${call.cache_read_tokens}` : '';\n const reasoning = call.reasoning_tokens ? ` reasoning=${call.reasoning_tokens}` : '';\n const cacheWrite = call.cache_write_tokens !== undefined ? ` cache_write=${call.cache_write_tokens}` : '';\n const model = call.model ? ` model=${call.provider ?? '?'}/${call.model}` : '';\n lines.push(`${indent} #${call.index} in=${call.prompt_tokens} out=${call.completion_tokens}${cache}${reasoning}${cacheWrite}${model}`);\n }\n\n if (trace.context_budget) lines.push(...renderContextBudget(trace.context_budget, indent));\n\n lines.push(`${indent} 工具调用 ${trace.tool_calls.length} 次`);\n for (const tool of trace.tool_calls) {\n const details = [\n tool.duration_ms !== undefined ? `${tool.duration_ms}ms` : undefined,\n tool.result_size !== undefined ? `${tool.result_size} 字符` : undefined,\n tool.policy ? `策略 ${tool.policy}` : undefined,\n ].filter(Boolean).join(', ');\n lines.push(`${indent} ${TOOL_MARK[tool.status]} ${tool.tool_name}${details ? ` (${details})` : ''} ${tool.args_summary}`.trimEnd());\n if (tool.error) lines.push(`${indent} ${tool.error}`);\n }\n\n if (trace.budget.extensions.length > 0 || trace.budget.continuations > 0) {\n const extensions = trace.budget.extensions.map(item => `${item.from}→${item.to}`).join(', ');\n lines.push(`${indent} 预算: 延长 ${trace.budget.extensions.length} 次${extensions ? ` (${extensions})` : ''},自动续跑 ${trace.budget.continuations} 次`);\n }\n\n for (const task of trace.subagent_tasks) {\n lines.push(`${indent} 子任务 ${task.task_id} [${task.status}] ${task.description}`);\n }\n for (const child of trace.subagents) {\n lines.push(`${indent} 子 agent:`);\n lines.push(...renderTrace(child, `${indent} `, false));\n }\n\n if (withTimeline && trace.timeline.length > 0) {\n lines.push(`${indent} 时间线:`);\n for (const entry of trace.timeline) {\n lines.push(`${indent} +${formatDuration(entry.at_ms).padStart(7)} ${entry.kind.padEnd(14)} ${entry.detail}`.trimEnd());\n }\n }\n if (trace.dropped > 0) lines.push(`${indent} (有 ${trace.dropped} 条记录因超出上限被省略)`);\n return lines;\n}\n\nconst SEGMENT_LABEL: Record<ContextBudgetSegment, string> = {\n system: '系统提示',\n tool_schemas: '工具 schema',\n output_schema: '输出 schema',\n tool_index: '按需工具索引',\n skill_catalog: '技能目录',\n agent_catalog: '专家 agent 目录',\n active_skills: '已加载技能',\n memory: '记忆',\n bootstrap: 'bootstrap',\n history: '历史消息',\n user: '本轮输入',\n};\n\nconst TOP_SECTIONS = 5;\n\n/** Estimated prompt cost of the first model request, by segment. */\nfunction renderContextBudget(budget: ContextBudgetReport, indent: string): string[] {\n const parts = CONTEXT_BUDGET_SEGMENTS\n .filter(segment => budget.segments[segment] > 0)\n .map(segment => `${SEGMENT_LABEL[segment]} ${budget.segments[segment]}`);\n const lines = [\n `${indent} 上下文估算 ~${budget.total_tokens} tokens(固定开销 ~${budget.fixed_tokens};工具 ${budget.tools.loaded} 个常驻 / ${budget.tools.deferred} 个按需)`,\n ];\n if (parts.length > 0) lines.push(`${indent} ${parts.join(' · ')}`);\n const top = budget.sections.slice(0, TOP_SECTIONS).filter(section => section.tokens > 0);\n if (top.length > 0) lines.push(`${indent} 最大的提示段: ${top.map(section => `${section.id} ${section.tokens}`).join(', ')}`);\n return lines;\n}\n\nfunction formatDuration(ms: number | undefined): string {\n if (ms === undefined) return '-';\n if (ms < 1000) return `${Math.round(ms)}ms`;\n if (ms < 60_000) return `${(ms / 1000).toFixed(1)}s`;\n return `${Math.floor(ms / 60_000)}m${Math.round((ms % 60_000) / 1000)}s`;\n}\n"]}
|
|
@@ -20,6 +20,12 @@ export interface TurnTracePrincipal {
|
|
|
20
20
|
readonly trust_level?: string;
|
|
21
21
|
}
|
|
22
22
|
export interface TurnTraceModelCall {
|
|
23
|
+
readonly provider?: string;
|
|
24
|
+
readonly model?: string;
|
|
25
|
+
readonly duration_ms?: number;
|
|
26
|
+
readonly stop_reason?: string;
|
|
27
|
+
readonly cache_write_tokens?: number;
|
|
28
|
+
readonly reported?: import('@zhin.js/ai').TokenUsage['reported'];
|
|
23
29
|
readonly index: number;
|
|
24
30
|
readonly prompt_tokens: number;
|
|
25
31
|
readonly completion_tokens: number;
|
package/lib/trace/turn-trace.js
CHANGED
|
@@ -98,11 +98,17 @@ export class TurnTraceCollector {
|
|
|
98
98
|
case 'usage': {
|
|
99
99
|
const call = Object.freeze({
|
|
100
100
|
index: this.#modelCalls.length + 1,
|
|
101
|
+
...(event.provider !== undefined ? { provider: event.provider } : {}),
|
|
102
|
+
...(event.model !== undefined ? { model: event.model } : {}),
|
|
103
|
+
...(event.durationMs !== undefined ? { duration_ms: event.durationMs } : {}),
|
|
104
|
+
...(event.stopReason !== undefined ? { stop_reason: event.stopReason } : {}),
|
|
105
|
+
...(event.usage.reported ? { reported: Object.freeze({ ...event.usage.reported }) } : {}),
|
|
106
|
+
...(event.usage.cacheWriteTokens !== undefined ? { cache_write_tokens: event.usage.cacheWriteTokens } : {}),
|
|
101
107
|
prompt_tokens: event.usage.promptTokens,
|
|
102
108
|
completion_tokens: event.usage.completionTokens,
|
|
103
109
|
total_tokens: event.usage.totalTokens,
|
|
104
|
-
...(event.usage.cacheReadTokens ? { cache_read_tokens: event.usage.cacheReadTokens } : {}),
|
|
105
|
-
...(event.usage.reasoningTokens ? { reasoning_tokens: event.usage.reasoningTokens } : {}),
|
|
110
|
+
...(event.usage.cacheReadTokens !== undefined ? { cache_read_tokens: event.usage.cacheReadTokens } : {}),
|
|
111
|
+
...(event.usage.reasoningTokens !== undefined ? { reasoning_tokens: event.usage.reasoningTokens } : {}),
|
|
106
112
|
});
|
|
107
113
|
this.#modelCalls.push(call);
|
|
108
114
|
this.#addTimeline('model_call', `#${call.index} in=${call.prompt_tokens} out=${call.completion_tokens}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"turn-trace.js","sourceRoot":"","sources":["../../src/trace/turn-trace.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * TurnTrace — one readable, redacted record of what happened in a single\n * Agent turn: who asked, every model call and its tokens, every tool call\n * with an argument summary, duration and result size, approval denials,\n * budget extensions, nested sub-agent turns and how the turn ended.\n *\n * The collector only folds TurnEvents (plus a few trace notes), so it never\n * influences execution. Persisted JSON uses snake_case keys.\n */\nimport type { TurnEvent } from '../event/turn-event.js';\nimport { redactTraceValue } from '../plugin-runtime/agent-trace-runtime.js';\nimport type { ContextBudgetReport } from './context-budget.js';\n\nexport const TURN_TRACE_VERSION = 1;\n\nconst MAX_TOOL_CALLS = 200;\nconst MAX_TIMELINE_ENTRIES = 300;\nconst MAX_SUBAGENTS = 20;\nconst ARGS_SUMMARY_LENGTH = 200;\nconst TEXT_SUMMARY_LENGTH = 300;\n\nexport type TurnTraceStatus = 'running' | 'completed' | 'failed' | 'cancelled' | 'budget_exceeded' | 'unknown';\nexport type TurnTraceToolStatus = 'running' | 'ok' | 'failed' | 'denied' | 'cancelled';\n\nexport interface TurnTracePrincipal {\n readonly subject_id: string;\n readonly display_name?: string;\n readonly roles: readonly string[];\n /** Initiator trust level (#726); every tool call in this trace ran under it. */\n readonly trust_level?: string;\n}\n\nexport interface TurnTraceModelCall {\n readonly index: number;\n readonly prompt_tokens: number;\n readonly completion_tokens: number;\n readonly total_tokens: number;\n /** Prompt tokens served from the provider cache (#730). */\n readonly cache_read_tokens?: number;\n /** Completion tokens spent on reasoning (e.g. Gemini thoughtsTokenCount). */\n readonly reasoning_tokens?: number;\n}\n\nexport interface TurnTraceToolCall {\n readonly tool_use_id: string;\n readonly tool_name: string;\n readonly args_summary: string;\n readonly status: TurnTraceToolStatus;\n /** Milliseconds since the turn started. */\n readonly started_at_ms: number;\n readonly duration_ms?: number;\n /** Size of the tool output in characters (output itself is never stored). */\n readonly result_size?: number;\n readonly error?: string;\n /** Policy that denied the call, for `denied`. */\n readonly policy?: string;\n}\n\nexport interface TurnTraceBudgetExtension {\n readonly from: number;\n readonly to: number;\n readonly at_ms: number;\n}\n\nexport interface TurnTraceSubagentTask {\n readonly task_id: string;\n readonly agent_name?: string;\n readonly description: string;\n readonly status: 'running' | 'ok' | 'error';\n readonly result_summary?: string;\n}\n\nexport interface TurnTraceTimelineEntry {\n readonly at_ms: number;\n readonly kind: string;\n readonly detail: string;\n}\n\nexport interface TurnTraceTermination {\n readonly status: TurnTraceStatus;\n readonly code?: string;\n readonly reason?: string;\n}\n\nexport interface TurnTrace {\n readonly version: typeof TURN_TRACE_VERSION;\n readonly turn_id: string;\n readonly session_key: string;\n readonly principal?: TurnTracePrincipal;\n /** Epoch milliseconds. */\n readonly started_at: number;\n readonly ended_at?: number;\n readonly duration_ms?: number;\n readonly model_calls: readonly TurnTraceModelCall[];\n readonly usage: Readonly<{ prompt_tokens: number; completion_tokens: number; total_tokens: number }>;\n /** Estimated prompt cost by segment for the turn's first model request (#729). */\n readonly context_budget?: ContextBudgetReport;\n readonly tool_calls: readonly TurnTraceToolCall[];\n readonly budget: Readonly<{ extensions: readonly TurnTraceBudgetExtension[]; continuations: number }>;\n readonly subagent_tasks: readonly TurnTraceSubagentTask[];\n /** Nested sub-agent turns (`<parent>:subagent:<n>`). */\n readonly subagents: readonly TurnTrace[];\n readonly timeline: readonly TurnTraceTimelineEntry[];\n readonly termination: TurnTraceTermination;\n /** Entries dropped because a bounded list was full. */\n readonly dropped: number;\n}\n\n/** Facts that are not TurnEvents but belong in the trace. */\nexport type TurnTraceNote =\n | Readonly<{ kind: 'budget_extended'; from: number; to: number }>\n | Readonly<{ kind: 'continuation'; index: number }>\n | Readonly<{ kind: 'context_budget'; report: ContextBudgetReport }>\n /** How the turn was admitted behind other turns of its session (#725). */\n | Readonly<{ kind: 'admission'; queued_ms: number; merged_messages: number; superseded_previous: boolean }>;\n\nexport interface TurnTraceInit {\n readonly turnId: string;\n readonly sessionKey: string;\n readonly principal?: Readonly<{\n subjectId: string;\n displayName?: string;\n roles: readonly string[];\n trustLevel?: string;\n }>;\n}\n\ninterface MutableToolCall {\n tool_use_id: string;\n tool_name: string;\n args_summary: string;\n status: TurnTraceToolStatus;\n started_at_ms: number;\n duration_ms?: number;\n result_size?: number;\n error?: string;\n policy?: string;\n}\n\n/** Folds the events of one turn (and its sub-agent turns) into a TurnTrace. */\nexport class TurnTraceCollector {\n readonly turnId: string;\n readonly sessionKey: string;\n readonly #principal?: TurnTracePrincipal;\n readonly #now: () => number;\n readonly #startedAt: number;\n #endedAt: number | undefined;\n #termination: TurnTraceTermination = { status: 'running' };\n readonly #modelCalls: TurnTraceModelCall[] = [];\n readonly #toolCalls = new Map<string, MutableToolCall>();\n readonly #extensions: TurnTraceBudgetExtension[] = [];\n #continuations = 0;\n #contextBudget: ContextBudgetReport | undefined;\n readonly #subagentTasks = new Map<string, TurnTraceSubagentTask>();\n readonly #children = new Map<string, TurnTraceCollector>();\n readonly #timeline: TurnTraceTimelineEntry[] = [];\n #dropped = 0;\n\n constructor(init: TurnTraceInit, now: () => number = Date.now) {\n this.turnId = init.turnId;\n this.sessionKey = init.sessionKey;\n this.#now = now;\n this.#startedAt = now();\n if (init.principal) {\n this.#principal = Object.freeze({\n subject_id: init.principal.subjectId,\n ...(init.principal.displayName ? { display_name: init.principal.displayName } : {}),\n roles: Object.freeze([...init.principal.roles]),\n ...(init.principal.trustLevel ? { trust_level: init.principal.trustLevel } : {}),\n });\n }\n }\n\n get finished(): boolean {\n return this.#endedAt !== undefined;\n }\n\n /** Nested collector for a sub-agent turn; created on first use. */\n child(turnId: string, sessionKey = `${this.sessionKey}#subagent`): TurnTraceCollector {\n const existing = this.#children.get(turnId);\n if (existing) return existing;\n const created = new TurnTraceCollector({ turnId, sessionKey, principal: this.#principalInit() }, this.#now);\n if (this.#children.size >= MAX_SUBAGENTS) {\n this.#dropped += 1;\n return created;\n }\n this.#children.set(turnId, created);\n this.#addTimeline('subagent_turn', turnId);\n return created;\n }\n\n /** Finds this collector or a nested one by turn id. */\n find(turnId: string): TurnTraceCollector | undefined {\n if (turnId === this.turnId) return this;\n for (const child of this.#children.values()) {\n const found = child.find(turnId);\n if (found) return found;\n }\n return undefined;\n }\n\n note(note: TurnTraceNote): void {\n const at = this.#elapsed();\n if (note.kind === 'budget_extended') {\n this.#extensions.push(Object.freeze({ from: note.from, to: note.to, at_ms: at }));\n this.#addTimeline('budget_extended', `${note.from} → ${note.to} steps`);\n } else if (note.kind === 'context_budget') {\n // Keep the first request's report: it is the fixed cost of the turn.\n if (this.#contextBudget) return;\n this.#contextBudget = note.report;\n this.#addTimeline('context_budget', `~${note.report.total_tokens} tokens (fixed ~${note.report.fixed_tokens})`);\n } else if (note.kind === 'continuation') {\n this.#continuations = Math.max(this.#continuations, note.index);\n this.#addTimeline('continuation', `#${note.index}`);\n } else {\n this.#addTimeline('admission', describeAdmission(note));\n }\n }\n\n record(event: TurnEvent): void {\n switch (event.type) {\n case 'turn_start':\n case 'chunk':\n case 'thinking':\n return;\n case 'iteration_start':\n this.#addTimeline('iteration', `${event.iteration}/${event.maxIterations}`);\n return;\n case 'usage': {\n const call = Object.freeze({\n index: this.#modelCalls.length + 1,\n prompt_tokens: event.usage.promptTokens,\n completion_tokens: event.usage.completionTokens,\n total_tokens: event.usage.totalTokens,\n ...(event.usage.cacheReadTokens ? { cache_read_tokens: event.usage.cacheReadTokens } : {}),\n ...(event.usage.reasoningTokens ? { reasoning_tokens: event.usage.reasoningTokens } : {}),\n });\n this.#modelCalls.push(call);\n this.#addTimeline('model_call', `#${call.index} in=${call.prompt_tokens} out=${call.completion_tokens}`);\n return;\n }\n case 'tool_call':\n this.#startTool(event.toolUseId, event.toolName, summarizeArgs(event.args));\n this.#addTimeline('tool_call', `${event.toolName} ${summarizeArgs(event.args)}`);\n return;\n case 'tool_result': {\n const tool = this.#tool(event.toolUseId, event.toolName);\n if (!tool) return;\n tool.status = 'ok';\n tool.duration_ms = Math.round(event.durationMs);\n tool.result_size = outputSize(event.output);\n this.#addTimeline('tool_result', `${event.toolName} ok ${tool.duration_ms}ms ${tool.result_size} chars`);\n return;\n }\n case 'tool_failed': {\n const tool = this.#tool(event.toolUseId, event.toolName);\n if (!tool) return;\n tool.status = 'failed';\n tool.duration_ms = Math.round(event.durationMs);\n tool.error = summarizeText(event.error);\n this.#addTimeline('tool_failed', `${event.toolName}: ${tool.error}`);\n return;\n }\n case 'tool_denied': {\n const tool = this.#tool(event.toolUseId, event.toolName);\n if (!tool) return;\n tool.status = 'denied';\n tool.policy = event.policy;\n tool.error = summarizeText(event.reason);\n this.#addTimeline('tool_denied', `${event.toolName} (${event.policy}): ${tool.error}`);\n return;\n }\n case 'tool_cancelled': {\n const tool = this.#tool(event.toolUseId, event.toolName);\n if (!tool) return;\n tool.status = 'cancelled';\n tool.duration_ms = Math.round(event.durationMs);\n tool.error = summarizeText(event.reason);\n this.#addTimeline('tool_cancelled', `${event.toolName}: ${tool.error}`);\n return;\n }\n case 'subagent_start':\n this.#subagentTasks.set(event.taskId, Object.freeze({\n task_id: event.taskId,\n ...(event.agentName ? { agent_name: event.agentName } : {}),\n description: summarizeText(event.description),\n status: 'running',\n }));\n this.#addTimeline('subagent_start', `${event.taskId} ${summarizeText(event.description, 80)}`);\n return;\n case 'subagent_progress':\n this.#addTimeline('subagent_progress', `${event.taskId} ${summarizeText(event.summary, 80)}`);\n return;\n case 'subagent_end': {\n const task = this.#subagentTasks.get(event.taskId);\n this.#subagentTasks.set(event.taskId, Object.freeze({\n task_id: event.taskId,\n ...(task?.agent_name ? { agent_name: task.agent_name } : {}),\n description: task?.description ?? '',\n status: event.status,\n ...(event.result ? { result_summary: summarizeText(event.result) } : {}),\n }));\n this.#addTimeline('subagent_end', `${event.taskId} ${event.status}`);\n return;\n }\n case 'turn_end':\n this.#finish({ status: 'completed' });\n return;\n case 'error':\n this.#finish({\n status: 'failed',\n ...(event.code ? { code: event.code } : {}),\n reason: summarizeText(event.error?.message ?? String(event.error)),\n });\n return;\n case 'turn_cancelled':\n this.#finish({ status: 'cancelled', code: event.code, reason: summarizeText(event.reason) });\n return;\n case 'budget_exceeded':\n this.#finish({ status: 'budget_exceeded', code: event.budget });\n return;\n default:\n this.#addTimeline(event.type, summarizeText(JSON.stringify(redactTraceValue(withoutType(event))), 120));\n }\n }\n\n /** Closes the trace when the turn ended without a terminal event. */\n finalize(fallback: TurnTraceTermination = { status: 'unknown' }): void {\n for (const child of this.#children.values()) child.finalize();\n if (this.finished) return;\n this.#finish(fallback);\n }\n\n snapshot(): TurnTrace {\n const usage = this.#modelCalls.reduce(\n (total, call) => ({\n prompt_tokens: total.prompt_tokens + call.prompt_tokens,\n completion_tokens: total.completion_tokens + call.completion_tokens,\n total_tokens: total.total_tokens + call.total_tokens,\n }),\n { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },\n );\n return Object.freeze({\n version: TURN_TRACE_VERSION,\n turn_id: this.turnId,\n session_key: this.sessionKey,\n ...(this.#principal ? { principal: this.#principal } : {}),\n started_at: this.#startedAt,\n ...(this.#endedAt !== undefined\n ? { ended_at: this.#endedAt, duration_ms: this.#endedAt - this.#startedAt }\n : {}),\n model_calls: Object.freeze([...this.#modelCalls]),\n usage: Object.freeze(usage),\n ...(this.#contextBudget ? { context_budget: this.#contextBudget } : {}),\n tool_calls: Object.freeze([...this.#toolCalls.values()].map(tool => Object.freeze({ ...tool }))),\n budget: Object.freeze({\n extensions: Object.freeze([...this.#extensions]),\n continuations: this.#continuations,\n }),\n subagent_tasks: Object.freeze([...this.#subagentTasks.values()]),\n subagents: Object.freeze([...this.#children.values()].map(child => child.snapshot())),\n timeline: Object.freeze([...this.#timeline]),\n termination: Object.freeze({ ...this.#termination }),\n dropped: this.#dropped,\n });\n }\n\n #startTool(toolUseId: string, toolName: string, argsSummary: string): MutableToolCall | undefined {\n if (this.#toolCalls.size >= MAX_TOOL_CALLS && !this.#toolCalls.has(toolUseId)) {\n this.#dropped += 1;\n return undefined;\n }\n const existing = this.#toolCalls.get(toolUseId);\n if (existing) return existing;\n const created: MutableToolCall = {\n tool_use_id: toolUseId,\n tool_name: toolName,\n args_summary: argsSummary,\n status: 'running',\n started_at_ms: this.#elapsed(),\n };\n this.#toolCalls.set(toolUseId, created);\n return created;\n }\n\n /** Result/denial events may arrive without a prior tool_call (e.g. pre-call denial). */\n #tool(toolUseId: string, toolName: string): MutableToolCall | undefined {\n return this.#toolCalls.get(toolUseId) ?? this.#startTool(toolUseId, toolName, '');\n }\n\n #finish(termination: TurnTraceTermination): void {\n if (this.finished) return;\n this.#termination = Object.freeze(termination);\n this.#endedAt = this.#now();\n for (const tool of this.#toolCalls.values()) {\n if (tool.status === 'running') tool.status = 'cancelled';\n }\n this.#addTimeline('end', [termination.status, termination.code, termination.reason].filter(Boolean).join(' '));\n }\n\n #addTimeline(kind: string, detail: string): void {\n if (this.#timeline.length >= MAX_TIMELINE_ENTRIES) {\n this.#dropped += 1;\n return;\n }\n this.#timeline.push(Object.freeze({ at_ms: this.#elapsed(), kind, detail }));\n }\n\n #elapsed(): number {\n return Math.max(0, this.#now() - this.#startedAt);\n }\n\n #principalInit(): TurnTraceInit['principal'] {\n if (!this.#principal) return undefined;\n return {\n subjectId: this.#principal.subject_id,\n ...(this.#principal.display_name ? { displayName: this.#principal.display_name } : {}),\n roles: this.#principal.roles,\n ...(this.#principal.trust_level ? { trustLevel: this.#principal.trust_level } : {}),\n };\n }\n}\n\n/** Redacted, single-line, length-bounded summary of tool arguments. */\nfunction describeAdmission(note: Extract<TurnTraceNote, { kind: 'admission' }>): string {\n const parts = [`queued ${Math.round(note.queued_ms)}ms`];\n if (note.merged_messages > 1) parts.push(`merged ${note.merged_messages} messages`);\n if (note.superseded_previous) parts.push('replaced the sender\\'s unanswered turn');\n return parts.join(', ');\n}\n\nexport function summarizeArgs(args: unknown): string {\n if (args == null) return '';\n const redacted = redactTraceValue(args);\n const text = typeof redacted === 'string' ? redacted : JSON.stringify(redacted);\n return clip(scrubSecrets(text ?? ''), ARGS_SUMMARY_LENGTH);\n}\n\nfunction summarizeText(value: unknown, max = TEXT_SUMMARY_LENGTH): string {\n const redacted = redactTraceValue(value);\n const text = typeof redacted === 'string' ? redacted : JSON.stringify(redacted) ?? '';\n return clip(scrubSecrets(text.replace(/\\s+/g, ' ').trim()), max);\n}\n\nconst INLINE_SECRET_RULES: ReadonlyArray<readonly [RegExp, string]> = [\n [/\\b(bearer|basic)\\s+[A-Za-z0-9._~+/=-]{8,}/gi, '$1 [redacted]'],\n [/\\b(?:sk|pk|rk)-[A-Za-z0-9_-]{12,}/g, '[redacted]'],\n [/\\b(?:ghp|gho|ghs|github_pat|xox[abpr])[_-][A-Za-z0-9_-]{12,}/g, '[redacted]'],\n [/(\"?(?:api[_-]?key|access[_-]?token|refresh[_-]?token|password|secret)\"?\\s*[:=]\\s*\"?)[^\"\\s,}]+/gi, '$1[redacted]'],\n];\n\n/** Masks credentials that appear inline in free text (error messages, string args). */\nexport function scrubSecrets(text: string): string {\n return INLINE_SECRET_RULES.reduce((current, [pattern, replacement]) => current.replace(pattern, replacement), text);\n}\n\nfunction outputSize(output: unknown): number {\n if (output == null) return 0;\n if (typeof output === 'string') return output.length;\n try {\n return JSON.stringify(output)?.length ?? 0;\n } catch {\n return 0;\n }\n}\n\nfunction clip(text: string, max: number): string {\n return text.length <= max ? text : `${text.slice(0, max)}…`;\n}\n\nfunction withoutType(event: TurnEvent): Record<string, unknown> {\n const { type: _type, ...rest } = event as unknown as Record<string, unknown>;\n return rest;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"turn-trace.js","sourceRoot":"","sources":["../../src/trace/turn-trace.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * TurnTrace — one readable, redacted record of what happened in a single\n * Agent turn: who asked, every model call and its tokens, every tool call\n * with an argument summary, duration and result size, approval denials,\n * budget extensions, nested sub-agent turns and how the turn ended.\n *\n * The collector only folds TurnEvents (plus a few trace notes), so it never\n * influences execution. Persisted JSON uses snake_case keys.\n */\nimport type { TurnEvent } from '../event/turn-event.js';\nimport { redactTraceValue } from '../plugin-runtime/agent-trace-runtime.js';\nimport type { ContextBudgetReport } from './context-budget.js';\n\nexport const TURN_TRACE_VERSION = 1;\n\nconst MAX_TOOL_CALLS = 200;\nconst MAX_TIMELINE_ENTRIES = 300;\nconst MAX_SUBAGENTS = 20;\nconst ARGS_SUMMARY_LENGTH = 200;\nconst TEXT_SUMMARY_LENGTH = 300;\n\nexport type TurnTraceStatus = 'running' | 'completed' | 'failed' | 'cancelled' | 'budget_exceeded' | 'unknown';\nexport type TurnTraceToolStatus = 'running' | 'ok' | 'failed' | 'denied' | 'cancelled';\n\nexport interface TurnTracePrincipal {\n readonly subject_id: string;\n readonly display_name?: string;\n readonly roles: readonly string[];\n /** Initiator trust level (#726); every tool call in this trace ran under it. */\n readonly trust_level?: string;\n}\n\nexport interface TurnTraceModelCall {\n readonly provider?: string;\n readonly model?: string;\n readonly duration_ms?: number;\n readonly stop_reason?: string;\n readonly cache_write_tokens?: number;\n readonly reported?: import('@zhin.js/ai').TokenUsage['reported'];\n readonly index: number;\n readonly prompt_tokens: number;\n readonly completion_tokens: number;\n readonly total_tokens: number;\n /** Prompt tokens served from the provider cache (#730). */\n readonly cache_read_tokens?: number;\n /** Completion tokens spent on reasoning (e.g. Gemini thoughtsTokenCount). */\n readonly reasoning_tokens?: number;\n}\n\nexport interface TurnTraceToolCall {\n readonly tool_use_id: string;\n readonly tool_name: string;\n readonly args_summary: string;\n readonly status: TurnTraceToolStatus;\n /** Milliseconds since the turn started. */\n readonly started_at_ms: number;\n readonly duration_ms?: number;\n /** Size of the tool output in characters (output itself is never stored). */\n readonly result_size?: number;\n readonly error?: string;\n /** Policy that denied the call, for `denied`. */\n readonly policy?: string;\n}\n\nexport interface TurnTraceBudgetExtension {\n readonly from: number;\n readonly to: number;\n readonly at_ms: number;\n}\n\nexport interface TurnTraceSubagentTask {\n readonly task_id: string;\n readonly agent_name?: string;\n readonly description: string;\n readonly status: 'running' | 'ok' | 'error';\n readonly result_summary?: string;\n}\n\nexport interface TurnTraceTimelineEntry {\n readonly at_ms: number;\n readonly kind: string;\n readonly detail: string;\n}\n\nexport interface TurnTraceTermination {\n readonly status: TurnTraceStatus;\n readonly code?: string;\n readonly reason?: string;\n}\n\nexport interface TurnTrace {\n readonly version: typeof TURN_TRACE_VERSION;\n readonly turn_id: string;\n readonly session_key: string;\n readonly principal?: TurnTracePrincipal;\n /** Epoch milliseconds. */\n readonly started_at: number;\n readonly ended_at?: number;\n readonly duration_ms?: number;\n readonly model_calls: readonly TurnTraceModelCall[];\n readonly usage: Readonly<{ prompt_tokens: number; completion_tokens: number; total_tokens: number }>;\n /** Estimated prompt cost by segment for the turn's first model request (#729). */\n readonly context_budget?: ContextBudgetReport;\n readonly tool_calls: readonly TurnTraceToolCall[];\n readonly budget: Readonly<{ extensions: readonly TurnTraceBudgetExtension[]; continuations: number }>;\n readonly subagent_tasks: readonly TurnTraceSubagentTask[];\n /** Nested sub-agent turns (`<parent>:subagent:<n>`). */\n readonly subagents: readonly TurnTrace[];\n readonly timeline: readonly TurnTraceTimelineEntry[];\n readonly termination: TurnTraceTermination;\n /** Entries dropped because a bounded list was full. */\n readonly dropped: number;\n}\n\n/** Facts that are not TurnEvents but belong in the trace. */\nexport type TurnTraceNote =\n | Readonly<{ kind: 'budget_extended'; from: number; to: number }>\n | Readonly<{ kind: 'continuation'; index: number }>\n | Readonly<{ kind: 'context_budget'; report: ContextBudgetReport }>\n /** How the turn was admitted behind other turns of its session (#725). */\n | Readonly<{ kind: 'admission'; queued_ms: number; merged_messages: number; superseded_previous: boolean }>;\n\nexport interface TurnTraceInit {\n readonly turnId: string;\n readonly sessionKey: string;\n readonly principal?: Readonly<{\n subjectId: string;\n displayName?: string;\n roles: readonly string[];\n trustLevel?: string;\n }>;\n}\n\ninterface MutableToolCall {\n tool_use_id: string;\n tool_name: string;\n args_summary: string;\n status: TurnTraceToolStatus;\n started_at_ms: number;\n duration_ms?: number;\n result_size?: number;\n error?: string;\n policy?: string;\n}\n\n/** Folds the events of one turn (and its sub-agent turns) into a TurnTrace. */\nexport class TurnTraceCollector {\n readonly turnId: string;\n readonly sessionKey: string;\n readonly #principal?: TurnTracePrincipal;\n readonly #now: () => number;\n readonly #startedAt: number;\n #endedAt: number | undefined;\n #termination: TurnTraceTermination = { status: 'running' };\n readonly #modelCalls: TurnTraceModelCall[] = [];\n readonly #toolCalls = new Map<string, MutableToolCall>();\n readonly #extensions: TurnTraceBudgetExtension[] = [];\n #continuations = 0;\n #contextBudget: ContextBudgetReport | undefined;\n readonly #subagentTasks = new Map<string, TurnTraceSubagentTask>();\n readonly #children = new Map<string, TurnTraceCollector>();\n readonly #timeline: TurnTraceTimelineEntry[] = [];\n #dropped = 0;\n\n constructor(init: TurnTraceInit, now: () => number = Date.now) {\n this.turnId = init.turnId;\n this.sessionKey = init.sessionKey;\n this.#now = now;\n this.#startedAt = now();\n if (init.principal) {\n this.#principal = Object.freeze({\n subject_id: init.principal.subjectId,\n ...(init.principal.displayName ? { display_name: init.principal.displayName } : {}),\n roles: Object.freeze([...init.principal.roles]),\n ...(init.principal.trustLevel ? { trust_level: init.principal.trustLevel } : {}),\n });\n }\n }\n\n get finished(): boolean {\n return this.#endedAt !== undefined;\n }\n\n /** Nested collector for a sub-agent turn; created on first use. */\n child(turnId: string, sessionKey = `${this.sessionKey}#subagent`): TurnTraceCollector {\n const existing = this.#children.get(turnId);\n if (existing) return existing;\n const created = new TurnTraceCollector({ turnId, sessionKey, principal: this.#principalInit() }, this.#now);\n if (this.#children.size >= MAX_SUBAGENTS) {\n this.#dropped += 1;\n return created;\n }\n this.#children.set(turnId, created);\n this.#addTimeline('subagent_turn', turnId);\n return created;\n }\n\n /** Finds this collector or a nested one by turn id. */\n find(turnId: string): TurnTraceCollector | undefined {\n if (turnId === this.turnId) return this;\n for (const child of this.#children.values()) {\n const found = child.find(turnId);\n if (found) return found;\n }\n return undefined;\n }\n\n note(note: TurnTraceNote): void {\n const at = this.#elapsed();\n if (note.kind === 'budget_extended') {\n this.#extensions.push(Object.freeze({ from: note.from, to: note.to, at_ms: at }));\n this.#addTimeline('budget_extended', `${note.from} → ${note.to} steps`);\n } else if (note.kind === 'context_budget') {\n // Keep the first request's report: it is the fixed cost of the turn.\n if (this.#contextBudget) return;\n this.#contextBudget = note.report;\n this.#addTimeline('context_budget', `~${note.report.total_tokens} tokens (fixed ~${note.report.fixed_tokens})`);\n } else if (note.kind === 'continuation') {\n this.#continuations = Math.max(this.#continuations, note.index);\n this.#addTimeline('continuation', `#${note.index}`);\n } else {\n this.#addTimeline('admission', describeAdmission(note));\n }\n }\n\n record(event: TurnEvent): void {\n switch (event.type) {\n case 'turn_start':\n case 'chunk':\n case 'thinking':\n return;\n case 'iteration_start':\n this.#addTimeline('iteration', `${event.iteration}/${event.maxIterations}`);\n return;\n case 'usage': {\n const call = Object.freeze({\n index: this.#modelCalls.length + 1,\n ...(event.provider !== undefined ? { provider: event.provider } : {}),\n ...(event.model !== undefined ? { model: event.model } : {}),\n ...(event.durationMs !== undefined ? { duration_ms: event.durationMs } : {}),\n ...(event.stopReason !== undefined ? { stop_reason: event.stopReason } : {}),\n ...(event.usage.reported ? { reported: Object.freeze({ ...event.usage.reported }) } : {}),\n ...(event.usage.cacheWriteTokens !== undefined ? { cache_write_tokens: event.usage.cacheWriteTokens } : {}),\n prompt_tokens: event.usage.promptTokens,\n completion_tokens: event.usage.completionTokens,\n total_tokens: event.usage.totalTokens,\n ...(event.usage.cacheReadTokens !== undefined ? { cache_read_tokens: event.usage.cacheReadTokens } : {}),\n ...(event.usage.reasoningTokens !== undefined ? { reasoning_tokens: event.usage.reasoningTokens } : {}),\n });\n this.#modelCalls.push(call);\n this.#addTimeline('model_call', `#${call.index} in=${call.prompt_tokens} out=${call.completion_tokens}`);\n return;\n }\n case 'tool_call':\n this.#startTool(event.toolUseId, event.toolName, summarizeArgs(event.args));\n this.#addTimeline('tool_call', `${event.toolName} ${summarizeArgs(event.args)}`);\n return;\n case 'tool_result': {\n const tool = this.#tool(event.toolUseId, event.toolName);\n if (!tool) return;\n tool.status = 'ok';\n tool.duration_ms = Math.round(event.durationMs);\n tool.result_size = outputSize(event.output);\n this.#addTimeline('tool_result', `${event.toolName} ok ${tool.duration_ms}ms ${tool.result_size} chars`);\n return;\n }\n case 'tool_failed': {\n const tool = this.#tool(event.toolUseId, event.toolName);\n if (!tool) return;\n tool.status = 'failed';\n tool.duration_ms = Math.round(event.durationMs);\n tool.error = summarizeText(event.error);\n this.#addTimeline('tool_failed', `${event.toolName}: ${tool.error}`);\n return;\n }\n case 'tool_denied': {\n const tool = this.#tool(event.toolUseId, event.toolName);\n if (!tool) return;\n tool.status = 'denied';\n tool.policy = event.policy;\n tool.error = summarizeText(event.reason);\n this.#addTimeline('tool_denied', `${event.toolName} (${event.policy}): ${tool.error}`);\n return;\n }\n case 'tool_cancelled': {\n const tool = this.#tool(event.toolUseId, event.toolName);\n if (!tool) return;\n tool.status = 'cancelled';\n tool.duration_ms = Math.round(event.durationMs);\n tool.error = summarizeText(event.reason);\n this.#addTimeline('tool_cancelled', `${event.toolName}: ${tool.error}`);\n return;\n }\n case 'subagent_start':\n this.#subagentTasks.set(event.taskId, Object.freeze({\n task_id: event.taskId,\n ...(event.agentName ? { agent_name: event.agentName } : {}),\n description: summarizeText(event.description),\n status: 'running',\n }));\n this.#addTimeline('subagent_start', `${event.taskId} ${summarizeText(event.description, 80)}`);\n return;\n case 'subagent_progress':\n this.#addTimeline('subagent_progress', `${event.taskId} ${summarizeText(event.summary, 80)}`);\n return;\n case 'subagent_end': {\n const task = this.#subagentTasks.get(event.taskId);\n this.#subagentTasks.set(event.taskId, Object.freeze({\n task_id: event.taskId,\n ...(task?.agent_name ? { agent_name: task.agent_name } : {}),\n description: task?.description ?? '',\n status: event.status,\n ...(event.result ? { result_summary: summarizeText(event.result) } : {}),\n }));\n this.#addTimeline('subagent_end', `${event.taskId} ${event.status}`);\n return;\n }\n case 'turn_end':\n this.#finish({ status: 'completed' });\n return;\n case 'error':\n this.#finish({\n status: 'failed',\n ...(event.code ? { code: event.code } : {}),\n reason: summarizeText(event.error?.message ?? String(event.error)),\n });\n return;\n case 'turn_cancelled':\n this.#finish({ status: 'cancelled', code: event.code, reason: summarizeText(event.reason) });\n return;\n case 'budget_exceeded':\n this.#finish({ status: 'budget_exceeded', code: event.budget });\n return;\n default:\n this.#addTimeline(event.type, summarizeText(JSON.stringify(redactTraceValue(withoutType(event))), 120));\n }\n }\n\n /** Closes the trace when the turn ended without a terminal event. */\n finalize(fallback: TurnTraceTermination = { status: 'unknown' }): void {\n for (const child of this.#children.values()) child.finalize();\n if (this.finished) return;\n this.#finish(fallback);\n }\n\n snapshot(): TurnTrace {\n const usage = this.#modelCalls.reduce(\n (total, call) => ({\n prompt_tokens: total.prompt_tokens + call.prompt_tokens,\n completion_tokens: total.completion_tokens + call.completion_tokens,\n total_tokens: total.total_tokens + call.total_tokens,\n }),\n { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },\n );\n return Object.freeze({\n version: TURN_TRACE_VERSION,\n turn_id: this.turnId,\n session_key: this.sessionKey,\n ...(this.#principal ? { principal: this.#principal } : {}),\n started_at: this.#startedAt,\n ...(this.#endedAt !== undefined\n ? { ended_at: this.#endedAt, duration_ms: this.#endedAt - this.#startedAt }\n : {}),\n model_calls: Object.freeze([...this.#modelCalls]),\n usage: Object.freeze(usage),\n ...(this.#contextBudget ? { context_budget: this.#contextBudget } : {}),\n tool_calls: Object.freeze([...this.#toolCalls.values()].map(tool => Object.freeze({ ...tool }))),\n budget: Object.freeze({\n extensions: Object.freeze([...this.#extensions]),\n continuations: this.#continuations,\n }),\n subagent_tasks: Object.freeze([...this.#subagentTasks.values()]),\n subagents: Object.freeze([...this.#children.values()].map(child => child.snapshot())),\n timeline: Object.freeze([...this.#timeline]),\n termination: Object.freeze({ ...this.#termination }),\n dropped: this.#dropped,\n });\n }\n\n #startTool(toolUseId: string, toolName: string, argsSummary: string): MutableToolCall | undefined {\n if (this.#toolCalls.size >= MAX_TOOL_CALLS && !this.#toolCalls.has(toolUseId)) {\n this.#dropped += 1;\n return undefined;\n }\n const existing = this.#toolCalls.get(toolUseId);\n if (existing) return existing;\n const created: MutableToolCall = {\n tool_use_id: toolUseId,\n tool_name: toolName,\n args_summary: argsSummary,\n status: 'running',\n started_at_ms: this.#elapsed(),\n };\n this.#toolCalls.set(toolUseId, created);\n return created;\n }\n\n /** Result/denial events may arrive without a prior tool_call (e.g. pre-call denial). */\n #tool(toolUseId: string, toolName: string): MutableToolCall | undefined {\n return this.#toolCalls.get(toolUseId) ?? this.#startTool(toolUseId, toolName, '');\n }\n\n #finish(termination: TurnTraceTermination): void {\n if (this.finished) return;\n this.#termination = Object.freeze(termination);\n this.#endedAt = this.#now();\n for (const tool of this.#toolCalls.values()) {\n if (tool.status === 'running') tool.status = 'cancelled';\n }\n this.#addTimeline('end', [termination.status, termination.code, termination.reason].filter(Boolean).join(' '));\n }\n\n #addTimeline(kind: string, detail: string): void {\n if (this.#timeline.length >= MAX_TIMELINE_ENTRIES) {\n this.#dropped += 1;\n return;\n }\n this.#timeline.push(Object.freeze({ at_ms: this.#elapsed(), kind, detail }));\n }\n\n #elapsed(): number {\n return Math.max(0, this.#now() - this.#startedAt);\n }\n\n #principalInit(): TurnTraceInit['principal'] {\n if (!this.#principal) return undefined;\n return {\n subjectId: this.#principal.subject_id,\n ...(this.#principal.display_name ? { displayName: this.#principal.display_name } : {}),\n roles: this.#principal.roles,\n ...(this.#principal.trust_level ? { trustLevel: this.#principal.trust_level } : {}),\n };\n }\n}\n\n/** Redacted, single-line, length-bounded summary of tool arguments. */\nfunction describeAdmission(note: Extract<TurnTraceNote, { kind: 'admission' }>): string {\n const parts = [`queued ${Math.round(note.queued_ms)}ms`];\n if (note.merged_messages > 1) parts.push(`merged ${note.merged_messages} messages`);\n if (note.superseded_previous) parts.push('replaced the sender\\'s unanswered turn');\n return parts.join(', ');\n}\n\nexport function summarizeArgs(args: unknown): string {\n if (args == null) return '';\n const redacted = redactTraceValue(args);\n const text = typeof redacted === 'string' ? redacted : JSON.stringify(redacted);\n return clip(scrubSecrets(text ?? ''), ARGS_SUMMARY_LENGTH);\n}\n\nfunction summarizeText(value: unknown, max = TEXT_SUMMARY_LENGTH): string {\n const redacted = redactTraceValue(value);\n const text = typeof redacted === 'string' ? redacted : JSON.stringify(redacted) ?? '';\n return clip(scrubSecrets(text.replace(/\\s+/g, ' ').trim()), max);\n}\n\nconst INLINE_SECRET_RULES: ReadonlyArray<readonly [RegExp, string]> = [\n [/\\b(bearer|basic)\\s+[A-Za-z0-9._~+/=-]{8,}/gi, '$1 [redacted]'],\n [/\\b(?:sk|pk|rk)-[A-Za-z0-9_-]{12,}/g, '[redacted]'],\n [/\\b(?:ghp|gho|ghs|github_pat|xox[abpr])[_-][A-Za-z0-9_-]{12,}/g, '[redacted]'],\n [/(\"?(?:api[_-]?key|access[_-]?token|refresh[_-]?token|password|secret)\"?\\s*[:=]\\s*\"?)[^\"\\s,}]+/gi, '$1[redacted]'],\n];\n\n/** Masks credentials that appear inline in free text (error messages, string args). */\nexport function scrubSecrets(text: string): string {\n return INLINE_SECRET_RULES.reduce((current, [pattern, replacement]) => current.replace(pattern, replacement), text);\n}\n\nfunction outputSize(output: unknown): number {\n if (output == null) return 0;\n if (typeof output === 'string') return output.length;\n try {\n return JSON.stringify(output)?.length ?? 0;\n } catch {\n return 0;\n }\n}\n\nfunction clip(text: string, max: number): string {\n return text.length <= max ? text : `${text.slice(0, max)}…`;\n}\n\nfunction withoutType(event: TurnEvent): Record<string, unknown> {\n const { type: _type, ...rest } = event as unknown as Record<string, unknown>;\n return rest;\n}\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type Logger } from '@zhin.js/logger';
|
|
2
|
+
import type { TurnTrace } from './turn-trace.js';
|
|
3
|
+
export interface UsageLogContext {
|
|
4
|
+
traceId: string;
|
|
5
|
+
scene: string;
|
|
6
|
+
selectedModel?: string;
|
|
7
|
+
status?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function formatTurnUsageLogs(trace: TurnTrace, context: UsageLogContext, includeDetails?: boolean): {
|
|
10
|
+
summary: string;
|
|
11
|
+
details: string[];
|
|
12
|
+
};
|
|
13
|
+
/** Logging is a projection of the existing trace, never execution authority. */
|
|
14
|
+
export declare function logTurnUsage(logger: Pick<Logger, 'info' | 'debug' | 'isLevelEnabled'>, trace: TurnTrace, context: UsageLogContext): void;
|