From cb7fba6130e1c1b89c65421e11a7b5b216803aad Mon Sep 17 00:00:00 2001 From: Hibryda Date: Fri, 27 Mar 2026 04:09:00 +0100 Subject: [PATCH] =?UTF-8?q?feat(electrobun):=20load=20full=20session=20his?= =?UTF-8?q?tory=20from=20JSONL=20when=20resuming=20=E2=80=94=20conversatio?= =?UTF-8?q?n=20stays=20visible?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui-electrobun/src/bun/claude-sessions.ts | 111 ++++++++++++++++++ .../src/bun/handlers/agent-handlers.ts | 10 ++ .../src/mainview/SessionPicker.svelte | 14 ++- .../src/mainview/agent-store.svelte.ts | 45 +++++++ ui-electrobun/src/shared/pty-rpc-schema.ts | 15 +++ 5 files changed, 190 insertions(+), 5 deletions(-) diff --git a/ui-electrobun/src/bun/claude-sessions.ts b/ui-electrobun/src/bun/claude-sessions.ts index 55658c7..39a9825 100644 --- a/ui-electrobun/src/bun/claude-sessions.ts +++ b/ui-electrobun/src/bun/claude-sessions.ts @@ -146,3 +146,114 @@ export function listClaudeSessions(cwd: string): ClaudeSessionInfo[] { return results; } + +// ── Message types for display ─────────────────────────────────────────────── + +export interface SessionMessage { + id: string; + role: 'user' | 'assistant' | 'tool_call' | 'tool_result' | 'system'; + content: string; + timestamp: number; + model?: string; + toolName?: string; +} + +/** + * Load conversation messages from a Claude JSONL session file. + * Extracts user prompts and assistant responses for display. + */ +export function loadClaudeSessionMessages(cwd: string, sdkSessionId: string): SessionMessage[] { + const encoded = encodeCwd(cwd); + const filePath = join(homedir(), ".claude", "projects", encoded, `${sdkSessionId}.jsonl`); + + let content: string; + try { + content = readFileSync(filePath, "utf-8"); + } catch { + return []; + } + + const messages: SessionMessage[] = []; + const lines = content.split("\n"); + + for (const line of lines) { + if (!line.trim()) continue; + try { + const obj = JSON.parse(line); + + // User messages + if (obj.type === "user" && obj.message?.content) { + const mc = obj.message.content; + let text = ""; + if (typeof mc === "string") { + text = mc; + } else if (Array.isArray(mc)) { + const tb = mc.find((b: Record) => b.type === "text"); + if (tb?.text) text = String(tb.text); + } + if (text) { + messages.push({ + id: obj.uuid || `user-${messages.length}`, + role: "user", + content: text, + timestamp: obj.timestamp ? new Date(obj.timestamp).getTime() : Date.now(), + }); + } + } + + // Assistant messages + if (obj.type === "assistant" && obj.message?.content) { + const mc = obj.message.content; + let text = ""; + if (Array.isArray(mc)) { + for (const block of mc) { + if (block.type === "text" && block.text) { + text += block.text; + } else if (block.type === "tool_use") { + messages.push({ + id: block.id || `tool-${messages.length}`, + role: "tool_call", + content: `${block.name}(${JSON.stringify(block.input || {}).slice(0, 200)})`, + timestamp: obj.timestamp ? new Date(obj.timestamp).getTime() : Date.now(), + toolName: block.name, + }); + } + } + } + if (text) { + messages.push({ + id: obj.uuid || obj.message?.id || `asst-${messages.length}`, + role: "assistant", + content: text, + timestamp: obj.timestamp ? new Date(obj.timestamp).getTime() : Date.now(), + model: obj.message?.model, + }); + } + } + + // Tool results + if (obj.type === "tool_result" || (obj.role === "tool" && obj.content)) { + const rc = obj.content; + let text = ""; + if (typeof rc === "string") { + text = rc; + } else if (Array.isArray(rc)) { + const tb = rc.find((b: Record) => b.type === "text"); + if (tb?.text) text = String(tb.text); + } + if (text && text.length > 0) { + messages.push({ + id: obj.uuid || `result-${messages.length}`, + role: "tool_result", + content: text.length > 500 ? text.slice(0, 497) + "..." : text, + timestamp: obj.timestamp ? new Date(obj.timestamp).getTime() : Date.now(), + }); + } + } + } catch { + continue; + } + } + + return messages; +} diff --git a/ui-electrobun/src/bun/handlers/agent-handlers.ts b/ui-electrobun/src/bun/handlers/agent-handlers.ts index 969a63a..4e7493c 100644 --- a/ui-electrobun/src/bun/handlers/agent-handlers.ts +++ b/ui-electrobun/src/bun/handlers/agent-handlers.ts @@ -179,5 +179,15 @@ export function createAgentHandlers( return { sessions: [] }; } }, + + "session.loadMessages": ({ cwd, sdkSessionId }: { cwd: string; sdkSessionId: string }) => { + try { + const { loadClaudeSessionMessages } = require("../claude-sessions.ts"); + return { messages: loadClaudeSessionMessages(cwd, sdkSessionId) }; + } catch (err) { + console.error("[session.loadMessages]", err); + return { messages: [] }; + } + }, }; } diff --git a/ui-electrobun/src/mainview/SessionPicker.svelte b/ui-electrobun/src/mainview/SessionPicker.svelte index fcd4d30..0d916a9 100644 --- a/ui-electrobun/src/mainview/SessionPicker.svelte +++ b/ui-electrobun/src/mainview/SessionPicker.svelte @@ -1,7 +1,7 @@