From 4ff0d07b1d4180800ac5ab39a2c8d4c98bd9dbe9 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Sun, 10 May 2026 19:18:02 -0400 Subject: [PATCH] Migrate configurable compaction tests (#26732) --- .../opencode/test/session/compaction.test.ts | 221 ++++++++---------- 1 file changed, 103 insertions(+), 118 deletions(-) diff --git a/packages/opencode/test/session/compaction.test.ts b/packages/opencode/test/session/compaction.test.ts index 5aeb0804e2..028cefe7de 100644 --- a/packages/opencode/test/session/compaction.test.ts +++ b/packages/opencode/test/session/compaction.test.ts @@ -232,6 +232,14 @@ async function lastCompactionPart(sessionID: SessionID) { ?.parts.find((item): item is MessageV2.CompactionPart => item.type === "compaction") } +function readLastCompactionPart(sessionID: SessionID) { + return SessionNs.Service.use((ssn) => ssn.messages({ sessionID })).pipe( + Effect.map((messages) => + messages.at(-2)?.parts.find((item): item is MessageV2.CompactionPart => item.type === "compaction"), + ), + ) +} + function fake( input: Parameters[0], result: "continue" | "compact", @@ -300,6 +308,30 @@ const env = Layer.mergeAll( const it = testEffect(env) +const processEnv = Layer.mergeAll(SessionNs.defaultLayer, CrossSpawnSpawner.defaultLayer) +const itProcess = testEffect(processEnv) + +function compactionProcessLayer(options?: { + result?: "continue" | "compact" + plugin?: Layer.Layer + provider?: ReturnType + config?: Layer.Layer +}) { + const bus = Bus.layer + return SessionCompaction.layer.pipe( + Layer.provideMerge( + Layer.mergeAll( + (options?.provider ?? wide()).layer, + layer(options?.result ?? "continue"), + Agent.defaultLayer, + options?.plugin ?? Plugin.defaultLayer, + bus, + options?.config ?? Config.defaultLayer, + ), + ), + ) +} + function llm() { const queue: Array< Stream.Stream | ((input: LLM.StreamInput) => Stream.Stream) @@ -911,43 +943,33 @@ describe("session.compaction.process", () => { }), ) - test("marks summary message as errored on compact result", async () => { - await using tmp = await tmpdir() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const session = await svc.create({}) - const msg = await user(session.id, "hello") - const rt = runtime("compact", Plugin.defaultLayer, wide()) - try { - const msgs = await svc.messages({ sessionID: session.id }) - const result = await rt.runPromise( - SessionCompaction.Service.use((svc) => - svc.process({ - parentID: msg.id, - messages: msgs, - sessionID: session.id, - auto: false, - }), - ), - ) + itProcess.instance( + "marks summary message as errored on compact result", + Effect.gen(function* () { + const ssn = yield* SessionNs.Service + const session = yield* ssn.create({}) + const msg = yield* createUserMessage(session.id, "hello") + const msgs = yield* ssn.messages({ sessionID: session.id }) - const summary = (await svc.messages({ sessionID: session.id })).find( - (msg) => msg.info.role === "assistant" && msg.info.summary, - ) + const result = yield* SessionCompaction.use.process({ + parentID: msg.id, + messages: msgs, + sessionID: session.id, + auto: false, + }) - expect(result).toBe("stop") - expect(summary?.info.role).toBe("assistant") - if (summary?.info.role === "assistant") { - expect(summary.info.finish).toBe("error") - expect(JSON.stringify(summary.info.error)).toContain("Session too large to compact") - } - } finally { - await rt.dispose() - } - }, - }) - }) + const summary = (yield* ssn.messages({ sessionID: session.id })).find( + (msg) => msg.info.role === "assistant" && msg.info.summary, + ) + + expect(result).toBe("stop") + expect(summary?.info.role).toBe("assistant") + if (summary?.info.role === "assistant") { + expect(summary.info.finish).toBe("error") + expect(JSON.stringify(summary.info.error)).toContain("Session too large to compact") + } + }).pipe(Effect.provide(compactionProcessLayer({ result: "compact" }))), + ) it.instance( "adds synthetic continue prompt when auto is enabled", @@ -980,94 +1002,57 @@ describe("session.compaction.process", () => { }), ) - test("persists tail_start_id for retained recent turns", async () => { - await using tmp = await tmpdir() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const session = await svc.create({}) - await user(session.id, "first") - const keep = await user(session.id, "second") - await user(session.id, "third") - await SessionCompaction.create({ - sessionID: session.id, - agent: "build", - model: ref, - auto: false, - }) + itProcess.instance( + "persists tail_start_id for retained recent turns", + Effect.gen(function* () { + const ssn = yield* SessionNs.Service + const session = yield* ssn.create({}) + yield* createUserMessage(session.id, "first") + const keep = yield* createUserMessage(session.id, "second") + yield* createUserMessage(session.id, "third") + yield* SessionCompaction.use.create({ sessionID: session.id, agent: "build", model: ref, auto: false }) - const rt = runtime( - "continue", - Plugin.defaultLayer, - wide(), - cfg({ tail_turns: 2, preserve_recent_tokens: 10_000 }), - ) - try { - const msgs = await svc.messages({ sessionID: session.id }) - const parent = msgs.at(-1)?.info.id - expect(parent).toBeTruthy() - await rt.runPromise( - SessionCompaction.Service.use((svc) => - svc.process({ - parentID: parent!, - messages: msgs, - sessionID: session.id, - auto: false, - }), - ), - ) + const msgs = yield* ssn.messages({ sessionID: session.id }) + const parent = msgs.at(-1)?.info.id + expect(parent).toBeTruthy() + yield* SessionCompaction.use.process({ + parentID: parent!, + messages: msgs, + sessionID: session.id, + auto: false, + }) - const part = await lastCompactionPart(session.id) - expect(part?.type).toBe("compaction") - expect(part?.tail_start_id).toBe(keep.id) - } finally { - await rt.dispose() - } - }, - }) - }) + const part = yield* readLastCompactionPart(session.id) + expect(part?.type).toBe("compaction") + expect(part?.tail_start_id).toBe(keep.id) + }).pipe(Effect.provide(compactionProcessLayer({ config: cfg({ tail_turns: 2, preserve_recent_tokens: 10_000 }) }))), + ) - test("shrinks retained tail to fit preserve token budget", async () => { - await using tmp = await tmpdir() - await WithInstance.provide({ - directory: tmp.path, - fn: async () => { - const session = await svc.create({}) - await user(session.id, "first") - await user(session.id, "x".repeat(2_000)) - const keep = await user(session.id, "tiny") - await SessionCompaction.create({ - sessionID: session.id, - agent: "build", - model: ref, - auto: false, - }) + itProcess.instance( + "shrinks retained tail to fit preserve token budget", + Effect.gen(function* () { + const ssn = yield* SessionNs.Service + const session = yield* ssn.create({}) + yield* createUserMessage(session.id, "first") + yield* createUserMessage(session.id, "x".repeat(2_000)) + const keep = yield* createUserMessage(session.id, "tiny") + yield* SessionCompaction.use.create({ sessionID: session.id, agent: "build", model: ref, auto: false }) - const rt = runtime("continue", Plugin.defaultLayer, wide(), cfg({ tail_turns: 2, preserve_recent_tokens: 100 })) - try { - const msgs = await svc.messages({ sessionID: session.id }) - const parent = msgs.at(-1)?.info.id - expect(parent).toBeTruthy() - await rt.runPromise( - SessionCompaction.Service.use((svc) => - svc.process({ - parentID: parent!, - messages: msgs, - sessionID: session.id, - auto: false, - }), - ), - ) + const msgs = yield* ssn.messages({ sessionID: session.id }) + const parent = msgs.at(-1)?.info.id + expect(parent).toBeTruthy() + yield* SessionCompaction.use.process({ + parentID: parent!, + messages: msgs, + sessionID: session.id, + auto: false, + }) - const part = await lastCompactionPart(session.id) - expect(part?.type).toBe("compaction") - expect(part?.tail_start_id).toBe(keep.id) - } finally { - await rt.dispose() - } - }, - }) - }) + const part = yield* readLastCompactionPart(session.id) + expect(part?.type).toBe("compaction") + expect(part?.tail_start_id).toBe(keep.id) + }).pipe(Effect.provide(compactionProcessLayer({ config: cfg({ tail_turns: 2, preserve_recent_tokens: 100 }) }))), + ) test("falls back to full summary when even one recent turn exceeds preserve token budget", async () => { await using tmp = await tmpdir({ git: true })