diff --git a/apps/x/packages/core/src/turns/context-elision.test.ts b/apps/x/packages/core/src/turns/context-elision.test.ts index 19f9c5ef..88f3ff28 100644 --- a/apps/x/packages/core/src/turns/context-elision.test.ts +++ b/apps/x/packages/core/src/turns/context-elision.test.ts @@ -2,7 +2,9 @@ import { describe, expect, it } from "vitest"; import type { z } from "zod"; import type { TurnContext, TurnEvent } from "@x/shared/dist/turns.js"; import { + DEFAULT_ELISION_POLICY, ElidingContextResolver, + createContextResolver, elideHistoricImages, elideHistoricMiddlePaneContent, elideHistoricToolResults, @@ -307,6 +309,32 @@ const POLICY_OFF = { middlePaneContent: false, }; +describe("createContextResolver", () => { + // The factory is the app's one sanctioned constructor (DI container and + // inspect CLI). If this stops returning the eliding decorator, every + // construction site silently regresses to full historic replay. + it("wraps the repo resolver in the eliding decorator", () => { + const resolver = createContextResolver({ + turnRepo: new InMemoryTurnRepo(), + }); + expect(resolver).toBeInstanceOf(ElidingContextResolver); + }); + + it("applies the default policy through the decorated resolver", async () => { + const repo = new InMemoryTurnRepo(); + repo.seed(toolTurnLog(T1, [], "x".repeat(5000))); + // Pinned to the shipped defaults (not the machine's context.json) so + // the test is hermetic while still exercising the full wiring. + const resolved = await createContextResolver({ + turnRepo: repo, + loadPolicy: () => DEFAULT_ELISION_POLICY, + }).resolve({ previousTurnId: T1 }); + const tool = resolved.find((m) => m.role === "tool"); + expect(tool?.content).toContain("elided"); + expect(tool?.content.length).toBeLessThan(1000); + }); +}); + describe("ElidingContextResolver", () => { function resolver( repo: InMemoryTurnRepo, diff --git a/apps/x/packages/core/src/turns/context-elision.ts b/apps/x/packages/core/src/turns/context-elision.ts index 4db63a57..22d7d175 100644 --- a/apps/x/packages/core/src/turns/context-elision.ts +++ b/apps/x/packages/core/src/turns/context-elision.ts @@ -230,10 +230,14 @@ export class ElidingContextResolver implements IContextResolver { // the loop transmits. export function createContextResolver({ turnRepo, + loadPolicy, }: { turnRepo: ITurnRepo; + // Injectable for tests; the app default reads config/context.json. + loadPolicy?: () => ElisionPolicy; }): IContextResolver { return new ElidingContextResolver({ inner: new TurnRepoContextResolver({ turnRepo }), + ...(loadPolicy ? { loadPolicy } : {}), }); } diff --git a/apps/x/packages/core/src/turns/context-resolver.ts b/apps/x/packages/core/src/turns/context-resolver.ts index 3785cc26..623bdb4e 100644 --- a/apps/x/packages/core/src/turns/context-resolver.ts +++ b/apps/x/packages/core/src/turns/context-resolver.ts @@ -28,6 +28,10 @@ export interface IContextResolver { ): Promise>; } +// NOTE: app code must not construct this directly — use createContextResolver +// (context-elision.ts), which wraps it in the eliding decorator. A raw +// instance silently transmits full historic tool results, frames, and note +// snapshots. Direct construction is for tests of the raw resolution only. export class TurnRepoContextResolver implements IContextResolver { private readonly turnRepo: ITurnRepo;