feat: full-fidelity conversation export (chat + tool calls)
Chat only ever lived in SQLite's `exchanges` table (what was *said*); tool
calls were transient — logged to the in-memory ring buffer and gone at
end-of-turn. This adds a persistent record of what Lyra *did* and exports the
two merged into one transcript.
- memory: new `tool_events` table + `add_tool_event`/`tool_events` accessors;
`delete_session` cascades to it.
- chat: persist each tool call (name/args/result) right where it fires, in both
the non-stream and stream paths. Move the user `remember` to just after
assembly so its timestamp precedes mid-turn tool events — keeps the export in
true chronological order (and records when the message actually arrived).
- transcript: new module renders a session as Markdown (Brian/Lyra speech with
⚙ tool-call lines interleaved) or JSON (machine-readable event stream).
- server: GET /sessions/{id}/export?format=md|json (attachment download).
- ui: ⬇ Export button by the session selector (Markdown or JSON).
Doubles as the receipt for "did the tool actually fire?" — the thing that was
invisible when she'd reply about a hand without logging it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -88,6 +88,7 @@
|
||||
<select id="sessions"></select>
|
||||
<button id="newSessionBtn">➕ New</button>
|
||||
<button id="renameSessionBtn">✏️ Rename</button>
|
||||
<button id="exportSessionBtn" title="Download full transcript (chat + tool calls)">⬇ Export</button>
|
||||
<button id="thinkingStreamBtn" title="Show live activity log">📜 Live Log</button>
|
||||
</div>
|
||||
|
||||
@@ -987,6 +988,19 @@
|
||||
addMessage("system", `Session renamed to: ${newName}`);
|
||||
});
|
||||
|
||||
document.getElementById("exportSessionBtn").addEventListener("click", () => {
|
||||
if (!currentSession) { addMessage("system", "No session to export."); return; }
|
||||
const fmt = window.confirm("Export as Markdown? (Cancel = JSON)") ? "md" : "json";
|
||||
// Hitting the download endpoint navigates a hidden anchor so the browser
|
||||
// saves the file (chat + interleaved tool calls) instead of rendering it.
|
||||
const a = document.createElement("a");
|
||||
a.href = `${RELAY_BASE}/sessions/${encodeURIComponent(currentSession)}/export?format=${fmt}`;
|
||||
a.download = "";
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
});
|
||||
|
||||
// Settings Modal
|
||||
const settingsModal = document.getElementById("settingsModal");
|
||||
const settingsBtn = document.getElementById("settingsBtn");
|
||||
|
||||
Reference in New Issue
Block a user