Skip to content

fix(web): mid-turn refresh resilience, artifact side panel, clipboard over HTTP, recall purge on chat delete#104

Open
dingirut wants to merge 8 commits into
BlckLvls:mainfrom
dingirut:feature/chat-actions
Open

fix(web): mid-turn refresh resilience, artifact side panel, clipboard over HTTP, recall purge on chat delete#104
dingirut wants to merge 8 commits into
BlckLvls:mainfrom
dingirut:feature/chat-actions

Conversation

@dingirut

Copy link
Copy Markdown

Follow-up to #103 — polish and fixes surfaced by daily use of the web console.

Artifact previews (.md / .html)

  • File cards in chat get a preview action: markdown renders in place, HTML loads in a sandboxed iframe (opaque origin — page scripts cannot reach the console API or cookies), plus open-in-new-tab and download links.
  • Preview opens in a resizable side panel next to the chat (drag the divider, 300px–75% of the window, width persisted; Esc closes; full-screen on mobile).
  • Files tab: .html renders by default with a Rendered/Source toggle.

Mid-turn page refresh no longer loses anything

Refreshing while the agent is working used to drop the whole picture. Fixed end to end:

  • the transcript endpoint 500'd on multimodal tool results (list content, e.g. browser screenshots) — whole history failed to load;
  • the history query raced the in-flight turn and was dropped forever if it lost — now it applies under a streaming turn, with a monotonic guard so a stale fetch can never wipe live-committed messages, plus a post-turn reconcile refetch;
  • the live turn (tool timeline, intermediate text) existed only in the browser tab — the web channel now accumulates it server-side and replays a snapshot in the connect state event;
  • the turn's user messages are persisted only at end of turn — they're buffered and replayed in the same snapshot, and messages sent into a restored turn join the buffer so nothing duplicates.

Clipboard on plain-HTTP deployments

navigator.clipboard only exists in secure contexts, so the copy buttons silently did nothing over LAN http. Added a hidden-textarea execCommand fallback (reply copy + code blocks) and a visible failed state.

Privacy: deleting a chat now really forgets it

Deleting a session only unlinked the transcript file — its chunks stayed in the recall index and the agent could still surface the deleted conversation. Deletion now purges the dialogue's chunks (vec + FTS) and its index cursor.

Testing

  • New unit tests: live-turn snapshot accumulation (incl. pending user messages), multimodal tool-result transcripts, recall purge (store + manager), clipboard-adjacent UI flows.
  • Full suite: 1002 passed (the 8 pre-existing codex-fast failures also fail on a clean main checkout).
  • Manually exercised on a live deployment: mid-turn refresh with web-search timelines, artifact panel resize, md/html previews, chat delete → recall no longer finds it.

🤖 Generated with Claude Code

dingirut and others added 8 commits July 22, 2026 15:10
navigator.clipboard only exists in secure contexts, so the copy buttons
(reply + code blocks) silently did nothing over LAN http. Fall back to a
hidden-textarea execCommand copy, and surface a failed state instead of
silence.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Chat: file cards for markdown/html artifacts get a preview toggle —
markdown renders in place, html loads in a sandboxed iframe (opaque
origin, no access to the console API), plus an open-in-new-tab link.

Files: html files render by default with a Rendered/Source toggle,
alongside the existing markdown view.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Preview now opens in a right-hand panel next to the chat instead of
expanding inside the message box. The panel's left edge drags to
resize (300px – 75% of the window, width persisted in localStorage);
Escape or × closes it, switching chats closes it too. On mobile the
panel goes full-screen. The originating file card highlights while its
artifact is open, and iframe pointer events are muted during the drag
so the divider never loses the pointer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two bugs compounded:

- the transcript endpoint 500'd on multimodal tool results (list
  content, e.g. browser screenshots) — after a refresh the whole
  history failed to load

- the history query result was only applied when no turn was in
  flight, but `processing` was not a dependency of the apply effect,
  so a fetch that raced an active turn was dropped forever — the page
  showed a live "thinking" turn with no prior messages, and the next
  committed reply rendered as the only message in the chat

History now applies as soon as the turn settles, refuses to let a
stale fetch wipe newer live-committed messages, and refetches a few
seconds after each turn to reconcile with the lazily-persisted session.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Deleting a session only unlinked its transcript file — the chat's
chunks stayed in the recall index, so the agent could still surface a
conversation the user had explicitly deleted. Session deletion in the
web console now also removes the dialogue's chunks (vec + FTS via the
existing delete_source) and its index_state cursor.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The live turn (tool timeline, intermediate text) existed only in the
browser tab's memory, so refreshing mid-turn dropped the visible
web-search/tool activity even though the agent kept working. The web
channel now accumulates the in-flight turn server-side and ships a
snapshot in the connect state event; the client rebuilds the live view
from it, and `processing` in the snapshot is now real.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The history-apply effect refused to run while processing — with the
now-accurate processing flag from the connect snapshot, a mid-turn
refresh kept the history empty for the whole turn. Apply immediately;
the monotonic length guard plus the post-turn refetch already protect
against a stale fetch wiping live-committed messages.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The agent persists a turn's user messages only when the turn ends, so
after a mid-turn refresh the freshly fetched history lacked the message
that started the turn. The web channel now buffers the in-flight turn's
user-message echoes and ships them in the connect snapshot; the client
renders them above the live turn and folds them into the transcript on
commit. Messages sent into a snapshot-restored turn join the same
buffer so nothing duplicates.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant