From cee7106054e36bc4cd7197e28fea953afd4d0e48 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Thu, 23 Oct 2025 16:28:20 -0400 Subject: [PATCH] session summaries in data --- packages/opencode/src/server/server.ts | 8 +-- packages/opencode/src/session/index.ts | 5 ++ packages/opencode/src/session/prompt.ts | 4 +- packages/opencode/src/session/summary.ts | 82 ++++++++++++++---------- 4 files changed, 60 insertions(+), 39 deletions(-) diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts index d8c80a47..8d10ef87 100644 --- a/packages/opencode/src/server/server.ts +++ b/packages/opencode/src/server/server.ts @@ -36,7 +36,7 @@ import { MCP } from "../mcp" import { Storage } from "../storage/storage" import type { ContentfulStatusCode } from "hono/utils/http-status" import { Snapshot } from "@/snapshot" -import { MessageSummary } from "@/session/summary" +import { SessionSummary } from "@/session/summary" const ERRORS = { 400: { @@ -629,19 +629,19 @@ export namespace Server { validator( "param", z.object({ - id: MessageSummary.diff.schema.shape.sessionID, + id: SessionSummary.diff.schema.shape.sessionID, }), ), validator( "query", z.object({ - messageID: MessageSummary.diff.schema.shape.messageID, + messageID: SessionSummary.diff.schema.shape.messageID, }), ), async (c) => { const query = c.req.valid("query") const params = c.req.valid("param") - const result = await MessageSummary.diff({ + const result = await SessionSummary.diff({ sessionID: params.id, messageID: query.messageID, }) diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index cf321952..64f64082 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -36,6 +36,11 @@ export namespace Session { projectID: z.string(), directory: z.string(), parentID: Identifier.schema("session").optional(), + summary: z + .object({ + diffs: Snapshot.FileDiff.array(), + }) + .optional(), share: z .object({ url: z.string(), diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index 3ce7ff9d..3fb7d85b 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -49,7 +49,7 @@ import { spawn } from "child_process" import { Command } from "../command" import { $, fileURLToPath } from "bun" import { ConfigMarkdown } from "../config/markdown" -import { MessageSummary } from "./summary" +import { SessionSummary } from "./summary" export namespace SessionPrompt { const log = Log.create({ service: "session.prompt" }) @@ -1292,7 +1292,7 @@ export namespace SessionPrompt { } snapshot = undefined } - MessageSummary.summarize({ + SessionSummary.summarize({ sessionID: input.sessionID, messageID: assistantMsg.parentID, providerID: assistantMsg.modelID, diff --git a/packages/opencode/src/session/summary.ts b/packages/opencode/src/session/summary.ts index 36441ea4..6d7b59e5 100644 --- a/packages/opencode/src/session/summary.ts +++ b/packages/opencode/src/session/summary.ts @@ -8,7 +8,7 @@ import { Flag } from "@/flag/flag" import { Identifier } from "@/id/id" import { Snapshot } from "@/snapshot" -export namespace MessageSummary { +export namespace SessionSummary { export const summarize = fn( z.object({ sessionID: z.string(), @@ -16,46 +16,62 @@ export namespace MessageSummary { providerID: z.string(), }), async (input) => { - const messages = await Session.messages(input.sessionID).then((msgs) => - msgs.filter( - (m) => m.info.id === input.messageID || (m.info.role === "assistant" && m.info.parentID === input.messageID), - ), - ) - const userMsg = messages.find((m) => m.info.id === input.messageID)! - const diffs = await computeDiff({ messages }) - userMsg.info.summary = { + const all = await Session.messages(input.sessionID) + await Promise.all([ + summarizeSession({ sessionID: input.sessionID, messages: all }), + summarizeMessage({ messageID: input.messageID, messages: all }), + ]) + }, + ) + + async function summarizeSession(input: { sessionID: string; messages: MessageV2.WithParts[] }) { + const diffs = await computeDiff({ messages: input.messages }) + await Session.update(input.sessionID, (draft) => { + draft.summary = { diffs, - text: "", } - if ( - Flag.OPENCODE_EXPERIMENTAL_TURN_SUMMARY && - messages.every((m) => m.info.role !== "assistant" || m.info.time.completed) - ) { - const small = await Provider.getSmallModel(input.providerID) - if (!small) return - const result = await generateText({ - model: small.language, - maxOutputTokens: 100, - messages: [ - { - role: "user", - content: ` + }) + } + + async function summarizeMessage(input: { messageID: string; messages: MessageV2.WithParts[] }) { + const messages = input.messages.filter( + (m) => m.info.id === input.messageID || (m.info.role === "assistant" && m.info.parentID === input.messageID), + ) + const userMsg = messages.find((m) => m.info.id === input.messageID)! + const diffs = await computeDiff({ messages }) + userMsg.info.summary = { + diffs, + text: "", + } + if ( + Flag.OPENCODE_EXPERIMENTAL_TURN_SUMMARY && + messages.every((m) => m.info.role !== "assistant" || m.info.time.completed) + ) { + const assistantMsg = messages.find((m) => m.info.role === "assistant")!.info as MessageV2.Assistant + const small = await Provider.getSmallModel(assistantMsg.providerID) + if (!small) return + const result = await generateText({ + model: small.language, + maxOutputTokens: 100, + messages: [ + { + role: "user", + content: ` Summarize the following conversation into 2 sentences MAX explaining what the assistant did and why. Do not explain the user's input. ${JSON.stringify(MessageV2.toModelMessage(messages))} `, - }, - ], - }) - userMsg.info.summary = { - text: result.text, - diffs: [], - } + }, + ], + }) + userMsg.info.summary = { + text: result.text, + diffs: [], } - await Session.updateMessage(userMsg.info) - }, - ) + } + await Session.updateMessage(userMsg.info) + } export const diff = fn( z.object({