Skip to main content

xdog-coding

Interactive coding-agent CLI.

A terminal coding agent with session management, built on xdog-agent and the xdog-tui rendering layer.

uv run xdog-coding

Resume a session from the current directory with uv run xdog-coding -r. The selector lists all matching sessions, newest first. Use Up/Down, Page Up/Down, Home/End and Enter to choose; Escape cancels. --working-dir PATH changes the directory being listed. --pick-session opens the same selector.

--resume-id ID restores a session directly, including in non-interactive modes. The selector shows current-directory sessions first, followed by older sessions marked [Unknown dir] whose directory was never recorded. Choosing and resuming an unknown session associates it with the current directory; canceling leaves it unchanged. Sessions known to belong to other directories are excluded. Existing saved model, messages and usage are preserved.

Interactive terminal

The interface uses native scrollback, one status row, and a shared multiline editor. Enter submits or queues; Ctrl+Enter inserts a newline. Ctrl+O opens recent tool/reasoning details: Left/Right chooses an entry and Page Up/Page Down scrolls. Escape closes autocomplete/details first, then cancels active work and restores queued text ahead of the current draft. Ctrl+Z suspends on POSIX. Ctrl+L repaints the visible UI without clearing scrollback, the draft, or focus. The terminal cursor is visible in the focused editor and hidden while inspecting details or approving a tool; returning to input restores it at the draft position. Streaming updates are processed in bounded FIFO batches so input and rendering continue to get a turn under sustained output.

Permission prompts take priority. Up/Down chooses an action, Enter confirms, Escape denies that call, and Page Up/Page Down scrolls the command summary. Confirmation requires visible action/context; tiny or clipped previews ask you to resize. Space, modified Enter and reported key repeats never approve. The editor draft stays intact when panels open or the terminal resizes. Exit, EOF and terminal failures cancel owned agent/pre-turn work, discard queued execution and close approvals; cleanup is not limited to keyboard callbacks. Slash commands are serialized. Model/session/skill changes require an idle agent; commands rejected while busy and messages submitted during compaction remain in the draft. Escape cancels asynchronous command work. Standard console warnings are rendered as sanitized UI messages rather than printing over terminal frames. Transcript messages use shared TUI components: user text, Thinking, assistant prose and tool previews. Thinking and prose remain separate message objects during streaming; the transcript controls spacing and tools use a bounded branch preview. User messages have a subtle background and one blank separator before replies. The light theme uses a pale background; plain/NO_COLOR mode omits colors. See terminal verification; remote coding and fullscreen application modes are excluded.

Details and permission prompts appear below the input. Coding shows the actual activity and retains final tool durations. Queue/context metadata takes priority over long model names; /status displays full model, session and working-directory data. Context displays used tokens / prompt capacity, including cached input tokens, even when the percentage would round to zero. Resume restores user text blocks in both the transcript and input history, alongside saved model and usage. Ctrl+Enter, Shift+Enter and Alt+Enter insert a newline when the terminal forwards the modified key. If a desktop shortcut intercepts Ctrl+Enter, rebind that shortcut or use Alt+Enter; the application cannot override desktop window management.

  • /details or Ctrl+O opens a focused panel, including feedback when empty.
  • Left/Right selects an entry; PgUp/PgDn reads pages; Home reads from the top.
  • Details always open at the first line, including tool logs. Page Up/Page Down scrolls; End jumps to the latest entry/output and starts following new output.
  • Thinking shows a two-row plain-text preview without opening details. Tool summaries keep multiline scripts on one header row; full commands are in details.
  • Escape, Ctrl+C or Ctrl+O closes details and restores the draft/cursor.
  • Permission panels show summary position and identify session approval as approval of the same call. Short terminals omit decorations and hints first.

Configure styling before launch:

XDOG_TUI_THEME=native uv run xdog-coding  # default: terminal-native body colors
XDOG_TUI_THEME=light uv run xdog-coding
XDOG_TUI_THEME=dark uv run xdog-coding
NO_COLOR=1 uv run xdog-coding             # unstyled content
XDOG_TUI_ASCII=1 uv run xdog-coding       # ASCII tool-state markers
XDOG_TUI_COLOR=16 uv run xdog-coding      # force basic ANSI colors
XDOG_TUI_COLOR=256 uv run xdog-coding
XDOG_TUI_COLOR=truecolor uv run xdog-coding

XDOG_TUI_THEME=plain also disables styling. TERM=dumb selects plain styling and ASCII tool markers. Terminal-control sequences are still required for this interactive mode; print mode is available for noninteractive output.

Color depth defaults to auto: COLORTERM=truecolor/24bit enables truecolor, otherwise TERM=*256color* enables 256 colors, with a basic 16-color fallback. The light theme supplies contrast-checked semantic colors for white backgrounds; user-customized terminal palettes still need a visual check.

Compact tools use two text rows: name/state/duration/command, then an output preview. Streaming previews retain the latest text; complete results remain accessible in details. Rich image content may add rows. Permission prompts gain a box when space permits, and shed it on short/narrow terminals.

The immutable UI run state tracks waiting, reasoning, responding, running, permission and cancellation. Permission resolution restores the current work phase; cancellation cannot be revived by progress events; completion clears the timer. Worker locks and cancellation signals remain separate from UI state.

Tool permissions

Potentially mutating tool calls are gated immediately before execution. By default, read-only filesystem operations and current_time run automatically; bash, filesystem writes/edits/deletes, and unknown extension tools ask first.

xdog-coding --permission-mode ask       # default: ask for dangerous calls
xdog-coding --permission-mode ask-all   # ask before every tool
xdog-coding --permission-mode allow-all # trusted unattended automation
xdog-coding --permission-mode deny      # allow read-only calls only

Interactive approvals offer Allow once, Allow for this session (the exact same call only), and Deny. Non-interactive runs fail closed when no approver is available unless allow-all was explicitly selected.

The same setting can be stored in ~/.config/xdog/coding/settings.json:

{"permission_mode": "ask"}

Permission prompts are an execution gate, not an OS sandbox: an approved shell command still has the permissions of the xdog-coding process.

Part of xdog

This package is one piece of xdog, a local-first toolkit for building, running and scheduling LLM workflows. The centrepiece is xdog-flow — a typed workflow format and compiler.

Documentation: https://xdog.942295.xyz

Licence

Copyright (c) 2026 HugeMan <942295.xyz>

GNU Affero General Public License v3.0 or later — see LICENSE.

Interactive file references

Type @name to fuzzy-search names under the session working directory, or use @src/, ./, ../, ~/, or an absolute path to browse a directory. Up/Down selects a suggestion; Tab or Enter inserts it without sending your draft. Directory selection continues browsing. Quote paths containing spaces, for example @"src/my file.py". Escape dismisses completion; Tab reopens it. References remain prompt text, not eagerly read attachments or tool approval.

Name discovery and matching are asynchronous, resumable batches—not a total 20,000-file cutoff. A private SQLite index keeps candidate storage off the UI thread; progress appears within the editor's row budget. Git supplies nested .gitignore, repository-local and global exclude rules, even outside a Git repository using private temporary metadata. Tracked names remain eligible when ignored, but deleted names are removed on refresh. Untracked generated/hidden paths and directory symlink traversal are omitted. Explicit directory paths bypass ignore rules without bypassing tool permissions.

Results refresh after three seconds. Without Git (or if enumeration fails), the UI explicitly reports a resumable basic-exclusion fallback; Git ignore semantics are then unavailable. No repository hooks/fsmonitor commands are enabled and no workspace/index metadata is modified. See docs/agent-ui-gap-plan.md at the repository root for verification and remaining parity work.

Keybindings and external editor

The live UI loads $XDG_CONFIG_HOME/xdog/coding/keybindings.json (or $CODING_DIR/keybindings.json) on startup. It is a JSON object mapping action names to one key string, a list of strings, or [] to disable the action:

{
  "send": "ctrl+s",
  "newline": "alt+n",
  "details": "ctrl+t",
  "external_editor": "f4"
}

Overrides replace each action's default bindings. Conflicting, unknown, duplicate, or malformed configuration is rejected as a whole with a UI diagnostic; defaults remain active. Bare printable characters cannot be shortcuts. Escape and Ctrl+L are reserved for contextual cancel/deny and recovery repaint; approval and dialog navigation keys are not remapped. Modified keys require terminal reporting support. Use F1 or /keys to see the effective map, including editing, history, undo/redo, kill/yank, interrupt, exit, suspend, details and editor actions. Restart for configuration changes. Prompt hints show current bindings, not stale defaults.

Ctrl+G (by default) or /editor opens the composer in $VISUAL, then $EDITOR, falling back to vi. Commands may contain quoted arguments; they are not interpreted by a shell, and the temporary draft filename is appended. GUI editors must be configured to wait for the file (for example their --wait flag). Project settings and agent output cannot choose the editor command.

The external editor owns a cooked terminal while it runs. Its UTF-8 draft uses a private directory and mode-0600 file, limited to 1 MiB and removed after exit. Success updates the composer with undo support; nothing is submitted. Nonzero exit, Ctrl+C interruption, invalid output or a missing program preserves the old draft and cursor. Symlink/FIFO replacements are rejected. The TUI restores modes even if the editor itself leaves raw input enabled, then re-anchors without replaying native history or clearing scrollback. Editing requires prompt focus, not a permission/details/overlay dialog.

Agent work can continue while the external editor owns the terminal; UI events wait for the handoff to finish. Interrupting the editor does not undo tool effects or cancel the agent: return to the prompt and use Escape for agent cancellation. Linux/tmux behavior is automated; desktop editor/IME and Windows checks remain manual and unverified.

Steering, native follow-ups, and editable queues

While a turn is active, there are three explicit delivery choices:

  • Enter keeps the existing local FIFO: a new full turn after the active SDK continuation settles, including normal prompt rebuild/compaction checks.
  • Alt+S (default) or /steer TEXT queues text at the next SDK checkpoint. It does not interrupt an already running model/tool call or undo tool effects.
  • Alt+F (default) or /follow-up TEXT queues text after the active tool/ steering continuation, within the same SDK run.

These shortcuts are configurable as steer, follow_up, and queue actions. Ctrl+Q or /queue inspects stable IDs, delivery lane and state. Local next-turn IDs look like q1; runtime IDs look like i1. Use:

/queue edit q1 replacement text
/queue remove i2
/queue move i3 up
/queue move q2 down

Moves operate within that delivery lane. A local edit applies under the queue lock; runtime changes require an acknowledgement from the owning event loop. Checkpoint-selected/consumed entries cannot be edited or removed. Replacement text from a rejected edit stays in the draft. Queue inspection never resets the active turn's timer. Edited queued text beginning with / remains literal agent input, not an automatic slash command.

Acceptance means queued, not consumed. Consumption is reported only when the input enters agent history. Cancellation returns only unconsumed runtime inputs; these and local backlog are restored ahead of the live draft without automatic resubmission. Idle/closing owners reject explicit delivery and preserve the text. Local and identified runtime queues each permit 50 pending messages; runtime text is limited to 64 KiB. Overflow rejects the new submission visibly, never drops old messages. Tool approvals and Escape denial are unchanged.

Repository change review

Alt+D (configurable review action) or /diff opens a read-only change browser rooted at the session's Git repository, including files outside a nested working directory. /diff refresh refreshes an open browser; Escape/Alt+D or /diff close closes it without submitting/clearing the draft or canceling the agent.

  • Up/Down or Left/Right: changed file; /: filename filter (Enter accepts, first Escape exits filtering).
  • s / w: selected file's staged / working patch; untracked files have working previews, not staged patches.
  • n / p: next/previous hunk; PgUp/PgDn and Home/End: patch pages; Shift+Left/Right: horizontal movement.
  • r: refresh snapshot; d / b: inspect an initialized nested repository/submodule and return to its parent scope.

Git discovery/diff runs off the UI thread. Patches are disk-paged rather than truncated to a small fixed preview, with sanitized controls and four-cell tabs. Binary/non-regular untracked entries get explicit summaries; symlink targets are shown without following them. The browser has no stage/apply/revert/commit operations. External diff, textconv, fsmonitor, hooks and pagers are disabled; inspection does not modify Git index/worktree contents or call a model.

Review uses a snapshot: changes made during agent work need r to refresh. A permission prompt takes priority, and denial/completion restores the open review and draft. Private temporary patch storage is removed when the browser closes. Git/OS failures and timeouts show feedback; Linux/tmux is verified, not Windows or desktop terminal/IME behavior.

In-session browsing and conversation trees

Alt+R / /sessions opens a searchable, paginated browser while retaining the composer draft. /tree opens the active session's branches and stable history checkpoints. In either panel: / searches (atomic paste supported), Up/Down and PgUp/PgDn navigate, Right enters a session/branch tree, Left goes back, Enter activates, r refreshes, and n creates a new session. Escape first exits search, then closes the panel. sessions/tree are configurable keymap actions; tree is unbound by default to avoid stealing existing Alt+T custom shortcuts.

/name inspects the current session name; /name NAME sets a durable plain-text name and /name - clears it. Names take precedence over automatic summaries in session-browser titles/search; they never replace conversation content or model instructions. Unicode is retained (256 UTF-8 bytes maximum); line/terminal controls and bidi overrides are rejected. Naming requires an idle current owner, makes no model/tool calls, and does not activate a session or rewrite its history.

Coding publishes a name only after its atomic session save succeeds and retains the same plan/extension persistence owner. Existing unnamed sessions remain valid; model summaries, branches and subsequent metadata saves retain explicit names.

/resume ID, /new, /fork [INDEX], /branch [SESSION] ID, and /goto [SESSION [BRANCH|-]] INDEX expose the same operations. Message indexes are one-based. Browsing is read-only and can coexist with an active turn; activation, new sessions and branch/checkpoint changes require an idle agent and use the owned command lifecycle. A rejected activation is not silently queued for later.

Switching retains saved context/model/reasoning/instructions, rebinds built-in workspace tools and file completion, rebuilds project context, and clears old session approvals/active skills/queues without relaxing the current permission mode. Old session heads are persisted before switching. Branches record parent lineage; navigation saves a return checkpoint before replacing the current head. Tool-call/result batches cannot be split into invalid provider context. Session writes are atomic private temporary-file replacements; traversal IDs/symlink session files are refused. These are persistence guards, not OS isolation.

Real images and explicit clipboard paste

/attach PATH reads a PNG/JPEG/WebP file into the editable draft (quote spaces). /attachments lists stable IDs, names, dimensions and sizes; /attachments preview ID, /attachments remove ID, and /attachments clear inspect or change the draft without sending. The bounded preview uses terminal image support when available and metadata fallback otherwise; Escape returns to the draft. An active approval keeps priority over previews and clipboard results.

Ctrl+V (configurable paste_clipboard) or /paste explicitly reads the local clipboard off-thread. Linux Wayland uses wl-paste, X11 uses xclip, and macOS uses the native osascript/AppKit image/text snapshot adapter described below. pbpaste is a text-only fallback when that native helper is absent. Missing helpers, unsupported formats and timeouts are visible; ordinary terminal/bracketed text paste remains available. There is no background clipboard polling. Late reads after submission remain in the new editable draft, never auto-send; explicit /attachments clear discards pending read results too.

Enter can submit an image-only draft. Up to 4 images, 10 MiB each, and 40 MiB retained across draft/queues are accepted, subject to stricter model limits and an advertised image-input capability. Unknown/text-only capabilities reject visibly and preserve the draft. Validation checks MIME/header/dimensions (maximum 100 megapixels), not a full decoder or a security boundary. CLI --file image inputs also become typed image content in print mode rather than path placeholders. Image bytes are persisted in session history: treat those files as sensitive.

Local queue edits/reorders and explicit steering/follow-ups retain their image parts. /queue shows attached-image counts; removing a queue item releases its attachments. Rejected/unconsumed inputs and failed turns preserve recoverable bytes for explicit inspection/retry. Images from a completed turn are not returned just because a later queued turn failed. Recovery never silently resends; a failed provider input may already be in context, so inspect before retrying. Normal input caps can be exceeded only by lossless recovery; remove excess before resubmission. Linux/private-tmux and controlled clipboard helpers are tested; desktop clipboard, Kitty/iTerm preview, the actual macOS AppKit bridge/desktop and Windows remain unverified as noted in the gap tracker; macOS image adapter contract tests do not prove native-platform compatibility.

Transcript search, selection and optional fullscreen

Alt+H / /transcript [QUERY] opens the full retained transcript browser. It reads source text, not Markdown's rendered layout or truncated tool previews: user/assistant prose, reasoning, complete tool output/arguments and system notices remain searchable. Tool arguments follow the existing presentation/redaction policy; image bodies are not converted to visible base64. Search is literal and Unicode-case-insensitive, with every non-overlapping occurrence navigable, including repeated hits on a single long line. Selected matches pan horizontally when needed.

  • / edits the query; Enter finishes query editing without submitting a prompt. n / Shift+N selects next/previous match, wrapping across the retained history.
  • Up/Down, PgUp/PgDn and Home scroll source lines; Left/Right selects an adjacent source entry. Shift+Left/Right pans horizontally.
  • v begins/clears a whole-line range selection; navigation extends it, and Shift+Up/Down can start a range. a selects the complete current source entry, including lines outside the viewport. Escape clears a selection before closing.
  • r refreshes an immutable source snapshot while retaining the source position where possible; refresh clears selection. End refreshes and goes to the latest retained source. Streaming continues normally; the browser does not silently change selected source text underneath you.
  • f / /fullscreen toggles an alternate-screen transcript viewport using the full terminal height. /transcript fullscreen, refresh, and close are also available. transcript and fullscreen are configurable keymap actions; the fullscreen shortcut is unbound by default.

The default application still uses native main-buffer scrollback. Fullscreen is opt-in and temporary, retains the original main-buffer painter, and re-anchors its restored cursor after resize rather than replaying history. Draft/cursor, queued images, active work and timers stay owned by the application. Escape or Ctrl+C closes the viewer, not the agent. Approvals leave fullscreen, take focus, and return to the inline browser after denial/resolution; fullscreen does not resume automatically. Exit/EOF/failure restore terminal modes and screen ownership. The viewer has no tool execution, session mutation, clipboard upload or network operation. Copy/export/share remains separate work in the gap tracker.

Search covers retained client-visible context, including restored session history; use /sessions to activate an archive. Hidden runtime instructions, other sessions and binary image data are not silently exposed. Native terminal mouse/text selection is unchanged; browser range selection is whole-line/entry based. Linux private-tmux resize/screen-return/approval behavior is tested; desktop terminal, Windows and IME manual validation remains open.

Copy, private export and explicit local sharing

Alt+C / /copy [response|selection|entry|conversation|all] copies a captured source snapshot, defaulting to the latest assistant response. A marked browser selection is used by Alt+C when present. In the transcript browser, y copies selected lines or the current entry; e chooses a local export destination and s reviews a local share artifact (selection if marked, otherwise conversation). These gestures never submit a prompt or execute a tool. Clipboard helpers are wl-copy on Wayland, xclip on X11, and pbcopy on macOS. No helper means an explicitly reported private temporary text file fallback, not silent OSC52 output. Helper errors/timeouts are not retried because clipboard outcome may be uncertain. The OS clipboard can expose text to other applications.

/export PATH [text|md|json] [scope] creates a private captured transcript file; without PATH it opens a destination/review panel. Default scope is all retained source, including tool/reasoning/notices. Existing files, final-path symlinks and directories are never replaced. Parent directories must already exist. Creation uses a private temporary file in that directory followed by exclusive atomic publication; failures/cancellation before commit leave no partial destination. Binary image bodies are not embedded: visible source has attachment indicators.

/share PATH [scope] always reviews before saving a portable local HTML file. Default scope is user/assistant conversation, excluding tool output, reasoning and system notices. Explicit selection/entry/all scopes can include private roles and are listed in the review. Common credential patterns are redacted, but this is heuristic—not a confidentiality guarantee. Review the preview and destination, scroll with pages/arrows, Tab to edit the path, and press plain Enter to save only when the complete review/confirmation is visible. Tiny geometry, resize races, modified Enter, paste and unrendered/loading previews cannot confirm. Escape or Ctrl+C cancels the output dialog, not agent work. An active Coding tool approval has higher focus priority and does not grant sharing permission.

The HTML escapes source text and contains no scripts, remote resources, forms, links or upload behavior; a restrictive CSP is embedded. Distributing the artifact is your separate explicit action. No hosted service/control plane is required. All preparation, helper execution and file I/O is off the terminal thread, with one owned operation at a time. Source is immutable even if streaming continues. A session/context change cancels an uncommitted review; already committed clipboard/ file changes cannot be rolled back or automatically replayed. /copy cancel, /export cancel or /share cancel cancels a pending output operation.

Claw requires a ready updated full-source gateway. Prompt actions fetch source before output; failed/stale/disconnected reads never copy/export a clipped or old fallback. Selection/entry actions use only a settled verified browser snapshot. Draft/cursor, queue/attachment ownership, native scrollback and terminal modes are preserved. Linux tmux and controlled real clipboard helpers are verified; desktop clipboard ownership, macOS and Windows manual validation remain open. Private fallback files persist until you delete them. Kernel-blocked filesystem operations cannot be forcibly interrupted; helper success may transfer clipboard ownership to the platform's normal persistent clipboard process.

MCP stdio tools (first transport slice)

Global settings.json and project .coding/settings.json accept mcp_servers; project entries override the same global server ID. Example:

{"mcp_servers": {"local": {"command": "python", "args": ["/trusted/server.py"], "timeout": 30}}}

Configuration alone never executes a server. /mcp connect local explicitly launches its configured argv in the session workspace—trust/review project commands before doing this. /mcp opens the browser; /mcp list shows connection/protocol/tool status; /mcp refresh local rediscovers schemas, and /mcp disconnect local releases the owned process. Connection/schema changes require idle agent/command ownership and run off the terminal loop. Workspace/session switching closes old connections, removes old proxies and reloads layered project configuration. Exit requests owned cleanup. Status does not print environment credentials.

Discovered namespaced tools enter the existing single SDK agent, not a second loop. The normal permission hook still gates every external tool; untrusted MCP readOnlyHint annotations cannot make it auto-approved. Unattended default/deny modes do not execute these tools; allow-all remains the explicit exception. Timeout/disconnect/late responses never automatically replay a call. See the Agent README for current/legacy negotiation, schema validation and limits. OAuth, richer schemas and further negotiated capabilities remain open; this is not a declaration that the whole MCP gap row is complete.

mcp_servers now also supports explicit current/legacy HTTP endpoints:

{"mcp_servers": {"remote": {"transport": "http", "url": "https://host.example/mcp",
  "headers_env": {"Authorization": "MCP_AUTH_HEADER"}, "timeout": 30}}}

The selected environment value is the full header value (for example, a Bearer header), not printed in status. /mcp connect remote reuses the same idle-gated management, SDK proxies and approval policy as stdio. HTTP JSON/SSE decoding, metadata/header mirrors, legacy session lifecycle and explicit auth/redirect/errors are documented in the Agent README. No calls are automatically replayed or tokens refreshed. Interactive OAuth, richer management and resource/prompt UI remain open; only configured static/environment credentials are currently supported.

MCP browser and inert context previews

/mcp (or /mcp browse) opens a focused server browser. Nothing connects implicitly. Select a server and use c/d/r to connect/disconnect/refresh; u/t/p lists resources/templates/prompts. / filters, arrows navigate, and Enter retrieves a preview, never model input. Template selection opens an editable URI: resolve expressions explicitly before reading. Prompt selection opens a JSON object of named-string arguments; Enter retrieves the prompt only.

In preview, i inserts text into the existing editable draft with an untrusted MCP label; a separate normal submit is required to send it. Binary insertion is refused, not silently converted/truncated. e starts the existing reviewed JSON export, retaining base64/MIME data. Export remains local, requires confirmation, and uses the selected workspace rather than guessing the TUI launch directory. Escape returns to servers/closes; Ctrl+C closes the browser. These gestures never grant external tool approval or invoke tools/call by themselves.

Coding reads run off the UI thread and cancel when the browser closes or is replaced. Connection/schema mutations retain the existing owned command lifecycle.

Remaining MCP scope includes interactive authorization/OAuth, negotiated completion, subscriptions and richer server/tool schema inspection. Binary/image resources are exportable, not directly attached to model input. Real third-party endpoints and additional platforms remain unverified.

Legacy HTTP MCP tools now support bounded cursor-based response recovery: an interrupted SSE response can be retrieved with GET/Last-Event-ID, retaining the original request identity and timeout. No second tool POST, automatic approval or connection replay occurs. Concurrent calls keep separate cursors; cancellation, auth/redirect/session errors and reset/exhausted cursors fail without replay. Current stateless HTTP never sends recovery GETs. See the Agent README for limits.

Session planning workflow

Use /plan start OBJECTIVE to enter read-only planning, then send a normal request asking the agent to build/refine the plan. The session-owned update_plan tool maintains structured pending/in-progress/completed steps with revision checks; it cannot change mode or authorize execution. /plan opens the focused inspector. Arrows select and reveal steps, pages scroll long text, r returns to planning, e reviews the execution transition, s reviews stopping the plan, and Escape closes the inspector without disabling planning. The prompt shows a mode badge.

Execution requires an idle owner and a current plan revision. The inspector's Enter confirmation works only after a visible confirmation has been rendered; resize, busy refresh or revision changes invalidate it. Direct user commands are /plan execute REVISION, /plan revise [REVISION] and /plan stop [REVISION]. These commands only change mode: no model turn or work is automatically started. Send a normal message to proceed under the host's existing tool policy.

Planning blocks shell, workspace mutations, external MCP and unknown tools in the host pre-tool hook. Read-only filesystem calls still follow the normal policy. Structured plan updates and automatic session/compaction persistence are managed application metadata, not permission to modify project files. This is a tool gate, not an OS sandbox. Durable plan state survives session reloads and branch snapshots; invalid saved state fails rather than silently removing restrictions. Plans are captured at branch creation, not reconstructed for every older message checkpoint.

Coding applies this restriction even with allow-all; entering execution does not change the selected permission mode or grant new session approvals.

Metadata writes are serialized, committed before publication and settled during cancellation before ownership is released; cancellation cannot undo an already committed metadata update. Plan changes are idle-only, preserve drafts, and never bypass approval/output-review focus. Claw-specific transport/compaction behavior is documented in its README. Desktop/platform validation remains limited to the Linux/private-tmux evidence in the goal tracker.

Trusted Python extensions (runtime API v1)

Extensions now run in owned Python subprocesses, using the existing bounded stdio transport/lifecycle owner rather than a second agent loop. They are trusted local code with your full user privileges. Process ownership contains accidental output, timeouts and cleanup; it is NOT an OS sandbox or filesystem/network confinement. Imports, hooks and user-invoked commands can themselves have effects. Tool-use permission is separate from trusting extension code.

Enable only selected IDs in user/global configuration, not project settings:

{"extensions": ["sample"], "extension_dir": "/home/user/.local/share/xdog/extensions",
 "extension_settings": {"sample": {}}}

Each selected directory contains extension.yaml (name: sample, entry_point: main) and main.py. No recursive project scanning, installation, package-manager call or workspace-authorized Python import occurs. Relative roots resolve against the global configuration directory, not the project cwd. Global names activate lazily at the first agent turn, /extensions, or extension command.

from xdog.agent.extensions import ExtensionAPI

def setup(api: ExtensionAPI):
    async def echo(params, context):
        return "echo: " + params.get("text", "")
    async def hello(args, context):
        api.notify("Hello from the explicitly trusted extension")
        return "hello: " + args
    api.tool("echo", "Echo text", {"type": "object", "properties": {
        "text": {"type": "string"}}}, echo)
    api.command("hello", "Say hello", hello)
    api.status("ready", "Extension ready")
    api.widget("summary", ["Declarative widget text"])

Tools get collision-safe ext_... aliases; slash commands are namespaced as /ext-sample-hello ARGS. The live completion provider reflects current commands. /extensions [list|reload NAME|unload NAME] manages ONLY globally trusted IDs and requires idle ownership. Commands do not silently submit an agent prompt. API v1 supports sync/async callbacks, session/turn/message/tool lifecycle events, pre-tool veto/parameter transforms, post-tool text transforms and bounded notices/status/ widgets. Legacy Python pre/post/stop hook types remain compatible.

Rewritten tool arguments are schema-validated and checked by the host's ORIGINAL permission and planning gates AFTER transformation. A plugin cannot use the API to grant tool approval/change planning mode, or mask a denied/failed call as success. UI text is sanitized and row-bounded; direct plugin prints never paint the host terminal. Timed-out/failed/canceled callbacks close their owned process, with no replay. An unavailable selected extension makes pre-tool validation fail closed until explicit unload/reload. Shutdown runs bounded stop hooks and reaps normal owned process groups; cancellation cannot roll back already completed effects.

This is a first runtime slice, NOT full Pi API compatibility: rich custom dialogs/ renderers, shortcuts/input interception, provider registration remain on the goal tracker; durable entries/context hooks are described below. Binary tool return values and tools called through extension command context are not yet supported. Code can deliberately escape normal process ownership; do not load untrusted code.

Coding project/session settings cannot enable extensions; global/user configuration is the trust source. Its regular approval mode still gates plugin tools, even after activation, and read-only planning still blocks them under allow-all. Workspace/ session switching closes the prior owner and clears old widgets/registrations.

Durable extension entries and context hooks

Extensions can now call api.append_entry("note", {"text": "durable data"}) from an active callback. Host-owned JSON entries are bounded, independently namespaced per extension and committed before the callback receipt is returned. Subsequent callback context contains that extension's context["entries"]; unload/reload retains it. Session/new/branch activation follows the selected durable snapshot, not process globals. Old owners cannot publish entry writes after navigation. Close invalidates late writes, so shutdown callbacks should not append entries.

Register api.context(callback) to transform the ephemeral message list before EACH model invocation. The callback receives serialized typed user/assistant/tool content plus its entry snapshot and returns a message list. It may inject summaries or filter complete history batches, but does not edit the canonical transcript, system prompt, static cache prefix, planning mode or tool permissions. Coding also composes an existing SDK transform before its extension pipeline.

def setup(api):
    async def remember(args, context):
        api.append_entry("note", {"text": args})
        return "Saved entry"
    async def model_context(messages, context):
        notes = [row["data"]["text"] for row in context["entries"] if row["kind"] == "note"]
        if notes:
            messages.append({"role": "user", "content": [
                {"type": "text", "text": "Context note: " + notes[-1]}]})
        return messages
    api.command("remember", "Persist a note", remember)
    api.context(model_context)

Unknown/system roles, unsupported parts, invalid images/signatures and incomplete/ invented tool-result batches fail explicitly, not by silently dropping content. No-op transforms preserve original typed messages. Images and signed thinking/tool parts survive chunked full-context transfers beyond the individual RPC frame limit. Transfers are checksummed, order/identity fenced and spooled off-loop, with a 128 MiB explicit context limit, 64 KiB blocks and an absolute whole-transfer deadline.

Entry capacity is 128 entries across a session, 16 KiB per entry and 512 KiB total. Invalid/corrupt stored state fails instead of quietly resetting it. Persistence failure keeps the previous snapshot. Cross-loop cancellation settles committed metadata/file ownership before the host releases its lifecycle reservation; it cannot roll back an already completed write. Context-hook failures disable the extension and do not trigger another model call or automatic replay.

This closes the basic durable-entry/pre-model-context gap, not the whole extension API row: custom renderers/dialogs, shortcuts/input interception, provider registration, richer command context and binary tool results remain unfinished. Extension code is still trusted code with the user's privileges, not an OS sandbox.

Coding saves entry snapshots with its session JSON and branch metadata. Resuming invalid entries refuses the session instead of falling back to an unrestricted replacement. Context contributions are not persisted as user chat messages.

Interactive extension dialogs

Active extension callbacks now support await api.select(title, choices), await api.input(title, message="", default="") and await api.confirm(title, message). Optional timeout is bounded to 0.1–600 s. Select/input return None on cancel/no UI; confirm returns False. Cancel answers this dialog only—it is not tool approval, permission revocation or automatic turn submission. Trusted extension code decides what to do with the answer.

The worker suspends the callback and returns an owned pending-callback/dialog receipt. The host displays a focused bounded form, then resumes that SAME callback with the validated answer; it does not rerun a command/tool to obtain the answer. Multiple sequential dialogs work. Without an interactive host, canceled values are returned immediately rather than hanging unattended execution.

Forms sanitize control codes, scroll long details, preserve the main editor draft and input cursor, and require a rendered visible question/action before Enter can answer. Select/confirm pastes do not submit; tiny/resize/release/repeat/unpainted selection input cannot confirm. Escape/Ctrl+C cancel the dialog. Tool approval and output review retain higher focus priority. An affirmative extension confirmation never grants the underlying agent tool permission or switches planning mode.

Dialog IDs are host-generated and scoped to their session/request/run/connection. Timeout, abort, navigation, disconnect and shutdown settle/cancel pending futures; late/foreign answers cannot resume a successor's callback. Claw command waits are connection-owned background jobs so the socket can still process UI replies and other controls while the group remains reserved. Context-hook dialogs still share the context transfer's absolute deadline; the UI does not renew model/transport budgets. Callback effects remain trusted code, not OS-enforced isolation.

Arbitrary custom renderers/overlays, richer forms, input/shortcut interception, provider registration and further extension command/binary capabilities remain unfinished; these dialogs do not declare full Pi API compatibility.

Supervised child agents

/agents start TASK starts bounded read-only child work. /agents start --inherit TASK inherits the current host policy; it does not copy approval grants or bypass planning. Inspect with /agents list or /agents inspect ID; use steer ID TEXT, follow-up ID TEXT, cancel ID, wait ID and close ID under /agents. Parents also receive reserved agent_spawn/agent_control tools; children cannot recursively delegate or update their parent's plan.

Children share the workspace, so coordinate conflicting writes. Context is copied at admission through a complete tool/result checkpoint. Model/options/tool execution mode are inherited. Defaults are three active children, 16 retained records, 120-second execution deadline, 16 model calls and a 32,000 reported-token stop threshold—not exact billing. Output limits are clamped; input accounting can overshoot on the final request. Cleanup/persistence may outlast the execution deadline.

Each child has independent Coding approvals and extension workers/state. Parent validation/context hooks and current planning/permission gates remain authoritative; rewritten effective arguments are checked before host approval. Approval panels identify the child, queue concurrent questions, and preserve the editor draft. Active children block navigation/policy/schema changes until canceled or settled. Interrupt cancels children; exit joins ownership. Atomic session/branch-scoped child sidecars retain final results and extension entries; interrupted work is never restarted automatically. Closed results remain archived on disk. Corrupt catalogs visibly disable delegation but do not disable the parent conversation. Shared parent pre-hooks remain serial; inherited validation uses no-UI canceled dialog values rather than another run's interactive lease.

/agents opens a live roster; Enter or /agents inspect ID opens complete retained child source with / search, navigation, selection, f optional fullscreen and explicit copy/export/share review. Escape returns to the roster and parent draft. n creates a task, Tab selects inherited access, s/t steer/follow-up, c cancels and x archives, with visible specific confirmations. A pending wait does not block cancel controls. l enables live snapshots without overwriting search/selection. Source conversion runs on one coalescing worker; output review is child-scoped. Arbitrary richer child forms and platform validation remain pending; child extension dialogs use independent child/parent-session/UI leases, never another run's question owner. Trusted Python callbacks remain unrestricted code; none of this is an OS sandbox.

Running child tools expose their latest reported progress through the same child source/search view. It is explicitly live/pending, not a final result. Transient progress has a separate 512 KiB escaped-JSON / 128-source display-retention budget; clipping/omission is labeled and does not abort valid execution. Completed canonical tool results replace progress; transient snapshots are not inserted into model history or persisted as fabricated results. Source IDs preserve concurrent/reused-call ownership.

Child extension select/input/confirm dialogs now display the child identity and resume the original suspended callback with actual user answers. An extension yes is NOT tool approval or a planning transition. Input/select cancel returns None; confirm cancel returns False. Detaching/exiting the origin UI settles pending questions and subsequent child questions use canceled values; callbacks/work are not replayed or automatically adopted after reconnect. Parent inherited validation still uses silent no-UI answers to avoid borrowing its separate dialog reservation. Arbitrary richer extension forms and platform validation remain open.

Child specialization and limits

/agents start --label Reviewer --instructions "Inspect only" --tools filesystem,current_time --fresh --timeout 60 --calls 4 --tokens 8000 -- TASK specializes a child without changing parent permissions. Options precede the task; -- preserves a task that starts with flags. Plain tasks preserve quotes/newlines. --tools none gives the child no executable tools; otherwise selected names must exist in its inherited tool set and cannot include delegation/parent-plan metadata tools. Unknown/malformed flags fail visibly. Instructions append to, rather than replace, parent restrictions.

In /agents press n, then Ctrl+G for settings JSON. Supported keys are label, instructions, tools (array or null for inherited tools), inherit_context, timeout, max_model_calls and max_tokens. Enter applies the settings draft only; start review requires every page to be painted before confirmation. Resize invalidates that review. This confirmation does NOT approve tool use. Effective settings persist with child results and appear in complete child source. Request/token caps may be lowered, not raised above 16 / 32,000; token usage is not exact billable accounting. --fresh omits canonical history/child entry inheritance, not mandatory parent validation/context or the shared workspace. Model/reasoning inherit by default; explicit overrides now use the actual adapter. Named presets now use explicit user configuration. Claw specialized starts require its advertised child_config capability, avoiding silent old-gateway settings loss.

Child model and reasoning overrides

/agents start --model provider/model --thinking low --fresh TASK selects child routing/reasoning independently. Settings JSON accepts model (null inherits) and thinking (null inherits, off disables, or advertised minimal/low/medium/high/xhigh). Choices resolve on the SAME request adapter's cached active catalog; no automatic provider activation/login/catalog sync/fallback occurs. Plain custom adapters without metadata reject overrides. Claw requires child_config: 2 for these fields.

Requested and effective routing/reasoning/limits persist separately in child source. Selected capabilities/window/output limits apply, while parent/child planning, approval/external-tool gates remain unchanged. Same-model aliases preserve explicit host native-tool overrides. Switching to a nonreasoning model disables inherited reasoning; unsupported positive choices fail visibly. Model switches omit inherited/ transformed provider continuity and old reasoning from the CHILD copy, leaving parent provenance/history intact. Transformed images are validated; oversized switched-model contexts fail a conservative byte-based preflight with fresh/lower-output/larger-model guidance, not automatic summarization or exact token accounting. Parent model/options/ settings/grants never change. Named presets now use explicit user configuration.

Named child presets

Define child_presets in Coding's user/global settings JSON or Claw's operator YAML using this descriptor shape:

{
  "child_presets": {
    "reviewer": {
      "description": "Inspect and report without workspace mutations",
      "config": {
        "instructions": "Review requested code; return findings with file locations.",
        "tools": ["filesystem", "current_time"],
        "inherit_context": false,
        "max_model_calls": 4,
        "max_tokens": 8000
      }
    }
  }
}

Browse /agents presets, or use /agents start --preset reviewer --tokens 6000 -- TASK. Explicit flags override template fields. Ctrl+P selects a template in a new child form, Ctrl+G adjusts the draft, and full expanded settings/task review still precedes execution. Selection itself makes no model request. Templates cannot encode access/permission changes, code/imports, recursion or provider activation. Starts remain read-only unless inherited access is explicitly selected separately; all host gates remain active.

Frozen catalogs are bounded to 32 entries / 256 KiB raw and expanded data. Expanded settings/provenance persist; catalog edits never replay historical work. Coding project/session data cannot activate presets, and invalid global preset data retains parent permission settings. Claw invalid operator templates fail without fallback; preset controls require child_config: 3. Old schemas receive supported inherited fields only; chosen nondefault routing/template settings are never silently dropped.

Interactive MCP authorization

/mcp authorize SERVER opens endpoint/resource/client/scope/TLS-trust consent; in /mcp use a on the selected server. Page through EVERY field before Enter confirms. Resize/tiny/unpainted/repeat/paste input cannot authorize. The resulting reviewed URI is for your browser, not code/token paste into chat. y explicitly copies after full URI review; no browser or model opens automatically. c cancels a pending callback, s reads state, and Escape leaves the view and cancels pending ownership.

Authorization alone does not connect/register tools or approve execution. Return to servers and c to connect explicitly. Server-list s inspects the authorization; f reviews local forget/disconnect (not remote revocation). Existing tool approvals, Claw allow_tools/planning and child/model rules remain active. Parent drafts/caret and terminal modes survive. Tokens remain memory-only with no automatic refresh/replay.

Consent is scoped to view/session/context and, in Claw, its original socket/group. Another connection cannot use a known receipt. Leaving, canceling, disconnecting or changing the scope rejects late credential commitment and settles pending listeners; reconnect does not adopt a waiting flow. Already completed resource credentials may survive closing the success view. Claw requires advertised mcp_auth controls; schema changes/forget still require idle ownership. Pending HTTP jobs do not block socket reads. The constrained pre-registered profile from pass 27 remains: broader metadata/ registration, persistence/refresh and full external-provider/platform verification are not claimed complete.

Authorization metadata discovery

Resource denial challenges now retain their resource_metadata pointer and operation-specific scopes for an explicit subsequent authorization, without replaying failed MCP POSTs/tools. Quoted multi-scheme headers are parsed with bounds; malformed or ambiguous challenges are not silently discarded. Without a pointer, path-specific then root protected-resource metadata are tried. Issuers use ordered OAuth and OIDC well-known candidates; only absent GET routes (404/405), never bad identity/200 data, redirects or token/code errors, permit fallback.

Pointer metadata uses no existing MCP credentials/cookies. Its origin must be the configured resource/issuer or an explicitly approved HTTPS origin in the optional OAuth metadata_origins array (at most eight origin-only URLs). This approval does not change the pinned issuer or authorization/token endpoint trust. Resource/issuer identities still match exactly. Challenge scopes are operation-specific and take priority without assuming they are listed in scopes_supported; the configured scopes remain an operator ceiling. Scope-less listings are displayed as no request, and over-ceiling requirements fail rather than silently expanding access. Actual review data carries discovery URLs and scope-source provenance. Consent/permissions/ leases/explicit connect remain separate. Registration and persistence/refresh are still pending; successful offline discovery is not proof of every external provider.

Client registration alternatives

OAuth profiles accept registration with an explicit strategy:

  • pre-registered (default): existing public client_id behavior.
  • metadata: client_id is the operator's hosted HTTPS JSON document URL (with a path), and callback_port is fixed/nonzero. The issuer must advertise metadata support; the document's client_id/name/public method/code types and exact http://127.0.0.1:PORT/callback registration are validated without linked-asset fetching, redirects or implicit document hosting. No hidden DCR fallback occurs.
  • dynamic: omit client_id. The issuer must advertise a pinned-origin HTTPS registration endpoint. The full review explicitly describes creating a remote native public-client record. Only AFTER consent does one JSON POST register the exact allocated callback/code/public method/scopes; a validated issued ID is used for the normal resource-bound PKCE flow. No secret/management token is stored/sent.

Example dynamic profile (legacy/deprecated registration is explicitly selected):

{
  "issuer": "https://issuer.example",
  "registration": "dynamic",
  "client_name": "My XDOG CLI",
  "scopes": ["tools:read"]
}

Cancel cannot undo a remote record. Unknown outcome/invalid response consumes the receipt, closes the listener and never replays registration; fresh review is required. Client metadata requires operator-hosted reachable data and the supported fixed callback, not automatic hosting or general port-variance compatibility. Same Coding/ Claw consent, lease, status/local forget, explicit connection and tool-policy gates apply. Claw negotiates mcp_registration capability. Credential/registration persistence, refresh, confidential client methods and remote management remain pending; offline peer tests are not a claim that every issuer supports these profiles.

Pre-registered confidential token methods

An OAuth profile can explicitly choose token_auth_method: client_secret_basic or client_secret_post and client_secret_env: MY_MCP_CLIENT_SECRET. Default none remains public. These methods require pre-registered client_id; metadata/DCR profiles cannot silently adopt secrets. Literal client_secret/config-file credentials and unimplemented methods fail rather than downgrade. Issuer method metadata must match.

The full consent shows method and environment variable NAME only. Secret values are resolved solely from the configured source, checked before discovery/start and anew at exchange; they are not stored on profiles/reviews/status/browser URLs or in files. Basic correctly form-encodes both credential components before base64; POST uses its form body, never both. Credential-bearing data is restricted to the exact reviewed pinned HTTPS token endpoint—not resource/metadata/registration hosts. Errors remain secret-safe. Missing/unsafe/removed sources fail closed, with no code/request replay.

Existing PKCE/resource/scope, Coding approvals, Claw allow_tools/planning and view/ connection leases remain authoritative. Claw negotiates mcp_client_auth and verifies reviewed method/source fields before confidential start. Authorization is not tool approval or implicit connection. Persistence/refresh, private-key/mTLS methods and full service/platform interoperability remain future work.

Explicit in-memory OAuth refresh

Refresh is an operator action, never a background token retry. After an issuer actually supplies a refresh token, open the server's authorization status and press r. Review every page of the resource, actual granted scopes, pinned endpoints, client ID and authentication method/source, then press Enter. This sends one resource-bound refresh POST, not a new browser login or client registration. Claw requires gateway capability mcp_refresh: 1; unsupported gateways cannot submit it.

The SDK equivalents are manage_auth("remote", "refresh_prepare"), followed by manage_auth("remote", "refresh", review_id) under the same live consent lease. Receipts are single-use and expire after five minutes. The request uses the actual prior grant's scopes, not the broader original configuration. Valid replies may narrow scopes but cannot widen them. Issued refresh tokens rotate; if a valid reply omits rotation, the prior token is retained only after that reply is validated.

The old refresh capability is consumed before exchange. Cancellation, a lost response, invalid response or revoked owner discards it: uncertain rotation must not replay the old token. c cancels a pending refresh; closing/disconnecting the view settles its task and fences late commits. A still-valid old access token can remain usable on refresh failure; authorize again to restore refresh capability. Failed refresh preparation without an existing lease does not reserve the server against another view. Local forget erases both access and refresh credentials.

Refresh never replays MCP requests, automatically connects tools, changes approvals, registers a client or widens permissions. Credentials remain memory-only: durable credential/registration storage and reload, further client methods and remote revocation/management remain open. Issuers may not issue refresh tokens (the dynamic registration request still declares the code grant only). Offline peer/terminal coverage is not evidence of live identity-provider compatibility. This section supersedes earlier descriptions of explicit refresh as entirely unimplemented; automatic refresh and persistence are still not supported.

macOS image/text clipboard adapter

Explicit Ctrl+V or /paste on macOS now selects one owned osascript JavaScript for Automation helper using AppKit/Foundation. It reads PNG, JPEG or WebP binary pasteboard types before text. TIFF-only image data is converted to PNG through NSBitmapImageRep; clipboard file URLs are not opened and no application is controlled. Unicode text and an empty clipboard use the same snapshot protocol.

The helper checks the pasteboard change count before/after the read and refuses a changed snapshot. Source/converted images remain capped at 10 MiB, TIFF conversion at 100 megapixels, and text at 1 MiB. Python bounds the encoded response, validates its JSON/base64 protocol, then reuses the usual image-header/dimension and composer admission checks. Existing off-thread timeout/cancel/reap and draft/epoch fencing remain in force. A native error is not retried as text or silently switched to a different clipboard backend; it never automatically sends input or grants approval. When osascript is absent, pbpaste remains explicitly text-only fallback.

Evidence here is independent scripted AppKit-contract execution and real owned helper subprocesses on Linux, plus both production terminal clients' Ctrl+V, preview, image send/recovery and terminal restoration with that helper protocol. The actual Apple JXA bridge, a real Mac pasteboard/desktop (including pasteboard access denial), Kitty/iTerm and Windows still require platform validation. This is an implemented adapter, not a claim that those native-platform gaps are verified.

Explicit MCP named-argument completion

After explicitly connecting a server, open its prompt or resource-template catalog and select an entry. In the JSON-argument/resolved-URI form, Ctrl+Space opens named completion. Ctrl+N/P selects an advertised variable, Tab explicitly requests suggestions for its current prefix, and Up/Down selects a returned value. Enter inserts the visible selection (or typed value) into the editable argument/URI draft only. A separate Enter in the parent form retrieves a preview; preview insertion into the main prompt remains another explicit action. Escape returns without retrieval or agent input. No completion runs on ordinary typing.

Requests require the server's negotiated completions capability, an advertised prompt/template and named variables. They carry other edited arguments as context, not as reserved top-level fields. Responses retain server ranking, at most 100 bounded strings, optional total count and hasMore; additional results are indicated rather than silently truncated or automatically paginated. Narrow the prefix and request again. Editing/changing variables cancels stale work and discards old suggestions; scope/context/prefix echoes and existing session/view/connection leases fence late replies. Claw requires mcp_completion: 1 from the gateway.

Accepted variable edits used as context are retained with the selected value. Template insertion expands RFC 6570 scalar strings (all operators, prefix and scalar explode modifiers); other undefined variables are omitted as specified and the resolved URI stays editable. Arrays/associative expansion are not accepted by this named-string form. It never opens a returned URI or clipboard/local path: resource preview uses the already connected MCP server only. Invalid JSON/URI input and busy submission preserve the draft instead of consuming it on failure.

Completion is read-only data, not authorization or a tool call. No automatic connection, token refresh, MCP call retry, model turn, permission widening or resource subscription is introduced. Repeat Enter cannot implicitly retrieve a preview after a suggestion is accepted. One active and one queued explicit intent per server are allowed; excess concurrency fails visibly. A short cancellable interval limits requests without losing fast explicit edits or retrying a POST. Results use Claw's existing bounded checksummed source transport, not oversized control frames.

Offline current/legacy stdio and loopback HTTP peers, both live app paths and both private-tmux terminal forms are covered. This does not establish third-party server or non-Linux platform interoperability. Resource subscriptions, richer negotiated client/schema capabilities, credential persistence and remaining extension/platform work stay open; earlier notes listing completion as entirely absent are superseded. The configured completion timeout includes queue wait, catalog and POST. Opaque/plain-resource completion references outside advertised named templates remain open rather than being inferred from the named-template implementation.

Opaque resource-reference completion

Use v on a configured server or a resource-catalog entry to open the opaque completion form. The selected URI is prefilled only when there is an actual catalog entry; otherwise enter it explicitly. Supply the server-documented argument name, current value and context as JSON, for example:

{"uri":"opaque://known/ref","argument":"documented-name","value":"pre","context":{"language":"python"}}

No argument name is guessed from a URI, description or local file path. Ctrl+Space opens the named-value picker; Tab explicitly requests ref/resource completion. The same bounded ranking, context, cancellation, timeout and late-result fencing apply as for prompt/template completion. Unlisted/dynamic resource references can be completed, and completion-only servers can connect without pretending to expose tools, prompt catalogs or resource catalogs. The server decides the meaning of the explicit reference/name/context; this client never follows the URI.

Enter accepts into the JSON argument draft only. A separate Enter reviews the selected/edited data locally, labelled "Operator-selected/edited" and "NOT a resource read or resolved URI". i in that preview explicitly inserts the quoted untrusted data into the preserved main draft; it still does not send a model turn. An opaque suggestion is an argument value, not an inferred replacement URI: there is no resources/read, template expansion, local path opening or implicit connection/authorization. JSON forms now use up to three bounded editor rows so wrapped reference/value/context fields remain inspectable; invalid forms preserve their text rather than consuming it.

Claw additionally negotiates mcp_resource_completion: 1; missing support fails before a remote request and leaves the prefix editable. This separate capability preserves existing mcp_completion: 1 prompt/template behavior. The selected-data review is local in both clients, not a new gateway resource-read operation.

Independent current/legacy stdio and HTTP peers (including completion-only profiles), both real app paths and private-tmux terminal forms verify this behavior. Invalid completion-only capability metadata fails closed. No live third-party server, identity-provider or non-Linux platform compatibility is inferred. This section supersedes earlier notes that opaque/plain-resource completion was unimplemented; subscriptions, richer schemas/client capabilities, persistence and the other original gaps remain open.

MCP JSON Schema and typed structured output

Current-protocol MCP results now retain any finite JSON value in structuredContent: objects, arrays, strings, numbers, booleans and explicit null. Missing content is tracked separately from null by structured_content_present; advertised output schemas must be satisfied. Legacy negotiated profiles still require object results. A successful text summary no longer hides canonical structured data: when no exact canonical JSON text block is already present, a labelled structured-output block is added to the tool content used by the model, full-source viewer and persistence. Compact rows may still collapse it; Ctrl+O or the transcript viewer inspects it.

JSON Schema defaults to 2020-12; recognized declared draft-04/06/07/2019-09/2020-12 semantics are used rather than silently substituting another dialect. Local pointers, anchors/dynamic references and bundled resource IDs resolve only inside the advertised document. External documents are never fetched, including through an ID/base change. Unknown dialects, unresolved references and unsupported required vocabularies fail closed. Unreferenced examples/defaults remain data; explicitly referenced targets are checked as schemas. Normal nullable unions, boolean property schemas and conditionals are supported. MCP arguments remain objects; model parameters get an object-root intersection while validation keeps all raw advertised constraints.

MCP uses an optional sync/async AgentTool argument validator before permission hooks and validates effective arguments again immediately before the remote call. Coding and Claw extension pre-hooks use the same check when they rewrite arguments. Existing permission/planning/allow_tools rules still decide execution; a schema/readOnly hint never approves a call. Coding's parameter prompt formatter now handles boolean/union property declarations rather than crashing on a valid schema.

The validation helper is local, owned and cancellable, started only for explicitly connected tool schemas. It runs isolated Python imports (-I) with only the trusted installed/vendored library root explicitly added, without a second agent/model loop or schema URL retrieval. A three-second deadline includes queue wait/validation; timeout/cancel kills and reaps that exact helper, including a child still being created. A later explicit validation may start a fresh local helper; remote MCP calls are never retried by it. Serialized schema caching is capped at 32 entries/4 MiB. Schema/value bytes, depth and node counts are bounded; these are application limits, not OS-enforced filesystem/network/memory sandboxing.

Bounded JSON tool details now survive save/reload/branches in both clients, preserving typed structured output and null-presence flags. Live/non-JSON handles stay transient. Transcripts may contain sensitive structured values: keep them private. Actual current/ legacy peers, both app permission paths and Linux/private-tmux inspectors are verified; provider-specific full-schema acceptance and mixed-dialect/native-platform interoperability are not inferred. Format-assertion/custom required vocabularies, ambiguous HTTP header mappings through schema composition, richer content/client capabilities, subscriptions, credential persistence and extension/platform gaps remain open. Earlier object-only/current structured-result limits are superseded by this pass.

Schema packaging includes a direct referencing dependency and the portable SDK bundle dependency list. The helper resolves its own trusted library root, so vendored bundles work without accepting project/PYTHONPATH shadow modules. An actual vendored helper test covers this; generated-output/vendored-package licence boundaries are unchanged. Lock updates only declare the already-resolved dependency, not unrelated version upgrades.

Reviewed typed extension forms

Trusted Python extensions can now use await api.form(title, fields, message="...", timeout=300) in their owned command/tool callbacks. It returns a typed named object, or None on cancel, timeout, headless/unattended execution or unsupported viewer. Defaults are offered to a live user, never silently submitted headlessly. Example:

answer = await api.form("Configure task", [
    {"name": "name", "kind": "text", "required": True, "min_length": 1},
    {"name": "count", "kind": "integer", "default": 0, "minimum": 0, "maximum": 10},
    {"name": "enabled", "kind": "boolean", "default": False},
    {"name": "mode", "kind": "choice", "default": None,
     "choices": [{"label": "None", "value": None}, {"label": "Zero", "value": 0}]},
    {"name": "notes", "kind": "text", "multiline": True},
])

Fields support text, integer, finite number, boolean and labelled typed scalar choices; required/default, descriptions, text length and numeric ranges are validated independently by the host. Unknown constraints/field names, duplicate names/typed choices and coercions fail. False, 0, empty text, explicit null choices and omitted optional fields remain distinct. Required means a field must be present; use min_length to require nonempty text. Forms are flat/bounded (1–32 fields, 128 KiB declaration and 64 KiB answer), not arbitrary code/custom rendering or nested JSON Schema UI. Integers are limited to the interoperable ±(2^53−1) range.

Tab/Shift+Tab commits/navigates fields. Text/numbers edit normally; Space or Left/Right cycles boolean/choice values and unset. Enter adds a line in a multiline field, otherwise opens full-form review; Ctrl+Enter reviews from any field. PgUp/PgDn pages help/review. Every review page must paint before Enter submits; Ctrl+E returns to editing and Esc/Ctrl+C cancels. Invalid drafts stay editable, and resize/visibility changes invalidate prior review. Field help, limits, requiredness and selected/omitted values remain inspectable in bounded rows.

Forms are extension input data, never host tool approval, planning/permission-mode changes or automatic agent submissions. Coding approvals retain priority. Parent and child callbacks use their existing session/connection/UI leases and independent brokers; malformed/foreign answers cannot resolve a form. Cancel, disconnect and unattended paths do not replay the callback or invent defaults.

Claw negotiates extension_forms: 1 in the client handshake and server pong. Older viewers receive cancellation rather than an unrenderable form; basic select/input/ confirm compatibility is unchanged. Both live parent/child paths and Linux/private- tmux command forms are tested. Arbitrary renderers/nested layouts, interception, provider registration, richer extension commands and native-platform validation remain open; this pass does not waive them. No OS sandbox is added.

Rich extension tool results

Trusted extension tools are no longer text-only. Existing string callbacks are unchanged. A callback may return a typed AgentToolResult, a content/structuredContent/ details object, or use the validated builder:

return api.result([
    {"type": "text", "text": "Result summary"},
    {"type": "image", "data": png_base64, "mimeType": "image/png"},
], structured=[False, 0, None], details={"count": 0, "enabled": False})

Text and PNG/JPEG/WebP images remain typed through the one existing agent loop, model context, tool UI and transcript persistence. Structured JSON (including explicit null) remains canonical inspectable content, not hidden behind a summary. Extension metadata is separate: extension_details_present distinguishes omission from explicit null and extension_details retains the exact bounded JSON value. It is not converted to a model message or interpreted as host permissions. Typed AgentToolResult content/details is accepted too; structured data uses the builder or explicit structuredContent envelope.

Rich tool data is snapshotted and independently checked at worker and host boundaries. Unknown content kinds/extra fields, invalid image base64/MIME, nonfinite/non-JSON values, coercions and oversized frames fail rather than silently discard data. Limits: 64 content blocks, four images, 2 MiB decoded per image, 1 MiB per rich text block, 256 KiB each for structured data/details, and 3 MiB for the encoded rich result envelope. These are explicit API/frame limits, not OS-enforced sandboxing. No file/URI is implicitly opened and no image is automatically saved or attached as a new user prompt. Raise an exception for tool errors; rich isError/audio/resource payloads are not silently guessed in this API version. Commands remain bounded text.

Coding approvals, Claw extension_allow_tools, planning and child inherited permission restrictions still gate execution. Text-only post-hooks do not replace or drop a result containing images. Legacy text callbacks/post-hooks retain their behavior. Both UIs display tool images without printing base64. Claw now forwards final tool image parts (previously missing from its display events), with a bounded live image budget. Larger generic tool images are visibly marked as omitted from the live preview; original data stays in canonical model/transcript history, not in an oversized socket line. The new rich-extension envelope fits that transport budget.

Independent real workers, denied/allowed live app paths, typed persistence, inherited child output, malformed/unsupported results, large-preview notices and both private- tmux image/JSON inspectors are verified. Rich command contexts/returns, arbitrary renderers, input interception/provider registration, audio/resource/error-image interop, remaining MCP/credential and native-platform gaps stay open. This section supersedes earlier text-only TOOL limits; it does not waive the remaining goal.

Rich extension command artifacts

Extension command callbacks now accept the same typed text/image/structured JSON/ details result builder as tool callbacks. Legacy string commands remain strings. The SDK runtime.command() returns either str or a frozen ExtensionCommandResult with extension/command identity, a validated result snapshot and safe summary. For example, a command can return:

return api.result([
    {"type":"text","text":"Command report"},
    {"type":"image","data":png_base64,"mimeType":"image/png"},
], structured=[False, 0, None], details={"enabled":False})

Both interactive clients append a read-only command artifact, not a tool result or agent message. Images render through the existing image component; base64 is not printed as prose. Ctrl+O/the transcript viewer inspects the complete text, typed JSON and details. Transcript copy/export can inspect its textual system-source record. Original image bytes and typed metadata remain owned in the local artifact; this does not pretend a text-only transcript export includes binary images.

Returning /quit, a URI or other command-looking text is data, never executed. Rich artifacts cannot trigger a model prompt, mutate the main draft, approve tools or change planning/permission modes. The prior draft/focus and normal approval priority remain; callbacks still require trusted activation/idle ownership. Results are fenced by command/session/connection identity; canceled/stale/foreign output is not appended to a new owner and callbacks are not replayed. Artifacts are local UI results, not newly persisted canonical agent-history entries or automatically saved files. Legacy command/builtin skill prompt behavior is unchanged.

Claw independently negotiates extension_command_results: 1 in hello/pong. Its controlled response carries a validated artifact separately from the safe output summary. Older viewers get a visible update-required summary without image bytes, not a dict stringification containing base64; no callback is rerun to degrade it. New clients reject unnegotiated/malformed artifacts rather than guessing semantics. The rich envelope's existing byte/count/image/JSON limits apply, keeping control responses below the receiving transport budget.

Independent real command workers, both production UI paths, permission/draft/history non-mutation, system-source inspection, stale-owner rejection, older-viewer behavior, cancellation and malformed returns are tested, plus both private-tmux image/JSON inspectors and parent terminal restoration. Rich command HOST ACTION CONTEXT (safe session/navigation/editor callbacks), arbitrary rendering/layouts, interception, provider registration, binary artifact export/persistence and remaining MCP/platform gaps stay open. Returning an artifact does not waive those requirements.

Lossless command artifact export and read-only reload

/export PATH artifact captures the latest owned rich command artifact, including original image bytes, text, structured JSON and typed details. It is private JSON only (json may be explicit); implicit binary copy/share or lossy text/Markdown artifact export is refused. Supplying a path does NOT bypass review. The existing output panel shows image MIME/size/checksum and a no-redaction/sensitive-binary warning, without rendering base64. Enter writes only the reviewed immutable snapshot.

Files use the existing private 0600, same-directory staging/fsync and no-overwrite commit path. Cancel, hidden/tiny/resize review, owner/context changes and stale jobs cannot authorize an unreviewed write. A commit already made is not remotely undone; cancellation is not rollback. No upload, provider request, tool call or automatic artifact save is introduced. Ordinary textual transcript exports retain their prior semantics and are not falsely labelled binary exports.

/artifact PATH explicitly reads a bounded regular artifact file off the UI thread and appends a read-only image/JSON command display. It does not activate extensions, execute stored command names/text/URIs, change permissions/planning or send input to the model. The 4 MiB file envelope is versioned as xdog.extension-command/v1; its SHA-256 covers canonical validated identity/content/metadata. Checksums detect corruption, NOT authenticity: treat files as untrusted data. Unsupported versions, tampered/malformed data, image/MIME/type failures and FIFO/nonregular inputs are refused. Original image bytes and false/zero/null/omission remain exact on reload.

Artifacts are selected by session/connection/history epoch. Old-owner results cannot be exported or appended to a new owner; loading snapshots publishes only under the captured context. A read can be canceled by closing/changing owner, suppressing late publication. An OS-blocked filesystem thread cannot be forcibly interrupted; these checks/time/memory bounds are not OS-enforced sandboxing or filesystem confinement. Loaded artifacts become owned display data and can be re-exported after a new review.

Both actual clients and private-tmux workflows verify binary save/reload, explicit review, private mode/no overwrite, owner fencing and no-model/draft non-mutation. SDK artifact_bytes/artifact_from_bytes/read_artifact expose the same validated codec. Remaining custom rendering/command host actions/interception/providers, further MCP/credential and native-platform requirements remain open.

Command-only editor host context

Trusted command callbacks can now use snapshot = await api.get_editor() and await api.set_editor(new_text, snapshot["revision"]). The captured object contains text, character cursor, image count and an opaque revision receipt. A replacement returns bool; conflict/unavailable contexts return False/None without overwriting the user's draft. Example:

snapshot = await api.get_editor()
if snapshot is not None:
    applied = await api.set_editor(snapshot["text"] + "\nPrepared by command", snapshot["revision"])

This capability exists only during an actual interactive extension COMMAND, under that extension's current command/session/connection owner. It is unavailable to ordinary tool/lifecycle/context/pre-/post-hook callbacks, inherited child tools, unattended/headless callers and unsupported viewers. A forged worker flag cannot borrow another operation's editor context; the host checks the original RPC action and current UI command/extension ownership independently.

Revision receipts are random, single-use and bound to the original text, cursor and attachment identities. User edits/caret moves/attachment changes, new commands or changed session/connection/history epoch invalidate replacement. Invalid/terminal- control/oversized text is rejected rather than silently sanitized into a different draft. Text replacement preserves typed attachments and normal undo behavior; it never sends a model turn, changes permissions/planning or executes draft text/URIs. Limit: 64 KiB UTF-8 editor snapshot/replacement and explicit bounded request timeout.

Coding's approval/output/overlay priority defers host editor service rather than changing the draft behind another dialog. The existing owned pending callback and validated UI-reply path carries editor requests/receipts; it is not a second agent loop. Cancel/exit/owner changes settle or refuse requests, and callbacks are not replayed. Claw independently negotiates extension_editor: 1 in hello/pong; older viewers cancel the request and leave command/editor behavior unchanged.

Both real command workers, UI CAS-success/conflict cases (including retained Coding attachments), wrong owner/receipt/cursor/image changes, invalid control data, headless/tool/older-viewer refusal and private-tmux typed draft transforms are tested. Session/navigation/model host-action APIs, broader command context, custom rendering, interception/provider registration and remaining MCP/native-platform goals stay open. Editor access alone does not redefine or complete that larger scope.

Command session metadata and native browser context

Failed extension inventory and cancellation

Failed/disconnected/unloaded worker registrations withdraw from the current tool inventory. Historical alias identities and stale-executor/argument guards remain; withdrawal does not authorize an old approved call or unload a mandatory validator. Projection settles on the owning Agent/command/child loop, including callback errors and cancellation. Other healthy workers/native tools remain unchanged. Child refreshes retain their explicit selected-tool subset. A post-hook failure preserves the actual tool result instead of replacing it with invented success.

Failure catalog updates are descriptive, owner-scoped metadata: they cannot adopt a different session, activate/reload a worker or approve tool execution. Old or foreign cached contributions never become current merely because a callback failed. Reload/unload remain explicit user actions; uncertain callbacks are not replayed.

Terminal command help

/help lists the current terminal command/completion menu, including UI-only controls and already registered extension commands. /help QUERY matches command names or descriptions case-insensitively. It does not execute callbacks, load an extension, submit model work or grant tool approval; availability is still checked when a command is actually invoked. Explicit unload/failure/menu changes appear on the next help request. Descriptions are inert single-line text and terminal controls are stripped. Oversized/invalid menus fail explicitly, never masquerade as a complete truncated list.

Help may be inspected during active work without resetting its timer or replacing the draft. Claw help reflects the current TUI completion snapshot, not an invented live runtime/catalog capability on older gateways. Use /extensions to refresh extension status explicitly. Coding's headless help retains its separate Skills section and also supports query filtering/known registered commands.

Registered command argument completion

Trusted Python commands may register an independent bounded data callback:

async def complete(prefix, context):
    return [{"value": "release", "label": "Release", "description": "Argument data"}]
api.command("deploy", "Deploy command", execute_deploy, complete=complete)

runtime.command_completions() exposes current registration identities, and await session.complete_command(command, prefix, identity) in BOTH client SDKs queries the corresponding worker without executing its command callback or model. The callback has no interactive UI/editor/navigation/session-mutation authority. Items are plain single-line text (32 unique entries/64 KiB result); the prefix and value/label/description bounds are validated on both sides. Failed/timed-out uncertain callbacks settle/reap their workers and withdraw current declarations; no retry/replay or fallback callback is implied.

Replacement/disposal changes the command completion identity. Stale references and registration changes during callback execution yield no completion data; an old disposer cannot remove a replacement. Both client SDK wrappers settle their owning Agent's tool projection if that worker fails.

Both interactive parent editors now query registered argument callbacks in the background. Suggestions replace only the cursor-local token (including its right suffix); quoted whitespace stays inside that token, with quotes passed literally rather than shell-expanded. Tab or Enter inserts without submitting. A subsequent Enter executes the typed command only through its normal command/permission path. Pending queries cannot turn Enter into an accidental command submission. Escape dismisses the suggestions and ignores their late result; Tab reopens explicitly.

One query is in flight plus one coalesced latest draft. Original session/context, command registration, full draft and cursor are revalidated before inserting. Changed cursor/draft/registration/connection results never overwrite the current editor. Claw separately negotiates extension_argument_completion: 1, runs the query under the actual idle group/worker owner, and does not activate a cold session or borrow the UI-command/model approval context. Previous gateways return unavailable.

Escape dismissal, loss of editor focus, hidden suggestions and application exit now signal the owned completion task immediately. One cancellation is sent per in-flight owner; its slot remains held until actual cleanup settles, so repeated Escape cannot interrupt worker reaping or launch a successor early. Uncertain callbacks are not replayed: their worker is reaped and current registrations are withdrawn; explicit reload is required if the extension remains desired. Coding settles its owned completion threads before retiring the extension manager at exit. Claw separately negotiates extension_argument_cancel: 1 and pins cancel messages to the original request/connection/group/session. Older cancel-unsupported peers retain the bounded deadline fallback and never receive guessed cancellation.

Argument menus now render each supplied label as presentation and show its actual insertion value in brackets when they differ, alongside the description. Duplicate labels are allowed but duplicate insertion values are not; selection and replacement stay keyed to the value. Unicode/narrow layouts use the shared bounded renderer. Changing only a label refreshes presentation without selecting a different value. Unsafe/oversized label or description data rejects the entire result, not a partial menu. Coding reconstructs owned row copies after immutable cross-thread delivery.

Completion callbacks may read context["argument_context"] when the current caller supports full argument context. It is a copied dict with arguments (the captured text after the command), cursor, start and end (Unicode code-point offsets relative to that argument body). The token prefix is checked against arguments[start:cursor]; this enables later-argument suggestions based on preceding choices without reading the rest of a session or granting editor mutation authority. Host selection still replaces its original cursor-local span. Callback edits to this dict do not change the editor or its captured request.

Prefix-only SDK/legacy queries explicitly supply None for that context; they cannot impersonate a full draft. New Claw editor queries independently negotiate extension_argument_context: 1. Unnegotiated/invalid/foreign-scope metadata is refused before callback admission. Withdrawing support cancels captured contextual jobs (including before task startup), refuses late contextual output and does not silently replay the query as prefix-only. A user may explicitly edit/Tab to request new data under the remaining capability. Existing prefix-only callbacks remain supported; argument_input= is optional on both SDK wrappers.

More expressive suffix semantics and broader argument API interoperability remain open. Callback-supplied spaces/quotes are literal replacement data; the host never silently adds shell quoting or interprets the resulting argument. Independent child prompt completion is not claimed. Native file/slash-name completion is retained.

Extension terminal command-menu inspection

page = await api.get_command_catalog(query="", offset=0, revision="")
# Continue using page["next_offset"] and page["revision"].

Interactive parent COMMAND callbacks may read all entries in the same current terminal menu used by /help and completion, in 32-entry pages. Entries contain only normalized command name and sanitized description, with a terminal-menu source label, session identity, revision and explicit pagination. This is NOT execution approval, activation, model input or a claim that every remote command is available. Tools/headless/independent-child callbacks return None instead of borrowing the parent editor's command context.

Each response is scoped to its invoking session/command/context. Caller-selected session/branch/action fields are refused; copied pages cannot mutate the host. Changed menu metadata or another callback invalidates an old revision and returns status="stale", no entries and no next page. Restart at offset zero explicitly; there is no silent partial merge, command callback execution or automatic replay. Claw separately negotiates extension_command_catalog: 1; previous session/tree/ model/name/inspection capability does not substitute for that viewer support.

Extension session names

name = await api.get_session_name()        # str (including "") or None if unavailable
queued = await api.set_session_name("Release review")
queued_clear = await api.set_session_name("")

Interactive parent COMMAND callbacks inspect the actual session owner; Claw reads through its reserved gateway/broker, not the viewer's cached footer. Outside an interactive command, reads use an invocation-owned, copied metadata snapshot when available. Tools, hooks and headless callbacks cannot borrow rename authority. Independent children and unadmitted/stale workers never borrow a parent name; without separately supplied owning metadata they receive None, not an empty name.

Setting a name opens a focus-owned, paginated host review showing the exact current owner, expected old name and proposed new name. True means queued after callback success, not saved. Failed/canceled callbacks discard it. The existing native metadata owner applies the change only if the name still matches the reviewed old name, preserving history/model/tools/permissions and the current persistence owner. Names are plain Unicode text with the native 256-byte/control bounds. The API uses "" to clear; "-" is a literal name (unlike the native /name - clear shortcut).

Claw independently negotiates extension_session_name: 1; older/withdrawn viewers cannot substitute model-inspection or navigation support. Missing name data/optional frame-budget exhaustion is unavailable, never a cached parent name or a replay. This metadata feature does not add extension history mutation or model execution.

Interactive extension COMMAND callbacks can use await api.get_session() for a bounded snapshot of session_id, workspace and group_id (Coding uses an empty group). No hidden system prompt, full conversation or foreign-session data is included. await api.open_sessions(tree=False) queues the native session browser; tree=True queues the current session's branch/checkpoint tree. A True receipt means queued, not that a session was activated or that the callback has finished.

The host opens that browser only AFTER the same command callback settles successfully under the same session/connection/history owner. Failure, cancel, stale command IDs, owner/epoch changes and unavailable viewers discard the deferred request. Existing Coding/Claw session providers, group/session control protocol, idle exclusion, branch/checkpoint validation and persistence remain authoritative. The operator must separately select/activate/create a session through the ordinary browser; opening it never switches session, launches a model turn or changes the draft.

These APIs use the existing owned/validated pending host-request path. They are unavailable to tool/hooks/context/child callbacks, headless execution and older viewers. Original RPC action and active extension/command ownership are checked at the host, independently of a worker's capability claims. Claw negotiates extension_session: 1 in hello/pong separately from editor/form/rich-result controls. Unsupported viewers cancel the request rather than render an unknown session dialog.

The parent draft/focus, typed attachments and permission/planning policy remain unchanged; closing the browser restores the editor. Both real workers, successful/ failed live commands, existing navigation regressions, stale-owner checks, tool/ headless/older-viewer refusal and private-tmux browser entry/return are tested. This is session inspection plus explicit native browsing, NOT direct session mutation or a paginated extension catalog API. Those host-action/context gaps, model control, custom renderers/interception/providers and remaining MCP/platform requirements stay open and are not waived by this increment.

Reviewed direct command navigation

Interactive command callbacks can now request:

queued = await api.navigate_session("resume", session_id=target_id)
# Also: "new", "branch" (branch_id), "goto" (zero-based index), or "fork".

The API queues at most one reviewed intent per command. Its bool receipt means queued after review, NOT activated/committed. New requires no target fields; resume requires an explicit session; branch requires an explicit branch; goto requires a zero-based checkpoint index. Empty session for branch/goto means the captured current owner. Fork is limited to the current session/head, with optional zero-based index. IDs/field combinations and checkpoint types/ranges are validated; returned strings/paths cannot substitute a host command.

The UI defaults to denial. Right/Space selects approval, Enter opens whole-target review, PgUp/PgDn reviews every page and another Enter confirms. Current owner and the exact action/session/branch/index are displayed, with a queued-not-activated warning. Paste/repeat Enter, hidden/tiny/changed-layout review and default denial do not approve a mutation. Normal host approval retains priority. No tool/model/ permission change is authorized by this form.

Only AFTER the original callback completes successfully under the same command/ extension/session/connection/history owner does the host dispatch the existing native navigation control. Failure, cancel or owner change discards the intent. Existing idle/child gates, target/group ownership, safe checkpoint/tool-boundary checks and persisted plan/extension state remain authoritative; invalid or vanished targets fail without a substitute selection. Claw uses its group-owned session protocol; Coding uses its normal serialized commands. Fork saves a checkpoint branch; branch/goto activate context according to existing client semantics.

The main draft/attachments are not submitted or discarded, permission/planning policy is not overridden, and no provider request is launched. Claw separately negotiates extension_navigation: 1; viewers supporting only metadata/browser requests cancel direct navigation before receiving unfamiliar review semantics. Tool/hooks/context/child/headless callers cannot borrow command authority.

Navigation and rename reviews now require a host extension_dialog_accepted receipt emitted only when the broker actually delivers True to the callback. A sent answer or transport ACK is not a navigation grant. Canceled/expired futures cannot authorize a later successful callback; receipt ownership and current session/history context are rechecked before queueing the native operation.

Real current-host dispatch for all five actions, denial/failed callbacks, missing targets, stale owners, exact-review bounds and older-viewer behavior are covered, plus private-tmux reviewed new-session synchronization in both clients. Direct navigation is now implemented; paginated extension session/tree catalog APIs, model/other host context, custom rendering/interception/providers and remaining MCP/credential/native-platform requirements stay open. No subset waives them.

Command session/tree catalogs

Interactive extension commands can now read actual host navigation metadata without opening a dialog or activating context:

first = await api.list_sessions(query="project")
if first is not None and first["has_more"]:
    second = await api.list_sessions(query="project", offset=first["next_offset"], revision=first["revision"])
tree = await api.get_session_tree()  # current host session
branch = await api.get_session_tree(target_id, branch_id=branch_id)

Pages contain scope, owner_session, resolved tree session_id/branch_id, query, offset/total, at most 50 entries, has_more/next_offset and membership revision. Entries are inert bounded id/title/detail/kind/session/branch/checkpoint/depth/active metadata and safe checkpoint previews—not full hidden conversations or system prompts. Text/type/ID/offset/count/frame validation rejects malformed responses rather than coercing them. Page encoding is capped at 256 KiB, query at 1000 UTF-8 bytes and catalog traversal at one million entries.

Command session pages use stable identity order, not the native browser's recency order, so the command's own durable entries cannot move sessions between pages. Pass the returned revision with continuation calls to refuse a changed membership/ owner/query/target rather than mix catalogs. It is a membership/topology identity revision, NOT a frozen full-history/metadata version; previews/active flags may be fresh on each read. Restart at offset zero after a None refusal. Leaving revision empty explicitly requests a fresh current page. Native browser behavior is unchanged.

Coding reads the existing session-manager/tree sources off-thread under captured command/session context; Claw reads the owning GroupRuntime catalog, not a client cache or launch-directory guess. Claw tree targets remain restricted to that group. Host catalog operations preserve their actual filesystem owner until creation/read settles on cancellation, then refuse late adoption. An OS-blocked thread cannot be forcibly killed; these limits/ownership checks are not an OS sandbox.

Only actual interactive command RPCs receive catalog authority. Tools/hooks/context/ child/unattended callbacks return None; original RPC and host extension/command/group/ session ownership are independently checked. Missing/foreign/unreadable targets or membership changes yield unavailable without disabling the extension or replaying reads. Invalid plugin request/response codecs still fail closed. No model turn, permission/planning mutation or session activation is introduced.

Real workers, both live hosts' 61-entry multi-page catalogs, search/tree/branch previews, revision ordering/changed membership, foreign-group refusal, cancellation settlement and production terminal catalog commands are verified. This supersedes catalog APIs being listed absent; other model/host-action APIs, custom rendering/ interception/providers and remaining MCP/credential/native-platform gaps stay open.

Runtime-registered extension shortcuts

Trusted extensions can register a shortcut and its actual command callback:

async def compose(args, context):
    snapshot = await api.get_editor()
    if snapshot is not None:
        await api.set_editor(snapshot["text"] + " prepared", snapshot["revision"])
    return "Draft prepared; not submitted"
api.shortcut("compose", "alt+k", "Prepare the command draft", compose)

Registration contributes the namespaced /ext-EXTENSION-NAME command plus a canonical default key; pressing it uses the EXISTING interactive command owner, not another tool loop or arbitrary input interception. Plugins must already be explicitly trusted/activated; a key does not discover/load an extension. Callbacks retain normal idle/child/tool-policy/session/connection checks and any command host APIs. No model turn or tool approval is implicitly created by a shortcut itself.

/keys lists effective extension keys and disabled conflicts. Built-in and USER editor bindings always win. When different extensions claim one effective key, ALL colliding shortcuts are disabled (manual commands remain available). Typing, unmodified editor/dialog keys, Escape, Ctrl+C/D/L/Z and confirmation/recovery routes cannot be captured. Release/repeat events cannot duplicate callback invocation. Registration capacity is 32 per extension/64 across active registrations.

The existing keybindings.json can remap or unbind a registered shortcut using its full lowercase command name, for example:

{"ext-keyed-compose":"alt+x","ext-keyed-blocked":[]}

Multiple explicit keys may be provided as a list. Overrides still reject duplicate/ built-in collisions and reserved routes. Configuring an inactive command does NOT activate a plugin; /keys reports it unavailable. Changes take effect when the client loads its keymap, like existing prompt bindings. Terminal encodings can make some Ctrl/Alt combinations indistinguishable from Enter/Tab; choose an unambiguous key or a terminal with extended keyboard support. Alt+K/Alt+X are physically tested.

Dispatch is confined to the ready idle prompt: approvals, extension dialogs, viewers, completion pickers, child/MCP/plan panels, overlays, output reviews, active commands/model work and closing/disconnected owners retain priority. Unload/ failure/reload updates registrations; stale keys cannot adopt a new callback. Coding now refreshes actual registration snapshots after extension commands even if a plugin has no widget/notices. Claw separately advertises extension_shortcuts: 1; missing support is both dispatch-disabled and reported in effective help.

Real workers, both UI command/editor paths, user remap/unbind/inactive overrides, reserved/default/user/inter-extension conflicts, contextual suppression, repeat, load/reload/unload and both private-tmux default/remapped key paths are verified. Arbitrary input interception and custom renderers remain separate OPEN requirements, as do provider registration, other host APIs and remaining MCP/platform work.

Submitted-input extension hooks

Trusted extensions can register api.on_input(callback) for newly admitted human input, before its response model call or canonical user-message commit:

async def prepare(text, metadata, context):
    # metadata: prompt/steer/follow_up source, active channel and image metadata.
    if text.startswith("reject:"):
        return {"action":"reject","reason":"Explain why this input was refused"}
    return {"action":"transform","text":"Prepared input: " + text}
api.on_input(prepare)

None or { "action": "continue" } leaves text unchanged; a string is shorthand for transformed text. Explicit transform/reject objects have exact typed fields; unknown/handled/image/command-action payloads are NOT guessed. Text is limited to 1 MiB UTF-8, rejection reasons to 2000 bytes and callbacks to 32 per extension. Callbacks run in deterministic registration/activation order in their actual owned worker. Missing/malformed/failed/timed-out hook decisions refuse admission and quarantine the failed worker; known failed filters cannot silently pass raw input until explicitly unloaded/reloaded. A normal explicit rejection keeps the extension active. Callback side effects are not rollback and are not replayed.

The Agent's optional input_transform hook now covers initial input, identified steering/follow-ups and plain explicit continuation before canonical admission. Transforms preserve queued identity/recovery receipts: consumption happens only when the transformed message is committed. Rejected selected identified input remains recoverable with original text/images; plain rejected steering/follow-up messages enter an explicit-recovery quarantine (agent.drain_rejected_messages()), not a live queue that could replay them on the next prompt. Initial rejected input belongs to its caller, not a fabricated assistant/provider response. Hook cancellation is settled before idle/owner release. Preexisting Coding SDK input hooks are composed rather than replaced.

The extension API sees text plus image MIME/encoded-size metadata, never original base64. Its output cannot silently remove/replace images: transformed messages keep original typed attachments/timestamps. All later model capability, tool argument, approval, planning and child permission gates remain authoritative. A transformed string resembling /quit/a URI stays model input data, not a reparsed host command. Hook callbacks cannot borrow editor/session/navigation command authority.

Both interactive clients show the actual transformed user text/queued consumption, with visible notices. Initial refusal restores original draft/attachments without model input or an automatic replay. Coding uses the current worker-owned image batch for recovery, not a potentially lagging UI active-image pointer. Claw applies its existing per-connection/run input-ID normalization/fencing to transform/refusal receipts too. Child agents use their own extension worker/input history, not parent history. Claw scheduler/goal/system initial inputs are explicitly NOT human input hooks; queued human steering/follow-up remains intercepted. Channel metadata is the active turn channel, not a claim of a separate queued sender's identity.

Real workers, generic initial/steer/follow-up/continuation/abort receipts, both live clients' typed attachments/canonical history/refusal/recovery, actual queued follow-up images/consumption, child isolation/system exclusion and private-tmux transform/ refusal recovery are tested. Arbitrary raw-key interception, handled-input dispatch, custom renderers/providers and remaining MCP/credential/native-platform gaps stay open; submitted text transforms do not waive those distinct requirements.

Pre-turn compaction of PREVIOUS history retains its existing lifecycle; the input hook is a pre-admission/user-response check, not a veto of independent maintenance work. Failed reload of a previously registered input filter remains failed/required, not an implicit unload: only successful reload or explicit unload changes that policy. Print mode reports a refused input as a nonzero error; RPC emits an error, not an empty successful message_end. No synthetic assistant answer is fabricated.

Terminal verification also caught a generic resize race during output review: painting after dimensions changed during tick callbacks could skip the next loop's cursor query. That paint is now deferred until the normal input loop re-anchors. Existing visible-confirmation/query assertions were retained, not weakened.

Explicit handled-input outcomes

Submitted-input hooks may now return an explicit handled decision with bounded feedback, for example:

if text.startswith("local-help:"):
    return {"action":"handled","feedback":"Extension handled this request; no model answer was generated"}

Feedback is required, nonempty and at most 64 KiB UTF-8; extra image/command/action fields are rejected. Handled is NOT rejection and NOT canonical/model history admission. It stops the remaining input-hook chain for that request and emits a separate InputHandledEvent logical-consumption receipt with original source/ID/ attachments. Identified inputs are removed exactly once; they do not also emit InputConsumedEvent or return for automatic replay. SDK callers can inspect/drain handled receipts with agent.drain_handled_inputs().

If all newly supplied input is handled, the host does not invoke the response model with old history just to obtain an answer. Other genuinely queued unhandled requests may still run normally, preserving ONE_AT_A_TIME steering boundaries. Explicit agent.prompt([]) retains its preexisting old-context continuation semantics: it contains no submitted request to intercept. If one request is handled and a later request rejects/fails, the handled receipt remains committed rather than falsely rolled back; remaining original input stays recoverable. Trusted callback side effects are not an OS sandbox or rollback transaction.

Original image bytes are never given to the hook, and handled decisions cannot silently discard attachments. Both interactive clients retain them in the editable composer without restoring/replaying the consumed text. Queue/run/connection ID fences apply to the handled receipt too. Feedback is labelled system/extension data, not fabricated assistant prose or tool approval. Permissions/planning/tool/model capability gates are unchanged; returned command-looking feedback is never executed.

Print mode outputs explicit feedback and returns success for handled-only input; JSON mode emits a valid system/input_handled envelope (optional real assistant output is separate). RPC emits input_handled receipts and an explicit completion marker, not a counterfeit model message. Claw sends a content-free final signal after actual handled feedback, releasing run ownership without synthesizing a response. Other non-TUI channels receive extension feedback as channel output, not model generation.

Child supervision retains labelled input feedback as separate bounded metadata (64 entries/64 KiB), with zero model calls when the task is handled. It survives child record reload and appears as SYSTEM feedback in source inspection, not invented user/assistant history. Parent conversations remain unchanged. New shipped clients are jointly verified; older-viewer handled-outcome interoperability remains unproven rather than being inferred from JSON accepting extra event types.

Generic initial/queued/old-history/queue-mode receipts, both live initial and queued image paths, child result/source metadata, text/JSON/RPC completion and production terminal model-call counters are tested. Raw-key interception, arbitrary custom renderers/providers/other host capabilities and remaining MCP/credential/platform gaps stay open. Handled outcomes no longer stay listed absent, but do not waive them.

Custom extension views

Trusted extensions' api.custom(title, render, on_input, ...) now uses the shared viewport-aware custom component, including independent child workers; inline remains the default. Python callbacks stay in the owned extension worker; painting only consumes bounded, sanitized frame data. Vetted SGR styles/colors are supported, never OSC clipboard, cursor/erase/graphics/alternate-screen commands or extension-supplied host cursor markers. Optional carets are host-clamped to whole grapheme boundaries. Escape or Ctrl+C cancels; redraw, suspend and exit remain host controls.

Keys/paste require a current visible frame. Hide/show, modal focus, row-budget and geometry changes fence old revisions with view generations, including same-size restoration. Queue/owner refusals are visible and never automatically replay input. Completion returns bounded JSON data, not tool approval; existing permissions and parent/child tool gates remain unchanged. No socket/worker I/O runs during painting. Claw additionally negotiates the capability and checks original connection/group/ session/child leases; older viewers or withdrawn capabilities cancel the question.

The shared codec, broker, real workers/clients and parent/child terminal paths are covered by offline tests. Richer custom containers and raw-key interception outside a custom view and native platform checks remain open; supported presentations do not waive those gaps or introduce an OS sandbox.

Custom declarations can now request fullscreen=True, message="..." and key_events=("press", "repeat", "release"). Inline/press-only remains the default. The host owns temporary alternate-screen entry/exit, approval/output/overlay preemption, suspend/resume and native-buffer restoration. Resize re-homes only the alternate painter; saved main rows and scrollback remain untouched. F1 switches between sanitized paginated metadata and the custom body; metadata navigation and paste cannot complete or feed the custom callback. Opted-in Kitty repeat/release keys use normalized event types, but reserved host controls stay outside callbacks. Claw requires the separately negotiated extension_custom_rich:1; unsupported or withdrawn richer requests are canceled rather than silently downgraded.

Custom input callbacks can await nested api.input/select/confirm/form/custom questions. The same parent component/state is cached while its child owns focus; fresh frames are required on restoration, including fullscreen exit/re-entry. Escape cancels the visible child. Rendering cannot create dialogs, and nested input callbacks cannot borrow command editor/session/navigation authority. Parent IDs come from an owned host renderer lease, never extension-supplied identifiers. The stack is bounded to four displayed levels and eight broker questions; closing a parent or disposing its context cascades to descendants. Claw requires separately negotiated extension_custom_nested:1 and the original connection/session/child leases. Unsupported children are canceled, not silently treated as standalone roots. Aborted uncertain callback chains are reaped rather than replayed.

Prompt terminal-input hooks (Coding)

Trusted extensions can register a raw prompt-input hook:

def setup(api):
    def terminal_input(data, metadata, context):
        if metadata["kind"] == "paste" and data == "shortcut":
            return {"data": "expanded draft text"}
        return None
    unsubscribe = api.on_terminal_input(terminal_input)

Callbacks may be async. None/{} continues, {"consume": True} consumes, {"data": "..."} replaces input, and the returned unsubscribe is idempotent. Hooks run serially in explicitly trusted workers after activation (for example, /extensions); existing lazy startup activation is preserved. Metadata identifies key or atomic paste and the prompt surface. Protocol negotiation, cursor reports and clipboard replies are filtered before interception; paste boundaries cannot be transformed into raw keys.

Only editable prompt input is hookable. Approval prompts, extension dialogs, custom/child views, output viewers, overlays and other host surfaces retain their input. Original submission controls keep FIFO behind held text without visiting a hook. Escape, Ctrl+C/D/L/Z and host shortcuts bypass delayed hooks immediately. Transformed keys are revalidated against the current keymap: they cannot synthesize submit/steer/follow-up, approval, exit, external-editor or other host controls. Callbacks cannot borrow dialogs, command actions or a model loop.

Input waits asynchronously with 32-item/128-KiB queue bounds, a 16,000-byte UTF-8 per-item limit and a five-second host deadline. An oversized intercepted paste is refused; pause hooks before pasting a larger payload through the normal editor. Session/focus/physical-size changes, interrupts and failures cancel held input rather than applying it to a successor surface or submitting a partial draft. Retained originals are bounded to 32 items/128 KiB, and hooks pause on rejection/interruption so recovery remains accessible.

Alt+I (terminal_input in keybindings.json) is a host-owned pause/resume and recovery shortcut. /terminal-input list, restore ID, discard ID, clear, pause and resume inspect/manage originals. Restore inserts original text into the editable draft; it never replays raw controls, imports an attachment, calls a hook, grants a permission or submits a model turn. Control-only records can be inspected/discarded, not replayed. This supersedes the earlier Coding raw-key exclusion above. Claw now wires its corresponding negotiated parent-prompt gateway path. Independent child supervision-editor interception and native-platform validation remain open; this is not a claim of full Pi/Codex parity.

Worker-selection generations also fence reload/unload/failure/close boundaries. A held callback cannot dispatch into a newly selected worker or quarantine its replacement. Retiring an old MCP/extension connection cannot remove successor registrations or tools.

Buffered input and live hook registration

Adjacent editable key tokens from one filtered terminal frame now travel as a bounded span, rather than one worker/socket RPC per character. Spans are split only at whole UTF-8/enhanced-key tokens and the 16,000-byte limit; submit/handoff controls and atomic paste keep separate FIFO boundaries. A callback's data may therefore contain several characters or encoded keys. Callback counts depend on terminal framing, not character count. This prevents an ordinary buffered sentence from exhausting the 32-item queue. Oversized encoded key tokens are refused explicitly, never silently dropped or replayed.

Workers now report live terminal-hook registration after callbacks, including commands and nested custom-view completion. Removing the last hook with its unsubscribe function updates runtime availability immediately. Already-held same-owner input drains exactly once without another hook; fresh input goes straight to the host editor. This is a normal registry change, not an error fallback: lifecycle changes, malformed replies, callback failure and cancellation still fence/reject held input. Re-registering from an extension command updates the same worker and re-enables interception without reload/reconnect; a user's manual pause remains respected.

Claw's matching terminal result carries an exact boolean live-registry field so an idle viewer does not depend on a previous model-run UI subscription. The field is validated only after original request/connection/session/sequence ownership. Updated workers/daemon are required for live registry refresh; older senders that omit this additive metadata retain their existing behavior and do not establish this newer guarantee. Independent child-editor and broader raw/host APIs remain on the open audit rows.

Independent child steering-editor hooks

In /agents, s opens the selected child's steering draft and t opens its follow-up draft (the transcript preview also offers Ctrl+S/Ctrl+T). While that child's independent extension worker is live and attached to this UI, its own api.on_terminal_input callbacks can consume/transform those editable drafts. The surface is child-prompt; parent and sibling hooks do not receive this input. No worker/model is created merely by opening the editor, and completed or unattached children do not borrow the parent's hooks.

New-child task/settings, access selection, preset/catalog views, confirmation, child extension dialogs and tool approvals remain host-owned. Transformed keys cannot synthesize review, execution, approval, configuration or exit. Original Enter opens the existing visible review; only a fresh explicit confirmation can execute a child control. Escape from review restores the child draft rather than discarding it, and re-entry advances the edit lease.

Held input is bound to the specific child worker, UI attachment, editing action, form generation, focus and physical geometry. Switching siblings, leaving/reopening the form, closing/completing a worker, overlays and resize reject stale results, not replay them elsewhere. Alt+I remains a host pause/recovery route. Last-hook unsubscribe and later registration update the same child worker's live state; ordinary queued same-owner text drains once without invoking a parent hook.

Claw additionally requires terminal_child_input:1 and the connection's negotiated child UI token. Requests/results/cancellation preserve child ID/token, parent group/session, worker generation and the existing terminal input leases. Owned bounded registry notifications update live registration and retire completed children without relying on the last model-run UI subscription. Foreign sockets cannot invoke or cancel a captured child worker, and capability withdrawal fences pending child input without disabling parent hooks.

Real child workers, both actual clients and Linux/private-tmux tests cover isolated callback streams, unchanged parent draft/model counts, cancel/review restoration, source ownership and native terminal recovery. This supersedes earlier statements that all child-editor interception was missing; richer containers/host/provider APIs, older-sender interoperability and native-platform validation remain on their open rows. These UI/permission leases are not an OS sandbox.

Structured custom-view containers

Trusted custom-view callbacks can now use the standalone worker-side xdog.tui.components.custom_layout helpers: fixed/weighted CustomRow and CustomColumn, CustomPadding, titled CustomBox, safe/wrapped/aligned CustomText, CustomSpacer, and CustomComponent adapters for existing editable TUI components. Return custom_frame(layout, width, height) from api.custom's render callback. Component state and input stay in the original extension worker; only bounded sanitized lines and a single cursor coordinate cross the existing frame protocol. No new model loop, host callback execution, tool approval or terminal owner is introduced.

Whole-grapheme clipping, deterministic cell allocation, style isolation, zero-size viewports, cycle/depth/node/byte bounds and multiple-cursor rejection are tested. Both clients and independent children exercise an editable two-column layout, Unicode caret, nested question/resumption and resized completion. Production Linux/private-tmux inline AND fullscreen paths verify nested editable state, native-buffer/termios restoration, once-only command invocation and zero model calls. These helpers extend the previous line-array API rather than replacing it or requiring a new gateway wire dialect. Richer arbitrary render/provider/host APIs, interoperability and native-platform validation retain their open scope.

Explicit MCP resource watches

In the MCP resource catalog (/mcp, select a server, u), press w on an advertised resource to watch or stop watching that exact URI. Change notices show state and a coalesced change counter. They remain visible over a resource preview without replacing its immutable contents. Watching never fetches the resource, inserts text into the composer, submits a model turn or grants a tool permission; resource retrieval still requires an explicit operator action.

Watches belong to the actual panel/session/connection and selected MCP client. Closing the panel, losing that owner, disconnecting or shutting down removes local ownership and closes/unsubscribes the original transport. Late notices cannot update another view. No watch is automatically recreated after reconnect, and no uncertain subscribe/unsubscribe operation is replayed. Repeated same-view watch requests reuse the existing handle; legacy shared-URI watches keep their own view sinks and unsubscribe remotely only when the last local owner leaves.

The SDK has explicit watch_resource, unwatch_resource, and detach_resource_watches methods. Resource capability must advertise an exact boolean subscribe: true. The current-dialect path uses a dedicated correlated subscriptions/listen lifetime with an acknowledged resource filter, update notifications and a terminal complete/error; stateless HTTP keeps the original POST SSE stream (no GET resumption or POST retry), and stdio retires request IDs on cancel. Legacy transports use resources/subscribe/unsubscribe and the existing notification channel. ACK omission, foreign URI/identity, unexpected ordering, stream/auth/redirect errors and capacity limits fail explicitly.

Bounds: 16 transport watches/client, 32 SDK view watches/manager, finite UI/gateway view/job maps, 8,192-byte URIs and coalesced notices at most 20 Hz/watch. HTTP reserves connection capacity for ordinary MCP operations rather than letting watch streams consume every connection. Payloads are change metadata only, not server content.

Owned offline peers exercise stdio and HTTP in both dialects; both real clients and four Linux/private-tmux paths verify visible notices, explicit stop, cleanup, zero automatic resource reads/model calls and native terminal recovery. Official source retrieval during this pass failed (connection reset), so third-party/current protocol conformance, additional subscription filters/capabilities, authorization interop and native-platform checks remain OPEN. This is implemented/tested local behavior, not a claim that the full MCP interoperability audit is complete.

Explicit saved MCP OAuth credentials

Completed resource-bound OAuth grants can now be saved only after a separate reviewed confirmation. In the authorization status view, p prepares save. From the MCP server list, l prepares explicit restore and x prepares saved-credential erase. Every action pages the issuer/resource/client/scopes/endpoints, TLS trust, destination and storage disclosure before Enter becomes available. No token/code/secret paste counts as consent; old or detached receipts cannot commit.

The default mcp_credential_backend: "file" stores a private local plaintext JSON file, not encrypted/keychain storage. Coding uses its user data directory's mcp-credentials; Claw uses the group's private data directory. This implementation requires verified local POSIX ownership and 0700-directory/0600-file permissions. Unsupported platforms and unsafe paths, symlinks, hard links, ownership/mode/type/size violations fail closed rather than claiming equivalent protection. Credential filenames are resource hashes; writes are bounded, fsynced and atomically replaced through pinned directory-relative I/O.

Nothing loads at startup. Explicit restore validates exact resource, configured issuer/client strategy/authentication method/environment-source name/scopes, certificate-file content binding and expiry, then admits the credential to memory. It performs no metadata lookup, token POST, connection, model turn or tool grant. Client-secret environment VALUES are never stored. Access/refresh credentials have no public diagnostic representation and never enter transcripts or review payloads.

Saved access expiry uses bounded wall-clock values and is converted back to an in-memory deadline on restore. Expired access can preserve its saved refresh grant, but refresh still needs its own live reviewed action. Before a potentially rotating refresh POST, an existing saved snapshot is removed, so restart cannot replay an old refresh token after uncertain rotation. Rotated credentials are not saved implicitly; explicitly review/save the new grant if desired.

x erases the saved credential only (not a live token or remote grant). The existing f forget/disconnect review erases both memory and saved state; neither action claims remote revocation. Save/erase cancellation revokes storage admission and waits for the actual writer to settle; if an operation committed before cancellation, inspect local state rather than automatically retrying it.

SDK callers can opt in with McpManager(..., credential_dir=...) and prepare/confirm credentials_save, credentials_restore, or credentials_forget through the existing owned manage_auth API. A manager without a credential directory remains memory-only. Actual client restart/consent and private-tmux tests cover the file backend. File permissions are not an OS sandbox.

Opt-in Linux Secret Service storage

Set "mcp_credential_backend": "secret-service" in your global ~/.config/xdog/coding/settings.json. Project and session settings cannot override this global storage choice. The default remains "file".

SDK hosts select McpManager(..., credential_dir=..., credential_backend="secret-service"). The directory is used only to derive a stable namespace; this backend creates no local token files. Moving that directory changes which saved credentials are found.

Prerequisites are Linux, /usr/bin/python3 with OS-provided GI bindings and the Secret 1/libsecret typelib, an existing DBUS_SESSION_BUS_ADDRESS, and an already running Secret Service with an unlocked default collection. XDOG does not install prerequisites, start the vault, create a collection, unlock it or launch an authorization prompt. Unlock your vault externally before storage actions. An encrypted Secret Service session is required; at-rest protection is supplied by your vault, not guaranteed by XDOG.

The same explicit save/restore/forget reviews apply, with a NATIVE SECRET SERVICE storage notice. No startup restore or implicit save is added. Invalid backend names, missing prerequisites, locked/ambiguous records and malformed data refuse without falling back to plaintext files or initiating login. The owned helper has a bounded 10-second operation timeout; cancellation terminates and reaps it. A mutation that was already admitted may have committed: inspect the vault before retrying; XDOG never automatically replays an uncertain mutation.

Tests cover actual encrypted save/read/forget, locked-vault refusal, manager restart/consent and both application UI owners against an isolated owned GNOME keyring service, plus helper timeout/cancellation. This is not a native-backend tmux test or proof of every Secret Service implementation. macOS/Windows native credential backends, broader service interoperability and native desktop validation remain OPEN. Neither backend is an OS sandbox.

Session-owned extension providers

Explicitly trusted extensions can declare provider callbacks during setup:

def setup(api):
    async def stream(model, context, options):
        yield {"type": "text", "text": "local response"}
        yield {"type": "done", "stop_reason": "stop"}
    api.register_provider("local", [{
        "id": "example", "name": "Example", "context_window": 32000,
        "max_tokens": 2048, "supports_tool_calls": False,
    }], stream)

Activate the extension explicitly, inspect /model, then select its full alias ext-EXTENSION-PROVIDER/MODEL. Registrations belong to that session's owned worker, not the global AI provider registry; aliases cannot replace built-in names. Context, input modalities, reasoning/tool flags, output bounds and optional pricing are validated cached metadata. Catalog inspection starts no model request. Current startup/config-only alias activation and additional provider/host APIs remain open; this slice supports explicit activation and interactive selection, not implicit login.

The callback receives lossless JSON context/options through a bounded checksummed transfer. It returns an async iterator of typed text, thinking, tool_call, usage and explicit done events. Adjacent text/thinking chunks coalesce by default, with the latest continuity signature; new_block: true starts a separate block. Tool calls are data routed through the SAME existing agent validation/permission loop, never executed by a provider event. Calls not offered in the actual context, duplicate IDs, malformed/nonfinite data, quota violations and missing completion fail rather than granting authority or using a fallback provider.

Four registrations/extension, 32 models/registration, 64 session models, 4,096 stream events and 8 MiB emitted content bound this callback path. Output options are capped by declared model limits. Only one owned stream/input transfer runs per worker. Provider callbacks cannot borrow dialogs/editor/session/command authority. Cancel settles the actual in-flight worker RPC; uncertain callbacks are quarantined/reaped, not replayed. Unload/reload/close generations fence results and make the explicit alias unavailable rather than silently routing to a built-in.

Child model overrides use the independently created CHILD extension worker and retain existing child context/vision/reasoning/budget guards and parent tool restrictions; selecting a child provider does not switch the parent's model. Existing primary routing and native/global provider registration are unchanged. Owned worker/client/child and production Linux/private-tmux model-selection paths verify this behavior without paid model calls. Additional native/embedding/search/ provider-auth APIs, initial startup/persisted alias activation, external interop and native-platform validation remain OPEN.

Configured and saved extension-model activation

Configured or saved ext-EXTENSION-PROVIDER/MODEL aliases now activate their explicitly trusted extension workers before readiness/first use. They do not go through unresolved-model fallback and are not rewritten to a built-in model. Untrusted/missing/failed declarations fail explicitly and release bootstrap owners. Metadata activation invokes no provider callback or model request.

Coding's synchronous SDK factory also works when called from an existing async loop, using one owned metadata bootstrap rather than nested event-loop execution. CLI --model, saved resume (--resume-id/session selection), and in-session saved activation restore the declared limits, tool/image metadata and normalized reasoning choice. New runtime activation occurs once; ordinary explicit unload is never an implicit reload. Bootstrap errors are reported cleanly by the CLI.

Claw activates configured aliases before its owned gateway reports readiness. Its explicit model selection is saved atomically in a session-owned model sidecar; restart/resume restores that alias and the corresponding worker/limits. Selection persistence completes before the live model is changed. Invalid/symlinked/excessive saved model files or unavailable trusted registrations fail, not choose a primary provider. Image metadata comes from the active extension registration, not a global catalog guess. Existing primary-provider routing is unchanged.

Owned SDK/actual socket/restart tests and production Linux/private-tmux startup and resume paths verify zero built-in/model calls during activation and correct first provider use/native recovery. This supersedes the earlier initial-alias activation exclusion. Additional dynamic/native/provider-auth and host APIs, interoperability and native-platform validation remain on their original open audit rows.

Combined MCP notification watches

From the focused MCP server list, Alt+W opens an editable JSON notification filter. Choose exact boolean toolsListChanged, promptsListChanged, resourcesListChanged flags and/or a resourceSubscriptions array of explicit resource URIs. Enter starts one current-protocol stream for the selected set; Enter again stops that same active set. Editing the JSON selects a different set. The draft survives submission/errors; Escape goes back and closing the MCP view stops all its watches. Alt+T/P/U singleton catalog watches and w resource watches remain separate.

Empty/invalid/duplicate filters are refused, not inferred. Bounds are 16 URI entries, 8 KiB per URI and 64 KiB total; URI order is canonicalized. Exact full ACK matching, view/session/connection ownership and no replay apply. These are notice-only subscriptions: no automatic reads, catalog refresh, tool registration or model input. Legacy combined streams are explicitly unavailable (legacy singleton watches still work). Claw requires independently negotiated mcp_combined_watch:1; withdrawing it settles combined watches without stopping unrelated singleton watches. Independent serving-SDK stdio/HTTP and actual both-client/terminal paths are verified; broader client-capability, external interoperability and native-platform gaps remain open.

Configured tool browser

/tools [QUERY] opens a read-only browser of the actual owning Agent's configured tool names, descriptions and full JSON schemas. Search includes names, descriptions and schema text—not just the visible viewport. /, n/N, arrows/pages, selection and f full-height viewing reuse the existing source browser; r explicitly refreshes and Escape returns to the unchanged parent draft/cursor. Copy/export/share use the captured tool-definition records, never conversation history as a fallback. Existing explicit output review/private-storage policy applies.

The summary separates configured definitions from the model API gate and explicitly states that listing a tool is NOT execution approval. Unsupported/text-only models can inspect their local definitions without sending API tools. Browsing does not execute or enable tools, register MCP/extension definitions, or submit model input. Snapshot refresh is explicit, not automatic during a run.

Coding reads metadata on one bounded background worker; its headless /tools command prints complete, query-filtered JSON without truncating the first inventory page. Claw separately negotiates tool_inventory:1 and reads the gateway's actual group/ session Agent, with request correlation, cancellation, timeout, capability withdrawal and session-owner invalidation. Cold inspection may initialize the group's local session cache and returns its actual session ID, but performs no model/tool turn or idle/daily reset. Older gateways report unavailable—no client footer/schema guess.

Full definitions remain bounded by the existing 2,048-tool/512-KiB inventory limit. Unavailable/invalid/changing owners are reported explicitly rather than displayed as an empty or approved tool set. Definitions are public model-facing data; authors must not place credentials in schemas. This is Linux/private-tmux evidence, not native Windows/macOS or complete CLI capability parity.

Explicit context compaction

Use /compact [focus instructions] to summarize older conversation context while retaining a complete recent user turn, including its tool exchanges. The optional focus is limited to 16 KiB. The summary uses the selected session model adapter (including registered extension providers), not a primary-provider fallback, and its request exposes no tools. Current model context/prompt limits determine the retained history budget. Too little safely separable history is an explicit no-op.

Compaction requires an idle session and idle children. The editor draft and existing permission mode remain unchanged. Empty, incomplete, canceled, or failed summaries are not substituted into history, and a persistence failure is not reported as successful compaction. Plan and extension metadata owners retain the compacted history for subsequent writes.

In the TUI, Escape or /compact cancel cancels an active compaction command. Once an atomic history commit has been admitted, cancellation settles that disk write and in-memory publication rather than pretending to roll it back. If cancellation or disconnection loses a receipt, inspect /transcript before retrying; the command is not automatically replayed. Native scrollback remains the record of what was previously displayed, while the transcript viewer can read the current context.

Trusted extensions may observe, veto, or provide a summary through api.on("session_before_compact", callback), and observe committed context through api.on("session_compact", callback). This applies to manual and automatic compaction; callbacks receive complete typed preparation rather than a UI preview. Custom summary boundaries must preserve complete recent user/tool turns. Bounded source/details metadata persists with the session and its branches. Hook callbacks cannot borrow command/dialog/navigation authority or change permissions through compaction results; see the Agent package's compaction extension contract.

Recovering context before compaction

Every successful manual or automatic compaction now saves a named Before compaction #N checkpoint containing the complete original typed history. The checkpoint and shortened active context are stored in one atomic session write; failed saves cannot publish a shortened context without its recovery data. Compaction does not automatically activate the checkpoint or change permissions.

Open /tree, search for Before compaction, and select the checkpoint to restore its conversation. Restoration retains a return checkpoint of the compacted head, so it is reversible. The existing transcript viewer can then search/copy/export that restored full history. This changes conversation context, not workspace files; restoration still requires an idle owner and preserves the normal permission mode. An incomplete tool-call tail is retained in recovery data but cannot be resumed as a complete branch: drill into the tree and choose an earlier valid checkpoint.

Checkpoint records retain model/settings, planning state, extension entries, and prior compaction metadata. Committed compaction metadata and extension post-events also carry an optional recovery_branch_id linking to that original preparation. Repeated compactions keep separate checkpoints rather than replacing the old one.

View saved context without activating it

In /sessions or /tree, select a saved session/branch and press v to open a read-only source viewer. Enter remains the separate activation action. You can also use:

/transcript branch BRANCH_ID
/transcript saved SESSION_ID [BRANCH_ID]

The viewer searches full saved text, reasoning, and tool input/output—not only navigation previews. It does not resume the saved conversation, call a model/tool, change the active branch, or modify the parent draft. Binary bodies/signatures are not displayed, and normal source redaction applies. Current-session entries without a branch open the ordinary live viewer; other saved sessions use persisted source.

Refresh explicitly with r or /transcript refresh; f toggles fullscreen. Copy/export/share uses the displayed saved snapshot and the existing reviewed output workflow, never the live conversation as a fallback. Loading/failed/retired reads cannot be copied or exported. Closing returns to the existing draft; reopening /transcript without a saved selector opens current source again.

Model selection uses the owning adapter

/model lists canonical streaming-chat IDs from the same session adapter that executes requests, including active registered extension providers. It does not assemble a second catalog from the global AI runtime. Explicit selection resolves through that adapter and persists the canonical ID before publishing model, context/prompt limits, tool/vision metadata, and effective reasoning settings.

Unavailable, non-chat, non-streaming, or invalid cached profiles are refused without switching the active model. Unsupported advertised reasoning efforts are not silently substituted; select a supported effort or off. Selecting a model that does not advertise reasoning clears the effective effort. Failed setting saves leave model/options/settings/capabilities unchanged. Native saved-session/branch profile and effort checks run before current context and owned resources are replaced.

Startup/resume no longer silently substitutes the first model for a missing requested or saved native profile. With no configured model, default selection must find a real streaming-chat profile; an empty catalog is an error, not a guessed sonnet request.

An explicitly supplied SDK stream function without model metadata remains an opaque, caller-owned adapter. Its explicit IDs/options can be delegated to that function, but selection does not borrow capability claims from the global runtime or the previous model: model/vision metadata stays unavailable, tool support is unreported, and context estimates use the documented host fallback rather than a claimed profile. Extension aliases still require their active, explicitly trusted worker declaration.

Prepare saved extension providers before navigation

Resume and branch restoration now prepare trusted extension workers against the target workspace, settings, planning state, and saved extension entries before replacing the current conversation or its resource owners. A saved provider alias must be declared by that prepared target runtime; the current runtime's catalog is not used as a substitute. Declared effort/capability checks still apply.

Preparation has no UI/dialog/navigation lease and buffers host-managed entry writes. A missing provider, failed activation, unsupported effort, canceled preparation, or failed target save leaves the current session/Agent context, extension workers, MCP manager, children, planning owner, permission mode, and draft intact. Buffered setup entries are not written into the saved target on failure. Successful activation adopts the prepared workers once, binds their entries/inspection to the live owner, and publishes model/history/settings together after the atomic target save. Branch activation retains a return checkpoint in that same committed session snapshot.

Callbacks receive context["staged"] == True during preparation. This is an informational lifecycle flag, not a permission grant. Extension code is explicitly trusted code with the process user's permissions: arbitrary filesystem/network or other external side effects cannot be rolled back by the host. Only host-managed entries/session publication is buffered. The synchronous SDK uses one owned bootstrap thread rather than nesting asyncio.run; the interactive main terminal loop stays responsive. Cancellation before commit discards the stage; a commit already admitted to atomic storage may complete, so inspect current state before retrying a lost receipt.

Exact slash-command submission

Enter submits a completely typed registered slash command as written, even when completion also offers a longer name—for example, /session does not become /sessions, and /ext-demo-show does not become /ext-demo-shows. Matching is case-insensitive, like command dispatch. Suggestions may still be browsed with Up/Down and explicitly accepted with Enter, or inserted with Tab; incomplete-prefix completion keeps its existing behavior.

Editing, history changes, and catalog changes clear prior neighbor-selection intent. A command withdrawn while it is being typed is not silently reinterpreted as a neighbor; the dispatcher reports its availability normally. A stale withdrawn suggestion is not submitted. File/path completion remains separate: selecting a file still inserts a reference without submitting a conversation.

Search the session's combined history graph

Use /transcript timeline [SESSION_ID] to view the current context and every saved branch of that session together, without activating a branch or sending a model turn. The normal full-source search, fullscreen, selection, and reviewed output workflow remain available. Current-session head data is captured from the admitted live Agent history; inactive sessions use persisted data. Refresh explicitly to capture updates.

This is a provenance graph, not a guessed timestamp chronology. Records shared by a recorded parent prefix or a verified compaction recovery link appear once and list all contributing source labels/IDs. Different roots, repeated later turns, divergent forks, changed protocol/model/content, and different tool results stay distinct even when their visible text happens to match. Compaction-retained history must match its original checkpoint before it can share identity; mismatched links fail the read, never silently omit content. Binary/signature payloads are not displayed, but their raw identity participates in lineage checks; normal source redaction applies.

Capacity failures report unavailable rather than exporting a clipped graph: at most 256 sources including the head, 20,000 messages per source, 200,000 raw references, and 20,000 unique formatted records. Inspect individual branches when that capacity is exceeded. The viewer leaves active context, permissions, branch and draft unchanged.

RPC lifecycle and shutdown ownership

--rpc owns stdin as a JSON-lines control stream. Startup emits ready without first reading the pipe as a one-shot prompt. Each RPC prompt runs through the same AgentSession.send_message lifecycle as other modes: extension turn/message/tool hooks, selected adapter/compaction, persistence and skill retirement are retained. The RPC adapter subscribes only to translate events; it does not implement another model/tool loop or silently change permissions.

EOF, quit and explicit abort cancel both pre-turn work and the actual streaming producer, settle owned cleanup and deny pending approvals. An explicit abort may be followed by status/a fresh prompt without borrowing the previous cancellation. Invalid configuration requests report protocol errors and leave the reader usable. A partial failed provider result retains content with message_end.is_error=true and an error event, never a success-only completion. Headless handled-input receipts remain separate from assistant output. Existing wire delta fields are unchanged.

The opt-in RPC extension control/dialog subset below is now implemented. Native terminal rendering, navigation mutation and independent-child RPC UI are separate remaining integration requirements; Claw gateway or interactive tests do not imply those RPC APIs. OS sandboxing is not added by approval/cancellation.

Opt-in RPC extension controls and dialog replies

ready.capabilities advertises supported protocol fields. Old clients remain headless unless they explicitly negotiate exact integer capabilities:

{"type":"rpc_hello","extension_dialogs":1,"extension_forms":1,"extension_multi_forms":1}

Use the session ID from ready; each control/negotiated prompt requires a fresh request ID. Control IDs are consumed once (256 per connection), not replayed:

{"type":"extension_control","request_id":"inspect-1","session_id":"SESSION","action":"command","name":"ext-example-ask","args":""}

Actions are list, reload, unload, command; management names must already be globally trusted. Controls are idle-only and mutually exclusive with parent prompts/policy changes/active children. The normal SDK/runtime owns activation, validation, callback execution, persistence and tool permissions. No second loop.

A scoped extension_dialog event carries session_id, request_id, scope, and dialog.id/kind. Supported kinds are select/input/confirm and separately negotiated typed forms/multi-choice forms. Copy ALL owner fields back verbatim:

{"type":"extension_dialog_response","session_id":"SESSION","request_id":"inspect-1","scope":"SCOPE","id":"DIALOG","value":"Blue"}

Replies are schema-validated, single-use and tied to the original pending broker; wrong/late/foreign/duplicate replies are refused. Tool approval remains ONLY the existing permission_response channel—answering a confirmation/form never grants approval, changes permission mode, or submits a model turn. Unsupported/expired/ withdrawn questions return cancellation, never silently inferred defaults. Typed false/zero/null and arrays remain distinct. Status remains readable during a dialog.

extension_control_cancel carries the original request/session IDs. EOF/quit/ explicit cancellation settle the actual pending worker (including pre-admission cancellation) without replay. Normal RPC CLI shutdown also awaits extension process cleanup before exit. extension_result reports success/cancel/error and current validated catalog; rich command content is inert returned data, never executed.

With dialog negotiation enabled, parent prompts also require request/session IDs, allowing tool/lifecycle questions to use the same scoped broker while the reader continues accepting approval and dialog replies. With no UI negotiation, existing prompt packets are unchanged. The host serves read-only session/model/usage/name inspection for command callbacks from the actual owner; native browser/name mutation and custom terminal components remain unavailable instead of borrowing a remote client's unsupported surface. This is not Pi wire compatibility or full RPC host-API parity; remaining original requirements stay open.

Typed RPC image prompts

ready.capabilities.image_prompts: 1 advertises protocol support, NOT that the selected model accepts images. Send an inline image array on a prompt; image-only prompts may omit/empty the message. No RPC file path or URI is fetched implicitly:

{"type":"prompt","message":"Inspect the image","images":[{"type":"image","data":"BASE64","mime_type":"image/png"}]}

type="image" is optional; data/mime_type are required and all other fields are refused. Inline attachments use the existing neutral ImageContent/SDK path and selected model's advertised image capability/count/byte/media limits. PNG/JPEG/WebP only, at most four attachments and 10 MiB decoded bytes per image (or lower model limits). Invalid base64, MIME mismatch, unsupported models/limits and malformed arrays produce explicit errors, never a silently text-only turn or provider fallback. These are shared SDK/base64/MIME checks, not a full raster decoder or OS sandbox. Binary contents remain typed in persisted history and are not echoed as prose in JSONL replies.

Binary admission checks run off the RPC reader loop so status/abort remain usable. After turn-start hooks refresh selected provider metadata, image admission is checked again BEFORE compaction/history/model work; the same race guard applies to Coding interactive and Claw sessions. A hook withdrawing vision cannot admit a formerly accepted attachment. Negotiated RPC UI prompts use the same typed image path and still require their request/session owner fields.

RPC retains inline JSONL images and now also supports the chunked transport below. Native POSIX input is byte-framed before JSON parsing; alternate custom/unsupported- platform input readers must honor sized reads and have no equivalent native proof. Complete rendered-context checks use actual adapter metadata where available; absent metadata/limits, decoder/provider and native desktop conformance remain open.

Complete rendered-context image admission

The shared Agent loop now checks typed images in the actual rendered Context after configured context hooks/message conversion and before every model adapter request. Retained user images, tool-result images and explicitly hook-added images all count against available model image limits. The validator never prunes images or silently converts them to text to make a request succeed. Rejection retains canonical messages/tool results and produces an explicit error.

Metadata comes only from the configured stream adapter's cached resolve_model interface; registered providers use the same contract. Declared positive count, byte and media limits and advertised image input are enforced. This does NOT apply the four-new-attachment composer cap to a model advertising a larger whole-context limit. Unspecified byte limits use the existing neutral 32 MiB payload safety bound, not an invented vendor limit. Base64/typed MIME checks are not a complete raster decoder or a guarantee of provider wire-format conformance.

Validation runs off the asynchronous loop. Recheck the same adapter's image profile after validation: changed/disappeared/malformed metadata refuses the request, without guessing successor limits or replay. A cooperative abort during validation also prevents the adapter request after byte checks settle.

Opaque SDK streams (or transparent wrappers returning no metadata) remain caller-owned: the validator does NOT borrow another/global provider's catalog or claim that unknown limits passed. Such callers need to supply their own capability contract/validation. Native/registered metadata-backed app paths are exercised; unknown-provider/raster/native-platform interoperability remains open.

Bounded RPC input and chunked image references

ready advertises capabilities.image_uploads: 1 and max_input_frame_bytes. Native POSIX stdio uses an owned asynchronous byte reader with a 64 MiB JSONL frame bound (preserving four legacy 10 MiB inline images plus JSON overhead). Oversized records are drained/rejected as WHOLE records; suffix fragments never become commands. Invalid UTF-8 is an explicit protocol error, and the next framed record remains usable. An unterminated record still needs its delimiter/EOF; this is framing/resource ownership, not an OS sandbox or a claim of instant parsing.

Upload base64 chunks with the original session ID and a fresh connection-local ID:

{"type":"image_chunk","session_id":"SESSION","id":"image-1","index":0,"mime_type":"image/png","data":"BASE64_CHUNK","final":false}

Indices are exact nonnegative integers, ordered from zero. Each encoded chunk is at most 100,000 characters; final is a real boolean. The final chunk includes sha256 over ALL decoded image bytes. image_upload_result reports partial/ready/ error and index. Limits: 16 active uploads, 10 MiB per decoded image, 40 MiB aggregate decoded cache bytes. PNG/JPEG/WebP header/dimension admission precedes ready; there is no URI/file loading. No disk storage, model work or permission changes happen on upload. The shared assembly helper stays in xdog.ai; each application retains its own ownership/header guards instead of Coding importing Claw policy.

{"type":"prompt","session_id":"SESSION","image_refs":["image-1"]}
{"type":"image_discard","session_id":"SESSION","id":"unused-image"}

Choose inline images OR references, not both. References require completed uploads, are single-use once a valid prompt lease is admitted, and are retired after use or explicit discard (4,096 connection identities including active IDs). Invalid owner/ header/duplicate/incomplete references never silently resolve another image. Busy or malformed prompt requests do not consume a valid cached reference. Session replacement evicts cached images; EOF/quit releases all buffers. Lost outcomes are not replayed; re-upload deliberately under a new ID for another attempt.

The caller should read acknowledgments between chunks and may interleave status or control frames. Model capability/byte/count/media and complete-context admission still happen through the existing SDK. Cache limits count decoded bytes, NOT total process RSS/encoded-history memory. Alternate-platform/custom reader cancellation, provider/raster conformance and goal-wide integration remain separately unproven.

Client-owned RPC editor CAS requests

Negotiate extension_editor: 1 independently of questions/forms:

{"type":"rpc_hello","extension_editor":1}

Interactive extension COMMAND callbacks may use existing get_editor()/ set_editor(text, revision) APIs. The host emits the ordinary scoped extension_dialog envelope with dialog.kind="editor", operation read/replace and (for replace) the original revision/text. Answer on extension_dialog_response with ALL original session/request/scope/dialog identities. A read snapshot is:

{"text":"client draft","cursor":12,"image_count":1,"revision":"64_HEX_REVISION"}

A replacement receipt is {"applied":true,"reason":"..."} or an explicit false conflict. The remote client owns its ACTUAL editor, cursor and attachment identities. Use xdog.tui.editor_actions.EditorActions or an equivalent implementation: capture those values under the original RPC owner, generate a fresh opaque revision, then apply only if that owner/revision/full state still matches. Invalidate on human edit, caret/attachment/view change and owner retirement. Reply true only AFTER actual CAS application; never apply returned text as a command or auto-submit a prompt.

The host validates copied snapshot/receipt fields and scopes, remembers only the latest served revision per extension/command, consumes it once before forwarding replace, and refuses fabricated/retired reads. It DOES NOT maintain a fake editor mirror or claim it can prove a remote screen/client's correctness. Capability withdrawal, EOF/quit/cancel invalidate pending receipts without replay.

Editor-only UI negotiation also requires request/session IDs on parent prompts, but only COMMAND callbacks get editor authority; tools/hooks/custom/prompt callbacks cannot borrow it. Questions/forms need their own separate bits. Native terminal renderers, RPC navigation/name mutation and independent-child editor surfaces stay open—not implicitly supported by this editor transport. Permissions, canonical history/model and uploaded/composer image ownership are unchanged by text CAS.

Native RPC session catalogs and navigation primitives

ready.capabilities.session_controls: 1 and ready.session_revision describe the native owner-backed controls. Every request needs a fresh ID and the actual current session from ready/status/the last navigation receipt:

{"type":"session_control","request_id":"list-1","expected_session":"CURRENT","action":"list","query":"name","offset":0}
{"type":"session_control","request_id":"resume-1","expected_session":"CURRENT","action":"resume","target_session":"TARGET"}

list/tree provide the same native Coding session/branch/checkpoint metadata as the terminal browser, with bounded catalog pages/filter/membership revisions. catalog_revision resumes a page; session_revision is the separate conversation lease. Reads run off the reader loop, do not activate an Agent/model, and never export full history/image data as metadata. The current tree is persisted native source, not an invented live streaming checkpoint.

Idle-only primitives: resume TARGET, new, current-session fork, rename, and combined branch/goto selections. Ready capabilities include session_checkpoint_targets: 1: branch accepts target_session plus branch_id; goto accepts target_session, optional branch_id, and index. Empty target means the current session. RPC index is ZERO-BASED, as reported by catalog checkpoints (distinct from terminal /goto numbering). Restore uses existing SDK staging/model validation, retained return heads/tool boundaries/resource/permission gates. A failed fork save does not publish a phantom branch; a failed new-session preflight removes only its unchanged unadmitted candidate, never independently modified target data.

Successful mutations report the ACTUAL session/model/head and a fresh session_revision. After using mutation controls, prompts, extension controls and native model/thinking/compact controls must include session_id plus that current revision. Stale/legacy owner-less packets cannot silently operate on the new head. Status/settings acknowledgments expose current revision. Old clients not using navigation retain their earlier prompt behavior.

session_control_cancel carries original request/expected-session IDs. Its session_cancel_requested acknowledgment means requested, not rolled back. The existing disk/preparation owner settles before another control can start. Admitted commits report actual state with receipt_canceled; unadmitted stages retain the head. Status stays responsive with a stable pre-publication snapshot while the thread owns restore work; it does not claim a half-published new model/context.

Cross-session branch/checkpoint selections now use one staged SDK admission: AgentSession.navigate_checkpoint(session_id, branch_id=..., index=...). The selected branch's history/model/settings/plan/extension state is preflighted before one target save/publication; the target's unrelated live head is never activated first. Failed preflight, invalid tool boundaries or failed target save retain the original owner without saving phantom checkpoint branches. Native /branch SESSION BRANCH and /goto SESSION [BRANCH|-] INDEX use the same operation; a saved return checkpoint preserves the target's previous live head. Same-head /goto also stages its checkpoint before persistence rather than saving a branch before attempting activation.

This is atomic application-level admission, not a multi-file/OS power-loss transaction. Outgoing history is persisted separately and admitted cancellation settles actual state, not invented rollback. Retained live/saved/timeline display-source replay and scoped child supervision are available below; raw binary/canonical, richer custom/ terminal, unclean-crash queue recovery and transient UI artifact surfaces remain OPEN. This is not a claim of all API parity, an OS sandbox or a complete goal. No returned extension command text is executed to navigate.

Reviewed extension navigation over RPC

Negotiate extension_navigation: 1 independently of questions/forms/editor controls. A trusted extension command may use api.navigate_session(...); the host sends a scoped extension_dialog with kind session, operation navigate and the EXACT typed target (action/session/branch/checkpoint). The remote client must visibly review that target under the original owner and answer a strict boolean, with all original IDs. This is not tool approval or arbitrary returned-text execution.

True means queued after successful callback, NOT activated. The host queues a target only after its broker future ACTUALLY yields true, not merely because a late transport answer was acknowledged. Callback failure/cancel, lost owner/revision or withdrawn capability discards it. On callback success, an independently correlated extension_navigation_queued.navigation_request_id maps to the native session_result; existing staged restore/model/idle/child/permission/lease checks still determine whether that later mutation commits.

The same actual-future rule now governs RPC editor-read revisions: an expired/ canceled read cannot create a usable host lease merely from a transport ACK. Remote client rendering remains its responsibility; an opted-in capability is not proof of an arbitrary screen's correctness or permission to change tools/models.

Cross-target combined branch/goto reviews use that same single staged admission, not resume-then-restore. Missing branches and invalid checkpoint/metadata selections are refused before review, and selection plus owner/capability are checked again after actual acceptance. Final native model/extension/workspace preflight still controls commit. fork remains a save of the current live head only, never a hidden cross-target operation. Terminal custom views, richer independent-child RPC surfaces and fuller reference/native-platform/atomic-root evidence remain OPEN; child supervision/source/ question transport is described below. Pass 143 separately covers the native Coding/Claw late-answer navigation/rename grant race; RPC correctness alone is not proof of other native/gateway races.

Complete read-only RPC transcript sources

Ready advertises transcript_sources: 1. Explicitly negotiate it with {"type":"rpc_hello","transcript_sources":1}; only the exact integer enables content reads. This flag is independent of extension questions/editor/navigation: a source-only hello does not withdraw an existing extension capability. The transcript_capabilities response reports enabled state and transfer limits.

{"type":"transcript_read","request_id":"source-1","session_id":"CURRENT","session_revision":"CURRENT_REVISION","scope":"saved","target_session":"SAVED","branch_id":"ARCHIVE"}
  • scope: "live" (default): a captured tuple of current admitted messages plus the actual partial response, without reloading stale disk history. Target must be current and branch empty. Later admitted messages require a fresh read.
  • scope: "saved": the exact saved session head or named branch. No activation, provider/model/compaction call, workspace switch or current-head fallback.
  • scope: "timeline": complete recorded branch/head provenance for the target, including verified parent prefixes and compaction recovery links. For the current session the head is captured live; other heads are saved. No branch argument or content-only deduplication. The wire branch is @timeline.

Reads require a fresh bounded request ID (256 per connection), exact current session and conversation revision, and no admitted navigation owner. One worker settles preparation/spool cleanup before starting the latest replacement; one pending request coalesces newer reads. Replaced, canceled or withdrawn requests cannot silently replay. A successful mutation/revision change invalidates an unfinished source. Status and tool-approval controls remain independently readable.

After transcript_queued, the server sends source_begin (byte count/SHA256), ordered source_chunk records (base64 of up to 64 KiB bytes), then source_end (chunk count). The payload is UTF-8 JSONL records with id/role/title/body, not terminal screen lines or model/tool execution instructions. Every frame carries the original request/session/conversation lease, target session, branch/scope and an integer snapshot ordinal (revision, distinct from session_revision). Assemble and verify all frames/checksum before publishing records or enabling search/selection/export. source_error invalidates any partial assembly. No client may treat queue/transport acknowledgments as a completed source or navigation/tool approval.

The shared xdog.tui.source_receiver.SourceReceiver supports this framing. Call expect(request_id, "", session_id, target_session_id=..., branch_id=..., session_revision=...) and construct it with the negotiated max_bytes bound. Its completed records feed the existing TranscriptBrowser/TranscriptPanel and captured-text output helpers; those components keep drafts and native scrollback separate. Claw's existing import remains a compatibility export of the same receiver.

Display sources preserve full text, reasoning and joined tool-call/final results, with terminal-control sanitation and heuristic credential masking. Binary image bodies, reasoning signatures, system prompt, and opaque custom metadata are NOT exported as prose; images receive explicit omission markers. This is complete retained DISPLAY text, not raw canonical context/binary export or a reconstruction of every transient notice/custom/child/live-tool-progress surface. Redaction is not a confidentiality guarantee. Message/timeline limits remain those of native readers; RPC transfer capacity is 128 MiB and oversize fails explicitly, never truncates.

transcript_cancel uses the original request/session/revision. The transcript_cancel_requested receipt requests cancellation, not completion. EOF, quit, capability withdrawal and replacement settle the existing preparation owner and close its private spool; connection closure never invents a completed snapshot.

Native POSIX pipe/socket/terminal RPC output now has one ordered nonblocking writer. Source frames await actual writes so a slow peer cannot queue an entire transcript in memory or block the command reader. Canceled producers cannot abandon half a JSON line while the connection remains live; output failure wakes the input owner. Ready reports rpc_async_output and a 64 MiB output backlog bound (whole-frame refusal, no truncation). Shutdown drains briefly then releases a stalled writer and restores descriptor flags. Custom sinks/regular files/unsupported platforms retain synchronous compatibility (rpc_async_output:0); that is NOT native Windows/macOS cancellation or interoperability evidence. No OS-enforced sandbox is implied.

Scoped child supervision over Coding RPC

Ready advertises child_controls, child_sources, child_approvals, child_dialogs, child_forms and child_multi_forms. Independently negotiate exact integer 0/1 flags via rpc_hello; none borrows extension/editor/navigation or transcript consent. Child-only hello leaves parent extension capabilities intact. child_controls:1 is required to use this surface. child_capabilities returns the actual current session_id, session_revision, connection-scoped child_owner, and native session/branch supervisor child_epoch. Copy those FOUR fields into every child control, source or reply. Refresh after navigation; old epochs/owners are refused.

{"type":"rpc_hello","child_controls":1,"child_sources":1,"child_approvals":1,"child_dialogs":1}
{"type":"child_control","request_id":"start-1","session_id":"CURRENT","session_revision":"CURRENT_REVISION","child_owner":"UI_LEASE","child_epoch":"SUPERVISOR_EPOCH","action":"start","task":"Inspect the tests","access":"read-only","config":{"label":"Reviewer","max_model_calls":4,"max_tokens":8000,"inherit_context":false,"tools":["filesystem"]}}

child_control actions: list, inspect (bounded redacted roster metadata, NOT raw canonical history), presets, configure (resolve native preset/config without starting), start, steer, follow-up, cancel, wait, and close. Existing CodingChildren/ChildSupervisor owns actual agents, captured context, independent extension workers/permissions, downward tool/model/budget limits and durable records. There is no second tool loop, recursive spawn, provider fallback or fabricated model. Configuration is the native ChildConfig schema; it cannot set access/permissions or include reserved delegation/planning tools. access is separately read-only by default; explicit inherit retains current parent gates, never copies its approvals.

Start requires an idle parent/extension/control/context owner. Parent prompt and context-changing RPC controls cannot borrow a pending admission. Once admitted, existing supervised children can run alongside the parent. Four control jobs and 256 fresh request IDs per connection are bounded; source IDs have their existing separate bound. child_status notifications are marshaled from the child owner loop onto the RPC reader loop and carry scope plus redacted roster metadata. Do not rely on a status/question arriving after the start receipt: child startup is genuinely concurrent. child_result correlates the original control and reports actual outcomes.

child_control_cancel matches the ORIGINAL request and all four scope fields. child_cancel_requested is only a request acknowledgment. Canceling a wait job does NOT cancel the child; use control action cancel. A canceled start settles its exact admission and cancels that admitted child, never guessed catalog members. Close archives the existing record rather than restarting it. Effects already admitted are not rolled back/replayed. EOF, quit or output failure stops the parent first, denies pending child approvals, settles control jobs and closes/joins all connection-owned child supervisors/workers before disposing the session.

child_sources:1 enables child_source_read with fresh request ID, child ID and the four scope fields. It uses the same complete verified source protocol/byte bounds as transcript reads, but scope child and the envelope adds child_id, child_owner, child_epoch, and captured child_revision. It is independent of parent transcript_sources consent. Shared SourceReceiver.expect also accepts the three child fields to reject mixed-child frames. transcript_cancel must copy the original parent/revision AND child ID/owner/epoch for these source requests; a parent-only or foreign-child cancel is refused. Sources include actual canonical child text, partials, bounded live tool progress with explicit capacity markers, effective model/config and input-handler feedback as SYSTEM data (not a model answer). Final canonical results win over older progress. Original native child retention/budget limits remain; no raw binary/signature/private model header export is implied.

child_approvals:1 enables child_permission_request, carrying child-specific request ID, actual tool/arguments/summary, all four scope fields and approval_generation. Reply with child_permission_response, those ORIGINAL fields, child ID, ID and decision allow_once, allow_session or deny. Parent permission responses cannot settle these child futures. No child borrows parent session rules or another connection's requests. Approval withdrawal clears pending requests/rules and changes generation: an already resolved approval held before final tool admission cannot revive by toggling the UI back on. Current parent deny, read-only child access and parent planning restrictions still veto mutations. Unattended mutation remains possible only with explicitly selected allow-all policy.

child_dialogs enables child input/select/confirm questions; forms require child_forms and multi-choice fields require child_multi_forms. Reply via child_dialog_response with original scope, child ID, host dialog ID and typed value. These are task data, NOT tool approval. Withdrawn/unsupported pending dialogs cancel at the native broker even before UI publication was delivered. Repeated, expired or foreign answers cannot reuse another future. Editor/session navigation/rename/custom/ terminal callbacks are refused, not forwarded as parent command authority.

This implements real supervision/source/question/approval transport, not an arbitrary client's physical review/rendering, richer child terminal/custom APIs, unclean-crash input recovery, deeper delegation, native-platform verification or whole-goal parity. Live editable child queues are described below.

Editable live child input queues

The child roster now offers q (or Ctrl+Q from transcript preview) to inspect the actual owning agent's pending steering/follow-up inputs. e edits, x removes, k/j reorders one step within the original delivery lane, and u returns text to the CHILD steering/follow-up draft. Return is not submission and never overwrites the parent editor. Every change requires complete painted target/content/revision review and a fresh physical Enter; paste, repeat events and narrow/unpainted frames cannot confirm it. Failed edits keep the local draft. An exact owner removal receipt, matching the reviewed input, is required before draft restoration.

Hosts read/change the existing Agent queue on the child owner loop, not a mirrored UI FIFO. The captured revision covers identity/order/content/delivery/checkpoint selection and image identity. Changed/stopped queues and selected/consumed inputs refuse mutation without automatic replay. Explicit refresh is required. Image parts stay attached during an edit; return refuses image-bearing input because the child form is text-only, rather than silently losing attachments. These are live input controls, not permission approval or conversation/model-context replacement.

Claw advertises exact child_queues:1; older/unready/withdrawn peers cannot dispatch queue controls. Coding RPC negotiates child_queues:1 separately, then uses child_control action queue with a typed queue object: {"action":"list"} or {"action":"edit","id":"INPUT","revision":"CAPTURED_SHA256","content":"TEXT"}; remove/return omit content, move instead has exact integer direction -1/+1. Keep all original parent/revision/UI-owner/native-epoch fields. Native socket hosts use that same JSON request in the child_control text field. Responses contain a queue with child_id/revision/inputs and optional exact returned removal receipt. Metadata is bounded to 32 inputs/16KiB text per input, with explicit refusal rather than truncation. It is exact queued draft text, not heuristic-redacted display source.

Gracefully completed/canceled-child undelivered input recovery is described below; image-capable child drafts, unclean-process crash durability, richer terminal/custom APIs and the broader platform/interoperability goal remain separate open requirements. No automatic mutation/model call, OS sandbox or full child-surface parity is implied by these queue controls.

Saved stopped-child input recovery

After a child actually settles, the supervisor drains only identified inputs not admitted to canonical history, releases the live agent, and persists those inputs with the canceled/completed child record. Already consumed follow-ups are not copied again. Existing records without this field load unchanged; malformed, duplicate, oversized or invalid typed images refuse load rather than silently disappear or restart a child. Recovery JSON is bounded to 32 inputs/16KiB text each and 512KiB aggregate, plus the existing complete child-record persistence budget. Save failure is explicitly reported; no partial/truncated recovery is advertised as durable.

The same q child input viewer now identifies saved recovery. u reviews a recover request, then copies the exact retained text into a NEW read-only child TASK draft after a matching recovery receipt. This does not steer a dead agent, restore its approvals/model context, submit the draft, restart work or change the parent editor. The backing saved input remains: a lost or canceled copy receipt cannot erase the only durable copy. Starting a replacement task still requires the normal whole configuration/task/access review and fresh explicit submission.

x is separate reviewed permanent discard. Its revision-checked removal saves the candidate record before publishing the removed input. Failed save leaves both memory and disk intact; admitted cancellation/shutdown settles the actual write without inventing rollback or allowing overlapping close/discard operations. Stopped inputs are not editable/reorderable live state; make edits in the new draft. The API uses queue action recover, original input ID/revision and original parent/ child scope. The response has stopped:true, unchanged inputs, and an exact recovered row. This is distinct from live return, which actually removes an input and yields returned. Explicit consent to child queue data remains required.

Typed image parts are retained privately in saved recovery, omitted from queue metadata except image_count. Copy to the current text-only child task draft refuses image-bearing input rather than lose attachments. Native image-capable child drafts and unclean SIGKILL/power-loss durability during active runs remain open; settlement persistence and canceled-owner tests are not proof of those broader guarantees. No auto-replay, paid provider call or OS sandbox is implied.

Child rendered-image admission

Child requests now validate the complete rendered image context even when the child uses the parent's default model or only overrides thinking. This includes retained user/tool images and images added by context hooks. The final guard resolves the actual child adapter's current model metadata, checks streaming-chat and image capabilities, image-count/byte/media limits and typed base64 payloads, and refuses a model profile change during admission before calling the adapter. A withdrawn model is not revived from the earlier selection snapshot. Metadata-less custom adapters remain supported for text, but cannot send a child image context without an advertised model profile; no images are silently converted to text.

Retained-context counts use the advertised model limits, not the four-image composer limit. Unspecified counts do not impose that per-draft limit on retained history. Existing neutral payload and child transcript/persistence budgets remain in force. Offline tests cover both actual application child factories, retained user/tool images and a real owned context-hook worker. Existing private-terminal child approval/recovery checks are separate compatibility evidence, not an image attachment UI test. Native child start/steering/follow-up image drafts and typed submission are implemented below. Image queue return/stopped recovery and remaining draft retention/rendering/transport requirements stay OPEN.

Reviewed child image drafts

The native child editor now supports real image attachments for start, steer and follow-up. In a child task form, Ctrl+O opens a literal file-path entry (relative to the workspace); Ctrl+V explicitly reads the clipboard; Ctrl+B opens the child attachment list (Enter previews, x removes, c clears). These controls act on the child draft only. The parent text, cursor and attachments are not borrowed or changed. File/clipboard reads run outside the UI loop, and images stay editable until a separately painted confirmation review.

The review pages the task, access, configuration, image identity/dimensions/size and SHA256 digest. Every page must be painted before physical Enter can admit work; pasted newlines/repeated or unpainted Enter cannot confirm. Image-only tasks and image-only steering/follow-ups are supported. Successful admission clears the child draft only after the actual owner returns matching text, image digests and MIME types. Missing/mismatched/failed receipts retain the exact draft; an uncertain receipt is disclosed again on reopening. Reopening never automatically submits.

The normal input limits are four PNG/JPEG/WebP images per draft, 10 MiB each. Editor retention is shared with other local attachments; the child runtime has its own 40 MiB retained-image and serialized-collection guards. Images no longer consume the 512 KiB text/metadata transcript budget. Initial images are saved before child execution and preserved when factory setup fails; canonical and unadmitted queue images persist without truncation. Reload does not restart work. Switched-model context preflight no longer treats base64 transport as plain text but uses a rough image allowance alongside the conservative text guard. This is not exact provider token accounting or a promise that any image fits any model. Final actual-context model validation and all existing permission/budget rules apply.

SDK callers can pass images=tuple[ImageContent, ...] to ChildSupervisor.start or its steering/follow-up control methods. The application child APIs propagate these same typed inputs. No file URI, remote fetch or implicit text fallback is introduced. Claw requires the gateway's exact child_images:1 capability and sends bytes through the existing checksummed chunk uploads. Large canonical image bodies are omitted from socket control receipts, explicitly marked history_omitted; use the separately owned child-source reader for complete display history, not raw binary/canonical data. Successful image refs are released; an exact replay of a cached request returns its old receipt without rerunning or requiring those refs.

Coding RPC independently negotiates child_images:1 via rpc_hello. Scoped child_control start/steer/follow-up accepts either inline images or completed image_refs, never both. Larger images use existing session/connection-owned image_chunk uploads; successful admission consumes those references. Replies include accepted_input with exact content/digests/MIME types, not image bodies. Revocation cancels pending image jobs; it is not a rollback of already admitted work. No image capability grants tool, approval or parent-editor authority.

Offline evidence includes both actual app/UI owners with file reads and 10 MiB payloads, the real Claw socket upload path, an actual Coding RPC subprocess with 2 MiB chunk uploads, queue settlement/reload, mismatched-receipt draft retention, and both private-tmux file/preview/review-cancel/terminal-recovery paths. Large test payloads exercise bounded byte transport with a valid PNG header, not a hosted pixel decoder or general model-provider interoperability.

Still open: live image-bearing queue return requires its verified removal/ receipt protocol. Stopped-input image copy is implemented in the following section; legacy text-only recovery still refuses image inputs rather than dropping bytes. Saved initial-task images from failed setup use the explicit recovery editor below. Local child drafts survive hide/reopen in the same view context, but cross-reconnect or cross-context draft browsing/recovery and unclean-crash durability are not complete. Those retained caches are bounded and never automatically adopted into a different owner. Native desktop clipboard/provider/decoder interoperability also remains open.

Non-destructive stopped-child image recovery

In a stopped child's input queue (q in the roster), u now prepares a reviewed image-aware copy. The source input and its exact revision are reviewed before Enter. A complete, scope-bound, checksummed binary transfer and matching owner receipt are required before the UI creates a separate read-only child start draft, with inherit_context: false. Images, text and MIME types are kept; no agent is constructed, model/compaction request sent, original input erased or parent draft altered. Existing local child drafts are not overwritten. Each copy has its own local draft slot; the local/cross-context draft browser below makes retained slots accessible, but its memory caches are not cross-process crash recovery.

The SDK queue action is recover_images, with the exact input id and queue revision. Its Python result contains the non-destructive image_recovery payload and queue.image_receipt. The legacy text-only recover action still refuses image inputs instead of dropping bytes. Running children are not recovered by this action; live image return still requires its own verified removal/admission protocol and remains unavailable.

Claw advertises exact child_image_recovery: 1; older gateways do not silently receive a text fallback. Coding RPC requires independently negotiated child_image_recovery:1 and child_queues:1. This read/copy capability is separate from sending new image prompts. Socket/RPC replies stream child_recovery_begin, ordered child_recovery_chunk frames (at most 64 KiB raw), then child_recovery_end; final control metadata contains no base64 image bodies. The manifest binds content, image sizes, digests and MIME types to the requested session/connection child lease, child ID, input ID and queue revision. Receivers reject foreign/out-of-order/oversized/truncated/checksum/MIME mismatches, close private spools on failure/cancel, and never manufacture a draft from partial data. Transfers are bounded to four images, 10 MiB each / 40 MiB total, and a ten-second sender deadline. Owner/revision changes or RPC capability withdrawal stop the copy. The original durable input remains available for explicit fresh review/retry.

Evidence includes both actual native client owners with 10 MiB stopped-image copies, real Claw socket framing, an actual Coding RPC subprocess with multi-MiB outbound copies, revocation and malformed-frame checks, and both private-tmux review, preview, no-execution and native terminal/cursor recovery paths. This is offline Linux evidence, not general hosted image-provider or native desktop conformance. Live image return, durable draft recovery and remaining host APIs, active-run unclean-crash durability and remaining platform/API gaps stay OPEN.

Local child draft browser

Use d in the child roster, or /agents drafts, to browse retained local child start/steering/follow-up drafts and image recovery copies. The catalog includes older session/branch/view/connection contexts, with workspace/session provenance, CURRENT/OLD CONTEXT labels, image counts and uncertain-receipt disclosure. / searches task text and provenance. This catalog lives in the current application process; it is not on-disk draft persistence or active-run crash recovery.

Enter prepares a non-destructive copy into a new read-only start draft with inherit_context: false. Old access, tool selection, instructions, model/thinking and preset settings are disclosed but not imported. Text/images remain exact, old caches stay intact, and no model/tool/session navigation is executed. A copy gets its own draft slot; it does not overwrite an existing default task or another edited recovery copy. Source uncertainty stays visible in the copied draft.

r prepares reopening only a CURRENT-context draft. Start drafts retain their settings and saved cursor; existing-child steering/follow-up drafts require that child to still be running. An old-context or stopped-child draft cannot resume; explicitly copy it fresh/read-only instead. The normal model, tool, permission and full start/input confirmation rules still apply when submitting the reopened form.

x prepares discarding only the selected LOCAL draft. It does not cancel an agent, mutate a live queue, erase the durable stopped-input record or revoke tools. Copy/reopen/discard each require all provenance/task/config/image digest pages to be painted before physical Enter. Pasted or repeated/unpainted Enter cannot commit. Source identity/content changes or a changed browser owner reject the action, without replay. Discarding the local cache releases its retention accounting; a separate fresh copy continues owning any shared immutable image data.

Both native clients wire this browser to their actual context-owned caches, so closing auxiliary panels/reconnecting does not silently strand bytes forever in an inaccessible old view. Old-context entries remain inspectable/copyable but do not regain their previous execution or approval authority. Final pixel/provider/ native-desktop interoperability and durable cross-process draft recovery remain on their existing open requirements. RPC remote draft browsing is not implemented by this local native UI browser.

Initial child task recovery after setup failure

The stopped child input queue now includes a distinct initial-task source when setup fails before the original task is admitted. Text and initial images remain available even if no child history was created. u prepares an explicit copy; x prepares discarding that retained initial input only. Both actions require the exact source/revision and full physical review. Image copies use the existing verified binary recovery transfer and open a separate fresh read-only start draft with inherit_context: false. No model, agent restart or original rewrite occurs on copy, and parent/local sibling drafts remain untouched.

The runtime records initial admission separately: pending, admitted, handled, uncertain or discarded. Actual user-message admission marks the initial task admitted; an initial prompt handled by an extension is not offered as unadmitted. A provider failure after actual admission does not reclassify that task as unsent. Interrupted/legacy records without sufficient admission evidence are explicitly uncertain, not proof of zero tool/model effects. Review the old history before choosing to submit a recovered copy. This is not active-run power-loss queue journaling or automatic retry.

SDK queue actions recover_task / remove_task identify the initial-task row; ordinary edit/remove/return actions cannot accidentally reinterpret it as a live queued message. Copy is non-destructive. Discard publishes only after the actual persisted state change settles; failed writes leave it available, and cancellation waits for the writer before reporting an uncertain committed outcome. Image bytes are released from the retained initial field after explicit committed discard.

Claw advertises exact child_task_recovery:1; Coding RPC negotiates it independently alongside child_queues:1. Initial-image copy additionally needs child_image_recovery:1. Final RPC/socket control frames omit binary bodies and use the existing manifest/chunk/end receiver plus exact task receipt. Capability withdrawal or owner/revision change stops copy work, not the original durable task.

Offline tests cover real setup failures with/without images in both native owners, legacy interrupted records, admitted-provider failure, failed/canceled discard, RPC capability isolation, and both private-tmux image recovery/review/preview/draft browse/native-terminal restoration paths. The full goal remains active: live image return, durable local drafts, broader raw/custom/RPC APIs and native platform/ provider/credential interoperability still require implementation/evidence.

Live image-return runtime contract (client integration pending)

The shared supervisor now provides a two-phase SDK contract for returning a live child image input. The native and Coding RPC integrations are described below. Legacy one-step image return still refuses lossy text conversion; use the two-phase protocol. This section records the implemented foundation, not a claim that the UI gap is closed.

Call ChildSupervisor.control("queue", child_id, json_request, return_owner="explicit-host-lease") with return_prepare, the input id and exact queue revision. Preparation copies/validates bytes off the owner loop but leaves the original queued. The result contains image_recovery, a matching queue.image_receipt, return_prepared metadata and a random return_token. Before removal, a host must fully receive/verify those bytes, preserve their ownership and separately review the removal. Preparation alone grants none of that consent and is not a dequeue operation.

return_commit requires the same owner, input, revision, token and exact verified receipt. On the supervisor loop it removes only the same still-unselected input, then returns queue.returned metadata and the matching receipt/token. Selected, consumed, edited, stopped, foreign-owner, expired or malformed inputs refuse without removal. return_cancel releases only that owner's prepared token and leaves the original input queued. Leases expire after 30 seconds; each child has four combined prepared/completed slots. Exact same-owner/content commit retries within the lease return the original copied receipt, never remove another input.

Runtime tests use actual held child Agents, including 10 MiB payloads, concurrent queue edits during byte preparation, cancelled preparation, selected/edited/stopped/ expired/foreign receipt rejection, bounded leases and immutable retry receipts. They do not verify client-owned binary transport/consent/persistence, connection revocation or terminal image return. That integration remains required; crash/power- loss durability is not supplied by a process-local return token/cache.

Coding RPC live image return

Coding RPC now independently negotiates child_image_return:1 together with child_controls:1 and child_queues:1. The returned child-capability envelope includes child_return_owner, separate from child/session/connection identity. Return requests must use that current lease; withdrawing/re-enabling controls, queues or return capability changes it, so old tokens cannot regain authority. Other pre-existing child commands do not require this additional return-only field.

Send scoped child_control with action queue, containing return_prepare, the input id and captured queue revision. The existing binary manifest/chunk/end stream provides exact images to a verified receiver, followed by a final result with return_prepared, return_token and image_receipt. The input remains queued; stream completion is NOT removal consent. No separate stopped-image-recovery capability or new image-prompt capability is required for this return operation.

After actually receiving/verifying and taking ownership of those bytes, the client must separately review removal before sending return_commit with the same input, revision, token, current return-owner envelope and verified receipt. The reply contains returned metadata, not base64 bodies; exact commit retries with fresh request IDs return the prior receipt and cannot dequeue another input. Explicit return_cancel releases a prepared lease without removing its original. All original selected/stopped/edited/expired/foreign checks apply on the actual supervisor loop.

Closed peers, revoked capabilities, cancellation or changed queue ownership stop preparation and settle token cleanup. An operation already committed before a transport failure is not rolled back: inspect its exact receipt/current queue; never automatically replay work or replace parent history. An explicit live host revocation guard is checked again at the owning-loop removal boundary.

Evidence includes an actual offline Coding CLI subprocess returning 2 MiB through bounded native output, verified byte receipts and separate commit/retry, plus withdrawal, closed-peer, foreign-owner and disable/re-enable tests. The RPC contract alone does NOT supply native queue buttons or cross-process durable draft ownership. Native Coding/Claw integrations are documented below; durable cross-process ownership remains open. Those UI/ platform/durability requirements remain OPEN; remote clients own their review and byte-retention implementation rather than borrowing approval authority from XDOG.

Native Coding live image return

Coding's child input queue now supports u for live image inputs. It requires two independent full physical reviews: first prepare and verify the byte copy without dequeuing; then a visibly labelled SECOND removal review before committing removal. Unpainted/repeated Enter or pasted newlines cannot skip either review. Cancel during the second review releases its lease and leaves the original input queued. Selected/edited/expired/stopped inputs still fail on the owning loop.

Before presenting removal consent, the verified image/text copy is stored as a separate conservative read-only local backup, marked uncertain and available in /agents drafts. A lost/mismatched commit receipt therefore cannot lose the only local copy. This memory backup is NOT crash/power-loss persistence. After an exact successful removal receipt, a separate child steering/follow-up draft owns the same image bytes; no parent text/images are borrowed, no sibling draft overwritten, and no child/model turn starts until explicit submission. The backup remains available for inspection/discard because transport outcomes may be uncertain.

Coding's native worker binds return leases to the exact panel AND queue-view owner, checks that owner at actual runtime admission and fences late results from a replaced queue view. Auxiliary queue Escape does not detach a commit already settling; forced closure still leaves its prior local backup accessible. Four images/10 MiB per-image and shared local retention limits remain; a failed backup allocation refuses removal rather than dropping bytes.

Tests cover actual native Coding 10 MiB commit/cancel, exact precommit backup, unchanged parent/history/model-call count, mismatched receipts and a real private- tmux image submit → queue → preparation review → second removal review → child image draft → cursor/termios recovery path. Claw's equivalent native socket return is implemented in the following section, with its own negotiated connection/view lease and verified transfer wiring. RPC uses the previously documented explicit client-owned contract. Durable backups, remaining native-platform/interoperability and whole-goal parity stay OPEN.

Claw socket/native live image return

Claw now supports the same two-review live image return as Coding: u in a live child's queue prepares and verifies the byte copy without removal, then the SECOND removal review requires fresh physically painted consent. A separate read-only uncertain local backup is retained in /agents drafts before commit. Successful exact removal receipts open a child-only draft with the original text/images; cancellation leaves the queued input and parent draft untouched. Backups are process-local memory, not crash/power-loss persistence.

The TUI explicitly negotiates child_image_return:1 in its socket ping. Gateway responses supply a random child_return_owner; return requests also carry the exact queue_view token. The gateway forwards their combined lease through the existing orchestrator/session path into the actual supervisor removal guard. Disabling/re-enabling the capability rotates/revokes its connection lease, and socket teardown revokes it before cleanup. A different view or old/reconnected lease cannot commit a prepared token. Native results/verified copies are checked against the original queue view, not adopted into a replacement panel.

Preparation uses the bounded checksummed binary recovery stream with additional connection/view scope; image bodies never enter the final control frame. A failed or cancelled preparation settles its token cleanup without dequeuing/restarting. The normal selected/edited/stopped/expired checks apply again before removal. A committed operation whose response is lost is not rolled back: inspect the retained local backup/current queue and never automatically resubmit work.

Actual Coding/Claw native tests cover 10 MiB commit and cancellation, two reviews, precommit backup, exact child draft and untouched parent/history/model-call count. Claw socket tests cover foreign views, capability disable/re-enable and disconnect with the original unremoved image preserved through stopped-input persistence. Both private-tmux paths physically exercise live image preparation, second removal review, returned image editor and native cursor/termios recovery. This is Linux/ offline evidence, not general provider/decoder/native-desktop interoperability. Durable drafts/active-crash journaling, richer RPC/custom/raw host APIs and remaining platform/credential/MCP interoperability stay on the open goal requirements.

Concurrent return capacity and malformed queue transport

The four-slot live-return budget now includes in-flight image preparations as well as prepared leases/completed receipts. Slots are reserved before asynchronous validation yields and released on cancellation/failure. Simultaneous requests cannot each pass the limit and create unbounded image copies. This does not add crash durability or change the two-review consent workflow.

Claw validates typed queue request shape before examining return-specific fields. Malformed arrays/null/scalars/missing fields or invalid JSON produce a failed control receipt while the owned socket remains responsive; they do not disconnect the client or admit queue/agent work.

Image worker cancellation settlement

Image recovery/return preparation and sender decoding now wait for their actual owned worker thread to exit when cancelled. Cancellation revokes delivery: no receipt or binary frames are published afterward. Repeated cancellation does not release a preparation capacity slot while decoding/hashing is still running. Late worker errors are consumed during cancellation; ordinary results/errors are unchanged. Python cannot forcibly stop a thread, so cancellation settlement may outlast a transfer deadline while bounded local image work finishes. This is not process isolation, an OS sandbox or crash durability.

POSIX child-record directory durability

Completed child-record writes now fsync the temporary file, atomically replace its destination, then fsync the containing directory. Newly created child storage directories use private modes and their parent directory entries are also flushed. Existing directory permissions are not changed. This applies to initial records, settled results and explicit recovery/discard metadata writes.

A directory-flush failure after replacement is an uncertain committed outcome: XDOG propagates the failure without rolling back or automatically rewriting. Inspect actual persisted state before retrying. Owned directory descriptors close even on failure. Non-POSIX platforms retain their existing behavior without an equivalent directory-fsync claim. These flush-order tests are not physical power-loss testing, and this change does not implement active-run write-ahead input journaling or cross-process local draft persistence; those goal requirements remain OPEN.

Write-ahead child input snapshots

Both clients now persist a bounded child steering/follow-up snapshot before acknowledging live queue acceptance. Text and images are stored in a private atomic .inputs.json sidecar using the child store's file/directory flush path. Failed writes refuse live admission. Cancellation waits for the actual admitted writer; terminal settlement waits too, so an old writer cannot overwrite a later settled record. No blocking filesystem writes are added to the agent event loop.

After an unclean restart, an interrupted child with a valid snapshot exposes those inputs as crash-snapshot / uncertain recovery rows. Copy still needs explicit review and a fresh read-only child draft; no Agent is constructed or restarted on load. Snapshots may contain inputs already consumed before the crash, and may precede subsequent edits/removals. Inspect history/workspace effects before any explicit resubmission. This is conservative data retention, NOT an exact once-only consumption journal or a claim that recovered inputs were never executed.

Settled child records take precedence over stale sidecars. The terminal record is flushed before journal cleanup, so a crash between those steps cannot revive old accepted inputs. An actual owned SIGKILL test verifies a previously acknowledged 2 MiB image input survives and is explicitly recoverable without agent creation; both native clients also verify their actual acceptance writes the sidecar. Physical power-loss/native filesystem conformance, exact edit/remove/consumption journaling and durable local editor drafts remain OPEN. No automatic replay or permission escalation is introduced.

Write-ahead live queue changes

Live child edit, remove, move and text-only return now write their prospective queue snapshot before acknowledging the change. The runtime validates the input/revision and delivery lane first, writes off the agent loop, then checks the exact queue lease again before applying the live mutation. Failed writes leave the live queue and prior disk snapshot unchanged. Cancellation waits for the actual writer and does not falsely acknowledge a live change; terminal settlement replaces the prospective sidecar with actual final state.

An owned SIGKILL test acknowledges an image edit and a separate removal, kills the process, and verifies recovery retains the edited image but not the removed input. This covers acknowledged changes, not every ambiguous boundary: if the process dies after the prospective write but before live mutation/acknowledgment, the saved state is explicitly uncertain. Consumption during disk work rejects the stale mutation; there is no automatic replay. Two-phase live image return commits and exact consumption/handled-input journaling are not updated by this change. Those boundaries, physical power-loss proof and durable local editor drafts remain OPEN.

Write-ahead image-return commits

Two-phase live image-return commits now persist the prospective removal snapshot before acknowledgment. Exact owner/token/input/revision/byte-receipt validation runs before writing and again before actual live removal. Failed writes, cancelled writers, checkpoint selection or host revocation cannot remove the input through a stale commit. Writer cancellation waits for actual settlement. Exact completed commit retries return the original receipt without rewriting the journal.

An owned SIGKILL test acknowledges an image-return commit, kills the process, and verifies the returned input is absent from the restarted queue snapshot. This does not persist the native editor backup: that remains memory-only. A crash between a prospective disk write and live mutation/acknowledgment is still an uncertain boundary, and exact consumed/handled-input journaling is not supplied here. Recovery never automatically replays work or restores old execution permissions.

Durable returned-image handoff

Both native clients now persist an exact conservative image/text backup before a live image-return commit can remove the queued input. A bounded .returns.json sidecar is flushed through the child store, then the prospective queue-removal snapshot is saved, and only then can the same live lease commit removal. Failed backup or queue writes refuse removal; uncertain write outcomes retain a conservative copy for settlement. No permission, model or automatic resubmission is introduced.

After SIGKILL/restart, interrupted child records merge validated return backups. On normal settlement, backups are included in the durable terminal record before sidecars are removed. The stopped input browser exposes durable-return / uncertain rows: explicitly copy (u) into a separate fresh read-only task, or explicitly discard (x) after review. Copy is non-destructive, preserves exact image bytes, and never restarts a child. Discard publishes only after its actual record write. The source is deliberately uncertain: a backup can exist even when removal failed, and prior tool effects are not undone. Inspect history/workspace before resending.

Backups have their own bounded collection (32 rows / 40 MiB images), separate from queued-input retention. Capacity exhaustion refuses new return commits instead of losing input. Record capacity accounts for the bounded canonical, queue and return collections. No binary backup bodies are embedded in Claw control response frames; explicit recovery uses the existing verified transfer. Returned data is not silently reinserted into a live queue on startup.

Evidence includes actual owned SIGKILL after acknowledged image removal, exact returned-byte recovery without Agent construction, backup/queue failure tests, both native 10 MiB return owners and both private-tmux durable-row recovery flows. This closes the returned-image handoff loss window, not general durable editor drafts, exact consumed/handled-input journaling or physical power-loss/native filesystem conformance. Local edited copies still live in memory until a supported explicit persistence operation; those remaining goal requirements stay OPEN.

Return retry after partial write failure

An explicit same-lease image-return retry now reuses an identical retained backup instead of adding a duplicate recovery row. This covers a failure after backup replacement or during the following queue journal write. Conflicting backup identity/ content still refuses; a completed retry does not rewrite or remove twice. This is not automatic retry permission: inspect uncertain effects/receipts before retrying.

Awaited child input-consumption persistence

Identified child inputs now trigger an awaited host persistence hook after history admission or explicit extension handling, before the next model request. The child host writes its current remaining queue off the agent loop and settles the actual writer on cancellation. A failed write stops continuation; already admitted history or handler effects are not rolled back. Other generic Agent clients have no hook unless explicitly configured with input_settled / set_input_settled.

An owned SIGKILL test reaches the next model boundary, verifies the consumed input has been flushed out of the queue snapshot, kills the process, and confirms it is not offered as queued recovery data. A crash between admission and flush remains uncertain; this is not an atomic history-plus-queue transaction or exact-once effect guarantee. General durable editor drafts, physical power-loss/native conformance and remaining host/platform interoperability requirements remain open.

Durable local child-draft storage foundation (UI integration pending)

The shared TUI package now provides a ChildDraftStore codec/storage foundation for explicit private snapshots. It preserves task text, image bytes and provenance, uses create-only publication with file/directory fsync on POSIX, and requires owned 0700 directories / 0600 regular single-link files. Symlinks, hardlinks, unsafe modes, oversized/invalid records, duplicate JSON fields and mismatched image receipts refuse without partial text recovery. Exact digest checks guard discard; cooperating writers lock the pinned directory so simultaneous saves cannot bypass the 32-record catalog limit. Each record supports four images / 10 MiB each and 40 MiB aggregate image data with bounded metadata.

A loaded snapshot becomes an uncertain read-only fresh task with context inheritance false. Former model/tools/access/preset/instructions are NOT applied; provenance remains available for review. Nothing submits text, starts a child or authenticates on save/load. Existing data is never overwritten by save. A post-publication fsync failure can have an uncertain committed outcome; do not automatically retry/discard. The host must run filesystem/codec work off the UI loop and separately review it.

This storage layer is not yet wired into native application save/load/discard controls or automatic draft persistence. The existing /agents drafts catalog still describes its in-process caches. A process roundtrip test verifies storage and conservative recovery, but not an application restart/terminal save workflow or physical power-loss/native-platform conformance. Those integration requirements remain OPEN; storage code alone does not make current editor drafts durable.

Explicit durable child-draft save/load/discard

In /agents drafts, s prepares a full reviewed save of the selected local child draft. The disclosure includes task, provenance, old settings and image digests; only physical Enter after all pages are painted admits the save. It creates a private plaintext snapshot, never overwrites the source or executes work. Coding stores these under its session directory's saved-child-drafts; Claw uses its local user data directory's saved-child-drafts. The application parent directory must exist; unsafe paths or unsupported private-storage primitives refuse instead of silently falling back.

/agents saved explicitly reads saved snapshots off the UI loop. The draft browser shows SAVED PRIVATE rows separately from CURRENT/OLD memory caches. Saved rows cannot resume old child execution: reviewed Enter copies exact text and images into a separate read-only start draft with context inheritance false. No old model/tool/access configuration or permissions are adopted. x on a saved row reviews deletion of that DISK snapshot only; independent local copies stay editable. Deletion requires the exact saved digest and actual completion receipt.

One owned disk operation runs at a time; repeated actions refuse until settlement. Graceful quit waits for its receipt rather than abandoning admitted work. A delayed read cannot populate a replacement panel/context. Save failure keeps the local source, while errors are redacted and uncertain post-publication effects require explicit inspection rather than automatic replay. Disk operations/codec work run outside UI/event loops. Loaded catalog images have a cumulative 40 MiB bound; over-capacity reads refuse without partial recovery.

Both actual native owners are tested for reviewed save, NEW owner reload, exact 10 MiB image recovery, read-only copy, disk-only discard, unchanged parent draft, failed save and stale-view refusal. Private-tmux workflows physically save/load recovered image drafts and restore native cursor/termios. Separate storage tests exercise process restart; these UI tests do not prove every platform filesystem. Autosave and general parent-editor persistence are not implemented by this explicit child-snapshot feature. Unreviewed edits remain memory-only until explicitly saved; broader durability/platform/RPC requirements remain open.

Saved-draft capacity recovery

/agents saved list now lists validated metadata (snapshot ID, provenance, image count/bytes) without adopting drafts or retaining catalog image bodies. Records are validated one at a time on the owned disk worker. This listing stays available when loading all saved images would exceed the cumulative 40 MiB UI limit.

/agents saved SNAPSHOT_ID explicitly loads one complete snapshot for the existing full-reviewed read-only recovery/discard browser. Invalid IDs refuse before disk work; no URI/path is interpreted and other saved records remain unchanged. Bare /agents saved retains the bounded all-snapshot load behavior. Neither catalog listing nor single-ID loading executes agents, changes the parent draft or imports old permissions; no oversized image is silently truncated. Autosave/general editor persistence and broader remote/native-platform requirements remain OPEN.

Nonblocking saved-draft worker settlement

Durable draft polling no longer joins a live worker on the UI thread. A queued receipt remains owned until its worker actually exits, keeping the single-operation capacity without stalling terminal frames. Failed thread startup releases ownership and admits no disk operation; rejected delivery is not automatically replayed. Explicit save/load/discard semantics and graceful-quit settlement remain unchanged.

Bounded saved-draft lock contention

Saved-draft directory locking now uses nonblocking attempts with a three-second deadline. If another cooperating process holds the lock, the operation refuses without modifying snapshots instead of leaving save/load/discard and graceful quit waiting indefinitely. The owned worker then reports its failure receipt; retry is explicit after the lock is released. This bounds lock acquisition, not arbitrary filesystem stalls or physical power-loss behavior.

Coding RPC saved child snapshot discovery/read

Coding RPC independently negotiates child_saved_drafts:1 alongside child controls. Scoped child_control actions saved_catalog and saved_read expose the same local private saved child snapshots as native /agents saved. Catalog responses contain bounded metadata only. Reads require the exact snapshot_id and snapshot_sha256 captured from that catalog; stale/missing IDs or changed content refuse without selecting a different snapshot or reading a supplied path/URI.

Image-bearing reads additionally negotiate child_saved_images:1. Exact bytes travel through the existing checksummed binary begin/chunk/end stream, scoped to session/revision/connection owner, snapshot ID/digest and a saved-draft source. The final result contains conservative read-only/context-fresh draft metadata and an exact image receipt, never large base64 bodies. Text-only reads need no image capability. All filesystem/codec work runs off the RPC event loop and actual owned workers settle on cancellation. Capability withdrawal/re-enable cancels in-flight saved reads; stale owner generations cannot revive them.

Reading returns a copy only: it does not install parent/child history, enqueue or submit work, invoke a model, remove snapshots or import prior access/model/tools. Clients own review before any later submission. Tests include an actual offline RPC subprocess reading 2 MiB, digest changes, missing image negotiation, revocation and closed peers. Remote saved-draft save/discard, autosave/general editor durability and remaining native-platform/host API gaps are still OPEN.

Coding RPC two-phase saved snapshot deletion

RPC independently negotiates child_saved_discard:1 alongside saved-draft access. saved_discard_prepare requires the catalog snapshot ID/digest and returns a 30-second, connection/generation-bound review_id and exact metadata disclosure. Preparation never deletes files. Clients must actually review that disclosure before saved_discard_confirm echoes the exact review, review_id and boolean confirmed: true. Truthy strings/numbers are not consent. saved_discard_cancel revokes an unused review without changing the snapshot.

Confirmation is single-use; changed files, expired/foreign/cancelled reviews and capability withdrawal/re-enable refuse. The actual disk removal rechecks the host owner after acquiring its lock and validating the digest. Cancellation settles the worker, not an automatic rollback of admitted deletion. Inspect uncertain outcomes before explicitly preparing a fresh action. Only the saved disk file is deleted; local editable drafts, queues and agent history are untouched. No model calls or permission grants occur. Review capacity includes in-flight preparations and is bounded to four. Remote save/autosave and broader platform/API gaps remain open.

Coding RPC two-phase saved snapshot creation

RPC independently negotiates child_saved_save:1 alongside child_saved_drafts:1. saved_save_prepare accepts bounded text and an optional provenance label. Images additionally require child_saved_images:1, supplied either as inline typed images or completed session-owned image_refs, never both. No file/URI destination or caller tool/model/access settings become execution authority.

Preparation owns an exact conservative read-only snapshot and returns its text, image digests/MIME types, byte counts, SHA256 and PRIVATE PLAINTEXT disclosure with a 30-second connection/generation-bound review ID. It publishes no file. Once preparation succeeds, uploaded image references are consumed; the review retains its own exact bytes. The client must separately review the receipt before sending saved_save_confirm with that review_id, the exact echoed review, and actual boolean confirmed: true. saved_save_cancel releases an unused review without writing. Expired, cancelled, foreign or revoked reviews cannot publish snapshots.

Confirmation uses create-only private storage and rechecks live consent immediately before publication. The review is single-use; failed/uncertain writes are not silently retried. A post-publication flush failure may have committed: explicitly inspect the catalog before preparing another save. No parent/child history is changed, agent/model invoked or original upload turned into live queued input. Review count is bounded to four, with a separate prepared-byte limit; native save/load controls and their original private-path guards remain unchanged.

Tests include an actual RPC subprocess preparing and confirming a 2 MiB chunk-uploaded image snapshot with zero model/history changes, exact-once review use, consent/ expiry/cancel/withdrawal checks and revocation at the publication boundary. This is explicit RPC snapshot creation, not autosave, durable general parent-editor state, physical client-review proof or remaining native-platform/API conformance.

Opt-in native child-draft autosave

/agents autosave on enables session-local debounced child-draft snapshots in both native clients. It is OFF by default and resets to off on application restart. The command discloses PRIVATE PLAINTEXT local storage. /agents autosave off stops future scheduling; an already admitted write still settles. This is not a project-controlled setting, automatic submission or an execution permission grant.

After a 0.5-second debounce, unsent local child text/images are saved on the owned worker. Later edits update only that owner's exact previous snapshot using a digest check and private atomic replacement; externally changed files are not overwritten. Failure disables autosave rather than automatically retrying uncertain writes. Saved-catalog entries are not automatically resaved. General parent-composer text is not included. Existing snapshot size/catalog bounds and private-path checks apply.

Recovery is explicit through /agents saved or /agents saved ID and its full reviewed read-only/context-fresh copy workflow. Old access/model/tool configuration never becomes recovered execution authority. Graceful quit requests a pending draft flush and refuses exit while its worker is settling. An actual owned SIGKILL test verifies a COMPLETED autosave survives, but edits during debounce/in-flight work are not guaranteed durable. Clearing or submitting a draft does not automatically delete its prior saved recovery copy; inspect/discard snapshots explicitly.

Tests cover both actual native owners, exact image/text persistence and same-record updates, disable behavior, external mutation refusal, debounce/explicit flush, independent-process restart and native terminal on/off controls. This implements opt-in CHILD autosave, not general parent-editor durability, physical power-loss/ native filesystem conformance or remote autosave APIs. Those remain open.

Autosave retained-memory accounting

Completed autosaves retain only the ID/digest update receipt, not a second decoded text/image copy outside the editable-draft retention budget. Tracking for removed local slots is released without deleting their saved recovery snapshots. A later reappearing slot creates a new snapshot rather than overwriting an old recovery copy. Existing explicit disk discard and autosave opt-in semantics are unchanged.

Deferred quit preserves live UI services

A quit request deferred by an admitted durable draft write now leaves argument completion and custom presentation/input services intact. Claw also retains its owned name read. Teardown begins only when actual exit is admitted after write settlement; repeated exit requests do not rerun cleanup. This keeps the still-live UI usable instead of partially closing it while waiting for storage.

Saved reload preserves editable-image ownership

Reload capacity now releases only the old catalog's exclusive image ownership. Images still held by reviewed editable copies or an active child composer remain counted when new catalog bytes are decoded. Reloads that would exceed local retention refuse without replacing the existing catalog or losing editable copies; exclusive old catalog bytes can still be replaced without false double-counting.

Opt-in parent composer recovery

/draft autosave on enables session-local parent-composer snapshots; off stops future saves. It is OFF by default and discloses PRIVATE PLAINTEXT local storage. Debounced snapshots keep unsent text and image attachments separately from child state, with the same owned off-thread writes, digest-bound updates, storage limits and failure-stop behavior. Parent text supports up to 64 KiB; image limits remain four per snapshot, 10 MiB each / 40 MiB total. The stored record capacity also accounts for JSON-escaped Unicode text rather than truncating it.

/draft saved [list|SNAPSHOT_ID] explicitly discovers/loads parent snapshots. Recovery has its own Saved parent drafts review: physical Enter after every text/provenance/image page copies into an unchanged empty parent composer. An existing or changed draft is preserved and recovery refused. Nothing sends the prompt, invokes a model, changes history or imports former permissions. Parent attachment ownership and all unrelated local image bytes remain counted; recovery releases the temporary saved catalog after moving bytes into the composer.

x reviews deleting only the saved DISK snapshot; it does not clear current composer text or attachments. A delayed read or changed review owner cannot adopt old data into a replacement context. Graceful exit flushes pending snapshots and holds while their actual workers settle. Snapshots remain available after submit/ clear until explicitly discarded; recovered text is never automatically replayed.

Both native owners are tested for text/image autosave, long Unicode saved recovery with a NEW UI owner, changed-composer rejection, disk-only discard and zero model/ permission/history changes. Both private-tmux workflows perform full physical parent-image recovery review and cursor/termios restoration. This is opt-in snapshot recovery, not exact persistence of every keystroke: debounce/in-flight crash windows, physical power-loss/native filesystem conformance, remote parent-draft APIs and other host/platform interoperability requirements remain OPEN.

Parent recovery view ownership

Parent snapshot reads are tied to the requesting auxiliary view. Closing that view revokes delivery: a delayed disk result cannot reopen it or populate a replacement view. Back/close and completed recovery release temporary read snapshots and saved image catalogs without deleting disk files. Admitted disk operations still settle; view cancellation is not a rollback or automatic retry.

Parent recovery fences same-panel navigation

Saved-parent reads also bind to the requesting panel's view lease and child-view identities. Switching to a child form, queue, preview or another draft browser revokes late delivery even if the outer panel stays the same. Returning to the roster does not revive the old read. Successful reads transfer their existing composer/context receipt to the exact parent-recovery browser; stale callbacks cannot copy or delete after navigation. Saved files and current drafts remain unchanged, with no automatic read retry or focus takeover.

Explicit parent draft save

Press Ctrl+Alt+S while the parent composer is focused to prepare a private snapshot save without consuming its text or attachments. The native /draft save control opens the same review for the current composer. The shortcut is preferable when preserving an existing typed prompt: submitting a slash command normally consumes that command's editor text. No autosave opt-in is required.

Every task/provenance/image digest page must be physically painted before Enter admits the save. Changed composer text/images/cursor or a replaced view reject old consent; pasted/repeated/unpainted Enter cannot confirm. Successful save leaves the parent composer intact and creates one private snapshot with no model, history or permission change. Confirmation is single-use; saved recovery still uses /draft saved and the full reviewed empty-composer copy workflow. A failed or uncertain write is not automatically retried. Existing autosave and native private-storage limits remain unchanged.

Parent save respects modal input ownership

The parent-save shortcut and explicit save review cannot take focus from an active extension dialog, queued question, output review or pending output operation. Coding also refuses while approval prompts/waiters own input. These guards apply to stale review confirmation as well as initial shortcut admission; existing composer text/images and permission decisions remain unchanged.

Configurable parent-save shortcut

Parent save is now the save_draft action in keybindings.json, defaulting to ctrl+alt+s. For example, {"save_draft": "f8"} rebinds it; {"save_draft": []} disables the shortcut without a hard-coded fallback. /keys shows the current binding. The action remains host-only, not editor submission, and respects existing modal ownership and physical press/review guards.

Read-only image preview in draft reviews

Press p in a saved/local draft list or full review to preview its images before copying, saving or deleting. Left/Right selects attachments; Escape returns to the same review. Preview performs no draft action, file deletion or agent call. Returning resets review painting, so preview cannot count as consent or skip full physical confirmation. A changed source invalidates preview/review rather than applying an action against stale bytes. Both parent and child draft reviews use this shared behavior. Linux/private-terminal evidence does not establish every native desktop image protocol or provider/decoder interoperability.

Parent recovery respects modal input ownership

Saved-parent read admission, delayed-result presentation and stale recovery copy/ delete callbacks now respect extension dialogs, queued questions, output reviews and Coding approval owners. A read that completes under a new modal cannot take focus or adopt a saved catalog. Reload explicitly after that owner settles; composer text/images, permission decisions and disk snapshots remain unchanged.

Save-and-quit completes after storage settlement

An explicit quit request deferred by draft storage now completes automatically after all pending child/parent snapshots settle. The UI stays responsive and input services remain live while waiting; edits made during that wait are flushed before exit. A new storage failure cancels the deferred exit and keeps the UI open for inspection instead of silently exiting with unsaved data. This fulfills an explicit quit intent, not background submission, replay or a crash guarantee.

Literal argument suffix, quotes and escapes (UI-035)

Registered completions replace the entire cursor-local token, including its suffix. The callback receives the literal prefix and (when independently negotiated) copied argument_context codepoint coordinates. It can inspect the suffix as arguments[cursor:end] and must return the complete literal replacement in value, including any intended quotes/escapes. No automatic quoting, shell expansion, suffix concatenation, command execution or prompt submission occurs.

Outside single quotes, a backslash protects the next character, including a space or quote; an escaped backslash does not protect the following separator. Inside single quotes backslashes remain literal. An unfinished quote extends the token to the end of the line. Between arguments, completion can insert into an empty cursor-local whitespace slot without consuming the next argument. Tab/Enter inserts only; undo restores the original token and surrounding arguments. Existing registration/owner/draft/cursor/cancellation fences still apply. Actual Coding and Claw worker/socket checks and Linux private-terminal suffix/quote/escape/undo and worker-cancellation recovery have passed; no non-Linux native validation is claimed.

Independently negotiated canonical RPC transcript export (UI-038, partial)

The RPC ready envelope advertises transcript_canonical: 1. A client must explicitly negotiate the exact integer with rpc_hello; transcript_sources: 1 only authorizes the existing display source, not binary export. Send transcript_read with format: "canonical", the current session_id and session_revision, a fresh request_id, and the existing live/saved/timeline scope and target/branch fields. Default format remains display. Revoking one format does not revoke the independently authorized other format.

Canonical source_begin/chunk/end/error frames carry format: "canonical" and the same bounded chunks, byte count, SHA-256, order and owner/context fences as display exports. The verified UTF-8 JSONL body starts with the xdog.canonical-transcript.v1 manifest, followed by source/provenance headers and indexed typed message records (plus a separate live partial record). Timeline export retains each recorded branch/head separately, not a text-deduplicated conversation. Explicit typed image base64 and MIME values are retained; signatures, opaque response IDs, redacted reasoning, common inline credentials and structured credential fields are withheld. Numeric typed usage is preserved. Arbitrary metadata cannot impersonate an image block to bypass redaction.

This is a read-only export, not a session-resumption or replay format. It neither grants tool permission nor starts a model, activates history or resolves resource links. Binary image contents are intentionally disclosed only under the explicit canonical capability; clients must treat them as sensitive, untrusted bytes, not terminal instructions. Redaction is bounded known-field/pattern filtering, not a promise to detect secrets embedded in arbitrary prose or pixels. SourceReceiver.expect(..., format="canonical") requires an explicit decoder; format mismatches are refused before content is delivered to display decoders.

Actual offline CLI tests cover saved/branch/timeline 2 MiB image framing, long Unicode text, redaction, unchanged permissions/history and slow/closed-output cleanup. Owner/format/checksum/order/capacity/cancel/revision/withdrawal/preparation settlement negatives are covered. Child canonical export remains unimplemented (the existing child source is display-only); UI-038 remains open for that scope and its complete acceptance evidence. No non-Linux native verification is claimed.

Child canonical source implementation (UI-038 follow-up)

child_canonical: 1 is now independently advertised and negotiated. A current child-control owner can issue child_source_read with format: "canonical", child_id, session/revision and child owner/epoch. Neither parent canonical nor child display capability grants child binary export. Revoking child display or parent canonical does not revoke an independently authorized child canonical read; revoking child canonical/controls, changing owner/epoch or closing the child does.

The same canonical JSONL manifest/indexed messages/typed images/redaction format includes a captured child metadata header and an explicit partial record when a stream update has actually been admitted. It excludes transient tool-progress UI artifacts (tracked separately under UI-039). The original approved child history is not installed into the parent, and a read does not execute more child work. Actual CLI checks verify 2 MiB image upload/export, digest/order/format, unchanged parent permissions/history and owned child cleanup on quit/EOF/closed output with an intentionally undrained binary stream. Full parent/child source matrix: 158 passed in /tmp/ui038-child-verified.log. UI-038 is pending verification closeout, not declared complete while the earlier unbounded test process remains live; see audit tracker for process ownership and the no-interruption constraint.

Independently negotiated parent transient RPC events (UI-039, partial)

ready.capabilities.transient_events advertises 1; opt in explicitly with rpc_hello and exact integer transient_events: 1 (or withdraw with 0). This capability does not grant transcript/canonical/child source access. Its receipt has event_owner, session/revision, max_event_bytes and replay: false. Parent transient_event envelopes include the same owner/session/revision, monotonic sequence, retained: false, the original event name and projected data. Supported events in this increment: tool execution start/update/end, assistant message updates, handled input feedback, and agent end. Tool progress is text-only; typed image bodies are omitted and counted. Known credential fields/inline forms are redacted; signatures and redacted reasoning are never copied. The terminal projection is not canonical/history resumption data.

At most 32 projected callbacks are pending; payload/frame sizes are bounded at 64 KiB. Overflow or oversized projection is explicitly marked limited: true, not silently presented as a complete event or stored for later replay. Late callbacks check their captured connection lease and actual session/agent/revision; withdrawal and teardown unsubscribe. Re-enable rotates the event lease. Existing legacy response/approval frames remain separate; this opt-in API does not change tool permission or open another model loop.

The actual parent tool loop verifies start/progress/end in order and one tool execution. Actual offline CLI verifies streaming partial delivery, withdrawal and abort/producer settlement. This increment is parent-only: child transient tool progress and broader custom artifact transport are not supplied by a retained child source or this capability. UI-039 remains open until those applicable contracts and complete safety/transport evidence are implemented and verified.

Child transient progress (UI-039 follow-up, partial)

Negotiate exact child_transient_events: 1 with current child controls. This is independent of parent transient_events, child display/canonical sources and child approvals. Its capability receipt includes event_generation. child_transient_event carries the actual parent session/revision, child owner/ epoch/id, event generation, monotonic sequence and retained: false. It projects the same named lifecycle/progress events as the parent stream and does not export binary progress bodies or execute another model/tool loop.

The original session child event hook is awaited, not replaced or skipped, and is restored on detach. Event admission captures generation BEFORE that await; withdraw/re-enable during an existing hook cannot resurrect old events under the new grant. Queued delivery checks generation, child identity and owner again. The queue is bounded to 32 projected callbacks with explicit overflow/oversize markers. Actual child-tool start/update/end, original hook chaining, queue bounds, redaction, stale owner/re-enable and real CLI child partial/quit settlement are verified. Custom artifact/event contracts and complete slow-peer terminal/transport acceptance remain UI-039 work; this increment alone is not feature completion.

Scoped declarative extension contributions (UI-039, partial)

Negotiate exact extension_contributions: 1 independently of dialog/editor/ navigation capabilities. The separate receipt supplies generation, session, 64 KiB event bound and replay: false. During an owned command/prompt invocation, extension_contribution carries request/scope/session, generation, sequence, retained: false, and redacted notice text or a declarative status/widget snapshot. Cleared slots are absent from the latest snapshot; snapshots are presentation metadata, not executable callbacks or model messages. No images/custom terminal frame bytes are implicitly exported. Contributions settle before the command's result receipt. A contribution-only hello preserves existing UI grants.

At most 32 projected callbacks are pending, with explicit overflow/oversize markers. Withdrawal refuses queued old-generation delivery and does not downgrade to legacy notice/UI disclosure. Clients which have never negotiated this capability retain the existing compatibility path. Re-enable rotates generation. Real worker and actual CLI tests cover notice/status/widget/clear, no model/history/policy change, original scope ownership, redaction and withdrawal. Child declarative contributions, rich artifact contracts and complete transient transport verification remain open; this is not arbitrary custom terminal rendering over RPC.

Independent child declarative contributions (UI-039, partial)

Negotiate child_contributions: 1 independently of parent contributions, child transient events, sources and approvals. The capability receipt supplies contribution_generation. child_contribution carries actual child id/owner/epoch, parent session/revision, generation, sequence, retained: false and projected notice/status/widget snapshot data. This includes actual child-worker bootstrap and turn-start contributions; parent worker state is never substituted for child state.

Withdraw/re-enable, changed owner/revision, revoked controls or a closed child deny queued old contributions. At most32 projected callbacks are pending,32KiB payloads, with visible overflow/oversize and known-secret redaction. Actual worker+CLI tests verify delivery without parent history/model/policy changes. Richer artifact/custom protocol contracts and full slow-peer transport verification remain UI-039 work.

Transient output backpressure and visible transport loss (UI-039)

Native POSIX RPC output now reserves the main output memory budget for control, approval and lossless source frames: parent/child transient events and declarative contributions have a 1 MiB presentation backlog ceiling, bounded further by half the configured total budget. Once reached, whole new transient frames are withheld (never half-written), without blocking the input reader or tool producer. The next delivered event of the same family reports limited: true and transport_dropped; status.transient_transport_dropped exposes outstanding per-family loss even if no later event occurs. These are presentation-loss counts, not lost tool inputs or proof that effects were rolled back. Sequence gaps remain visible. Other event families cannot inherit a dropped family's loss count.

Actual offline CLI tests admit 128 provider progress events with parent/child output deliberately undrained, then exercise quit/EOF/closed-output producer settlement. Real pipe tests verify control/approval memory remains available and loss is reported after draining resumes. This is not a way to deliver unread terminal output: it bounds memory and keeps lifecycle control responsive. Custom artifact contracts and remaining complete-scope requirements stay open.

Complete named Agent lifecycle projection (UI-039)

Parent and child transient streams now project all fourteen declared Agent event kinds: agent/turn start/end; message start/update/end; tool execution start/update/ end; input consumed/transformed/rejected/handled. Message events carry a redacted typed message (including cumulative tool-call arguments), never provider signature handles. Input consumption carries its identified delivery receipt and omitted-image count, not an execution grant. Tool-end includes final text/redacted details and omitted-image count. Agent-end does not embed the whole retained history.

Stream update metadata includes the event kind and valid content index only; raw deltas are withheld because fragmented credentials/signatures cannot be safely redacted independently. Cumulative messages preserve typed usage and tool-call structure; redacted reasoning and every image body remain withheld from transient streams regardless of independently enabled canonical export. Oversized projections retain the existing explicit limited marker and transport-loss contract.

The full named current lifecycle/transient/contribution/navigation/child/real-CLI/ backpressure gate passes188 cases (/tmp/ui039-acceptance.log). Custom artifact protocol implementation is tracked once under UI-042; its integration into the UI-039 event surface remains required, not claimed implemented by this event matrix.

Client-rendered RPC custom views (UI-042, partial)

Negotiate exact extension_custom_views: 1 independently. The separate receipt includes capability generation and viewport/frame limits. The custom-only hello preserves existing dialog/editor/navigation grants. Owned extension calls may open a kind: custom dialog, and the client sends extension_custom_input with request/session/scope/dialog identity, custom_generation, an increasing sequence and a bounded event (resize, key, or paste). The existing worker and broker render extension_custom_frame replies with geometry, revision, generation, lines and cursor. Unsafe terminal controls are stripped from rendered lines.

Input needs the current delivered frame's revision/geometry/generation; resize establishes new geometry. Completion uses the existing broker's painted-frame, viewport and current-input checks, not an arbitrary dialog answer. Clients may cancel with a null dialog answer; non-null custom answers are refused. Escape and protected Ctrl+C/D/L/Z remain unavailable to extension custom input. Withdrawal cancels owned custom dialogs and late/in-flight frames cannot regain authority. The client owns rendering: the server never writes custom frame ANSI to the CLI terminal or changes tool permission. Frame receipt is not proof of external-client physical painting; clients must supply truthful painted revisions.

Actual owned-worker and real offline CLI tests validate initial frame, selected input completion, stale/foreign/reused sequence refusal, reserved controls, resize, cancel and withdrawal during rendering; no model/history/policy changes. This increment does not yet complete child custom UI, nested dialog/focus interaction, live-overlay controls or the full final client/transport matrix. UI-042 stays open; Pi wire/native custom equivalence is not claimed.

RPC nested custom/dialog focus ownership (UI-042, partial)

The broker's latest admitted live dialog owns interactive input, independently of whether its transport publication has arrived. A nested question/custom view blocks parent custom input before the frontend sees it. Admission of a nested dialog invalidates the cached parent frame; after child completion, only the freshly rendered parent revision can be used. Null host cancellation may still cascade from an ancestor; arbitrary non-null answers cannot borrow a sibling's focus or resurrect a completed future. Nested parent IDs come from the existing broker's owned render context, never client-supplied hierarchy.

Actual nested input/confirm/custom worker paths and a real CLI sequence exercise parent blocking, child completion, fresh parent frame, stale receipt refusal and cascade cancellation. Capability changes in an active invocation now update actual runtime custom/dialog handlers before returning capability receipts. Child-agent custom routing and live overlay ownership remain open; these parent nested tests are not evidence of all RPC custom or non-Linux native behavior.

Independently negotiated child RPC custom views (UI-042, partial)

child_custom_views: 1 enables client-rendered views from the actual child worker, independently of parent custom views and child ordinary questions/source grants. The capability receipt includes custom_generation. Child dialogs and custom frames carry their actual child id, parent session/revision and child owner/epoch; child_custom_input must include those identities, generation, increasing input sequence and the current frame revision/viewport. Existing broker painted completion checks remain authoritative. Arbitrary non-null custom dialog answers are refused; null host cancellation remains available. Unsafe terminal controls in frames are removed; reserved interruption keys remain host-owned.

Nested child questions protect input at broker admission before frontend delivery. After nested completion only a newly returned parent frame can authorize input. Withdraw/re-enable or changed controls revoke old custom frames, and late delivery checks original capability generation. Tool permission is still separate: actual CLI tests approve the child tool explicitly before interacting with its custom view; no view answer grants tool access or installs child history into the parent.

Current child/parent custom, original child controls/CLI and broker tests pass199 cases (/tmp/ui042-child-final.log). Live overlay controls, complete custom-view transport/backpressure and full client acceptance remain outstanding. UI-042 is not complete; neither these RPC fixtures nor a painted field claim non-Linux native or Pi wire compatibility.

Parent RPC live overlay controls (UI-042, partial)

Negotiate extension_overlay_controls: 1 separately from custom views. Owned worker hide/show/focus/unfocus requests use the existing broker's monotonic control sequence and emit extension_custom_control with invocation/session scope. Control changes invalidate delivered frame receipts. Hidden and non-capturing overlays cannot receive key/paste input; resize may establish geometry without granting focus. After focus changes, a fresh geometry generation and delivered frame are needed for the existing painted-frame completion proof. Nested dialog ownership remains authoritative. Withdrawal cancels live overlay views rather than downgrading to uncontrolled frames.

Actual worker and offline CLI tests cover non-capturing refusal, hide/show/focus, fresh geometry and completion, independent negotiation and withdrawal. Current related custom/broker/navigation suite:177 passed (/tmp/ui042-overlay-current.log). Child overlay routing, all custom transport/client acceptance and final full-root regression remain unverified; no physical remote-client painting or Pi compatibility is inferred from protocol receipts.

Child RPC live overlay controls (UI-042, partial)

Negotiate child_overlay_controls: 1 independently of child custom frames. Overlay control frames carry child/session/owner/epoch and capability generation; hide/show/focus/unfocus use the original child's broker, never the parent UI. A hidden/non-capturing child overlay cannot receive key/paste input, and control changes invalidate old frame proofs. Changing overlay/custom/controls capabilities revokes pending custom views and rotates the child custom generation. Fresh geometry and current painted-frame receipts are required after control changes.

Actual child worker and real CLI tests verify hide/show/focus, stale input rejection, withdrawal, current frame completion and a SEPARATE tool approval before custom interaction. Parent history and permission policy remain unchanged. Current related parent/child custom+overlay/control/CLI suite104 passed (/tmp/ui042-co-final.log). Full custom transport/backpressure/client acceptance and applicable terminal contracts remain unfinished; protocol receipt does not prove physical client painting.

Mixed RPC capability request safety (UI-042)

Extension capability batches are validated completely before any flag/generation, view cancellation or capability receipt changes. The actual rpc_hello reader also preflights extension, child and transient-event fields together: an invalid later child field cannot partially enable or revoke an earlier extension custom/overlay capability. Fields require exact integers0/1; booleans and coercible strings are not grants. Invalid batches retain an already live custom view and its frame proof. Actual CLI tests verify both no partial grant and no partial withdrawal. Existing transcript capability compatibility retains its separately documented fail-closed negotiation behavior; this change does not turn all operations into a transaction.

Large custom-frame pipe lifecycle verification (UI-042)

Actual Linux parent and child RPC tests leave roughly120KiB custom frames undrained (after verifying only their real output prefix), then exercise quit, EOF and closed output. The CLI exits and reaps the owning extension worker; child tool approval remains separate and no parent history/tool mutation is introduced. This verifies bounded shutdown with unread custom output, not physical client painting or complete custom protocol conformance. Final client/terminal/goal-wide gates remain open.

In-flight custom input frame ownership (UI-042)

Admitting a custom input or resize immediately retires its delivered frame receipt in both parent and child RPC handlers. Another key/paste cannot reuse that receipt while the callback is queued or rendering; it must await a fresh returned frame. This protects extension input state, not just the broker's eventual completion value. Failed admission does not retire the current receipt. Held-render and synchronous child stale-input tests verify that no extra key reaches the callback.

Superseded custom render results (UI-042)

When a newer geometry generation is admitted while rendering, the broker marks the older result superseded: true. Parent and child RPC handlers withhold that obsolete frame instead of restoring an interactive receipt while the new render is pending. Native broker consumers retain the explicit marker and existing completion checks; this does not cancel admitted callback effects or claim rollback. Held actual worker render tests cover both parent and child paths, with no obsolete frame/input admission.

RPC custom enhanced keys and atomic paste evidence (UI-042)

Actual parent/child worker tests cover declared press-only and enhanced press/repeat/ release modes. Unnegotiated repeat/release is refused before frame receipt retirement; enhanced Enter repeat/release cannot complete the view, while explicit Enter press can. Unicode multiline/tab/quoted paste is delivered once as data, not interpreted as commands or individual keystrokes. Actual parent CLI exercises the same protocol contracts. Full custom client/terminal requirement audit and final regression remain open; these tests do not claim native enhanced keyboard execution on other OSes.

Bounded custom-frame cross-thread delivery (UI-042)

Parent and child RPC frame publications now reserve capacity before scheduling onto the receiving loop: at most16 queued frames and2MiB serialized data per connection controller. Reservation is released after delivery, including callback failure; closed-loop scheduling refusal releases it immediately. If capacity is exceeded, the owning broker view is canceled with a null result rather than allowing an unbounded queue or silently delivering a partial interactive view. This is not rollback of extension callback effects, nor a tool permission change. Existing scope/generation/painted-frame fences still apply. Full goal regression remains open.

Explicit custom-frame capacity errors (UI-042)

Frame delivery capacity refusal now emits an owned extension_custom_error / child_extension_custom_error with code: "frame_delivery_capacity", canceled: true, original scope/child identity and capability generation. The view still returns null cancellation; this is not callback-effect rollback. Error delivery is deduplicated and bounded independently (maximum8 pending notices), so repeated refused large frames cannot build another unbounded queue. Late errors fail the original scope/generation checks and cannot clear or cancel a replacement view after re-enable. Existing permissions remain intact.

Output admission and custom frame receipts

Parent/child custom frame receipts are cached only after the output sink accepts the frame. Output refusal cancels the owning view and leaves no interactive cached receipt. Enqueue acceptance is not physical remote-client delivery or painting; clients still provide truthful painted revisions.

Independently negotiated child terminal input data (UI-042, partial)

Negotiate exact child_terminal_input: 1. Child capability receipts expose current terminal_generation and child_terminal_registry (active hook/worker generation). child_terminal_input packets require the current child/session/revision/owner/epoch, fresh request ID, terminal capability and worker generations, kind (key or atomic paste) and bounded data. Only editable keys are accepted; submission, host actions, approval/recovery controls stay client-owned. Paste remains inert data even with newlines/escape-looking text. This invokes the child's existing registered terminal hook and returns child_terminal_result; it never submits/queues child input or writes terminal controls. The client owns safe application of returned editing data.

At most one terminal request owns this child RPC control slot. Original lifecycle, hook/connection generations and pending dialog/approval admission are checked before and after the owned callback. Transform output is checked again; invalid/error/ timeout/cancel never silently falls back or replays original data. Withdrawal cancels and settles the actual worker operation. Pending approvals are checked at their actual admission, including before frontend publication. Real worker and CLI verify text/paste/consume, no extra model turn/queue/policy change and cancellation/reaping. Full custom/terminal client contract acceptance remains open.

Terminal cancellation and late approval receipts

A child terminal request canceled before worker admission returns one scoped canceled result with no transformed data and no hook execution. If an approval is admitted while the hook is running or before its result is returned, the post-hook owner check refuses data/consume delivery. Original input is not retried or submitted automatically; callbacks already executed are not rolled back.

Custom RPC contract acceptance status

The finite current contract audit is in docs/agent-ui-rpc-custom-contract-audit.md. Background refresh and child-terminal CLI disconnect/deadline reaping are verified; parent/child interactive custom input also rejects authoritative pending approvals before frontend publication. UI-042 remains pending verification of structured RPC renderer integration, physical Linux RPC terminal interaction and final integrated closure. Current221-pass named matrix is not final root regression or physical remote-client painting proof.

Grapheme-safe structured RPC frame projection

Parent/child RPC custom-frame control filtering now uses the vetted custom-line sanitizer before removing allowed styles, preserving ZWJ emoji/combining grapheme width. The old ordinary text sanitizer widened composed layout frames by deleting ZWJ. Actual structured worker layouts are now verified through parent/child RPC in inline/fullscreen/positioned-overlay modes with narrow resize/cursor, scroll/layers and nested focus. Physical Linux RPC terminal interaction and final contract closure remain open in docs/agent-ui-rpc-custom-contract-audit.md.

Physical Linux RPC terminal acceptance evidence

An owned private-tmux test frontend now renders received real parent/child RPC layout frames using shipped TUI components. Physical Unicode input, layer focus, narrow resize, nested questions, independent child tool approval, restored parent cursor and exact termios/native scrollback recovery pass. This is test evidence, not a shipped remote frontend or assurance about arbitrary clients. UI-042 final integrated contract closure remains pending in the finite audit.

RPC custom/child-terminal contract verification checkpoint

As of2026-10-10, all three finite acceptance actions in docs/agent-ui-rpc-custom-contract-audit.md are verified; UI-042 is completed. Earlier partial/Open notes above are historical implementation slices, not current additional requirements. The complete current RPC/custom broker suite579 cases passes without skips, including actual Linux parent/child physical terminal paths, with source files frozen during verification. This proves the documented named contract, not Pi wire compatibility, arbitrary external clients, a shipped remote frontend, non-Linux native validation or final full-workspace regression. Separate UI-039 event integration and final Linux safety/root gates remain pending.

RPC transient/custom event integration verification checkpoint

As of2026-10-10, UI-039's documented named parent/child transient/tool-progress and custom callback contribution contracts are verified. Actual custom callbacks emit scoped redacted notice/status/widget contributions before returned frames; withdrawing contribution capability does not cancel custom view capability, and no contributions are masqueraded as retained agent history. Full frozen RPC/custom broker581 cases pass with zero skips. Earlier partial OPEN notes are historical slices, not current extra requirements. This does not imply history replay of every transient artifact, Pi wire conformance, arbitrary client behavior or final whole-workspace regression.

Parent RPC terminal hook editing data (UI-036, partial)

Negotiate terminal_input: 1 and inspect terminal_capabilities for current terminal_owner, session/revision, worker generation and hook availability. terminal_input requests require those exact identities and a fresh request ID; terminal_result returns transform/consume data only. It does not submit a prompt, write terminal bytes or change tool policy. Protected/submission keys are refused, atomic paste stays paste, and results are revalidated before publication.

One owned5s operation; stale owner, approvals/dialogs, context changes, callback errors and cancellation do not replay original data. terminal_cancel uses the original request envelope; withdrawal and RPC teardown settle the actual worker. Normal prompt/navigation/control admission is excluded during the hook operation. Actual worker/CLI evidence exists; full raw-surface contract audit remains UI-036.

Cross-parent/child dialog terminal ownership

Parent and child RPC terminal hook data are denied while any owned parent/child extension dialog is pending. The check uses actual broker admission, not delayed frontend publications, and is repeated after the hook returns. A newly admitted question cannot receive old transformed data or cause automatic replay.

Parent/child RPC hook CLI lifecycle evidence

Actual Linux CLI tests now cover both parent and child hanging terminal hooks on quit, EOF, closed output and deadline, including exact owned worker reaping. The closed-output check performs a status write while stdin remains open. Parent editing hooks do not invoke the model; child producer cleanup settles without tool mutation. This is bounded named lifecycle evidence, not final full-workspace regression.

Prompt release-event boundary

Native Coding/Claw prompt hooks do not receive release events or allow transformed releases to synthesize editing presses. RPC editable input/results likewise refuse release; encoded repeat remains supported. Custom-view release events are a separate, explicitly declared protocol. The named remaining input acceptance actions are in docs/agent-ui-input-surface-audit.md; UI-036 remains open.

RPC hook configured key bindings

Parent/child RPC editing-key validation now uses the configured bounded keybindings.json snapshot, not just defaults, on both incoming and transformed key data. Keys remapped to submission, external editor, draft saving or extension commands remain host-owned. Invalid keymap configuration refuses key-hook data rather than guessing authority from fallback bindings. Atomic paste is still data; remote clients must additionally enforce their own local keymap before applying returned editing input. Full UI-036 acceptance remains open.

Physical RPC editing-data acceptance evidence

Owned Linux private-terminal frontend applies parent/child RPC hook data through shipped editor/arbitration components. Physical Unicode/paste/cursor, repeat phase, release exclusion, original-owner held-hook cancellation, paused recovery and exact terminal restoration pass. Test worker logs verify actual data application; this is not a shipped arbitrary remote client. UI-036 concrete raw-contract reconciliation and final integrated input matrix remain open.

Runtime shortcut-aware RPC hook data

Incoming and transformed parent/child RPC keys are checked against their owning extension runtime's current shortcut inventory as well as configured keybindings. The shared native effective resolver preserves conflict/override/disable behavior: active command shortcuts are host actions, not editing data, while a disabled conflicting shortcut does not take away its native editor binding. Atomic paste remains inert data. UI-036 reference/raw-contract reconciliation remains open.

Approval admission revision and terminal hook results

Parent/child RPC hook operations capture approval-manager identities and monotonic approval admission revisions. Opening then closing an approval still invalidates an earlier hook result, even when no request remains pending at delivery. Replacing the child approval-manager inventory also refuses the old result. This prevents edits crossing an input ownership change; it is not a tool policy change or callback rollback.

Metadata

Release files for xdog-coding 2.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for xdog-coding 2.3.0
File Size Uploaded
xdog_coding-2.3.0.tar.gz 758.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for xdog-coding 2.3.0
File Interpreter ABI Platform
xdog_coding-2.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 1.1 MB

Release files / xdog_coding-2.3.0.tar.gz

Download URL xdog_coding-2.3.0.tar.gz
Size 758.3 kB
Tags Source
SHA-256 checksum
How to use checksums
41ce8d96a1ce7997037603dae03454844948a1e3ee7ca047fc5cb927485e0e31
BLAKE2b-256 checksum
How to use checksums
7017617b2a36594d9d7ec4cf251a8c7a7ebffa3e5d7949761738928b3dd9be16
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.

Transparency log

Release files / xdog_coding-2.3.0-py3-none-any.whl

Download URL xdog_coding-2.3.0-py3-none-any.whl
Size 334.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b57f2276cc58464f033fde671bd79acea5bf5a00e4a596cf66ac4e7dd7d2915b
BLAKE2b-256 checksum
How to use checksums
9cec6bc1b3d59d32e97cbcc84555d86a26f5abb2a981c71303915abeab474c6c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.

Transparency log

Release history Release notifications | RSS feed

2.4.1

2 release files

This release

2.3.0 This release

2 release files

2.2.0

2 release files

2.1.2

2 release files

2.1.1

2 release files

2.1.0

2 release files

2.0.1

2 release files

1.1.0

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page