Skip to content

Unified local workspace

Clauz3 browser workspace

clauz3 ui [claude|codex|agy|opencode|pi] composes the existing approval authority with a local agent conversation. It follows AmiGO3's browser/WebSocket/ACP-stdio architecture and server-owned process lifetime; the implementation lives in sessions/chat.py and packaged sessions/web/ assets. There is no frontend build step.

Browser ── WebSocket ── Session host ── ACP/stdio ── Selected agent
   │                                                  │
   │                                           Clauz3 MCP tools
   │                                                  │
   │                                            proof/run worker
   │                                                  │
   └── approver cookie ── Approval authority ── signed receipt

Provider selection lives in sessions/providers.py. Codex and OpenCode use external ACP runtimes; native_adapter.py maps Antigravity's stream-json and Pi's SDK/RPC events into the existing host protocol. Pi's packaged pi_bridge.mjs connects the six Clauz3 MCP tools. All provider runtimes are optional executable dependencies. terminal.py reuses Conversation for the non-Claude CLI frontend, including streaming, run state, cancellation, and resume. Claude's native terminal launcher is unchanged.

Claude and pinned Pi support harness confinement. Other providers require --unrestricted, which is persisted with the conversation and displayed in the UI. Their native tools may bypass Clauz3. Provider and model identity are saved; a different provider never receives another provider's session ID. Runtime or authentication failures preserve the transcript. Provider switching is limited to server-defined commands and starts a new conversation. Claude's browser integration rejects --unrestricted, including switches from an unrestricted workspace; its native terminal launcher still offers that mode. Provider availability is cached per conversation and excluded from saved state. Unavailable or unknown saved providers preserve their transcripts and allow the operator to start another conversation.

Restricted Pi runs restricted_pi.mjs against the unmodified, installed @earendil-works/pi-coding-agent@0.82.1 SDK. Its tools allowlist filters both model-visible tools and the executable registry. Clauz3 provides an empty resource loader, in-memory settings, an isolated process environment, and an auth-only model runtime; no discovery loader runs. The adapter checks the exact tool inventory at startup and before each request. Only text prompts and a session-state query are accepted by this private transport; native RPC bash, configuration changes, and extension reload are unavailable. Fresh and resumed sessions use the same configuration. tests/test_pi_confinement.py drives the actual installed SDK with forced forbidden tool calls and hostile resources, without network access or model credentials. Install the pinned optional runtime to run that test; it skips when the runtime is absent or unsupported. The pi-confinement CI job installs it explicitly and also tests real proofs, signed approval, rejection, and cancellation through the SDK-to-MCP bridge.

The existing launcher discovers libraries, snapshots skill documents, checks the MCP bridge, generates separate credentials, and starts the local service. UI mode passes private configuration in a mode-0600 file inside the launcher's private temporary directory. Only the service receives the signing key and decision credential. The agent environment strips approval credentials; its MCP configuration includes only submission credentials and the verification key. No secrets are placed in the page or chat event history by the host.

The host performs ACP initialization and session creation, reconstructing all session parameters itself. The browser submits text prompts and controls Stop and New conversation. ACP updates remain protocol-shaped; clauz3/user, clauz3/turn_end, clauz3/state, clauz3/restore, and clauz3/notice are local workspace events. This endpoint is a constrained workspace transport, not a transparent general-purpose ACP proxy. Native terminal/filesystem capabilities are not advertised. Native tool permission requests are refused; configured Clauz3 MCP tools are already allowed to invoke the proof workflow. Codex additionally requests per-call MCP permission; only requests correlated with an announced Clauz3 tool call and marked as MCP approval are accepted, using allow_once. This grants entry to the bridge, never execution approval.

The Claude adapter version is deliberately pinned. Its _meta.claudeCode.options extension forwards the SDK restriction settings. On the reviewed release, settingSources: [] becomes --setting-sources=, tools: [] becomes --tools '', and strict MCP configuration and the six allowed tools reach the underlying CLI. All browser-supplied SDK configuration is excluded.

Runner state uses an atomic JSON snapshot in the private session directory, with bounded retained records. It carries run IDs, source hashes, phases, diagnostics, and approval request IDs independently of agent narration and optional ACP progress forwarding. The host associates each new run with the current turn; another prompt is refused while either the agent or a run is active. The UI joins these records to escaped, server-rendered approval cards by exact request ID. It does not infer approval from a slow tool call or infer execution from approval alone. Approval actions use the existing decision API.

Action reviews separate run status from individual proof diagnostics. The main card shows the execution phase and any failure reason; the last checked expression, proof conclusion, and exit code remain under Run diagnostics. The Verified limits list shows the library title and declared Mock/Mixed badge, the library-qualified call with named arguments, its trusted description (or first docstring paragraph), and the full resolved Python symbol. Source and documentation expands the original agent expression, captured source, file/line, source and layer fingerprints, recorded origin/revision, and full docstring. Live cards and saved history use the same ordering. Identity is independently verified against the selected registration, separately from description templates and eligibility for remembered approvals. Installing newer libraries does not rewrite history. Records without a captured identity say Resolved identity not recorded. See contract descriptions.

Conversation events and run snapshots are stored in an operator-owned SQLite file, conversations.sqlite, under the user-wide Clauz3 data directory. Records are isolated by canonical project path. New conversation keeps the previous transcript. The sidebar follows the chat-app pattern: workspace switcher, New conversation, Libraries and Approvals (with installed and waiting counts), then date-grouped history with search (⌘K) and a per-row menu for Rename and Copy handoff. The conversation header's ⋯ menu adds Export transcript. Menus are native popovers anchored by menus.js; grouping lives in history.js. Drafts are kept per conversation in browser session storage. Each conversation retains an 8 MB transcript limit, and the live subscriber queue remains bounded. Reaching the storage limit stops retaining new events and disables further prompts, while the current action continues to completion. The UI clearly marks later output as unsaved; Stop remains available. Backpressure restores use a snapshot of retained messages, so later events are not replayed twice.

One agent conversation runs at a time. Switching stops the previous agent and uses ACP session/load to resume the selected adapter session, with freshly constructed MCP credentials and the same server-controlled SDK restrictions. The host suppresses adapter history replay because its saved transcript already contains those messages. If the adapter cannot resume, the transcript remains visible and the UI reports the error. Wait for an active action to finish or use Stop before switching. An exclusive project lease prevents two UI services from resuming the same project concurrently. The lease is acquired before creating a conversation or resetting run state; the launcher reports a specific already-open error. Server restarts reopen the most recently updated conversation. CLI clauz3 resume continues to use its separate managed-session metadata.

Workspace selection and tutorials

sessions/workspaces.py keeps recently opened canonical paths in recent-workspaces.sqlite beside the conversation database. Startup selects the launch directory, unless --tutorial selects the bundled email tutorial. An authenticated, same-origin clauz3/workspace WebSocket command selects an existing folder or the hardcoded email tutorial; agents cannot invoke it. The switcher opens a dropdown menu of recent workspaces, Open folder…, and tutorials; stop and reset confirmations use a modal dialog. It stays available during proof and approval work. The server requires explicit stop confirmation when work is active, and refuses switches while a library operation is mutating or preparing the workspace.

The destination's project lease is acquired before stopping the source agent. Switching rebinds conversation storage, libraries, file browsing, and the approval profile, then releases the previous lease. Submission credentials rotate, pending approval requests are cancelled, and requests that finish verification after the switch are rejected by a generation check. Each switch uses a fresh run-status file to exclude late updates from old workers. The browser reloads the chat view to discard old previews and approval polls. There is one active workspace per process; no background workspace execution.

An explicit launch policy applies only to the launch project. Other projects use their own remembered profiles; returning restores the explicit policy. Custom skill snapshots apply only to the launch workspace and are restored when returning there. Startup prompts are not replayed when switching. Agent runtime configuration remains owned by this UI process.

src/clauz3/tutorials/email/ is a deliberately small, independently tested copy of the developer example, shipped in the wheel. examples/email/ retains the fuller proof collection; the walkthrough stays in docs/quickstarts/mock-email.md. The UI creates a writable copy under the user data directory's tutorials/email/, never in an OS temporary folder or the launch project. Existing copies survive updates. Confirmed reset replaces only that managed directory and clears its conversation rows, library preferences, and approval profile. Reset acquires the same project lease and rejects symlink destinations. It uses the current bundled template. Reset does not erase separately exported handoff snapshots.

The bundled sender is a no-op. Its @clauz3.mock declaration drives the purple Mock chip and menu badge; libraries with mixed declarations show Mixed. The label itself is descriptive, not a sandbox. The tutorial has no outbox. The inline introduction and starter prompts complement the documentation rather than embedding a second walkthrough.

Copy handoff saves a mode-0600 JSON snapshot in handoffs/ beside the conversation database. The clipboard reference includes the conversation's stable UUID, project, capture time, and absolute snapshot path. A developer assistant on the same machine can read it after the UI stops, without a login cookie. Each click creates a new snapshot; later turns do not change an earlier handoff. The snapshot includes saved messages, raw tool calls and results, run diagnostics, and conversation settings, but not the process configuration. Requests are authenticated, origin-checked, and scoped to the current project.

Messages and responses have copy buttons; responses copy their original Markdown, including streamed text received so far. Code blocks, tool results, and raw tool data can also be copied independently.

The Approvals view shares a reusable component in approvals/web/ with the standalone /ui/profile page. It reads the existing profile API and keeps the same remember/disable operations and authority checks. Navigation within the workspace preserves the live chat connection and draft. Saved approval request details can be shown with historical runs after a service restart; history does not create approval grants or re-execute actions.

Approval profile and history

Stop kills the agent process group. A small acp_worker supervisor watches a private lifetime pipe, so even abrupt service death stops the adapter and MCP bridge. A private Unix socket also ties MCP bridges directly to the service, including adapters that detach bridges into separate process groups. Each proof/execution worker already watches its bridge's lifetime pipe and terminates its separate process group if the bridge disappears. Advisory run-snapshot write failures do not interrupt execution. Stopping cannot undo effects that completed before cancellation.

The browser WebSocket and transcript endpoint require the approver session, a loopback peer and Host, and same-origin browser access. Chat content is inserted with textContent; agent HTML is never interpreted. The approval markup is generated and escaped by the existing service. No third-party scripts, remote images, or fonts are loaded. Explicit ACP startup errors survive cleanup; malformed responses terminate the failed adapter rather than leaving a stuck UI. Raw adapter stderr remains excluded from the UI and history because it may contain credentials. Authentication failures close the WebSocket with 1008; another active tab gets the retryable 1013 code.

File browser

Workspace file browser

The Files button opens a read-only browser alongside the conversation (a full-width panel on small screens). Browse folders, use the breadcrumbs to go back, toggle hidden files, and refresh to pick up changes. UTF-8 files have an inert text preview; Add path to message inserts the workspace-relative path into your unsent draft. This does not send the file contents to the agent.

The /api/workspace/files endpoint shares the transcript endpoint's operator session and same-origin checks. The server supplies the active workspace root on each request. Each path component is opened relative to an existing directory file descriptor with O_NOFOLLOW, so links and parent traversal cannot escape the root, including when a path is swapped during a request. Symbolic links and special files are unavailable. Listings are limited to 2,000 entries and text previews to 256 KB; binary and non-UTF-8 files show metadata only. File content is returned as uncached JSON and inserted with textContent, never rendered as HTML. Files cannot be edited or deleted through this browser.

Library management

Library descriptions and setup are read from domain manifests plus installed package snapshots; see Library manifests and setup. An installation review includes a warnings array. The UI displays missing domain-manifest notices without disabling installation of legacy libraries. An absent optional package manifest does not produce a notice. Invalid metadata or a declared package with a missing domain manifest fails preparation instead.

Workspace library management

The Libraries view uses libraries/workspace.py over the existing catalogue and CLI installer. Empty workspaces open this view automatically, without a terminal chooser. Operators can search bundled, curated, and configured global sources, inspect installed APIs, and review staged source, file changes, and runtime requirements before confirming installation or replacement. Installing a library does not install external runtimes or authenticate accounts.

The Libraries view is one list of cards with All and Installed filters. Each installed card's switch sets the default for new conversations, saved per canonical workspace with declared dependencies included. A conversation records its own domains and a content fingerprint, shown as chips above the message box. The chips' menu changes them: before the first message freely; afterwards by restarting the agent and resuming the same session with a rebuilt MCP bridge and approval authority. Removing libraries applies at once. Adding libraries or continuing after their files changed asks first, then pauses automatic approvals (remembered allow rules and --policy allows) for the rest of that conversation, since earlier messages may carry injected instructions; automatic rejections still apply. Every change in a started conversation adds a transcript marker, and the agent's next prompt starts with a note listing every change since its last prompt. Agent messages cannot select or install libraries. See docs/superpowers/specs/2026-10-14-library-selection-design.md.

Library endpoints require the operator cookie, loopback access, and same-origin checks; mutations require an explicit matching Origin header. Source identifiers must come from the server catalogue. Review stages the existing installer's output and validates declared domains; apply rechecks file hashes and refuses symlinks, overlapping trees, or changes to active libraries. Updates and removals retain originals under .clauz3/library-backups/<review-id>/, with rollback if a move fails. Selection and conversation changes are blocked while an operation is in progress. Install review parses source without importing library code. Git subprocesses retain operator Git authentication but receive no approval credentials, signing keys, or internal workspace configuration.

Verification

uv run pytest tests/test_workspace_switching.py tests/test_workspace_libraries.py tests/test_workspace_files.py tests/test_workspace.py tests/test_start.py tests/test_session_runs.py
just check
# Optional browser smoke check; install Chromium first if needed:
uv run --with playwright playwright install chromium
uv run --with playwright python scripts/check_workspace_browser.py

The deterministic ACP fixture checks SDK configuration and credential separation and can invoke the real Clauz3 MCP/prover/receipt pipeline against the mock email example. The browser check covers streaming, inert HTML, reload and draft recovery, pending approval across reload, execution after approval, export, Stop, switching/resuming/renaming conversations, service restart recovery, approval profile curation, file browsing, library installation, selection and removal, tutorial entry/reset, active workspace switching, approved execution with newly selected roots, and mobile overflow. It sends no model requests. Screenshots are written under /tmp/clauz3-workspace-*.png; the Files, Approvals and Libraries images in this page come from that run.

just docs-videos re-records the tutorial video, README GIF and the screenshot at the top of this page. Unlike the check above, it runs the real clauz3 ui --tutorial with your default agent, so it needs that agent's login and makes model requests. It uses a temporary data directory and fast-forwards the agent's thinking time with ffmpeg.

Re-record only after visible changes to the tutorial flow, not for every UI tweak. Each recording adds about 1.7 MB of binary files to the repository's history, and slightly stale media is better than a growing clone. The README loads the GIF from main on GitHub so it also renders on PyPI, so new media appears there only after merging.

An additional local check initialized claude-agent-acp 0.89.1 and created a session with the configured MCP server, capturing the spawned CLI arguments to verify restriction forwarding. No model prompt was sent in that check.