From b6c9413baa250fa4f360ec2ef8f08b5b3ab888e4 Mon Sep 17 00:00:00 2001 From: _Kerman Date: Tue, 7 Jul 2026 14:16:59 +0800 Subject: [PATCH] refactor(agent-core-v2): rename tool dedupe domain --- .../agent-core-v2/docs/di-scope-domains.puml | 8 ++-- .../agent-core-v2/docs/di-scope-domains.svg | 2 +- .../agent-core-v2/src/agent/tool/toolHooks.ts | 2 +- .../src/agent/toolDedupe/index.ts | 2 +- .../src/agent/toolDedupe/toolDedupe.ts | 14 +++---- .../src/agent/toolDedupe/toolDedupeService.ts | 34 ++++++++--------- .../tool-dedupe.test.ts} | 38 +++++++++---------- packages/agent-core-v2/test/turn/turn.test.ts | 6 +-- 8 files changed, 53 insertions(+), 53 deletions(-) rename packages/agent-core-v2/test/{toolDedup/tool-dedup.test.ts => toolDedupe/tool-dedupe.test.ts} (96%) diff --git a/packages/agent-core-v2/docs/di-scope-domains.puml b/packages/agent-core-v2/docs/di-scope-domains.puml index 6839add75..8724a3df3 100644 --- a/packages/agent-core-v2/docs/di-scope-domains.puml +++ b/packages/agent-core-v2/docs/di-scope-domains.puml @@ -84,7 +84,7 @@ package "Agent scope (per agent)" #FDF5E6 { rectangle "toolRegistry\nAgent\n IAgentToolRegistryService" as toolRegistry #FDEBD0 rectangle "toolExecutor\nAgent\n IAgentToolExecutorService" as toolExecutor #FDEBD0 rectangle "toolState\nAgent\n IAgentToolState" as toolState #FDEBD0 - rectangle "toolDedup\nAgent\n IAgentToolDedupeService" as toolDedup #FDEBD0 + rectangle "toolDedupe\nAgent\n IAgentToolDedupeService" as toolDedupe #FDEBD0 rectangle "permissionGate\nAgent\n IAgentPermissionGate" as permissionGate #FDEBD0 rectangle "permissionMode\nAgent\n IAgentPermissionModeService" as permissionMode #FDEBD0 rectangle "permissionPolicy\nAgent\n IAgentPermissionPolicyService" as permissionPolicy #FDEBD0 @@ -215,9 +215,9 @@ toolExecutor --> toolRegistry #34495E toolExecutor --> wire #34495E toolExecutor --> telemetry #34495E toolState --> wire #34495E -toolDedup --> telemetry #34495E -toolDedup --> loop #34495E -toolDedup --> toolExecutor #34495E +toolDedupe --> telemetry #34495E +toolDedupe --> loop #34495E +toolDedupe --> toolExecutor #34495E permissionGate --> permissionMode #34495E permissionGate --> permissionRules #34495E permissionGate --> permissionPolicy #34495E diff --git a/packages/agent-core-v2/docs/di-scope-domains.svg b/packages/agent-core-v2/docs/di-scope-domains.svg index a2b79101b..12d7edce1 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)bootstrapAppIBootstrapServicelog_base · App bindingILogServicetelemetryAppITelemetryServiceeventAppIEventServicestorageAppIFileSystemStorageServiceIAppendLogStoreIAtomicDocumentStoreIAtomicTomlDocumentStorefilestoreAppIFileStoregatewayAppIRestGatewayIWSGatewaysessionLifecycleAppISessionLifecycleServicesessionExportAppISessionExportServicesessionIndexAppISessionIndexhostFsAppIHostFileSystemworkspaceRegistryAppIWorkspaceRegistryhostFolderBrowserAppIHostFolderBrowserhostEnvironmentAppIHostEnvironmenthostProcessAppIHostProcessServiceauthAppIOAuthServiceIAuthSummaryServiceIWebSearchProviderServicewebAppIWebFetchServiceeditAppIFileEditServiceproviderAppIProviderServiceflagAppIFlagServiceIFlagRegistryconfigAppIConfigRegistryIConfigServicepluginAppIPluginServicechatProviderAppIChatProviderFactorymodelAppIModelServicemodelCatalogAppIModelCatalogServiceskillCatalogAppISkillDiscoveryISkillSourceBuiltin/UserSkillSourcesessionContextSessionISessionContext (seed)sessionMetadataSessionISessionMetadatasessionActivitySessionISessionActivityagentLifecycleSessionIAgentLifecycleServiceinteractionSessionIInteractionServiceworkspaceContextSessionIWorkspaceContextworkspaceCommandSessionISessionWorkspaceCommandServicesessionLogSession bindingILogServicesessionSkillCatalogSessionISessionSkillCatalogISkillCatalogSinkWorkspace/PluginSkillSourcesessionFsSessionISessionFsServiceapprovalSessionIApprovalServicequestionSessionIQuestionServiceprocessSessionIProcessRunnerIProcessterminalAppIHostTerminalServicesessionTerminalSessionISessionTerminalServicemodelProviderSessionIModelProvider (seed)todoSessionISessionTodoServicewireRecordAgentIAgentWireRecordService (event hub)wireAgentIAgentWireServiceblobStoreAgentIAgentBlobStoreServicecontextMemoryAgentIAgentContextMemoryServicecontextProjectorAgentIAgentContextProjectorServicecontextInjectorAgentIAgentContextInjectorServicecontextSizeAgentIAgentContextSizeServicesystemReminderAgentIAgentSystemReminderServiceprofileAgentIAgentProfileServicepromptAgentIAgentPromptServiceturnAgentIAgentTurnServiceloopAgentIAgentLoopServicellmRequesterAgentIAgentLLMRequesterServicetoolRegistryAgentIAgentToolRegistryServicetoolExecutorAgentIAgentToolExecutorServicetoolStateAgentIAgentToolStatetoolDedupAgentIAgentToolDedupeServicepermissionGateAgentIAgentPermissionGatepermissionModeAgentIAgentPermissionModeServicepermissionPolicyAgentIAgentPermissionPolicyServicepermissionRulesAgentIAgentPermissionRulesServiceplanAgentIAgentPlanServicegoalAgentIAgentGoalServiceskillAgentIAgentSkillServicequestionToolsAgentIAgentQuestionToolsServiceuserToolAgentIAgentUserToolServicetaskAgentIAgentTaskServicecronAgentIAgentCronServiceswarmAgentIAgentSwarmServicemcpAgentIAgentMcpServicefullCompactionAgentIAgentFullCompactionServicemicroCompactionAgentIAgentMicroCompactionServiceexternalHooksAgentIAgentExternalHooksServiceusageAgentIAgentUsageServicerpcAgentIAgentRPCServicefileToolsAgentRead/Write/Grep/Glob toolseditAgentEditToolshellToolsAgentIAgentShellToolsServicescopeContextAgentIAgentScopeContext (seed)environmentrecordeventSinkonDidChangeConfigurationcontext_size.measuredconfig.update / tools.set_active_toolsagent.status.updated / warningturn.launchhooks.onSplicedtools.update_storepermission.set_modepermission.rules.add / record_approval_resultgoal.create/update/clearturn lifecycle hooksstep/usage hooksskill.activatetools.register_/unregister_user_toolhooks.onSplicedcron.add / delete / cursorswarm_mode.enter/exithooks.onErrormicro_compaction.applyusage.recordcontext.splicetodo.set (main agent)full_compaction.begin/cancel/completehook.result / goal.updatedhooks.onEndedtask.started/terminatedonWillExecuteTool / onDidExecuteToolapproval hooksprompt/end hooksstop hookcompaction hooksnotification hookSubagentStart/Stop (mirrorAgentRun)hooks.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 +App scope (process-wide)Session scope (per session)Agent scope (per agent)bootstrapAppIBootstrapServicelog_base · App bindingILogServicetelemetryAppITelemetryServiceeventAppIEventServicestorageAppIFileSystemStorageServiceIAppendLogStoreIAtomicDocumentStoreIAtomicTomlDocumentStorefilestoreAppIFileStoregatewayAppIRestGatewayIWSGatewaysessionLifecycleAppISessionLifecycleServicesessionExportAppISessionExportServicesessionIndexAppISessionIndexhostFsAppIHostFileSystemworkspaceRegistryAppIWorkspaceRegistryhostFolderBrowserAppIHostFolderBrowserhostEnvironmentAppIHostEnvironmenthostProcessAppIHostProcessServiceauthAppIOAuthServiceIAuthSummaryServiceIWebSearchProviderServicewebAppIWebFetchServiceeditAppIFileEditServiceproviderAppIProviderServiceflagAppIFlagServiceIFlagRegistryconfigAppIConfigRegistryIConfigServicepluginAppIPluginServicechatProviderAppIChatProviderFactorymodelAppIModelServicemodelCatalogAppIModelCatalogServiceskillCatalogAppISkillDiscoveryISkillSourceBuiltin/UserSkillSourcesessionContextSessionISessionContext (seed)sessionMetadataSessionISessionMetadatasessionActivitySessionISessionActivityagentLifecycleSessionIAgentLifecycleServiceinteractionSessionIInteractionServiceworkspaceContextSessionIWorkspaceContextworkspaceCommandSessionISessionWorkspaceCommandServicesessionLogSession bindingILogServicesessionSkillCatalogSessionISessionSkillCatalogISkillCatalogSinkWorkspace/PluginSkillSourcesessionFsSessionISessionFsServiceapprovalSessionIApprovalServicequestionSessionIQuestionServiceprocessSessionIProcessRunnerIProcessterminalAppIHostTerminalServicesessionTerminalSessionISessionTerminalServicemodelProviderSessionIModelProvider (seed)todoSessionISessionTodoServicewireRecordAgentIAgentWireRecordService (event hub)wireAgentIAgentWireServiceblobStoreAgentIAgentBlobStoreServicecontextMemoryAgentIAgentContextMemoryServicecontextProjectorAgentIAgentContextProjectorServicecontextInjectorAgentIAgentContextInjectorServicecontextSizeAgentIAgentContextSizeServicesystemReminderAgentIAgentSystemReminderServiceprofileAgentIAgentProfileServicepromptAgentIAgentPromptServiceturnAgentIAgentTurnServiceloopAgentIAgentLoopServicellmRequesterAgentIAgentLLMRequesterServicetoolRegistryAgentIAgentToolRegistryServicetoolExecutorAgentIAgentToolExecutorServicetoolStateAgentIAgentToolStatetoolDedupeAgentIAgentToolDedupeServicepermissionGateAgentIAgentPermissionGatepermissionModeAgentIAgentPermissionModeServicepermissionPolicyAgentIAgentPermissionPolicyServicepermissionRulesAgentIAgentPermissionRulesServiceplanAgentIAgentPlanServicegoalAgentIAgentGoalServiceskillAgentIAgentSkillServicequestionToolsAgentIAgentQuestionToolsServiceuserToolAgentIAgentUserToolServicetaskAgentIAgentTaskServicecronAgentIAgentCronServiceswarmAgentIAgentSwarmServicemcpAgentIAgentMcpServicefullCompactionAgentIAgentFullCompactionServicemicroCompactionAgentIAgentMicroCompactionServiceexternalHooksAgentIAgentExternalHooksServiceusageAgentIAgentUsageServicerpcAgentIAgentRPCServicefileToolsAgentRead/Write/Grep/Glob toolseditAgentEditToolshellToolsAgentIAgentShellToolsServicescopeContextAgentIAgentScopeContext (seed)environmentrecordeventSinkonDidChangeConfigurationcontext_size.measuredconfig.update / tools.set_active_toolsagent.status.updated / warningturn.launchhooks.onSplicedtools.update_storepermission.set_modepermission.rules.add / record_approval_resultgoal.create/update/clearturn lifecycle hooksstep/usage hooksskill.activatetools.register_/unregister_user_toolhooks.onSplicedcron.add / delete / cursorswarm_mode.enter/exithooks.onErrormicro_compaction.applyusage.recordcontext.splicetodo.set (main agent)full_compaction.begin/cancel/completehook.result / goal.updatedhooks.onEndedtask.started/terminatedonWillExecuteTool / onDidExecuteToolapproval hooksprompt/end hooksstop hookcompaction hooksnotification hookSubagentStart/Stop (mirrorAgentRun)hooks.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/tool/toolHooks.ts b/packages/agent-core-v2/src/agent/tool/toolHooks.ts index af3c7c841..1b5e0c947 100644 --- a/packages/agent-core-v2/src/agent/tool/toolHooks.ts +++ b/packages/agent-core-v2/src/agent/tool/toolHooks.ts @@ -5,7 +5,7 @@ * `onWillExecuteTool` / `onDidExecuteTool` hooks and the decision results * handlers may return. Owned by `tool` because they describe tool execution, * not the turn lifecycle or the loop: participants such as `permission`, - * `toolDedup`, and `externalHooks` consume them without reaching upward into + * `toolDedupe`, and `externalHooks` consume them without reaching upward into * `loop` / `turn`. Pure contract (types only); no scoped service. */ diff --git a/packages/agent-core-v2/src/agent/toolDedupe/index.ts b/packages/agent-core-v2/src/agent/toolDedupe/index.ts index 980c49b95..eb7a00d1a 100644 --- a/packages/agent-core-v2/src/agent/toolDedupe/index.ts +++ b/packages/agent-core-v2/src/agent/toolDedupe/index.ts @@ -1,5 +1,5 @@ /** - * `toolDedup` domain barrel — re-exports the tool-call deduplication + * `toolDedupe` domain barrel — re-exports the tool-call deduplication * contract (`toolDedupe`) and its scoped service (`toolDedupeService`). Importing * this barrel registers the `IAgentToolDedupeService` binding into the scope registry. */ diff --git a/packages/agent-core-v2/src/agent/toolDedupe/toolDedupe.ts b/packages/agent-core-v2/src/agent/toolDedupe/toolDedupe.ts index 09706c1bd..f42433b66 100644 --- a/packages/agent-core-v2/src/agent/toolDedupe/toolDedupe.ts +++ b/packages/agent-core-v2/src/agent/toolDedupe/toolDedupe.ts @@ -1,5 +1,5 @@ /** - * `toolDedup` domain (L4) — per-turn tool-call deduplication. + * `toolDedupe` domain (L4) — per-turn tool-call deduplication. * * A self-wiring plugin: it participates in `turn` step boundaries and * `IAgentToolExecutorService`'s will/did hooks to suppress same-step duplicates and inject @@ -12,25 +12,25 @@ import type { ContentPart } from '#/app/llmProtocol/message'; import { createDecorator, type ServiceIdentifier } from '#/_base/di/instantiation'; -export type ToolDedupOutput = string | ContentPart[]; +export type ToolDedupeOutput = string | ContentPart[]; -export interface ToolDedupSuccessResult { - readonly output: ToolDedupOutput; +export interface ToolDedupeSuccessResult { + readonly output: ToolDedupeOutput; readonly isError?: false | undefined; readonly stopTurn?: boolean | undefined; readonly message?: string | undefined; readonly truncated?: boolean | undefined; } -export interface ToolDedupErrorResult { - readonly output: ToolDedupOutput; +export interface ToolDedupeErrorResult { + readonly output: ToolDedupeOutput; readonly isError: true; readonly stopTurn?: boolean | undefined; readonly message?: string | undefined; readonly truncated?: boolean | undefined; } -export type ToolDedupResult = ToolDedupSuccessResult | ToolDedupErrorResult; +export type ToolDedupeResult = ToolDedupeSuccessResult | ToolDedupeErrorResult; export interface IAgentToolDedupeService { readonly _serviceBrand: undefined; diff --git a/packages/agent-core-v2/src/agent/toolDedupe/toolDedupeService.ts b/packages/agent-core-v2/src/agent/toolDedupe/toolDedupeService.ts index 1a2f819fb..791b30587 100644 --- a/packages/agent-core-v2/src/agent/toolDedupe/toolDedupeService.ts +++ b/packages/agent-core-v2/src/agent/toolDedupe/toolDedupeService.ts @@ -1,5 +1,5 @@ /** - * `toolDedup` domain (L4) — `IAgentToolDedupeService` implementation. + * `toolDedupe` domain (L4) — `IAgentToolDedupeService` implementation. * * Self-wiring plugin: its constructor registers `loop` beforeStep/afterStep * hooks and `toolExecutor` onWillExecuteTool/onDidExecuteTool hooks to drive @@ -18,7 +18,7 @@ import { ITelemetryService } from '#/app/telemetry/telemetry'; import { IAgentLoopService } from '#/agent/loop'; import { IAgentToolExecutorService } from '#/agent/toolExecutor'; import type { ContentPart } from '#/app/llmProtocol/message'; -import { IAgentToolDedupeService, type ToolDedupResult } from './toolDedupe'; +import { IAgentToolDedupeService, type ToolDedupeResult } from './toolDedupe'; const REMINDER_TEXT_1 = '\n\n\n' + @@ -77,12 +77,12 @@ function argsHash(args: unknown): string { } interface CheckedToolCall { - readonly syntheticResult: ToolDedupResult | null; + readonly syntheticResult: ToolDedupeResult | null; } type ToolCallDupType = 'same_step' | 'cross_step'; -function appendReminder(result: ToolDedupResult, reminderText: string): ToolDedupResult { +function appendReminder(result: ToolDedupeResult, reminderText: string): ToolDedupeResult { const output = result.output; let newOutput: string | ContentPart[]; if (typeof output === 'string') { @@ -102,16 +102,16 @@ function appendReminder(result: ToolDedupResult, reminderText: string): ToolDedu : { ...result, output: newOutput }; } -function forceStopResult(result: ToolDedupResult, reminderText: string): ToolDedupResult { +function forceStopResult(result: ToolDedupeResult, reminderText: string): ToolDedupeResult { const withReminder = appendReminder(result, reminderText); return { ...withReminder, stopTurn: true }; } -const DEDUP_PLACEHOLDER_RESULT: ToolDedupResult = { output: '' }; +const DEDUPE_PLACEHOLDER_RESULT: ToolDedupeResult = { output: '' }; export class AgentToolDedupeService extends Disposable implements IAgentToolDedupeService { declare readonly _serviceBrand: undefined; - private readonly stepDeferreds = new Map>(); + private readonly stepDeferreds = new Map>(); private stepCalls: string[] = []; private readonly originalCallIndex = new Map(); private readonly syntheticCallIds = new Set(); @@ -127,15 +127,15 @@ export class AgentToolDedupeService extends Disposable implements IAgentToolDedu @IAgentToolExecutorService toolExecutor: IAgentToolExecutorService, ) { super(); - loop.hooks.beforeStep.register('toolDedup', async (ctx, next) => { + loop.hooks.beforeStep.register('toolDedupe', async (ctx, next) => { this.beginStep(ctx.turnId, ctx.step); await next(); }); - loop.hooks.afterStep.register('toolDedup', async (_ctx, next) => { + loop.hooks.afterStep.register('toolDedupe', async (_ctx, next) => { this.endStep(); await next(); }); - toolExecutor.hooks.onWillExecuteTool.register('toolDedup', async (ctx, next) => { + toolExecutor.hooks.onWillExecuteTool.register('toolDedupe', async (ctx, next) => { const checked = this.checkToolCall(ctx.toolCall.id, ctx.toolCall.name, ctx.args); if (checked.syntheticResult !== null) { ctx.decision = { syntheticResult: checked.syntheticResult }; @@ -143,7 +143,7 @@ export class AgentToolDedupeService extends Disposable implements IAgentToolDedu } await next(); }); - toolExecutor.hooks.onDidExecuteTool.register('toolDedup', async (ctx, next) => { + toolExecutor.hooks.onDidExecuteTool.register('toolDedupe', async (ctx, next) => { ctx.result = await this.finalizeResult( ctx.toolCall.id, ctx.toolCall.name, @@ -201,9 +201,9 @@ export class AgentToolDedupeService extends Disposable implements IAgentToolDedu if (existing !== undefined) { this.syntheticCallIds.add(toolCallId); this.recordDupType(toolCallId, toolName, args, 'same_step'); - return { syntheticResult: DEDUP_PLACEHOLDER_RESULT }; + return { syntheticResult: DEDUPE_PLACEHOLDER_RESULT }; } - this.stepDeferreds.set(key, makeDeferred()); + this.stepDeferreds.set(key, makeDeferred()); this.originalCallIndex.set(toolCallId, index); if (this.consecutiveKey === key && this.consecutiveCount > 0) { this.recordDupType(toolCallId, toolName, args, 'cross_step'); @@ -218,7 +218,7 @@ export class AgentToolDedupeService extends Disposable implements IAgentToolDedu args: unknown, dupType: ToolCallDupType, ): void { - this.telemetry.track('tool_call_dedup_detected', { + this.telemetry.track('tool_call_dedupe_detected', { turn_id: this.activeTurnId ?? 0, step_no: this.activeStep, tool_call_id: toolCallId, @@ -232,8 +232,8 @@ export class AgentToolDedupeService extends Disposable implements IAgentToolDedu toolCallId: string, toolName: string, args: unknown, - result: ToolDedupResult, - ): Promise { + result: ToolDedupeResult, + ): Promise { const key = this.callKeyByCallId.get(toolCallId); if (key === undefined) return result; this.callKeyByCallId.delete(toolCallId); @@ -303,5 +303,5 @@ registerScopedService( IAgentToolDedupeService, AgentToolDedupeService, InstantiationType.Eager, - 'toolDedup', + 'toolDedupe', ); diff --git a/packages/agent-core-v2/test/toolDedup/tool-dedup.test.ts b/packages/agent-core-v2/test/toolDedupe/tool-dedupe.test.ts similarity index 96% rename from packages/agent-core-v2/test/toolDedup/tool-dedup.test.ts rename to packages/agent-core-v2/test/toolDedupe/tool-dedupe.test.ts index 426846f43..8b5d9a60e 100644 --- a/packages/agent-core-v2/test/toolDedup/tool-dedup.test.ts +++ b/packages/agent-core-v2/test/toolDedupe/tool-dedupe.test.ts @@ -19,8 +19,8 @@ import type { import { AgentToolDedupeService, IAgentToolDedupeService, - __testing as toolDedupTesting, - type ToolDedupResult, + __testing as toolDedupeTesting, + type ToolDedupeResult, } from '#/agent/toolDedupe'; import { AgentToolExecutorService, @@ -36,7 +36,7 @@ import { registerLogServices } from '../log/stubs'; import { recordingTelemetry, type TelemetryRecord } from '../telemetry/stubs'; import { stubLoopWithHooks, stubTurnWithHooks } from '../turn/stubs'; -const { REMINDER_TEXT_1, REMINDER_TEXT_3, makeReminderText2 } = toolDedupTesting; +const { REMINDER_TEXT_1, REMINDER_TEXT_3, makeReminderText2 } = toolDedupeTesting; const ZERO_USAGE = emptyUsage(); let disposables: DisposableStore; @@ -64,7 +64,7 @@ interface Harness { /** * Builds a container wired the same way the agent is: real executor + registry, - * the dedup plugin registered (and realized so its constructor installs the + * the dedupe plugin registered (and realized so its constructor installs the * loop / tool-executor hooks), recording telemetry, and stub loop / turn with * real hook slots. `ix.get(IAgentToolDedupeService)` is what forces the eager * plugin to construct and register its hooks. @@ -82,7 +82,7 @@ function createHarness(telemetry: ITelemetryService = recordingTelemetry(telemet reg.defineInstance(IAgentWireRecordService, stubWireRecord()); reg.defineInstance( IAgentWireService, - disposables.add(new WireService({ logScope: 'wire', logKey: 'tool-dedup' })), + disposables.add(new WireService({ logScope: 'wire', logKey: 'tool-dedupe' })), ); reg.define(IAgentToolDedupeService, AgentToolDedupeService); registerLogServices(reg); @@ -95,11 +95,11 @@ function createHarness(telemetry: ITelemetryService = recordingTelemetry(telemet return { ix, loop, executor, registry }; } -function okResult(text: string): ToolDedupResult { +function okResult(text: string): ToolDedupeResult { return { output: text }; } -function errResult(text: string): ToolDedupResult { +function errResult(text: string): ToolDedupeResult { return { output: text, isError: true }; } @@ -191,7 +191,7 @@ function dummyExecution(): ToolWillExecuteContext['execution'] { return { approvalRule: 'x', execute: async () => ({ output: '' }) }; } -/** Minimal `onWillExecuteTool` context — the dedup handler reads only id/name/args. */ +/** Minimal `onWillExecuteTool` context — the dedupe handler reads only id/name/args. */ function willCtx( id: string, name: string, @@ -210,7 +210,7 @@ function willCtx( }; } -/** Minimal `onDidExecuteTool` context — the dedup handler reads only id/name/args/result. */ +/** Minimal `onDidExecuteTool` context — the dedupe handler reads only id/name/args/result. */ function didCtx( id: string, name: string, @@ -231,7 +231,7 @@ function didCtx( } describe('AgentToolDedupeService', () => { - describe('same-step dedup', () => { + describe('same-step dedupe', () => { it('returns a placeholder synchronously and resolves to the real result on finalize', async () => { const h = createHarness(); await beforeStep(h, 1, 1); @@ -311,7 +311,7 @@ describe('AgentToolDedupeService', () => { expect(tool.calls).toHaveLength(1); expect(results.map((result) => result.output)).toEqual(['same', 'same']); expect(telemetryEvents).toContainEqual({ - event: 'tool_call_dedup_detected', + event: 'tool_call_dedupe_detected', properties: expect.objectContaining({ turn_id: 3, step_no: 1, @@ -434,7 +434,7 @@ describe('AgentToolDedupeService', () => { registerRead(h); // 8 occurrences of the same call within a single step, but no prior // streak — the trigger is about sustained behaviour across steps, not - // intra-step spam. Same-step dedup already short-circuits execution. + // intra-step spam. Same-step dedupe already short-circuits execution. const calls = Array.from({ length: 8 }, (_, i) => toolCall(i === 0 ? 'orig' : `dup${String(i)}`, 'Read', { p: 1 }), ); @@ -485,8 +485,8 @@ describe('AgentToolDedupeService', () => { const [final] = await runStep(h, 1, 3, [toolCall('final', 'X', {})]); const arr = final!.result.output as Array<{ type: string; text?: string }>; // The executor prepends a non-text companion to media-only output before - // the dedup hook runs, so the array is [companion, image_url, reminder]; - // the dedup-specific behavior is the trailing reminder text part it pushed + // the dedupe hook runs, so the array is [companion, image_url, reminder]; + // the dedupe-specific behavior is the trailing reminder text part it pushed // because the trailing part was non-text. expect(arr.some((part) => part.type === 'image_url')).toBe(true); expect(arr.at(-1)).toEqual({ type: 'text', text: REMINDER_TEXT_1 }); @@ -531,7 +531,7 @@ describe('AgentToolDedupeService', () => { it('resolves the dup deferred even when the original call args are rewritten before finalize', async () => { // Models the loop contract: prepareToolExecution may return // {updatedArgs}, in which case finalizeToolResult sees the rewritten - // args. The dedup key is registered at onWillExecuteTool time under the + // args. The dedupe key is registered at onWillExecuteTool time under the // LLM-issued args (keyed by call id), so the deferred is resolved under // that same key regardless of the rewritten args seen at finalize time. const h = createHarness(); @@ -686,7 +686,7 @@ describe('AgentToolDedupeService', () => { ); expect(telemetryEvents).toContainEqual({ - event: 'tool_call_dedup_detected', + event: 'tool_call_dedupe_detected', properties: { turn_id: 7, step_no: 1, @@ -709,7 +709,7 @@ describe('AgentToolDedupeService', () => { await executeAll(h, [toolCall('c2', 'Read', { path: '/a' })], 7, signal); expect(telemetryEvents).toContainEqual({ - event: 'tool_call_dedup_detected', + event: 'tool_call_dedupe_detected', properties: { turn_id: 7, step_no: 2, @@ -731,7 +731,7 @@ describe('AgentToolDedupeService', () => { const [result] = await runStep(h, 7, 3, [toolCall('a2', 'Read', { path: '/a' })]); expect(result!.result.output as string).not.toContain(''); - expect(telemetryEvents.filter((e) => e.event === 'tool_call_dedup_detected')).toHaveLength(0); + expect(telemetryEvents.filter((e) => e.event === 'tool_call_dedupe_detected')).toHaveLength(0); expect(telemetryEvents.filter((e) => e.event === 'tool_call_repeat')).toHaveLength(0); }); @@ -819,7 +819,7 @@ describe('AgentToolDedupeService', () => { expect(firstInNewTurn!.result.output as string).not.toContain(''); expect(telemetryEvents.filter((e) => e.event === 'tool_call_repeat')).toHaveLength(0); - expect(telemetryEvents.filter((e) => e.event === 'tool_call_dedup_detected')).toHaveLength(0); + expect(telemetryEvents.filter((e) => e.event === 'tool_call_dedupe_detected')).toHaveLength(0); }); it('runs with a no-op telemetry service', async () => { diff --git a/packages/agent-core-v2/test/turn/turn.test.ts b/packages/agent-core-v2/test/turn/turn.test.ts index 59f7b1153..8051f3d89 100644 --- a/packages/agent-core-v2/test/turn/turn.test.ts +++ b/packages/agent-core-v2/test/turn/turn.test.ts @@ -199,7 +199,7 @@ describe('Agent turn flow', () => { await ctx.untilTurnEnd(); expect(records).toContainEqual({ - event: 'tool_call_dedup_detected', + event: 'tool_call_dedupe_detected', properties: { turn_id: 0, step_no: 1, @@ -237,7 +237,7 @@ describe('Agent turn flow', () => { await ctx.untilTurnEnd(); expect(records).toContainEqual({ - event: 'tool_call_dedup_detected', + event: 'tool_call_dedupe_detected', properties: { turn_id: 0, step_no: 2, @@ -259,7 +259,7 @@ describe('Agent turn flow', () => { }); }); - it('fires PostToolUse for same-step dups with the original real output, not the dedup placeholder', async () => { + it('fires PostToolUse for same-step dups with the original real output, not the dedupe placeholder', async () => { // Hook command asserts the dup's PostToolUse payload carries the real // stdout ('dup'), not the placeholder (''). const assertScript = [