chat: unified session history across Claude + OpenCode (Phase 3)

/chat's session list, transcript load, delete, and rename now span both harnesses.

- opencode-sessions.ts — REST-backed reader (OpenCode's SQLite via its HTTP API, never
  the DB): listOpenCodeSessions / loadOpenCodeSession / delete / rename, returning the
  same shapes as the Claude reader, tagged harness:'opencode'. A serve is directory-
  scoped, so listing a cwd = asking the serve rooted there. Transcript rebuild maps
  user/assistant/tool parts and drops reasoning (parity with the delta filter).
- client.ts — adds listSessions/getMessages/deleteSession/renameSession over /session/*.
- chat.ts — /sessions merges both (newest first); /sessions/:id, DELETE, and
  /title route by id shape (ses_ = OpenCode). ClaudeSessionSummary gains an optional
  `harness` tag.
- send-opencode.ts — resuming from history: when the sessionKey is itself a ses_ id,
  reuse that OpenCode session instead of creating a new one.
- SessionList.tsx — shows an "OpenCode" badge for OpenCode sessions.

Verified end-to-end against a live serve: list (5 sessions, tagged), load (transcript
rebuilt, reasoning filtered), and rename all work. Phases 1-3 complete; needs a restart.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-24 16:56:20 +00:00
co-authored by Claude Opus 4.8
parent 3a2f3317b2
commit 059539a64a
7 changed files with 210 additions and 15 deletions
+7 -3
View File
@@ -42,12 +42,16 @@ export async function sendOpenCodeStreaming(params: OpenCodeStreamingParams): Pr
const { baseUrl } = await ensureServer(cwd, home);
const conn = getConnection(baseUrl);
// Reuse the OpenCode session for this live sessionKey, else create one bound to the cwd.
let opencodeSessionId = getOpenCodeSession(params.sessionKey) ?? params.resumeSessionId;
// Resolve the OpenCode session: a known mapping, or — when resuming from history — the sessionKey is
// itself the OpenCode session id (`ses_…`); otherwise create one bound to the cwd.
let opencodeSessionId =
getOpenCodeSession(params.sessionKey) ??
(params.sessionKey.startsWith('ses_') ? params.sessionKey : undefined) ??
params.resumeSessionId;
if (!opencodeSessionId) {
opencodeSessionId = await conn.createSession(cwd);
setOpenCodeSession(params.sessionKey, opencodeSessionId);
}
setOpenCodeSession(params.sessionKey, opencodeSessionId);
const sessionId = opencodeSessionId;
let unsub = () => {};