diff --git a/src/agents/pi-embedded-runner/run.overflow-compaction.harness.ts b/src/agents/pi-embedded-runner/run.overflow-compaction.harness.ts index c415afa96d5..ac12d7d89c7 100644 --- a/src/agents/pi-embedded-runner/run.overflow-compaction.harness.ts +++ b/src/agents/pi-embedded-runner/run.overflow-compaction.harness.ts @@ -138,6 +138,16 @@ export const mockedIsLikelyContextOverflowError = vi.fn((msg?: string) => { export const mockedPickFallbackThinkingLevel = vi.fn<(params?: unknown) => ThinkLevel | null>( () => null, ); +export const mockedEvaluateContextWindowGuard = vi.fn(() => ({ + shouldWarn: false, + shouldBlock: false, + tokens: 200000, + source: "model", +})); +export const mockedResolveContextWindowInfo = vi.fn(() => ({ + tokens: 200000, + source: "model", +})); export const mockedGetApiKeyForModel = vi.fn( async ({ profileId }: { profileId?: string } = {}) => ({ apiKey: "test-key", @@ -236,6 +246,18 @@ export function resetRunOverflowCompactionHarnessMocks(): void { }); mockedPickFallbackThinkingLevel.mockReset(); mockedPickFallbackThinkingLevel.mockReturnValue(null); + mockedEvaluateContextWindowGuard.mockReset(); + mockedEvaluateContextWindowGuard.mockReturnValue({ + shouldWarn: false, + shouldBlock: false, + tokens: 200000, + source: "model", + }); + mockedResolveContextWindowInfo.mockReset(); + mockedResolveContextWindowInfo.mockReturnValue({ + tokens: 200000, + source: "model", + }); mockedGetApiKeyForModel.mockReset(); mockedGetApiKeyForModel.mockImplementation( async ({ profileId }: { profileId?: string } = {}) => ({ @@ -363,16 +385,8 @@ export async function loadRunOverflowCompactionHarness(): Promise<{ vi.doMock("../context-window-guard.js", () => ({ CONTEXT_WINDOW_HARD_MIN_TOKENS: 1000, CONTEXT_WINDOW_WARN_BELOW_TOKENS: 5000, - evaluateContextWindowGuard: vi.fn(() => ({ - shouldWarn: false, - shouldBlock: false, - tokens: 200000, - source: "model", - })), - resolveContextWindowInfo: vi.fn(() => ({ - tokens: 200000, - source: "model", - })), + evaluateContextWindowGuard: mockedEvaluateContextWindowGuard, + resolveContextWindowInfo: mockedResolveContextWindowInfo, })); vi.doMock("../../process/command-queue.js", () => ({ diff --git a/src/agents/pi-embedded-runner/run.overflow-compaction.test.ts b/src/agents/pi-embedded-runner/run.overflow-compaction.test.ts index 8f213aa8fd9..8156271c966 100644 --- a/src/agents/pi-embedded-runner/run.overflow-compaction.test.ts +++ b/src/agents/pi-embedded-runner/run.overflow-compaction.test.ts @@ -12,8 +12,10 @@ import { mockedCompactDirect, mockedContextEngine, mockedDescribeFailoverError, + mockedEvaluateContextWindowGuard, mockedGlobalHookRunner, mockedPickFallbackThinkingLevel, + mockedResolveContextWindowInfo, mockedResolveFailoverStatus, mockedRunContextEngineMaintenance, mockedRunEmbeddedAttempt, @@ -79,6 +81,28 @@ describe("runEmbeddedPiAgent overflow compaction trigger routing", () => { ); }); + it("blocks undersized models before dispatching a provider attempt", async () => { + mockedResolveContextWindowInfo.mockReturnValue({ + tokens: 800, + source: "model", + }); + mockedEvaluateContextWindowGuard.mockReturnValue({ + shouldWarn: true, + shouldBlock: true, + tokens: 800, + source: "model", + }); + + await expect( + runEmbeddedPiAgent({ + ...overflowBaseRunParams, + runId: "run-small-context", + }), + ).rejects.toThrow("Model context window too small (800 tokens). Minimum is 1000."); + + expect(mockedRunEmbeddedAttempt).not.toHaveBeenCalled(); + }); + it("passes trigger=overflow when retrying compaction after context overflow", async () => { mockOverflowRetrySuccess({ runEmbeddedAttempt: mockedRunEmbeddedAttempt,