feat(chat): terminal chat parity for every tool; Desktop-like artifacts

- The chat over a terminal pane now covers Claude, Codex, omp, Cursor and
  Antigravity: Cursor's agent transcript and agy's step log
  (brain/<id>/.system_generated/logs/transcript_full.jsonl) are read as
  conversations; Cursor sessions are claimed by their workspace
- Replies appear as soon as they are written: the projects-tree watcher
  broadcasts `transcript_changed` (at most every 250 ms per session) and the
  chat re-reads the transcript while the terminal underneath draws
- Stop shows from the pane's own busy line (Claude/Codex `esc to interrupt`,
  agy `… esc to cancel`), or fresh output for omp/Cursor — no hooks needed
- Permission prompts read off the terminal become a card with the tool, its
  command and "Deny with a note"; Codex gets Permission mode / Model buttons
  that open its own numbered pickers, answered from the menu card
- Artifacts: cards for pages/documents/diagrams/tables/React components, a
  Preview | Code panel with versions, copy, download, save to the Artifacts
  Hub, full screen; sandboxed iframes without allow-same-origin
- Claude panes get CLAUDE_CODE_ENABLE_TODO_TOOLS=1 so the Progress panel has a
  plan; multi-question pane menus read their Next row and question tabs
- The realtime chat bridge (structured stdio, /api/chat-bridge, /ws-chat)
  from the unreleased ad9d475 is not kept
This commit is contained in:
2026-10-21 15:44:17 +07:00
parent aee4fc190e
commit 383b9ae231
41 changed files with 2582 additions and 426 deletions
+4 -2
View File
@@ -90,7 +90,7 @@ function bindable(v) {
*/
/** A run row as it may leave this machine: never a pane credential's hash. */
function exportableRun(row) {
const { pane_token_hash: _secret, ...rest } = row;
const { pane_token_hash: _secret, chat_token_hash: _chat, ...rest } = row;
return rest;
}
@@ -185,7 +185,9 @@ function importExportBundle(db, bundle) {
events: makeInserter(db, "events", { omit: ["id"], wrap: { data: "trim_event_data" } }),
token_usage: makeInserter(db, "token_usage"),
workflows: makeInserter(db, "workflows"),
dashboard_runs: makeInserter(db, "dashboard_runs", { omit: ["pane_token_hash"] }),
dashboard_runs: makeInserter(db, "dashboard_runs", {
omit: ["pane_token_hash", "chat_token_hash"],
}),
alert_rules: makeInserter(db, "alert_rules"),
model_pricing: makeInserter(db, "model_pricing"),
};