From 9d9f6905c6db200757091a614f951f000c2dfabf Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Mon, 8 Jun 2026 15:55:22 +0700 Subject: [PATCH] Hide OpenCode compact summaries from chat --- .../server/agent/providers/opencode-agent.ts | 8 ++ ...encode-compact-event-dump.real.e2e.test.ts | 122 ++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 packages/server/src/server/agent/providers/opencode-compact-event-dump.real.e2e.test.ts diff --git a/packages/server/src/server/agent/providers/opencode-agent.ts b/packages/server/src/server/agent/providers/opencode-agent.ts index c5e440a51..c7568d450 100644 --- a/packages/server/src/server/agent/providers/opencode-agent.ts +++ b/packages/server/src/server/agent/providers/opencode-agent.ts @@ -2510,6 +2510,10 @@ function appendOpenCodeMessagePartDelta( const knownPartType = partID ? state.partTypes.get(partID) : undefined; const isReasoning = knownPartType === "reasoning" || field === "reasoning"; + if (messageID && state.compactionSummaryMessageIds.has(messageID)) { + return; + } + if (isReasoning) { if (partID) { state.streamedPartKeys.add(`reasoning:${partID}`); @@ -2527,6 +2531,10 @@ function appendOpenCodeMessagePartDelta( if (messageRole === "user") { return; } + if (messageID && state.suppressAssistantMessagesUntilIdle?.active === true) { + state.compactionSummaryMessageIds.add(messageID); + return; + } if (partID) { state.streamedPartKeys.add(`text:${partID}`); } diff --git a/packages/server/src/server/agent/providers/opencode-compact-event-dump.real.e2e.test.ts b/packages/server/src/server/agent/providers/opencode-compact-event-dump.real.e2e.test.ts new file mode 100644 index 000000000..b4c2122d0 --- /dev/null +++ b/packages/server/src/server/agent/providers/opencode-compact-event-dump.real.e2e.test.ts @@ -0,0 +1,122 @@ +import { afterAll, beforeAll, beforeEach, describe, expect, test } from "vitest"; +import { mkdtempSync, rmSync } from "node:fs"; +import { tmpdir } from "node:os"; +import path from "node:path"; +import pino from "pino"; + +import type { AgentStreamEvent } from "../agent-sdk-types.js"; +import { + canRunRealProvider, + createRealProviderClient, + getRealProviderConfig, +} from "../../daemon-e2e/real-provider-test-config.js"; +import { OpenCodeServerManager } from "./opencode/server-manager.js"; + +const OPENCODE_REAL_TEST_MODEL = getRealProviderConfig("opencode").model; +const logger = pino({ level: "silent" }); + +function tmpCwd(): string { + return mkdtempSync(path.join(tmpdir(), "opencode-compact-dump-")); +} + +function waitForTurnToFinish(session: { + subscribe: (callback: (event: AgentStreamEvent) => void) => () => void; +}): Promise { + return new Promise((resolve, reject) => { + let unsubscribe: () => void = () => undefined; + const timeout = setTimeout(() => { + unsubscribe(); + reject(new Error("Timed out waiting for OpenCode compact turn to finish")); + }, 60_000); + unsubscribe = session.subscribe((event) => { + if ( + event.type !== "turn_completed" && + event.type !== "turn_failed" && + event.type !== "turn_canceled" + ) { + return; + } + clearTimeout(timeout); + unsubscribe(); + if (event.type === "turn_failed") { + reject(new Error(event.error)); + return; + } + resolve(); + }); + }); +} + +function dumpCompactEvents(events: AgentStreamEvent[]): void { + console.info( + "OPENCODE_COMPACT_EVENT_DUMP", + JSON.stringify( + events.map((event) => ({ + type: event.type, + turnId: "turnId" in event ? event.turnId : undefined, + item: event.type === "timeline" ? event.item : undefined, + usage: event.type === "turn_completed" ? event.usage : undefined, + })), + null, + 2, + ), + ); +} + +describe("OpenCode compact event dump (real)", () => { + let canRun = false; + + beforeAll(async () => { + canRun = await canRunRealProvider("opencode"); + }); + + beforeEach((context) => { + if (!canRun) { + context.skip(); + } + }); + + afterAll(async () => { + await OpenCodeServerManager.getInstance(logger).shutdown(); + }); + + test("dumps live events emitted by a real /compact turn", async () => { + const cwd = tmpCwd(); + const client = createRealProviderClient("opencode", logger); + + try { + const session = await client.createSession({ + provider: "opencode", + cwd, + model: OPENCODE_REAL_TEST_MODEL, + modeId: "build", + }); + + await session.run("Reply with exactly: COMPACT_SEED_OK"); + + const compactEvents: AgentStreamEvent[] = []; + const unsubscribe = session.subscribe((event) => { + compactEvents.push(event); + }); + const compactFinished = waitForTurnToFinish(session); + + try { + await session.startTurn("/compact"); + await compactFinished; + } finally { + unsubscribe(); + } + + dumpCompactEvents(compactEvents); + + expect(compactEvents.some((event) => event.type === "turn_completed")).toBe(true); + expect( + compactEvents.filter( + (event) => event.type === "timeline" && event.item.type === "assistant_message", + ), + ).toEqual([]); + } finally { + rmSync(cwd, { recursive: true, force: true }); + } + }, 120_000); +});