apps/web/components/chat/messages/ and app/styles/chat/.tool-presentation.ts: commands green (terminal), file edits amber (pen), reads blue (file), searches cyan (search), web and browser indigo (globe or pointer), sub-agents green (bot), memory fuchsia, planning slate, media rose, system stone, other functions orange (hammer); thinking stays purple and LLM cyan. Failed = red tile with ✗; running = the normal glyph with a conic sweep orbiting the tile. Row text: a verb title (Ran, Edited, Searched the web…) at weight 500, then the identifying argument as a monospace chip, then the first line of the result in muted text. Raw JSON never appears in a row; the side panel keeps the full parameters and output.FunctionInvocation { name, kwargs } . Clicking Run in the form submits typed kwargs directly. Entire composer text that exactly matches a registered function with named arguments is converted to the same object before creating a user message. Both create only the ProgramRun below, without UserBubble or an extra Agent text turn. Function fragments with surrounding prose, backticks, or code fences remain ordinary messages to avoid executing code under discussion.| Input | Entry classification | Persistence and display |
|---|---|---|
gui_agent(task="…", surface="browser") | Exact whole-input match, registered function, named literal arguments only | POST /api/function/gui_agent → single ProgramRun |
| gui_agent form in Favorite Programs | Form schema generates the same typed kwargs | POST /api/function/gui_agent → single ProgramRun |
Please explain gui_agent(...) or `gui_agent(...)` | Not a complete function expression | Ordinary UserMessage → Agent |
sendChatMessage or write pending user text. Attachments, queuing during a run, and system decisions do not implicitly become function parameters.function-invocation.test.mjs covers direct calls, ordinary-text boundaries, typed kwargs, successful binding, navigation, and failure rollback. Corresponding Python regressions cover non-executable GUI results and metadata. Independent specification review passed.attachCards or contextTree ; expansion reveals the same continuously updated timeline record.const [open, setOpen] = useState(false); streaming state does not determine its initial open value.card_id changes to “Completed/Error,” without duplication or deletion.running affects only summary activity indicator and step status, not outer expansion. Collapsing does not delete records; expansion shows the original record as a timeline row.| Call path | Live data | Page node | Shared visibility control |
|---|---|---|---|
In chat, agent() | sub_agent events update by stable card_id : attachCards | SubAgentStep | ExecutionStripconst [open, setOpen] = useState(false) |
auto_workflow internal selection agent | tree_update : contextTree | TreeStep child node |
ExecutionStrip uses false as the common default for streaming and historical messages, with manual choices taking precedence. Related entries:execution-strip.tsx, chat-stream.ts, runtime-block.tsx, check-spawn-cards.mjs.callerId; later continuation in the parent branch is hidden.auto_workflow contains only TreeStep without an attach pointer still open right-panel details only and show none of these branch-navigation actions.FileTypeIcon: the @pierre/trees built-in icon set rendered as inline SVG, 15px in a 16px box), tinted per file type with separate light and dark palettes, so .ts, .md, .json and .py rows are told apart at a glance; the header keeps the pen icon. The header count is the turn’s file_count, and the list never silently disagrees with it. Collapsed, the card shows three rows plus a “Show N more files” toggle for the other loaded rows (at most 20). Counted files the card does not list — more than 20, or a bounded summary whose full list has not loaded — end the list in one “and N more files — open Review” row that opens the Review tab exactly like the Review button. Persisted summaries keep up to 20 rows; a summary that counts more files than it lists makes the card request the full list through review_scope, which then reads the complete mutation journal instead of the bounded summary.--font-sans; titles, paths, statistics, status, and buttons do not switch to a separate monospace font, while numbers retain tabular alignment. Header is 40px high with 12px left padding. FeatherIcon-to-title and title-to-statistics gaps are both 12px. Use the user-specified 19px FeatherIcon on the left, with a horizontal divider between summary and files. Dark-theme header and footer use --bg-tertiary, and the file list uses --bg-primary. In light mode, the card, header, Review button, and footer all use --bg-primary, without shadows or dark top/bottom blocks; only outer borders and dividers establish boundaries. File rows are transparent by default in every theme, adding only --bg-hover on hover. Footer hover likewise adds only --bg-hover. FeatherIcon indicates direct file edits in this turn, not the site logo. Hover uses the component’s 1.6-second movement/rotation sequence, disabled for reduced motion. Header FeatherIcon, 14px title, statistics, 13px Undo/Redo, and Review button share one vertical centerline rather than baseline alignment. Direction icons use 1em at the same height as button text. Always show both addition and deletion totals, including +0 or −0. Statistics are 12px; the 2.5px two-color proportion bar below encodes actual additions/deletions only, without category chips or decorative numbers. The card is an inline-size container. At width ≤420px, shorten “3 files changed” to “3 files,” retain FeatherIcon/statistics/actions, and restore compact 6px / 5px gaps. Viewport media queries are compatibility-only. When history preflight fails, hide Undo/Revert/Redo/Reapply and show one 12px theme-accent status before Review. Truncate it to one line;title retains full text; narrow cards limit it to 92px. Do not append a separate status below the file list or footer. After successful Undo, show Redo; the inverse of historical Revert is Reapply. Both use the same safe reapply transaction with renewed preflight, without a separate linear redo stack. Review is a neutral outlined button. Undo/Revert use text with a backward icon, not accent color. Code / Tests / Docs / Large categories appear only in Review. File-list padding is 4px on all sides. Rows are 28px high with 6px horizontal padding, 13px product sans paths, and 12px product sans statistics in fixed-width columns. The whole row navigates; add no expansion arrow or hover action group. Only the Show more footer expands. Once expanded, the footer shows only 13px Collapse. Full Review always opens from the header button, without an added “Review all” footer.| System | This turn / checkpoint | Review | Undo / rewind |
|---|---|---|---|
| Codex | Open-source TurnDiffTracker consumes only committed apply_patch exact deltas without rereading the workspace; inexact deltas clear this-turn diff. | /diff independently uses Git: tracked files use git diff, untracked files use git ls-files --others --exclude-standard. | Open-source thread/revert explicitly restores conversation history only. Codex App file Undo client implementation is not confirmed in the current open-source repository; do not infer it. |
| Claude Code | Create a checkpoint per user prompt, tracking only direct file-editing tools such as Write, Edit, and NotebookEdit. Retain the latest 100 checkpoints. | Desktop/Web exposes session-level additions/deletions; opening shows file navigation on the left and a single-file diff on the right, supporting line comments and another Agent revision. | Code and conversation can be restored separately. Bash, general background sub-agents, external concurrent changes, and symlink/hardlink recovery are not guaranteed. Without tracked edits, no code-restore option appears. |
| Current OpenProgram implementation | Beyond direct-tool checkpoints, recursively scans up to 20,000 files around every Bash call and precopies candidate content up to 5 MiB. | Chat cards request full diffs; historical messages request file lists individually; shadow Git is mixed with current-disk fallback. | Large files without baselines are recorded as pre_existing=false; Undo may delete preexisting files, and partial failures may still persist reverted state. |
| Decision | Design item | OpenProgram handling |
|---|---|---|
| Adopt | Codex’s exact incremental attribution | Turn statistics derive only from committed file-mutation events. Without exact before/after, omit from the turn card. |
| Adopt | Claude/Codex’s independent review surface | Chat retains summaries only. Review has left file navigation and right single-file content, with line comments. |
| Adapt | Claude session diff and Codex turn/repository diff | Place in three scopes within one Review tab: This turn, Current branch, Workspace. Switching scope does not alter terminology. |
| Adapt | Claude checkpoint recovery | Retain quick turn-level Undo with complete conflict preflight first. Store recovery as a new immutable journal transaction. |
| Reject | Bash directory-scan attribution | Delete cwd traversal and candidate precopy around Bash. Ordinary Bash, background processes, and external changes neither count toward this turn nor qualify for its Undo. |
| Reject | Approximate turn diffs and silent partial recovery | Unproven provenance appears only in Workspace Review. Failed Undo preflight changes no files and lists conflict paths. |
| Adapt | Reference-product header action hierarchy | Adopt “text Undo + neutral outlined Review.” FeatherIcon on the left identifies direct edits; dimensions, spacing, colors, and Review pages retain OpenProgram’s visual system. |
| Scope | Authoritative data source | Contains | Undo |
|---|---|---|---|
This turn | Exact mutation journal for the current turn | Primary change set; linked child change sets retain actual producers and group by origin without copying mutations. | Only when every record is recoverable and current digest equals the recorded after digest. |
Current branch | Mutation projection of the current active lineage | All completed turns and explicit revert/restore/reapply; excludes sibling branches, ordinary Bash, and external writes. | Start from a specific turn/checkpoint; no unscoped one-click branch Undo. |
Workspace | Git tracked diff + ls-files --others --exclude-standard | Current uncommitted tracked/untracked files; respects .gitignore. Non-Git workspaces show unavailable, without directory-scan substitution. | None. Workspace changes may come from users, other sessions, or background processes. |
Before writing, direct-edit tools record canonical path, file type,before_digest , and a recoverable baseline. If a baseline cannot be saved safely, mark recoverability=unavailable, never “original file absent.”
After successful writes, record after_digest, operation, line counts, binary/large,turn_id and actor_id. Failures create no committed mutation.
Turn finalize merges only this turn’s mutations, generating net file lists and addition/deletion statistics persisted with the transcript. Complexity depends only on changed paths, without cwd scans.
Review reads summaries first, requesting one bounded diff only after file selection. Switching files cancels old requests. Chat DOM never mounts diffs.
agent_loop._snapshot_cwd, _walk_scan and _checkpoint_changed_files must no longer participate in file attribution. In particular, “over 5 MiB not staged → use nonexistent path → pre_existing=false” must disappear. Missing baselines mean unrecoverable, never newly created files.| Phase / state | Rule | Visible result |
|---|---|---|
| Eligibility | Every file requires exact operation and before/after digests. Only regular files are recoverable; symlinks, hardlinks, and missing baselines are unavailable. | If any file is unavailable, the card shows no executable Undo; Review explains why. |
| Preflight | Reread all current digests before recovery. Newly created files may be deleted only while equal to after digest; modifications, deletions, and renames likewise require no subsequent edits. | Any conflict prevents all disk writes for this Undo; state becomes blocked-conflict, listing conflict paths. |
| Apply | After complete preflight, write restored contents to same-directory temporary files and atomically replace. Multi-file operations retain rollback staging. | Unexpected I/O failures roll back applied items; partial success must not be labeled Reverted. |
| Record | Successful recovery appends a revert_of=<turn_id> transaction; original-turn state derives from the journal. | Chat and Review show Reverted; historical file lists remain reviewable without changing original conversation content. |
| Path | Target limit | Fallback |
|---|---|---|
| Turn finalize | Never traverse workspace; merge only changed paths in the mutation journal. | Exclude mutations missing exact information and record diagnostics; produce no approximate statistics. |
| Transcript | Return summaries with messages; initially display at most three files and stop sending list_turn_files. | for every historical message. Query legacy messages only on viewport entry or explicit Review. |
| File list | Server pages contain 100 files; frontend retains adjacent pages only and supports keyboard navigation. | Category statistics come from summaries without loading every row. |
| Selected diff | Mount only one diff at a time, with at most 200 lines or 256 KiB per batch, continuing by hunk/page. | Binary or oversized single hunks show statistics, reasons, and Open file; never present incomplete content as a full diff. |
| Render budget | Diff computation has an explicit time budget. Exceeding it returns metadata; Review never blocks chat or Agent completion. | Allow retry later or opening the original file; never fall back to cross-turn approximate “before versus current disk” diffs. |
| Action or state | Result | Boundary |
|---|---|---|
| Review / file row | Open the same deterministic r:<sessionId>:<assistantMsgId> center tab; file rows also select their path. | Do not load inline chat diffs; repeated clicks only focus or update selection. |
| Layout | Without split view, reuse the existing group and place Review to the chat’s right; with a full split, use a normal active tab. | Do not replace existing peers, add a third pane, or create a second tab store. |
| Scope switching | This turn, Current branch, and Workspace share left navigation/right diff layout, updating title and source labels together. | Workspace has no Undo; ignored files are absent; non-Git is explicitly unavailable. |
| Line comments | Click diff lines to comment; send multiple comments with structured file locations in the next prompt. | Comments do not directly edit files or claim code review has been performed. |
| Large / binary | Show exact statistics, diff-unavailable reasons, and Open file. | Do not render entire large files or automatically classify logs as Agent edits. |
| Undo conflict | Card shows Review conflict; Review locates conflicting files. | No Force undo shortcut that could overwrite later user or other-session edits. |
| Scope | Preferred location | Explicit exclusions |
|---|---|---|
| Exact mutations and recovery | programs/tools/files/, store/snapshot/checkpoint/, turn context/finalize, and agent/internals/_revert.py. | Do not add Bash directory watchers, modify user Git, or infer this turn from current disk. |
| Review protocol | ws_actions/turn_files.py, message metadata, and existing Git helpers; add paginated summary/diff requests. | No unbounded strings or approximate fallback. |
| Review center tab | center-tab-ids.ts, center-tabs-store.ts, center-tab-groups.ts and a minimal Review pane. | No new window, modal, third pane, or reference-product branding. |
| Chat card | turn-files-chips.tsx, user-provided FeatherIcon , and existing CSS. | FeatherIcon identifies direct file edits only; no site logo, inline diff, file-row arrows, hover action groups, or reference-product branding. |
--bg-input fill (white in light themes, per theme in dark ones) with --composer-shadow. The assistant reply sits flat on the page without a bubble. Hover the messages below to see their action rows; rest on a button to see its tip.pyproject.toml.--user-msg-bg, which equals var(--bg-input) in every theme; the shadow is a static --composer-shadow. A sent message is not a control, so hover does not deepen it. Code blocks and collapsed pastes inside the bubble use a --bg-primary inset to stand apart from the bubble surface. Queued rows above the composer are user messages not yet sent and use the same surface with 10px corners.--bg-input fill, 23px corners, --composer-shadow at rest and a 180ms transition to --composer-shadow-focus on hover and while focus is inside; it spans the full column. Inside are the kept attachments (each removable), the new-attachment strip and the composer’s own input row (with @ file mentions). The bottom row holds the Solar paperclip in the official ghost round icon Button (icon-sm) with the tip “Add images or files” on the left, then Cancel (elevated sm) and Save & resend (default primary sm) on the right. Esc cancels and ⌘/Ctrl+Enter saves; files can be dropped onto the box. The action row is hidden while editing.Streaming, completed, and restored assistant messages use the same ExecutionStrip, ThinkingStep, and FunctionStep components. When ordered blocks are absent, existing thinking and tool records are adapted for presentation in their stored order; this fallback does not reconstruct missing interleaving or invent tool records. Tool results, error state, identifiers, and full-output references remain intact. Completion never switches to the legacy boxed ToolsBlock table.
Implementation acceptance: complete a turn with tool calls, expand its execution summary, then reload the conversation. The same shared rows and detail interaction remain available.
Chat markdown already stashes complete $$/$/\[/\( blocks before marked and restores them as escaped text after sanitize. The React bubble still has to run KaTeX: MarkdownText typesets after paint, and the chat-area pin path calls typesetMath on the observed message column. Unclosed streaming math is not a complete stash pair; the next paint with a closer typesets. Sanitize, code, links, and ordinary escapes stay as they were. No new math library.
Acceptance: the sample \(d_t\) / \(v_t = (d_t - d_{t+1}) / d_t\) produces KaTeX HTML on the assistant bubble; display \[/$$ and inline $ do too; prose without math and incomplete openers do not typeset; invalid math does not throw.
Assistant replies omit the token and service-tier usage footer. The current execution segment uses the original text shimmer and running dot. Reply footers add no loading mark.
The transcript uses bidirectional, byte-budgeted pages. A connection owns a small LRU of immutable disk-backed snapshots with message-ID indexes. A page request reads only its selected display groups, preserving tool descendants and compaction metadata. Snapshot tokens are scoped to the connection, session and captured branch head. Replaced snapshots and disconnected connections release temporary files.
The browser targets four cached conversations, allowing displayed panes and requests in flight to temporarily exceed that limit, and releases page references when conversations are deleted. Each conversation retains at most six ordinary pages and an 8 MiB content budget per loaded transcript, with an indivisible page allowed when needed to preserve a single large turn. Eviction occurs opposite the reading position, never deleting persisted records; active updates are retained. Scrolling back automatically retrieves evicted pages in either direction. Measured rows outside the viewport unmount, preserving their height. Heights of evicted rows are removed.
Prefetch distance follows scroll direction and speed with a bounded maximum. Only one page request runs at a time; failures retry with exponential backoff. Invisible panes stop requesting and stale responses cannot overwrite a newer generation. Page insertion and eviction restore the visible message and its offset before paint. Restoration uses a message ID rather than a pixel offset in an obsolete window. Jump to latest retrieves the latest window directly.
Acceptance uses a 300,000-message synthetic source: repeated bidirectional paging keeps cached pages, rendered rows and measurements bounded; rapid scrolling under latency preserves a visible message; stale replies, reconnect, branch changes and live updates cannot corrupt the window. Backend page reads must not scan or parse the full snapshot after indexing. Cold snapshot construction remains a separate measured operation.
Protocol v2 defers the full graph until its view is visible; ordinary branch metadata requests omit graph bodies. Cold snapshot construction reads and enriches the authoritative branch once. Subsequent indexed pages avoid full hydration.
| Module | Responsibility |
|---|---|
| message-list.tsx | Message composition and measured row rendering |
| use-chat-area-stick / use-message-viewport / use-history-window | Following, viewport measurement, active history lifecycle |
| history-window / history-viewport | Bounded page storage and message anchor operations |
| session-history-loader | Network requests, generation checks and state coordination |
| session_history.py | Disk-backed snapshot index and bounded page reads |
Interaction contract: the bottom of an intermediate history window is not the latest conversation position. Main and split panes share the same following, adaptive rendering and jump behavior. Jump remains available while newer history exists and after a failed request; repeated clicks share one pending intent. Wheel, pointer and keyboard interaction cancel a pending jump or saved-position restoration before it changes the visible window. Ordinary paging still compensates for inserted rows at the current reading position. Pane teardown saves the final reading anchor. Connectivity recovery resumes automatic loading without retaining an obsolete retry delay.
Interaction acceptance: browser tests exercise an intermediate-window bottom, failed and repeated jumps, interrupted delayed restoration, independent split panes and unmount/remount. These interaction refinements are implemented and pass independent specification and quality review. Browser acceptance also checks that the saved anchor survives queued animation frames after unmount.
Performance contract: bounded Chat loads skip graph construction when the captured messages have neither compaction coverage nor spawn/attach relationships requiring graph annotations. When those annotations are needed, the shared graph builder performs the same semantic filtering without viewport geometry; opening the DAG still requests its full layout. First-open persistence hydration remains linear in stored nodes. The Markdown cache uses a 256-entry LRU and an 8 MiB budget estimated from UTF-16 source and sanitized output lengths; an individual entry exceeding the budget is rendered but not retained. Cache policy never bypasses sanitization.
Performance acceptance uses persisted synthetic messages through the public load handler and production browser components. Timing, frame intervals and retained heap are recorded separately; stable regression checks assert skipped work, semantic equivalence, cache eviction and bounded retention rather than machine-specific timing. Performance refinements are implemented and pass independent specification and quality review. Public-handler regression verifies skipped graph work and compaction equivalence; browser regression verifies byte-budget eviction, LRU promotion and oversized-entry bypass. Timing and retained-heap comparisons are synthetic measurements, not guarantees for full-App workloads.
Implementation status: implemented. The 300,000-message synthetic-source browser test verifies bounded bidirectional paging, rapid reversals under delayed transport, saved-message restoration, live-row preservation and jumps during pending loads. Indexed backend reads, snapshot cleanup and the web regression suite pass. Cold construction still reads the authoritative branch once; synthetic measurements do not establish latency for large real-world attachments or tool output.
Only the existing empty-reply thinking… placeholder replaces its loading mark with the approved 18px open double ring. The rings counter-rotate while exchanging radius and stroke width. Colors follow theme tokens; reduced motion freezes the mark and forced colors use system text color.
Execution summaries, thinking steps, function steps, nested calls, pane titles, retry notices and reply footers do not use this mark. No additional indicator position is selected by tool, thinking or text phase. The function timeline retains its original layout, text shimmer, running dot, disclosure structure and detail interactions.
Acceptance: an empty-reply placeholder displays one new mark; tool calls, thinking content and reply text add no double rings to the timeline or footer. Approval and terminal states suppress the placeholder. Independent history reconciliation, nested progress and stable-message rendering fixes remain intact. Implementation and acceptance evidence is recorded in the generation-motion-layout task report.
Message IDs and ordered assistant blocks define the transcript. Execution trees supplement the owning reply; status trees never generate replacement chat rows. History snapshots reconcile with observed progress for the same message: running snapshots cannot remove received tool calls, shorten text, or reopen a settled reply. Terminal snapshots update lifecycle and metadata while retaining received trace when omitted. Branch and history-window replacement still removes rows absent from that window.
Each execution root keeps its persisted path. Repeated calls to the same function remain distinct, and a root is consumed once across all text-separated execution groups. A completed function remains in its existing execution summary. Direct function runs retain their standalone presentation. Acceptance exercises stream, partial history, completion, late tree updates, errors, cancellation, and reload through production reducers and rendered components.
Implementation status: verification pending.
Queued messages dispatch in order after the current turn. Consumed steering input appears between the preceding and subsequent reply content. Its user node remains independent; the reply stores an ordered boundary reference shared by streaming, cancellation and history loading. Older records without a boundary place steering before the owning reply using persisted predecessor edges, without guessing a text offset.
Receipt polling confirms the same command without changing the visible waiting label. Pending or uncertain delivery cannot be resent, edited or removed. Definitive rejection retains the ordinary queued draft; transport failure retains uncertain delivery and never resends it as another turn. Attachments use ordinary queue delivery. Session queues and editing order remain independent.
Streaming steer: an accepted text instruction interrupts the active ordinary text/reasoning response, preserves its emitted content, then applies the durable command at the completed provider boundary before the next model request. The request is closed before continuation. Tool-call argument streams, running tools and structured-output responses retain their existing safe-point delivery; no tool is replayed or cancelled by steer. Polling is bounded and works while the provider is silent.
Stop during pending steer preserves the partial reply and its active history position. A command rejected because of cancellation remains an unsent draft with an explicit Send action; cancellation never automatically converts that draft into a new turn. Cancellation projection may advance only the exact top-level turn from its own user node to its existing assistant node, using compare-and-set, and never changes a newer or explicitly selected branch.
Implementation status: streaming interruption and stop retention pass independent specification and quality review, affected tests and the full verification manifest. Installed-App acceptance is recorded separately in release evidence.
A failed reply retains its partial answer and ordered thinking/function trace. One terminal notice appears after that content, before message actions. A concise localized headline describes the failure; technical details expand on request and render as plain text. No automatic retry is added.
Partial reply blocks and failure detail have separate fields. The legacy node output retains the error text for model-context and non-Web consumers; the Web mapper restores the partial answer from ordered text blocks. The failed assistant node persists the detail, reason, retryability and retry delay before the final failure event. History restores the same notice. Legacy error output is recognized without rewriting history and does not repeat the answer inside the notice.
Scope: main chat failures, shared assistant bubble and history mapping. Existing cancellation, approval, nested function details, retry/branch semantics and Goal reports retain their behavior. The existing provider taxonomy design remains authoritative; this adds durable presentation without a competing error store or third-party UI dependency.
Acceptance: a public turn emits text and tools then fails; live rendering and cold history both show answer, trace and one final notice. Empty and legacy failed replies remain visible. Successful/cancelled replies gain no error notice. Details work in narrow and dark layouts.
Release regression coverage also retains browser permission inheritance and high-level agent delegation checks. Bound-page tests supply an explicit binding and expect the existing stop after three failed attempts on the same observation, including the concrete failure reason. They do not change the browser workflow or its permission policy.
Implementation status: implementation, independent specification and quality reviews, and the full declared verification manifest pass. The browser regression fixtures match the existing bound-page path and failure limit. Installed-App acceptance is recorded separately in release evidence.