diff --git a/packages/opencode/src/session/processor.ts b/packages/opencode/src/session/processor.ts index 225961aef05d..06784ab29968 100644 --- a/packages/opencode/src/session/processor.ts +++ b/packages/opencode/src/session/processor.ts @@ -312,10 +312,6 @@ export namespace SessionProcessor { } ctx.snapshot = undefined } - SessionSummary.summarize({ - sessionID: ctx.sessionID, - messageID: ctx.assistantMessage.parentID, - }) if ( !ctx.assistantMessage.summary && isOverflow({ cfg: yield* config.get(), tokens: usage.tokens, model: ctx.model }) diff --git a/packages/opencode/src/session/summary.ts b/packages/opencode/src/session/summary.ts index f2b53f3baf58..3ea2772e0f75 100644 --- a/packages/opencode/src/session/summary.ts +++ b/packages/opencode/src/session/summary.ts @@ -4,9 +4,11 @@ import { makeRuntime } from "@/effect/run-service" import { Bus } from "@/bus" import { Snapshot } from "@/snapshot" import { Storage } from "@/storage/storage" +import { Database, and, eq } from "@/storage/db" import { Session } from "." import { MessageV2 } from "./message-v2" import { SessionID, MessageID } from "./schema" +import { MessageTable } from "./session.sql" export namespace SessionSummary { function unquoteGitPath(input: string) { @@ -107,6 +109,15 @@ export namespace SessionSummary { sessionID: SessionID messageID: MessageID }) { + const row = Database.use((db) => + db + .select({ data: MessageTable.data }) + .from(MessageTable) + .where(and(eq(MessageTable.id, input.messageID), eq(MessageTable.session_id, input.sessionID))) + .get(), + ) + const summary = row?.data.summary + if (summary && typeof summary === "object" && "diffs" in summary && summary.diffs) return const all = yield* sessions.messages({ sessionID: input.sessionID }) if (!all.length) return diff --git a/packages/opencode/test/session/prompt-effect.test.ts b/packages/opencode/test/session/prompt-effect.test.ts index 38d7ed9f5aca..36a573ab054e 100644 --- a/packages/opencode/test/session/prompt-effect.test.ts +++ b/packages/opencode/test/session/prompt-effect.test.ts @@ -26,6 +26,7 @@ import { Instruction } from "../../src/session/instruction" import { SessionProcessor } from "../../src/session/processor" import { SessionPrompt } from "../../src/session/prompt" import { MessageID, PartID, SessionID } from "../../src/session/schema" +import { SessionSummary } from "../../src/session/summary" import { SessionStatus } from "../../src/session/status" import { Shell } from "../../src/shell/shell" import { Snapshot } from "../../src/snapshot" @@ -459,6 +460,42 @@ it.live("loop continues when finish is tool-calls", () => ), ) +it.live("loop summarizes once across a multi-step tool run", () => + provideTmpdirServer( + Effect.fnUntraced(function* ({ llm }) { + const prompt = yield* SessionPrompt.Service + const sessions = yield* Session.Service + const session = yield* sessions.create({ + title: "Pinned", + permission: [{ permission: "*", pattern: "*", action: "allow" }], + }) + let summaries = 0 + const runtime = SessionSummary as unknown as { summarize: typeof SessionSummary.summarize } + const original = runtime.summarize + runtime.summarize = (input) => { + summaries++ + return original(input) + } + yield* prompt.prompt({ + sessionID: session.id, + agent: "build", + noReply: true, + parts: [{ type: "text", text: "hello" }], + }) + yield* llm.tool("first", { value: "first" }) + yield* llm.text("second") + + const result = yield* prompt.loop({ sessionID: session.id }) + runtime.summarize = original + + expect(yield* llm.calls).toBe(2) + expect(result.info.role).toBe("assistant") + expect(summaries).toBe(1) + }), + { git: true, config: providerCfg }, + ), +) + it.live("loop continues when finish is stop but assistant has tool parts", () => provideTmpdirServer( Effect.fnUntraced(function* ({ llm }) {