diff --git a/packages/agent-core-v2/src/agent/toolDedupe/toolDedupe.ts b/packages/agent-core-v2/src/agent/toolDedupe/toolDedupe.ts index 498d03aec..09706c1bd 100644 --- a/packages/agent-core-v2/src/agent/toolDedupe/toolDedupe.ts +++ b/packages/agent-core-v2/src/agent/toolDedupe/toolDedupe.ts @@ -5,9 +5,7 @@ * `IAgentToolExecutorService`'s will/did hooks to suppress same-step duplicates and inject * cross-step repeat reminders. No other service injects it — the container * constructs it eagerly at Agent scope so its constructor registers the hooks. - * The only public surface is the read-only `currentStreak` observation; the - * step/result operations stay private to the service. Agent-scoped — one - * instance per agent. + * Agent-scoped — one instance per agent. */ import type { ContentPart } from '#/app/llmProtocol/message'; @@ -36,8 +34,6 @@ export type ToolDedupResult = ToolDedupSuccessResult | ToolDedupErrorResult; export interface IAgentToolDedupeService { readonly _serviceBrand: undefined; - - readonly currentStreak: number; } export const IAgentToolDedupeService: ServiceIdentifier = diff --git a/packages/agent-core-v2/src/agent/toolDedupe/toolDedupeService.ts b/packages/agent-core-v2/src/agent/toolDedupe/toolDedupeService.ts index 4f52034fa..1a2f819fb 100644 --- a/packages/agent-core-v2/src/agent/toolDedupe/toolDedupeService.ts +++ b/packages/agent-core-v2/src/agent/toolDedupe/toolDedupeService.ts @@ -191,7 +191,6 @@ export class AgentToolDedupeService extends Disposable implements IAgentToolDedu } } - private checkToolCall(toolCallId: string, toolName: string, args: unknown): CheckedToolCall { const key = makeKey(toolName, args); const index = this.stepCalls.length; @@ -287,10 +286,6 @@ export class AgentToolDedupeService extends Disposable implements IAgentToolDedu this.stepDeferreds.get(key)?.resolve(finalResult); return finalResult; } - - get currentStreak(): number { - return this.consecutiveCount; - } } export const __testing = { 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 02dc3bb18..426846f43 100644 --- a/packages/agent-core-v2/test/toolDedup/tool-dedup.test.ts +++ b/packages/agent-core-v2/test/toolDedup/tool-dedup.test.ts @@ -2,6 +2,7 @@ import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { DisposableStore } from '#/_base/di/lifecycle'; import { createServices, type TestInstantiationService } from '#/_base/di/test'; +import { IEventBus } from '#/app/event/eventBus'; import { type ToolCall } from '#/app/llmProtocol/message'; import { emptyUsage } from '#/app/llmProtocol/usage'; import { ITelemetryService } from '#/app/telemetry/telemetry'; @@ -41,6 +42,12 @@ const ZERO_USAGE = emptyUsage(); let disposables: DisposableStore; let telemetryEvents: TelemetryRecord[]; +const noopEventBus: IEventBus = { + _serviceBrand: undefined, + publish: () => {}, + subscribe: () => ({ dispose: () => {} }), +}; + beforeEach(() => { disposables = new DisposableStore(); telemetryEvents = []; @@ -53,7 +60,6 @@ interface Harness { readonly loop: IAgentLoopService; readonly executor: IAgentToolExecutorService; readonly registry: IAgentToolRegistryService; - readonly dedup: IAgentToolDedupeService; } /** @@ -68,6 +74,7 @@ function createHarness(telemetry: ITelemetryService = recordingTelemetry(telemet const ix = createServices(disposables, { additionalServices: (reg) => { reg.defineInstance(ITelemetryService, telemetry); + reg.defineInstance(IEventBus, noopEventBus); reg.defineInstance(IAgentLoopService, loop); reg.defineInstance(IAgentTurnService, stubTurnWithHooks()); reg.define(IAgentToolRegistryService, AgentToolRegistryService); @@ -82,10 +89,10 @@ function createHarness(telemetry: ITelemetryService = recordingTelemetry(telemet }, strict: true, }); - const dedup = ix.get(IAgentToolDedupeService); + ix.get(IAgentToolDedupeService); const executor = ix.get(IAgentToolExecutorService); const registry = ix.get(IAgentToolRegistryService); - return { ix, loop, executor, registry, dedup }; + return { ix, loop, executor, registry }; } function okResult(text: string): ToolDedupResult { @@ -338,7 +345,6 @@ describe('AgentToolDedupeService', () => { const last = await runStreak(h, 2); expect(typeof last.output).toBe('string'); expect(last.output as string).not.toContain(''); - expect(h.dedup.currentStreak).toBe(2); }); it('injects reminder1 at exactly 3 consecutive', async () => { @@ -348,7 +354,6 @@ describe('AgentToolDedupeService', () => { expect(last.output as string).toContain(''); expect(last.output as string).toContain('repeating the exact same tool call'); expect(last.output as string).not.toContain('repeated_times'); - expect(h.dedup.currentStreak).toBe(3); }); it('keeps injecting reminder1 at 4 consecutive', async () => { @@ -367,7 +372,6 @@ describe('AgentToolDedupeService', () => { expect(last.output as string).toContain('repeated_times: 5'); expect(last.output as string).toContain('tool: Read'); expect(last.output as string).toContain('arguments:'); - expect(h.dedup.currentStreak).toBe(5); }); it.each([6, 7])('keeps injecting reminder2 at %i consecutive', async (streak) => { @@ -385,7 +389,6 @@ describe('AgentToolDedupeService', () => { const last = await runStreak(h, 8); expect(last.output as string).toContain(''); expect(last.output as string).toContain('stuck in a dead end'); - expect(h.dedup.currentStreak).toBe(8); }); it('resets streak when a different call is interleaved', async () => { @@ -637,7 +640,6 @@ describe('AgentToolDedupeService', () => { // The underlying tool succeeded — force-stop must not flip it to error. expect(last.isError).toBeUndefined(); expect(stopTurnOf(last)).toBe(true); - expect(h.dedup.currentStreak).toBe(12); }); it('continues force-stopping past 12 consecutive', async () => {