diff --git a/bun.lock b/bun.lock index 9c21a738..09043e8f 100644 --- a/bun.lock +++ b/bun.lock @@ -33,7 +33,7 @@ "@ibm/plex": "^6.4.1", "@lezer/highlight": "^1.2.3", "@octokit/rest": "^22.0.1", - "@opencode-ai/sdk": "^1.4.39", + "@opencode-ai/sdk": "^1.4.41", "@radix-ui/react-collapsible": "^1.1.12", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.16", @@ -95,7 +95,7 @@ }, "packages/desktop": { "name": "@openchamber/desktop", - "version": "1.10.0", + "version": "1.10.2", "devDependencies": { "@tauri-apps/cli": "^2", "@types/node": "^24.3.1", @@ -104,7 +104,7 @@ }, "packages/electron": { "name": "@openchamber/electron", - "version": "1.10.0", + "version": "1.10.2", "dependencies": { "@openchamber/web": "workspace:*", "electron-context-menu": "^4.1.2", @@ -119,7 +119,7 @@ }, "packages/ui": { "name": "@openchamber/ui", - "version": "1.10.0", + "version": "1.10.2", "dependencies": { "@base-ui/react": "^1.4.0", "@codemirror/autocomplete": "^6.20.0", @@ -150,7 +150,7 @@ "@fontsource/ibm-plex-sans": "^5.1.1", "@ibm/plex": "^6.4.1", "@lezer/highlight": "^1.2.3", - "@opencode-ai/sdk": "^1.4.39", + "@opencode-ai/sdk": "^1.4.41", "@pierre/diffs": "1.1.0-beta.13", "@remixicon/react": "^4.7.0", "@simplewebauthn/browser": "13.3.0", @@ -218,10 +218,10 @@ }, "packages/vscode": { "name": "openchamber", - "version": "1.10.0", + "version": "1.10.2", "dependencies": { "@openchamber/ui": "workspace:*", - "@opencode-ai/sdk": "^1.4.39", + "@opencode-ai/sdk": "^1.4.41", "adm-zip": "^0.5.16", "jsonc-parser": "^3.3.1", "react": "^19.1.1", @@ -241,14 +241,14 @@ }, "packages/web": { "name": "@openchamber/web", - "version": "1.10.0", + "version": "1.10.2", "bin": { "openchamber": "./bin/cli.js", }, "dependencies": { "@clack/prompts": "^1.1.0", "@octokit/rest": "^22.0.1", - "@opencode-ai/sdk": "^1.4.39", + "@opencode-ai/sdk": "^1.4.41", "@simplewebauthn/server": "13.3.0", "adm-zip": "^0.5.16", "better-sqlite3": "^11.7.0", diff --git a/package.json b/package.json index f9f4c501..705a4a0e 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,7 @@ "@ibm/plex": "^6.4.1", "@lezer/highlight": "^1.2.3", "@octokit/rest": "^22.0.1", - "@opencode-ai/sdk": "^1.4.39", + "@opencode-ai/sdk": "^1.4.41", "@base-ui/react": "^1.4.0", "@radix-ui/react-collapsible": "^1.1.12", "@radix-ui/react-dialog": "^1.1.15", diff --git a/packages/ui/package.json b/packages/ui/package.json index ef61198e..5b4215b5 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -39,7 +39,7 @@ "@fontsource/ibm-plex-sans": "^5.1.1", "@ibm/plex": "^6.4.1", "@lezer/highlight": "^1.2.3", - "@opencode-ai/sdk": "^1.4.39", + "@opencode-ai/sdk": "^1.4.41", "@pierre/diffs": "1.1.0-beta.13", "@base-ui/react": "^1.4.0", "@remixicon/react": "^4.7.0", diff --git a/packages/ui/src/sync/session-actions.ts b/packages/ui/src/sync/session-actions.ts index 4de5df82..a8ed080f 100644 --- a/packages/ui/src/sync/session-actions.ts +++ b/packages/ui/src/sync/session-actions.ts @@ -13,6 +13,11 @@ import { useGlobalSessionsStore } from "@/stores/useGlobalSessionsStore" import { useConfigStore } from "@/stores/useConfigStore" import { registerSessionDirectory } from "./sync-refs" import { isSyntheticPart } from "@/lib/messages/synthetic" +import { materializeSessionSnapshots } from "./materialization" +import { stripMessageDiffSnapshots } from "./sanitize" + +const MESSAGE_REFETCH_LIMIT = 200 +const MESSAGE_REFETCH_SKIP_PARTS = new Set(["patch", "step-start", "step-finish"]) // Reference set by SyncProvider — allows actions to access SDK and stores let _sdk: OpencodeClient | null = null @@ -647,6 +652,26 @@ export async function revertToMessage(sessionId: string, messageId: string): Pro } } +export async function refetchSessionMessages(sessionId: string): Promise { + const store = dirStore() + const result = await sdk().session.messages({ sessionID: sessionId, directory: dir(), limit: MESSAGE_REFETCH_LIMIT }) + const records = (result.data ?? []).filter((record: { info?: { id?: string } }) => !!record?.info?.id) + if (records.length === 0) return + + store.setState((state) => { + const materialized = materializeSessionSnapshots( + state, + sessionId, + records.map((record: { info: Message; parts?: Part[] }) => ({ + info: stripMessageDiffSnapshots(record.info), + parts: record.parts ?? [], + })), + { skipPartTypes: MESSAGE_REFETCH_SKIP_PARTS }, + ) + return { message: materialized.message, part: materialized.part } + }) +} + /** * Unrevert — restore all previously reverted messages. * Restore all previously reverted messages. Aborts if busy, merges result. @@ -675,6 +700,7 @@ export async function unrevertSession(sessionId: string): Promise { store.setState({ session: sessions }) } } + await refetchSessionMessages(sessionId) } /** diff --git a/packages/ui/src/sync/session-ui-store.ts b/packages/ui/src/sync/session-ui-store.ts index acd9f598..3edf25e8 100644 --- a/packages/ui/src/sync/session-ui-store.ts +++ b/packages/ui/src/sync/session-ui-store.ts @@ -46,6 +46,7 @@ import { shareSession as shareSessionAction, unshareSession as unshareSessionAction, optimisticSend, + refetchSessionMessages, } from "./session-actions" import { useInputStore, type SyntheticContextPart } from "./input-store" import { useSelectionStore } from "./selection-store" @@ -967,8 +968,7 @@ export const useSessionUIStore = create()((set, get) => ({ const revertToId = currentSession?.revert?.messageID let targetMessage: typeof messages[number] | undefined if (revertToId) { - const revertIndex = userMessages.findIndex((m) => m.id === revertToId) - targetMessage = userMessages[revertIndex + 1] + targetMessage = [...userMessages].reverse().find((m) => m.id < revertToId) } else { targetMessage = userMessages[userMessages.length - 1] } @@ -996,10 +996,11 @@ export const useSessionUIStore = create()((set, get) => ({ const revertToId = currentSession?.revert?.messageID if (!revertToId) return + await refetchSessionMessages(sessionId) + const messages = getSyncMessages(sessionId) const userMessages = messages.filter((m) => m.role === "user") - const revertIndex = userMessages.findIndex((m) => m.id === revertToId) - const targetMessage = userMessages[revertIndex - 1] + const targetMessage = userMessages.find((m) => m.id > revertToId) if (targetMessage) { const targetParts = getSyncParts(targetMessage.id) diff --git a/packages/vscode/package.json b/packages/vscode/package.json index 7c7d81b2..b1ebc1c1 100644 --- a/packages/vscode/package.json +++ b/packages/vscode/package.json @@ -243,7 +243,7 @@ }, "dependencies": { "@openchamber/ui": "workspace:*", - "@opencode-ai/sdk": "^1.4.39", + "@opencode-ai/sdk": "^1.4.41", "adm-zip": "^0.5.16", "jsonc-parser": "^3.3.1", "react": "^19.1.1", diff --git a/packages/web/package.json b/packages/web/package.json index 5bcecd6b..276c43a9 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -25,7 +25,7 @@ "dependencies": { "@clack/prompts": "^1.1.0", "@octokit/rest": "^22.0.1", - "@opencode-ai/sdk": "^1.4.39", + "@opencode-ai/sdk": "^1.4.41", "@simplewebauthn/server": "13.3.0", "adm-zip": "^0.5.16", "better-sqlite3": "^11.7.0",