Repository navigation
feat(native-chat): show agent-written visuals inline in structured chats - #26103
Conversation
…s visuals folder
A shared grammar for the ::orca-visual{file="..." title="..."} reply line,
the per-chat visuals folder location on the owning host, and the
agentSession.readVisual runtime method that reads one visual with lexical and
canonical containment, a 512 KiB bounded read and UTF-8 refusal.
Native-chat assistant replies render a ::orca-visual{...} line as the chat's
HTML visual in an opaque, scripts-only sandboxed frame: CSP first, the host
frame navigation guard registered before content runs, live theme without a
reload, fitted height, links opened in the viewer's browser only from a real
gesture, lazy mount, and one muted line when the visual cannot be shown.
Open in sidebar shows the same frame in the right sidebar, widened while it
is open and restored after.
…streaming hold Registers agentSession.readVisual from the methods index so the structured method file stays under its line budget, replaces reflective reads with checked narrowing, moves the pure height governor to src/shared for mobile, and holds a half-written directive tail while the turn works (structured text rows carry no running state).
Re-checks after the open that the chat's visuals folder is still the real directory at Orca's path, reports unexpected filesystem faults by code without host paths, and lets one click in a visual open at most one page.
… review fixes One shared helper drops visual lines (outside fenced code) from reply text where it becomes plain text: the structured status summary that feeds the sidebar row, dashboard, notifications, phone rows and handoffs, and AI Vault reply previews. Review fixes: height also counts a pinned body's overflow, only the live frontier row holds a half-written visual line, the runaway-height stop needs the same step repeated, and any host refusal evicts the cached revision.
…nal-paths helper Main removed the per-chat journal paths and the journal database's state directory; the visuals folder keeps the same sha256 layout on its own and the read method uses the profile state directory the chat host is opened in.
…al lines Reply previews in Agent Session History drop visual lines per text part before lines are folded; the frame adds a body's overflow only when the body really overflows; fence tracking follows CommonMark closers and openers; the copy button copies a reply without visual lines; a coded read fault keeps its cause.
… visuals folder pair at once
…on agent-session surface
Removing visual lines now closes only the gap each removal leaves, instead of collapsing blank lines across the whole reply and trimming its indentation; the visuals folder is checked parent first again so a broken path answers the same way every time.
Review summaryWhat this PR does: native-chat replies can show an agent-written HTML visual inline (and in the right sidebar), read from an Orca-owned per-chat folder on the host that owns the chat, inside an opaque sandboxed frame. Details are in the PR body. Review rounds (self-review with separate reviewer agents; findings fixed before this comment)
Live QA (macOS, Electron dev build, hidden window over CDP, isolated profile, stand-in Claude CLI; driven by a separate QA agent)
Not verified
CI (head
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configuration
⛔ Files ignored due to path filters (1)
📒 Files selected for processing (49)
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 7 remain after this review. 📝 WalkthroughWalkthroughThe change adds native-chat visual directives and renders supported visuals inline or in the right sidebar. It adds shared directive parsing, a runtime RPC for reading session visual files, revision-aware renderer loading, and sandboxed iframe documents with theme and size messaging. Assistant previews, message summaries, and copy actions filter directive lines. Tests cover parsing, file access, RPC behavior, renderer state, frame handling, and sidebar state. Priority: ➖ Normal Merge Risk: ⚪ Minimal · up to This change shows agent-written HTML visuals inline in structured chats. Each visual is read from that chat's own folder on the host and runs in a sandboxed frame with a strict security policy. Review found no concrete defects. The web client and remote runtimes were not tested live, but the change looks ready to merge. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 34.31% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 102 functions across 48 files. (1 skipped: 1 unsupported.)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
ELI5
When an agent in Orca's native chat wants to show you a chart, a table or a small mockup, today it can only describe it in text. This PR lets the agent write one HTML page into a folder Orca keeps for that chat and put a single line in its reply; Orca then shows that page right inside the reply, as a live, interactive picture that matches your theme, and you can open it larger in the right sidebar.
What Changed
Problem. A structured native chat (the Claude/Codex chat view, not the terminal) can only show prose, Markdown tables and Mermaid. Agents that want to show data visually have no way to do it inside the conversation, and there was no safe place to keep what they produce or safe way to run it.
Before → after, as a user sees it.
::orca-visual{file="usage-chart.html" title="Usage by day"}shows that line as plain text.How it works (mechanism).
src/shared/native-chat-visual-directive.ts) for the reply line, shared with mobile: a single bare file name ending in.html(no folders,.., drive/UNC/device/stream forms), optional title, length caps, at most 8 visuals per reply. A helper removes these lines from text that becomes plain text (withoutNativeChatVisualDirectiveLines), applied at the one producer of the "last reply" summary and at AI Vault previews.<userData>/native-chat-visuals/<sha256(session id)>/on the execution host, outside the user's workspace (creating it and teaching agents to use it is the follow-up PR). A new runtime methodagentSession.readVisual {sessionId, file, knownRevision?}reads one file there: lexical and canonical containment,O_NOFOLLOW, folder/parent must be real directories (re-checked after open), regular files only, a 512 KiB bounded read, UTF-8 only. Errors are returned as values (not_found,outside_folder,too_large,not_text, …). An older host answers "method not found" and the client shows "unavailable"; loss of contact is never treated as "deleted". The client keeps a small revision cache (bounded, de-duplicated, revalidated on every mount, bounded retries, no permanent failure).<iframe sandbox="allow-scripts">(opaque origin; no same-origin, forms, popups or top navigation) built from a shell (src/shared/native-chat-visual-shell.ts, shared with mobile) whose Content-Security-Policy is the first thing parsed: scripts/styles/fonts/images only inline,data:/blob:, or from six pinned public CDNs; no fetch/XHR/WebSocket, frames, workers, objects, media, forms or<base>. The main process's frame navigation guard (generalized from the plugin-panel guard into a host-frame registry with a separate "chat visual" kind — it grants nothing) refuses any navigation of the frame. Theme tokens are injected before first paint and updated by message. The page reports its height; the host clamps it (80–2000 px), rate-limits it and stops a page that only grows because the frame grew. Links: the page asks; Orca opens http(s) links in the viewing client's browser only while the frame has focus and the user has just acted, at most one per 5 s.Why
Differences from the common pattern
data:images, inline SVG or CDN assets — intended v1.Linked Issue
N/A — maintainer change, part of the inline chat visuals stack (PR 1 of 3: rendering).
Visual Proof
Live QA on macOS (Electron dev build of
4cd8bd2bda1; later commits are review/CI fixes covered by unit tests, hidden window over CDP, isolated profile; the chat runs a stand-in Claude CLI so no real agent was involved; driven by a separate QA agent). Before this PR, the same reply line shows as plain text, as in the code-block/quote example below.QA results (run 3). Inline render (A), interaction + live theme without reload (B), fitted height after a width change (C, no inner overflow), Open in sidebar incl. keyboard focus, width restore and tab supersede (D), unavailable line (E), literal forms (F), plain-text sidebar row (I), Copy message without the line (K), containment —
location.hrefassignment,window.open→null,fetchblocked by CSP (L), console (X): pass.Usage by day(307 px) andUsage table(930 px). The agent counted new frames across the whole transcript; the likely reason the count came out short is that the transcript is windowed and unmounts rows scrolled far away — I did not confirm that cause. The runaway page held at 512 px over 24 samples (below the 2000 px cap).NativeChatMarkdown.visual.test.tsx(tail held while the turn works, shown once it ends, an earlier row of the same turn not held) andnative-chat-visual-directive.test.ts(streaming tail).~/.codex/config.toml,~/.codex/hooks.json,~/.claude/settings.json) were hashed before and after each run: unchanged in run 3.Testing
Automated (explicit files, all green locally): grammar and streaming tail (
native-chat-visual-directive.test.ts), shell CSP/message schema (native-chat-visual-shell.test.ts), height governor, host read containment incl. symlinks to a sibling chat / another workspace, a symlinked folder or root, FIFO, directory, size cap, binary/invalid UTF-8, swapped files (native-chat-visual-file-read.test.ts), the RPC method (params, refusals, capability gate, registration), the main-process frame guard for both kinds, Markdown contexts (fences, indented/inline code, quotes, lists, raw HTML forgery, per-reply cap, reference links), read-client cache/revalidation/de-dup/eviction, the frame (sandbox, name, CSP first, height from the right window/channel only, live theme without reload, link gating, second-load retire), the sidebar route (open/close/restore, supersede by a tab, width), the plain-text producers (status summary, AI Vault previews) and the copy button.Typecheck:
config/tsconfig.node.jsonandconfig/tsconfig.tc.web.jsonclean on the final head; full CI green onff394428854.Lint/gates: oxlint on changed files,
audit:anti-slop,audit:code-quality:native,check:code-quality:changed, RPC params catalog, localization coverage/extraction.Manual: macOS, Electron dev build, hidden window over CDP, isolated profile with a stand-in Claude CLI (no real agent) — see Visual Proof and the review comment.
I manually tested these changes locally
Automated tests added/updated, or explained why not below
Review
Review summary posted as a PR comment.
Agent skill upstream boundary
docs/reference/agent-skill-sharing-upstream-boundary.mdand copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.Notes
unsupported_location. Links open on the viewing client, never on a paired host.src/sharedfor it.Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)