summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDax Raad <[email protected]>2025-11-04 13:32:56 -0500
committerDax Raad <[email protected]>2025-11-04 13:35:44 -0500
commit7a7060ef156451c90137cfccd74a59e3f3e40f68 (patch)
tree3b7b99607baf50ca4b571ca29e97c8f7ce7c5f2c
parentf9af9fc22184c5e4a62a18a2df6746d6a1945264 (diff)
downloadopencode-7a7060ef156451c90137cfccd74a59e3f3e40f68.tar.gz
opencode-7a7060ef156451c90137cfccd74a59e3f3e40f68.zip
fix session performance issue from large diffs
-rw-r--r--packages/opencode/src/server/server.ts28
-rw-r--r--packages/opencode/src/session/index.ts11
-rw-r--r--packages/opencode/src/session/summary.ts5
-rw-r--r--packages/opencode/src/storage/storage.ts43
-rw-r--r--packages/sdk/js/src/gen/sdk.gen.ts9
-rw-r--r--packages/sdk/js/src/gen/types.gen.ts22
6 files changed, 101 insertions, 17 deletions
diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts
index 68c61453a..59e066e15 100644
--- a/packages/opencode/src/server/server.ts
+++ b/packages/opencode/src/server/server.ts
@@ -759,6 +759,34 @@ export namespace Server {
},
)
.get(
+ "/session/:id/diff",
+ describeRoute({
+ description: "Get the diff for this session",
+ operationId: "session.diff",
+ responses: {
+ 200: {
+ description: "List of diffs",
+ content: {
+ "application/json": {
+ schema: resolver(Snapshot.FileDiff.array()),
+ },
+ },
+ },
+ ...errors(400, 404),
+ },
+ }),
+ validator(
+ "param",
+ z.object({
+ id: z.string().meta({ description: "Session ID" }),
+ }),
+ ),
+ async (c) => {
+ const diff = await Session.diff(c.req.valid("param").id)
+ return c.json(diff)
+ },
+ )
+ .get(
"/session/:id/message/:messageID",
describeRoute({
description: "Get a message from a session",
diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts
index 71d59d84e..f8a2ba918 100644
--- a/packages/opencode/src/session/index.ts
+++ b/packages/opencode/src/session/index.ts
@@ -15,8 +15,8 @@ import { MessageV2 } from "./message-v2"
import { Instance } from "../project/instance"
import { SessionPrompt } from "./prompt"
import { fn } from "@/util/fn"
-import { Snapshot } from "@/snapshot"
import { Command } from "../command"
+import { Snapshot } from "@/snapshot"
export namespace Session {
const log = Log.create({ service: "session" })
@@ -42,7 +42,9 @@ export namespace Session {
parentID: Identifier.schema("session").optional(),
summary: z
.object({
- diffs: Snapshot.FileDiff.array(),
+ additions: z.number(),
+ deletions: z.number(),
+ diffs: Snapshot.FileDiff.array().optional(),
})
.optional(),
share: z
@@ -258,6 +260,11 @@ export namespace Session {
return result
}
+ export const diff = fn(Identifier.schema("session"), async (sessionID) => {
+ const diffs = await Storage.read<Snapshot.FileDiff[]>(["session_diff", sessionID])
+ return diffs ?? []
+ })
+
export const messages = fn(Identifier.schema("session"), async (sessionID) => {
const result = [] as MessageV2.WithParts[]
for (const p of await Storage.list(["message", sessionID])) {
diff --git a/packages/opencode/src/session/summary.ts b/packages/opencode/src/session/summary.ts
index ffd0f8da7..9795a3069 100644
--- a/packages/opencode/src/session/summary.ts
+++ b/packages/opencode/src/session/summary.ts
@@ -11,6 +11,7 @@ import { SystemPrompt } from "./system"
import { Log } from "@/util/log"
import path from "path"
import { Instance } from "@/project/instance"
+import { Storage } from "@/storage/storage"
export namespace SessionSummary {
const log = Log.create({ service: "session.summary" })
@@ -44,9 +45,11 @@ export namespace SessionSummary {
)
await Session.update(input.sessionID, (draft) => {
draft.summary = {
- diffs,
+ additions: diffs.reduce((sum, x) => sum + x.additions, 0),
+ deletions: diffs.reduce((sum, x) => sum + x.deletions, 0),
}
})
+ await Storage.write(["session_diff", input.sessionID], diffs)
}
async function summarizeMessage(input: { messageID: string; messages: MessageV2.WithParts[] }) {
diff --git a/packages/opencode/src/storage/storage.ts b/packages/opencode/src/storage/storage.ts
index 9eb0f8f51..66ce20d95 100644
--- a/packages/opencode/src/storage/storage.ts
+++ b/packages/opencode/src/storage/storage.ts
@@ -85,7 +85,9 @@ export namespace Storage {
const session = await Bun.file(sessionFile).json()
await Bun.write(dest, JSON.stringify(session))
log.info(`migrating messages for session ${session.id}`)
- for await (const msgFile of new Bun.Glob(`storage/session/message/${session.id}/*.json`).scan({
+ for await (const msgFile of new Bun.Glob(
+ `storage/session/message/${session.id}/*.json`,
+ ).scan({
cwd: fullProjectDir,
absolute: true,
})) {
@@ -98,12 +100,12 @@ export namespace Storage {
await Bun.write(dest, JSON.stringify(message))
log.info(`migrating parts for message ${message.id}`)
- for await (const partFile of new Bun.Glob(`storage/session/part/${session.id}/${message.id}/*.json`).scan(
- {
- cwd: fullProjectDir,
- absolute: true,
- },
- )) {
+ for await (const partFile of new Bun.Glob(
+ `storage/session/part/${session.id}/${message.id}/*.json`,
+ ).scan({
+ cwd: fullProjectDir,
+ absolute: true,
+ })) {
const dest = path.join(dir, "part", message.id, path.basename(partFile))
const part = await Bun.file(partFile).json()
log.info("copying", {
@@ -117,6 +119,29 @@ export namespace Storage {
}
}
},
+ async (dir) => {
+ for await (const item of new Bun.Glob("session/*/*.json").scan({
+ cwd: dir,
+ absolute: true,
+ })) {
+ const session = await Bun.file(item).json()
+ if (!session.projectID) continue
+ if (!session.summary?.diffs) continue
+ const { diffs } = session.summary
+ await Bun.file(path.join(dir, "session_diff", session.id + ".json")).write(
+ JSON.stringify(diffs),
+ )
+ await Bun.file(path.join(dir, "session", session.projectID, session.id + ".json")).write(
+ JSON.stringify({
+ ...session,
+ summary: {
+ additions: diffs.reduce((sum: any, x: any) => sum + x.additions, 0),
+ deletions: diffs.reduce((sum: any, x: any) => sum + x.deletions, 0),
+ },
+ }),
+ )
+ }
+ },
]
const state = lazy(async () => {
@@ -128,9 +153,7 @@ export namespace Storage {
for (let index = migration; index < MIGRATIONS.length; index++) {
log.info("running migration", { index })
const migration = MIGRATIONS[index]
- await migration(dir).catch((e) => {
- log.error("failed to run migration", { error: e, index })
- })
+ await migration(dir).catch(() => log.error("failed to run migration", { index }))
await Bun.write(path.join(dir, "migration"), (index + 1).toString())
}
return {
diff --git a/packages/sdk/js/src/gen/sdk.gen.ts b/packages/sdk/js/src/gen/sdk.gen.ts
index b76b6996f..1a54da8fa 100644
--- a/packages/sdk/js/src/gen/sdk.gen.ts
+++ b/packages/sdk/js/src/gen/sdk.gen.ts
@@ -55,6 +55,7 @@ import type {
SessionShareErrors,
SessionDiffData,
SessionDiffResponses,
+ SessionDiffErrors,
SessionSummarizeData,
SessionSummarizeResponses,
SessionSummarizeErrors,
@@ -475,12 +476,16 @@ class Session extends _HeyApiClient {
}
/**
- * Get the diff that resulted from this user message
+ * Get the diff for this session
*/
public diff<ThrowOnError extends boolean = false>(
options: Options<SessionDiffData, ThrowOnError>,
) {
- return (options.client ?? this._client).get<SessionDiffResponses, unknown, ThrowOnError>({
+ return (options.client ?? this._client).get<
+ SessionDiffResponses,
+ SessionDiffErrors,
+ ThrowOnError
+ >({
url: "/session/{id}/diff",
...options,
})
diff --git a/packages/sdk/js/src/gen/types.gen.ts b/packages/sdk/js/src/gen/types.gen.ts
index 7293f13cd..d4b763324 100644
--- a/packages/sdk/js/src/gen/types.gen.ts
+++ b/packages/sdk/js/src/gen/types.gen.ts
@@ -527,7 +527,9 @@ export type Session = {
directory: string
parentID?: string
summary?: {
- diffs: Array<FileDiff>
+ additions: number
+ deletions: number
+ diffs?: Array<FileDiff>
}
share?: {
url: string
@@ -1882,6 +1884,9 @@ export type SessionShareResponse = SessionShareResponses[keyof SessionShareRespo
export type SessionDiffData = {
body?: never
path: {
+ /**
+ * Session ID
+ */
id: string
}
query?: {
@@ -1891,9 +1896,22 @@ export type SessionDiffData = {
url: "/session/{id}/diff"
}
+export type SessionDiffErrors = {
+ /**
+ * Bad request
+ */
+ 400: BadRequestError
+ /**
+ * Not found
+ */
+ 404: NotFoundError
+}
+
+export type SessionDiffError = SessionDiffErrors[keyof SessionDiffErrors]
+
export type SessionDiffResponses = {
/**
- * Successfully retrieved diff
+ * List of diffs
*/
200: Array<FileDiff>
}