From 3c62637b675c80cf32fc9410bfecd3c699035d49 Mon Sep 17 00:00:00 2001 From: _Kerman Date: Fri, 3 Jul 2026 13:51:19 +0800 Subject: [PATCH] refactor: minimize llmRequester.request --- .../agent-core-v2/docs/di-scope-domains.puml | 1 + .../agent-core-v2/docs/di-scope-domains.svg | 2 +- .../src/agent/loop/loopService.ts | 13 +-- .../src/agent/toolExecutor/toolExecutor.ts | 4 - .../agent/toolExecutor/toolExecutorService.ts | 92 +++++++++++-------- packages/agent-core-v2/test/mcp/mcp.test.ts | 3 + .../test/toolDedup/tool-dedup.test.ts | 3 + .../test/toolExecutor/tool-executor.test.ts | 28 +++--- 8 files changed, 79 insertions(+), 67 deletions(-) diff --git a/packages/agent-core-v2/docs/di-scope-domains.puml b/packages/agent-core-v2/docs/di-scope-domains.puml index b334cc869..041145e42 100644 --- a/packages/agent-core-v2/docs/di-scope-domains.puml +++ b/packages/agent-core-v2/docs/di-scope-domains.puml @@ -202,6 +202,7 @@ llmRequester --> usage #34495E llmRequester --> modelProvider #34495E llmRequester --> config #34495E toolExecutor --> toolRegistry #34495E +toolExecutor --> record #34495E toolExecutor --> telemetry #34495E toolStore --> wireRecord #34495E toolDedup --> telemetry #34495E diff --git a/packages/agent-core-v2/docs/di-scope-domains.svg b/packages/agent-core-v2/docs/di-scope-domains.svg index e09a6f5a6..f590a91a1 100644 --- a/packages/agent-core-v2/docs/di-scope-domains.svg +++ b/packages/agent-core-v2/docs/di-scope-domains.svg @@ -1 +1 @@ -App scope (process-wide)Session scope (per session)Agent scope (per agent)bootstrapAppIBootstrapServicelogAppILogServiceILogWriterServicetelemetryAppITelemetryServiceeventAppIEventServicestorageAppIStorageServiceIAppendLogStoreIAtomicDocumentStoreIAtomicTomlDocumentStorefilestoreAppIFileStoregatewayAppIRestGatewayIWSGatewaysessionLifecycleAppISessionLifecycleServicesessionIndexAppISessionIndexhostFsAppIHostFileSystemworkspaceRegistryAppIWorkspaceRegistryhostFolderBrowserAppIHostFolderBrowserkaosAppIKaosFactoryauthAppIOAuthServiceIAuthSummaryServiceproviderAppIProviderServiceflagAppIFlagServiceIFlagRegistryconfigAppIConfigRegistryIConfigServicechatProviderAppIChatProviderFactorymodelAppIModelServicemodelCatalogAppIModelCatalogServiceglobalSkillCatalogAppIGlobalSkillCatalogISkillCatalogStoresessionSessionISessionServicesessionContextSessionISessionContext (seed)sessionMetadataSessionISessionMetadatasessionActivitySessionISessionActivityagentLifecycleSessionIAgentLifecycleServiceinteractionSessionIInteractionServiceworkspaceContextSessionIWorkspaceContextsessionLogSessionISessionLogServiceILogWriterServicesessionSkillCatalogSessionISessionSkillCatalogkaosSessionIKaos (seed)agentFsSessionIAgentFileSystemIFsServiceapprovalSessionIApprovalServicequestionSessionIQuestionServiceprocessSessionIProcessRunnerIProcessterminalSessionITerminalServiceITerminalBackendmodelProviderSessionIModelProvider (seed)wireRecordAgentIAgentWireRecordService (event hub)eventSinkAgentIAgentEventSinkServiceblobStoreAgentIAgentBlobStoreServicecontextMemoryAgentIAgentContextMemoryServicecontextProjectorAgentIAgentContextProjectorServicecontextInjectorAgentIAgentContextInjectorServicecontextSizeAgentIAgentContextSizeServicesystemReminderAgentIAgentSystemReminderServicereplayBuilderAgentIAgentReplayBuilderServiceprofileAgentIAgentProfileServicepromptAgentIAgentPromptServiceturnAgentIAgentTurnServiceloopAgentIAgentLoopServicellmRequesterAgentIAgentLLMRequesterServicellmRequestLogAgentIAgentLLMRequestLogServicetoolRegistryAgentIAgentToolRegistryServicetoolExecutorAgentIAgentToolExecutorServicetoolStoreAgentIAgentToolStoreServicetoolDedupAgentIAgentToolDedupeServicepermissionGateAgentIAgentPermissionGatepermissionModeAgentIAgentPermissionModeServicepermissionPolicyAgentIAgentPermissionPolicyServicepermissionRulesAgentIAgentPermissionRulesServiceplanAgentIAgentPlanServicegoalAgentIAgentGoalServiceskillAgentIAgentSkillServicequestionToolsAgentIAgentQuestionToolsServiceuserToolAgentIAgentUserToolServicebackgroundAgentIAgentBackgroundServicecronAgentIAgentCronServiceswarmAgentIAgentSwarmServicemcpAgentIAgentMcpServicefullCompactionAgentIAgentFullCompactionServicemicroCompactionAgentIAgentMicroCompactionServiceexternalHooksAgentIAgentExternalHooksServicetodoListAgentIAgentTodoListServiceusageAgentIAgentUsageServicerpcAgentIAgentRPCServicefileToolsAgentIAgentFileToolsServiceshellToolsAgentIAgentShellToolsServiceagentToolAgentIAgentToolServicescopeContextAgentIAgentScopeContext (seed)onDidChangeConfigurationcontext.splicehooks.onSplicedcontext_size.measuredconfig.update / tools.set_active_toolshooks.onSplicedhooks.onResumeEndedtools.update_storepermission.set_modepermission.rules.add / record_approval_resultplan_mode.enter/cancel/exitgoal.create/update/clearhook.result / goal.updatedturn lifecycle hooksstep/usage hooksskill.activatetools.register_/unregister_user_toolbackground.task.started/terminatedhooks.onSplicedcron.add / delete / cursorswarm_mode.enter/exitfull_compaction.begin/cancel/completehooks.onContextOverflowmicro_compaction.apply / full_compaction.completeusage.recordhooks.onEndedScope = node color      App (process-wide)      Session (per session)      Agent (per agent)Edgessolid: DI injection (ctor @IX)dashed: event-driven (subscribe/emit)direction: consumer ---> providerNotesGenerated from `node scripts/dep-graph.mjs` output;`_base` / seed / options deps are omitted. \ No newline at end of file +App scope (process-wide)Session scope (per session)Agent scope (per agent)environmenteventSinkbootstrapAppIBootstrapServicelogAppILogServiceILogWriterServicetelemetryAppITelemetryServiceeventAppIEventServicestorageAppIStorageServiceIAppendLogStoreIAtomicDocumentStoreIAtomicTomlDocumentStorefilestoreAppIFileStoregatewayAppIRestGatewayIWSGatewaysessionLifecycleAppISessionLifecycleServicesessionIndexAppISessionIndexhostFsAppIHostFileSystemworkspaceRegistryAppIWorkspaceRegistryhostFolderBrowserAppIHostFolderBrowserhostEnvironmentAppIHostEnvironmentauthAppIOAuthServiceIAuthSummaryServiceproviderAppIProviderServiceflagAppIFlagServiceIFlagRegistryconfigAppIConfigRegistryIConfigServicepluginAppIPluginServicechatProviderAppIChatProviderFactorymodelAppIModelServicemodelCatalogAppIModelCatalogServiceglobalSkillCatalogAppIGlobalSkillCatalogISkillCatalogStoresessionContextSessionISessionContext (seed)sessionMetadataSessionISessionMetadatasessionActivitySessionISessionActivityagentLifecycleSessionIAgentLifecycleServiceinteractionSessionIInteractionServiceworkspaceContextSessionIWorkspaceContextsessionLogSessionISessionLogServiceILogWriterServicesessionSkillCatalogSessionISessionSkillCatalogexecContextSessionIExecContext (seed)agentFsSessionIAgentFileSystemIFsServiceapprovalSessionIApprovalServicequestionSessionIQuestionServiceprocessSessionIProcessRunnerIProcessterminalSessionITerminalServiceITerminalBackendmodelProviderSessionIModelProvider (seed)wireRecordAgentIAgentWireRecordService (event hub)recordAgentIAgentRecordServiceblobStoreAgentIAgentBlobStoreServicecontextMemoryAgentIAgentContextMemoryServicecontextProjectorAgentIAgentContextProjectorServicecontextInjectorAgentIAgentContextInjectorServicecontextSizeAgentIAgentContextSizeServicesystemReminderAgentIAgentSystemReminderServiceprofileAgentIAgentProfileServicepromptAgentIAgentPromptServiceturnAgentIAgentTurnServiceloopAgentIAgentLoopServicellmRequesterAgentIAgentLLMRequesterServicetoolRegistryAgentIAgentToolRegistryServicetoolExecutorAgentIAgentToolExecutorServicetoolStoreAgentIAgentToolStoreServicetoolDedupAgentIAgentToolDedupeServicepermissionGateAgentIAgentPermissionGatepermissionModeAgentIAgentPermissionModeServicepermissionPolicyAgentIAgentPermissionPolicyServicepermissionRulesAgentIAgentPermissionRulesServiceplanAgentIAgentPlanServicegoalAgentIAgentGoalServiceskillAgentIAgentSkillServicequestionToolsAgentIAgentQuestionToolsServiceuserToolAgentIAgentUserToolServicebackgroundAgentIAgentBackgroundServicecronAgentIAgentCronServiceswarmAgentIAgentSwarmServicemcpAgentIAgentMcpServicefullCompactionAgentIAgentFullCompactionServicemicroCompactionAgentIAgentMicroCompactionServiceexternalHooksAgentIAgentExternalHooksServicetodoListAgentIAgentTodoListServiceusageAgentIAgentUsageServicerpcAgentIAgentRPCServicefileToolsAgentIAgentFileToolsServiceshellToolsAgentIAgentShellToolsServiceagentToolAgentIAgentToolServicescopeContextAgentIAgentScopeContext (seed)onDidChangeConfigurationpermission.set_modetools.register_/unregister_user_toolconfig.update / tools.set_active_toolscontext.splicecron.add / delete / cursorfull_compaction.begin/cancel/completehooks.onContextOverflowpermission.rules.add / record_approval_resultskill.activategoal.create/update/clearturn lifecycle hooksstep/usage hookshook.result / goal.updatedmicro_compaction.apply / full_compaction.completeswarm_mode.enter/exithooks.onEndedbackground.task.started/terminatedhooks.onSplicedonWillExecuteTool / onDidExecuteToolapproval hooksprompt/end hooksstop hookcompaction hooksnotification hooksubagent hookstools.update_storeusage.recordhooks.onSplicedcontext_size.measuredhooks.onSplicedhooks.onResumeEndedplan_mode.enter/cancel/exitScope = node color      App (process-wide)      Session (per session)      Agent (per agent)Edgessolid: DI injection (ctor @IX)dashed: event-driven (subscribe/emit)direction: consumer ---> providerNotesGenerated from `node scripts/dep-graph.mjs` output;`_base` / seed / options deps are omitted. \ No newline at end of file diff --git a/packages/agent-core-v2/src/agent/loop/loopService.ts b/packages/agent-core-v2/src/agent/loop/loopService.ts index 550785134..f929087e6 100644 --- a/packages/agent-core-v2/src/agent/loop/loopService.ts +++ b/packages/agent-core-v2/src/agent/loop/loopService.ts @@ -1,7 +1,5 @@ import { randomUUID } from 'node:crypto'; -import type { AgentEvent } from '@moonshot-ai/protocol'; - import { InstantiationType } from '#/_base/di/extensions'; import { LifecycleScope, registerScopedService } from '#/_base/di/scope'; import { IAgentLLMRequesterService, type LLMRequestFinish } from '#/agent/llmRequester'; @@ -151,11 +149,8 @@ export class AgentLoopService implements IAgentLoopService { const stepUuid = randomUUID(); const turnStep = `${turnId}.${String(currentStep)}`; - const emit = (event: AgentEvent): void => { - this.record.signal(event); - }; - emit({ type: 'turn.step.started', turnId, step: currentStep, stepId: stepUuid }); + this.record.signal({ type: 'turn.step.started', turnId, step: currentStep, stepId: stepUuid }); const emitStreamPart = this.createStreamPartHandler(turnId); const response = await this.llmRequester.request( @@ -164,7 +159,7 @@ export class AgentLoopService implements IAgentLoopService { retry: { maxAttempts: this.config.get(LOOP_CONTROL_SECTION)?.maxRetriesPerStep, onRetry: (retry) => { - emit({ + this.record.signal({ type: 'turn.step.retrying', turnId, step: currentStep, @@ -201,7 +196,6 @@ export class AgentLoopService implements IAgentLoopService { const toolResults = await this.toolExecutor.execute(response.message.toolCalls, { signal, turnId, - dispatchProtocolEvent: emit, onToolResult: (toolCallId, result) => { this.append({ ...createToolMessage(toolCallId, toolResultOutputForModel(result)), @@ -209,9 +203,6 @@ export class AgentLoopService implements IAgentLoopService { isError: result.isError, }); }, - onProgress: (toolCallId, update) => { - emit({ type: 'tool.progress', turnId, toolCallId, update }); - }, }); if (toolResults.some((r) => r.stopTurn === true)) { finishReason = 'completed'; diff --git a/packages/agent-core-v2/src/agent/toolExecutor/toolExecutor.ts b/packages/agent-core-v2/src/agent/toolExecutor/toolExecutor.ts index fd3555ff6..3525b50f1 100644 --- a/packages/agent-core-v2/src/agent/toolExecutor/toolExecutor.ts +++ b/packages/agent-core-v2/src/agent/toolExecutor/toolExecutor.ts @@ -1,11 +1,9 @@ import { createDecorator } from '#/_base/di'; import type { ToolResult, - ToolUpdate, ToolDidExecuteContext, ToolWillExecuteContext, } from '#/agent/tool'; -import type { AgentEvent } from '@moonshot-ai/protocol'; import type { ToolCall } from '#/app/llmProtocol'; import type { OrderedHookSlot } from '#/hooks'; @@ -13,8 +11,6 @@ export interface ToolExecutorExecuteOptions { readonly signal: AbortSignal; readonly turnId: number; readonly onToolResult?: (toolCallId: string, result: ToolResult) => void | Promise; - readonly dispatchProtocolEvent?: (event: AgentEvent) => void; - readonly onProgress?: (toolCallId: string, update: ToolUpdate) => void; } export interface IAgentToolExecutorService { diff --git a/packages/agent-core-v2/src/agent/toolExecutor/toolExecutorService.ts b/packages/agent-core-v2/src/agent/toolExecutor/toolExecutorService.ts index 6a9b517aa..518878607 100644 --- a/packages/agent-core-v2/src/agent/toolExecutor/toolExecutorService.ts +++ b/packages/agent-core-v2/src/agent/toolExecutor/toolExecutorService.ts @@ -12,6 +12,7 @@ import { import { PathSecurityError } from '#/_base/tools/policies/path-access'; import { isUserCancellation } from "#/_base/utils/abort"; import { isAbortError } from '#/agent/loop/errors'; +import { IAgentRecordService } from '#/agent/record'; import { ToolAccesses, type ExecutableTool, @@ -20,6 +21,7 @@ import { type ToolDidExecuteContext, type ToolExecution, type ToolResult, + type ToolUpdate, type ToolWillExecuteContext, } from '#/agent/tool'; import { IAgentToolRegistryService } from '#/agent/toolRegistry'; @@ -58,6 +60,7 @@ export class AgentToolExecutorService implements IAgentToolExecutorService { constructor( @IAgentToolRegistryService private readonly toolRegistry: IAgentToolRegistryService, + @IAgentRecordService private readonly record: IAgentRecordService, @ITelemetryService private readonly telemetry: ITelemetryService, @ILogService private readonly log?: ILogService, ) {} @@ -107,7 +110,7 @@ export class AgentToolExecutorService implements IAgentToolExecutorService { const finalized = await this.finalizeToolResult(call, rawResult, options); results.push(finalized); - await dispatchToolResult(call, finalized, options); + await this.dispatchToolResult(call, finalized, options); this.trackToolCall(call, finalized, timedResult.durationMs, options.turnId); } @@ -145,7 +148,7 @@ export class AgentToolExecutorService implements IAgentToolExecutorService { output: string, displayFields?: ToolCallDisplayFields, ): { task: ToolExecutionTask } => { - dispatchToolCall(call, args, options, displayFields); + this.dispatchToolCall(call, args, options, displayFields); return { task: makeResolvedTask(makeErrorToolResult(call, args, output)), }; @@ -160,7 +163,7 @@ export class AgentToolExecutorService implements IAgentToolExecutorService { stopBatchAfterThis?: boolean; } => { const toolResult = this.normalizeAndMergeResult(result, call.toolName, undefined); - dispatchToolCall(call, args, options, displayFields); + this.dispatchToolCall(call, args, options, displayFields); return { task: makeResolvedTask({ toolCall: call.toolCall, @@ -223,7 +226,7 @@ export class AgentToolExecutorService implements IAgentToolExecutorService { const executionMetadata = decision?.executionMetadata; - dispatchToolCall(call, call.args, options, displayFields); + this.dispatchToolCall(call, call.args, options, displayFields); return { task: { @@ -240,7 +243,7 @@ export class AgentToolExecutorService implements IAgentToolExecutorService { options: ToolExecutorExecuteOptions, ): ToolExecutionTask { const output = 'Tool skipped because a previous tool call stopped the turn.'; - dispatchToolCall(call, call.args, options); + this.dispatchToolCall(call, call.args, options); return makeResolvedTask(makeErrorToolResult(call, call.args, output)); } @@ -295,7 +298,7 @@ export class AgentToolExecutorService implements IAgentToolExecutorService { signal, onUpdate: (update) => { if (signal.aborted) return; - options.onProgress?.(call.toolCall.id, update); + this.dispatchToolProgress(call, update, options); }, }); rawResult = await raceWithGraceTimeout(executePromise, signal, call.toolName); @@ -326,6 +329,51 @@ export class AgentToolExecutorService implements IAgentToolExecutorService { }; } + private dispatchToolCall( + call: PreflightedToolCall, + args: unknown, + options: ToolExecutorExecuteOptions, + displayFields?: ToolCallDisplayFields, + ): void { + this.record.signal({ + type: 'tool.call.started', + turnId: options.turnId, + toolCallId: call.toolCall.id, + name: call.toolName, + args, + description: displayFields?.description, + display: displayFields?.display, + }); + } + + private async dispatchToolResult( + call: PreflightedToolCall, + result: ToolResult, + options: ToolExecutorExecuteOptions, + ): Promise { + await options.onToolResult?.(call.toolCall.id, result); + this.record.signal({ + type: 'tool.result', + turnId: options.turnId, + toolCallId: call.toolCall.id, + output: result.output, + isError: result.isError, + }); + } + + private dispatchToolProgress( + call: RunnableToolCall, + update: ToolUpdate, + options: ToolExecutorExecuteOptions, + ): void { + this.record.signal({ + type: 'tool.progress', + turnId: options.turnId, + toolCallId: call.toolCall.id, + update, + }); + } + private async finalizeToolResult( call: PreflightedToolCall, result: ToolResult, @@ -504,38 +552,6 @@ function toolCallDisplayFieldsFromExecution( }; } -function dispatchToolCall( - call: PreflightedToolCall, - args: unknown, - options: ToolExecutorExecuteOptions, - displayFields?: ToolCallDisplayFields, -): void { - options.dispatchProtocolEvent?.({ - type: 'tool.call.started', - turnId: options.turnId, - toolCallId: call.toolCall.id, - name: call.toolName, - args, - description: displayFields?.description, - display: displayFields?.display, - }); -} - -async function dispatchToolResult( - call: PreflightedToolCall, - result: ToolResult, - options: ToolExecutorExecuteOptions, -): Promise { - await options.onToolResult?.(call.toolCall.id, result); - options.dispatchProtocolEvent?.({ - type: 'tool.result', - turnId: options.turnId, - toolCallId: call.toolCall.id, - output: result.output, - isError: result.isError, - }); -} - function makeResolvedTask(result: PreparedToolResult): ToolExecutionTask { return { accesses: ToolAccesses.none(), diff --git a/packages/agent-core-v2/test/mcp/mcp.test.ts b/packages/agent-core-v2/test/mcp/mcp.test.ts index 76efd92f8..7e638fa4e 100644 --- a/packages/agent-core-v2/test/mcp/mcp.test.ts +++ b/packages/agent-core-v2/test/mcp/mcp.test.ts @@ -16,8 +16,10 @@ import { IAgentToolRegistryService } from '#/agent/toolRegistry'; import { AgentToolRegistryService } from '#/agent/toolRegistry/toolRegistryService'; import { IAgentTurnService } from '#/agent/turn'; import { IAgentProfileService } from '#/agent/profile'; +import { IAgentRecordService } from '#/agent/record'; import { createTestAgent, mcpServices, type TestAgentContext } from '../harness'; +import { stubRecord } from '../contextMemory/stubs'; import { stubTurnWithHooks } from '../turn/stubs'; import { discoverTools, executeTool, fakeMcpClient } from './stubs'; @@ -139,6 +141,7 @@ describe('AgentMcpService', () => { on: () => toDisposable(() => {}), }); ix.set(IAgentToolRegistryService, new SyncDescriptor(AgentToolRegistryService)); + ix.stub(IAgentRecordService, stubRecord()); ix.set(IAgentToolExecutorService, new SyncDescriptor(AgentToolExecutorService)); ix.stub(IAgentTurnService, stubTurnWithHooks()); }); diff --git a/packages/agent-core-v2/test/toolDedup/tool-dedup.test.ts b/packages/agent-core-v2/test/toolDedup/tool-dedup.test.ts index 0eb54ba0b..ee8a938f1 100644 --- a/packages/agent-core-v2/test/toolDedup/tool-dedup.test.ts +++ b/packages/agent-core-v2/test/toolDedup/tool-dedup.test.ts @@ -5,6 +5,7 @@ import { createServices } from '#/_base/di/test'; import { ITelemetryService } from '#/app/telemetry'; import type { ToolCall } from '#/app/llmProtocol'; import { IAgentLoopService } from '#/agent/loop'; +import { IAgentRecordService } from '#/agent/record'; import type { ExecutableTool, ExecutableToolContext, ToolExecution } from '#/agent/tool'; import { IAgentToolDedupeService, @@ -15,6 +16,7 @@ import type { ToolDedupResult } from '#/agent/toolDedupe'; import { AgentToolExecutorService, IAgentToolExecutorService } from '#/agent/toolExecutor'; import { AgentToolRegistryService, IAgentToolRegistryService } from '#/agent/toolRegistry'; import { IAgentTurnService } from '#/agent/turn'; +import { stubRecord } from '../contextMemory/stubs'; import { registerLogServices } from '../log/stubs'; import { recordingTelemetry, type TelemetryRecord } from '../telemetry/stubs'; import { stubLoopWithHooks, stubToolExecutor, stubTurnWithHooks } from '../turn/stubs'; @@ -159,6 +161,7 @@ describe('AgentToolDedupeService', () => { additionalServices: (reg) => { reg.defineInstance(ITelemetryService, recordingTelemetry(telemetryEvents)); reg.defineInstance(IAgentLoopService, loop); + reg.defineInstance(IAgentRecordService, stubRecord()); reg.defineInstance(IAgentTurnService, stubTurnWithHooks()); reg.define(IAgentToolRegistryService, AgentToolRegistryService); reg.define(IAgentToolExecutorService, AgentToolExecutorService); diff --git a/packages/agent-core-v2/test/toolExecutor/tool-executor.test.ts b/packages/agent-core-v2/test/toolExecutor/tool-executor.test.ts index e52e57274..8e308a1e3 100644 --- a/packages/agent-core-v2/test/toolExecutor/tool-executor.test.ts +++ b/packages/agent-core-v2/test/toolExecutor/tool-executor.test.ts @@ -2,18 +2,21 @@ import type { ToolCall } from '#/app/llmProtocol/kosong'; import type { AgentEvent, ToolInputDisplay } from '@moonshot-ai/protocol'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import { SyncDescriptor } from '#/_base/di/descriptors'; import { DisposableStore } from '#/_base/di/lifecycle'; import { createServices, type TestInstantiationService } from '#/_base/di/test'; +import { AgentRecordService, IAgentRecordService } from '#/agent/record'; import { ToolAccesses, type ExecutableTool, type ExecutableToolContext, type ExecutableToolResult, type ToolExecution, type ToolResult, type ToolUpdate } from '#/agent/tool'; import { IAgentToolExecutorService, AgentToolExecutorService, parseToolCallArguments } from '#/agent/toolExecutor'; import { IAgentToolRegistryService, AgentToolRegistryService } from '#/agent/toolRegistry'; +import { IAgentWireRecordService } from '#/agent/wireRecord'; import { ITelemetryService } from '#/app/telemetry'; +import { stubWireRecord } from '../contextMemory/stubs'; import { registerLogServices } from '../log/stubs'; import { recordingTelemetry, type TelemetryRecord } from '../telemetry/stubs'; type ToolExecutorEvent = - | { readonly type: 'tool.result'; readonly toolCallId: string; readonly result: ToolResult } - | { readonly type: 'tool.progress'; readonly toolCallId: string; readonly update: ToolUpdate }; + | { readonly type: 'tool.result'; readonly toolCallId: string; readonly result: ToolResult }; let disposables: DisposableStore; let ix: TestInstantiationService; @@ -32,11 +35,16 @@ beforeEach(() => { additionalServices: (reg) => { reg.define(IAgentToolRegistryService, AgentToolRegistryService); reg.define(IAgentToolExecutorService, AgentToolExecutorService); + reg.defineInstance(IAgentWireRecordService, stubWireRecord()); reg.defineInstance(ITelemetryService, recordingTelemetry(telemetryEvents)); registerLogServices(reg); }, strict: true, }); + ix.set(IAgentRecordService, new SyncDescriptor(AgentRecordService, [{}])); + ix.get(IAgentRecordService).on((event) => { + protocolEvents.push(event); + }); executor = ix.get(IAgentToolExecutorService); registry = ix.get(IAgentToolRegistryService); }); @@ -359,9 +367,9 @@ describe('AgentToolExecutorService', () => { await execute([toolCall('call_progress', 'progress', {})]); - expect(events.filter((event) => event.type === 'tool.progress')).toEqual([ - { type: 'tool.progress', toolCallId: 'call_progress', update: updates[0] }, - { type: 'tool.progress', toolCallId: 'call_progress', update: updates[1] }, + expect(protocolEvents.filter((event) => event.type === 'tool.progress')).toEqual([ + { type: 'tool.progress', turnId: 0, toolCallId: 'call_progress', update: updates[0] }, + { type: 'tool.progress', turnId: 0, toolCallId: 'call_progress', update: updates[1] }, ]); }); @@ -534,12 +542,6 @@ function execute(calls: ToolCall[], signal?: AbortSignal): Promise onToolResult: (toolCallId, result) => { events.push({ type: 'tool.result', toolCallId, result }); }, - dispatchProtocolEvent: (event) => { - protocolEvents.push(event); - }, - onProgress: (toolCallId, update) => { - events.push({ type: 'tool.progress', toolCallId, update }); - }, }); } @@ -568,9 +570,9 @@ function pairedToolCallIds(): { readonly calls: string[]; readonly results: stri event.type === 'tool.call.started', ) .map((event) => event.toolCallId), - results: events + results: protocolEvents .filter( - (event): event is Extract => + (event): event is Extract => event.type === 'tool.result', ) .map((event) => event.toolCallId),