crates/core-api is the public port boundary. crates/domain contains serializable session facts and pure projection rules. crates/core owns command admission, session state, tool-loop decisions and subscriptions. crates/state, crates/model, crates/tools, crates/host and crates/app-server implement external adapters. Only src/main.rs assembles concrete adapters.
ContextPort owns bounded environment/Git/AGENTS IO. Session owns the initial PromptSnapshot and commits it through PromptInitialized before any normal model request. Agent loop rebuilds a pure request prefix from that snapshot, freshly read instruction sources and the bound model identity. The prefix is not canonical transcript. Generated TS prompt assets are checked for drift; desktop/terminal and Anthropic cache boundaries are retained. See docs/specs/rust-request-context.md for default-main-agent scope; Skill catalogs and child profiles are described below. Main-agent memory extraction and output styles remain deferred.
Application code cannot import adapters or perform filesystem/network/process IO. Adapters cannot import the application. The Rust source boundary check and Cargo compile/tests enforce this; the repository architecture checker currently parses only JavaScript/TypeScript.
Host → runtime uses the existing ZCode NDJSON envelopes and V4 schemas under packages/shared/src/zcode-protocol*. The native executable accepts app-server --stdio --cwd <workspace> with optional --data-dir and --config. Without static config, Host-provided Registry files and account overlays supply model configuration. --surface desktop is accepted. --prepare-storage performs only the storage handshake on the Node session database, with no model/network initialization.
The runtime owns accepted input, run generation, permission requests and canonical transcript. The Host owns processes, attachment routes and generation/lease isolation. A Session's workspace identity uses the Host's original path or explicit identity, not its filesystem realpath.
Host task indexing reads session/read through the existing strict legacy snapshot schema. This is a pure projection of the same Session: reading never resumes execution, requests authentication or changes subscription delivery kind. It includes visible message/tool content, persisted task kind and archive time, selected model capabilities from ModelPort, and runtime state. messageLimit selects the message tail; oversized snapshots fail explicitly. V4 remains the Renderer history/stream contract. Per-message token/cost values are unavailable in the native row projection and use zero; aggregate usage retains the stored facts.
The runtime supports Chat Completions, Responses or Anthropic Messages, Registry model switching, text/attachment input, yolo, Read/Write/Edit/Glob/Grep/Bash/TaskOutput/TaskStop/AskUserQuestion/TodoRead/TodoWrite/Skill/Agent/SendMessage and configured MCP tools, Goal, retry/edit/fork/file rewind, stop, FIFO input, queue editing, rename and cold history. Unsupported commands fail. Rust shares the Node session database (~/.zcode/cli/db/db.sqlite and <storage.dir>/cli/artifacts): it writes Node records and cold-loads Node sessions directly, so either runtime can continue the other's sessions. A data-dir workspace lock admits one Rust owner per workspace. See docs/specs/rust-m11-node-storage.md.
Composer attachment transactions use the existing begin/chunk/commit/abort RPCs. Engine owns connection/session/upload metadata and bounded staging; SessionStore persists immutable bytes before the owner's reference commit. Only then is the reference returned. Closed connections and TTL discard staging; committed references remain session-owned. Local paths are snapshotted before input admission, including first and queued inputs. Canonical content stores private descriptors rather than base64; HttpModel materializes them once before request encoding, checks the bound model's media capabilities and drops expanded request trees before streaming. Retries retain encoded bytes. Source paths may label user-provided text; internal storage paths never enter model requests. Session/row/index authorization remains mandatory for historical preview. See docs/specs/rust-prompt-attachments.md for current limits and remaining full-parity work.
Session metadata/transcript and an accepted immediate input ACK commit in one SQLite transaction. Queued inputs are process-local; retained ACKs become discarded on restart unless the canonical user/maintenance turn or durable rust_started receipt proves execution started. History cuts preserve these receipts. Interrupted tools get a model-visible unknown-outcome result, never automatic re-execution. Draft sessions stay in memory until their first input.
deleteSession follows TS close semantics: the actor cancels and drains foreground/background work, clears pending interactions and uploads, commits queued-input dispositions and the close ACK, then releases the runtime and all conversation subscriptions and publishes index removal. Persisted history is retained; no-history drafts are reclaimed through a guarded Store transaction. Cold conversation subscriptions/queries use SessionStore.load_session to read only the requested history with a fresh epoch. Legacy session/read can project a temporary cold Session without activation; explicit close remains existing-only until V4 reopens it. ToolPort.close_session releases background handles and per-session file observations. Late old-run events cannot reopen a session. See docs/specs/rust-session-close.md.
Native storage separates session metadata, projected rows and canonical messages. Ordinary commits encode changed facts once, skip identical row/history updates, and append new messages. History cuts replace the retained prefix, canonical boundary index and replacement input in one transaction; action projection changes can update older rows. Legacy native inline histories migrate transactionally on first commit. Streaming display checkpoints are limited to once per 250 ms; semantic boundaries and input ACKs are always durable. The model/tool loop waits for an owner commit receipt before tool execution or the next model call. The native v1 binary cannot read the new split storage: keep a backup before upgrading existing native data; TypeScript databases remain outside this migration.
SessionStore.list_sessions is the typed read-only metadata port for session/list. The SQLite worker applies identity/archive/task-type filters and ordering without reading transcript tables or activating sessions. Explicit ID batches preserve order and include hidden children for the existing Host index repair; ordinary queries use directory separately from the returned workspace path. Older missing metadata may read a committed TS backup through request-scoped read-only connections; no query writes facts or generates a backup. The response budget fails explicitly rather than truncating. See docs/specs/rust-session-list.md for the stored projection and remaining live-runtime append difference.
See docs/specs/rust-cli-architecture.md for the crate boundaries, migration phases, capability boundary and acceptance cases. Example model config:
{
"providerId": "my-provider",
"modelId": "my-model",
"reasoningLevel": "none",
"reasoningParameters": { "reasoning_effort": "none" },
"baseUrl": "https://provider.example/v1",
"apiKeyEnv": "ZCODE_MODEL_API_KEY",
"requestTimeoutSeconds": 180
}Static config references an environment variable and accepts one provider/model/reasoning identity. Explicit reasoning parameters are forwarded; absent parameters mean provider defaults. Registry mode applies App templates, builtin/account/personal overlays and option maps. Registry owns atomic configuration snapshots; Session owns committed selection and queued selections. Each model step binds one snapshot. App retains ownership of defaultModelSelection. Account authentication is requested from Host on each HTTP attempt through correlated stdio replies; credentials remain transient and never enter transcript, queue, ACK or diagnostics.
ModelPort consumes an owned request projection so adapters can release intermediate JSON trees after encoding; retries retain only encoded bytes. ModelPort returns a structured ModelFailure, and internal Retry events project the existing V4 control.apiRetry. These events carry the same session/run identity as text and are never transcript facts. Provider retry is permitted only before nonempty text/reasoning is delivered. HttpModel owns the reusable connection pool and per-attempt cancellation; the app owns commit receipts and tool sequencing. Optional requestTimeoutSeconds limits an attempt; omitted means no fixed total deadline. streamIdleTimeoutMs defaults to 600000 and increases by 30000 per retry. retry overrides the existing ZCODEMODEL_RETRY* environment policy; defaults match the current TypeScript retry policy.
Workspace runtime ownership uses the standard library's exclusive file lock, held before loading sessions and released on process exit. Native storage preparation does not take this runtime lock.
The Coding package uses session-scoped ToolPort.execute_scoped. Session owns persisted mode and background task facts; the tool adapter owns process handles, artifact IO and bounded per-session file observations. A background start must receive the owner's durable registration receipt before spawn. Terminal events use session/run/task identity even after the foreground run ends. ToolOutput separates model content, adapter validation data, bounded App display and execution failure; nonzero shell exit remains a structured result while marking the tool row failed.
POSIX Bash cancellation starts with TERM and a 1500 ms termination grace period, then verifies and kills remaining owned groups and descendants through bounded snapshots. Normal child exit does not settle inherited pipes or descendant ownership. Tool completion and actor shutdown wait for actual cleanup. Unconfirmed cleanup raises ProcessCleanupFailure and a scoped ToolCleanupFailed event: the actor stops accepting work instead of treating this as a recoverable tool error and starting another model request. Stale run events remain isolated. This is process lifecycle management, not a daemon containment sandbox; Windows keeps its taskkill path pending native validation. See docs/specs/rust-shell-lifecycle.md.
New sessions use yolo; stored sessions without a mode resume as build (Node resumeFromStore) and ask before writing until switchCollaborationMode(yolo). Node sessions resume their stored mode and planEnabled. Other execution modes are rejected. Background task summaries share the input transaction before the next model run. Process shutdown cancels/drains handles; background tasks are not persisted (like Node) and never rerun after a restart. See docs/specs/rust-coding-tools.md for budgets and deferred parity.
Context policy is a pure domain value exposed by ModelPort. RunContext contains only a working projection and incrementally derived token estimate. The Session actor owns the durable ContextState (offset + summary); CompactStarted/CompactDone use commit receipts, and a failed boundary transaction prevents the next model request. Canonical messages stay append-only. Hidden summary text is consumed through a bounded sink; retry status and authentication requests still reach the App. Existing timelineMarker/compact and usage schemas carry status, with no protocol version change.
Queued-now reservation is process-local Session state. ACK commits before cancellation; the prior run Finished event is the promotion barrier. Held-queue discard ACKs and the new input commit together through Session.pending_acks into the existing rust_command table. Missing queued model configuration holds the queue with an error rather than losing the admitted input or changing its selection. See docs/specs/rust-context-management.md.
HttpModel selects a ProtocolStream from apiType; all three adapters return the same canonical ModelOutput through existing ports. Responses encrypted reasoning items and Anthropic signed/redacted thinking blocks are private canonical metadata, serialized only for the originating provider/model. Request transforms remove internal metadata and preserve call/result association. Protocol completion and tool identities/JSON are validated before the owner's ModelDone commit receipt. See docs/specs/rust-model-protocols.md. accountProviderConfig is true in Registry mode and false in static-config mode.
ModelOutput.output_limit denotes a validated successful truncation with no tool calls. The loop waits for ModelDone's durable receipt, then adds at most three request-only continuation prompts in RunContext. Empty truncations commit usage without adding an empty canonical assistant. Temporary prompts never enter Session messages, user rows, ACKs or context offsets. A compact summary must finish normally before its boundary can commit. See docs/specs/rust-output-continuation.md.
Workspace text generation and connectivity probes share model/auth/cancellation ports but never create durable sessions or execute tools. Actor-owned auxiliary jobs are bounded and cancellation clears authentication waiters; late replies cannot resurrect them. Imported local/data/artifact attachments use independent byte snapshots. Read/stat/preview is authorized against the owning Session row and attachment index before the storage IO port is invoked. Rollback to TS reads the unchanged source; new Rust history is not reverse-synchronized.
Workspace-config and negotiated workspace presentation declare optional executionCapabilities from one runtime projection. Presentation requires includeExecutionCapabilities=true after checking runtime/capabilities.workspaceExecutionCapabilities; old App strict responses keep their previous shape. Rust advertises permissionModes=[yolo], independentPlanState=false. App gates prewarm/submission on current routed workspace capabilities; an unsupported saved preference never silently becomes yolo. See docs/specs/rust-app-execution-modes.md.
Busy input uses the existing Session queue and the single queued-now reservation. setFollowupMode persists queue/guide routing. StepBoundary is a run-scoped handshake after the assistant and every tool result commit: the actor consumes at most one guide, persists its same-turn user row/history and frozen selection, then returns the committed input messages in order to the loop. Ordinary queued inputs cannot block the guide subsequence. Guide attachment/interruption fallback remains explicit. Busy startNow commits its admission/reservation before cancellation and promotes only after the previous Finished commit; stop/EOF/close disable promotion. A pending startNow reservation stays out of the ordinary queue projection, and restart dispositions retain the original ACK delivery. See docs/specs/rust-busy-input.md.
AskUserQuestion uses a run-scoped Question event and one actor-owned waiter per registered tool. The actor projects existing userInput interactions and owns their timers, selection order and preference eligibility. Pending interactions and normalized answers commit before publication or wakeup; the loop commits canonical results in tool-call order before requesting another model step. A committed question row recovers its actual answer if the process dies before the canonical tool result, without replaying the tool. Unanswered cold questions become interrupted. Yolo never supplies answers. Default head timing is 60 seconds hidden / 300 seconds total; snooze is permanent and preference reenable applies only to later registrations. Engine.with_question_timing is a construction-time test clock input; production composition uses the same guarded environment scale as TS. See docs/specs/rust-user-questions.md.
Session owns persisted Todo items and their timestamp. Run-scoped Todo events commit state and completed tool rows together before publishing App plan and returning a result; canonical tool results remain ordered behind their existing commit receipts. Cold recovery reuses committed results and never replays writes. Todos share Node's todo table. Todo reminders are committed hidden synthetic messages after the TS ten-assistant-turn thresholds. Todo progress does not enable independentPlanState or Plan permissions. See docs/specs/rust-todos.md.
Shared handover enters through the existing session/create importedHistory API. Host owns share retrieval and artifact installation; Session owns provenance and its pending/reserved/attached/discarded lifecycle. Candidate markdown lives in immutable Store bytes outside hot metadata; only a validated session-scoped reference can attach it to canonical messages. The hidden context, user input, state transition and input receipt commit before the loop runs. A queued reference reserves the single candidate by queue item ID; deletion/close/restart release it. StepBoundary returns an ordered message batch so guides can attach context without a second persistence path. Pending/discarded candidates never enter compaction or provider history. Node-written imports are read with their stored lifecycle. No wire version changes; see docs/specs/rust-shared-context.md for legacy migration limits and acceptance.
Startup reads only metadata summaries; canonical histories and ACKs are loaded by key. Unpinned durable Sessions use an LRU bounded by eight entries and 16 MiB of estimated retained memory. Estimates are cached until the next commit; pinned owners are outside the evictable budget. Active runs, queues, subscriptions, uploads, child results and background work pin their owner. Loading one session still reads that session's full history; see docs/specs/rust-session-loading.md.
Skill discovery freezes per-session metadata from user/project/enabled plugins; invocation reads bounded content and returns it through the canonical tool-result barrier. MCP uses real stdio, streamable HTTP or legacy SSE connections, bounded discovery and session/workspace-scoped pools. Children borrow parent MCP bindings and frozen Skill/prompt snapshots. Invalid persisted MCP servers expose config_invalid individually; explicit configurations remain strict. HTTP clients are initialized only when required. OAuth and full plugin option expansion remain deferred; see rust-skills.md and rust-mcp.md.
Agent/Task, SendMessage, TaskOutput and TaskStop operate real child Sessions with their own context and tool state. Profiles constrain tool dispatch, models and maxTurns; foreground results and background continuation facts commit before delivery. Parent cancellation drains its owned tree. Goal uses durable target/iteration/verifier transitions and hidden tool-less verification; malformed verdicts stop automatic progress without falsely reporting success. See rust-subagents.md and rust-goal.md.
Session read omits workspaceIdentity for local path fallback and preserves distinct remote identity. Child lifecycle commits a paired subagent row anchored to its original Agent/Task call, including resume and cancellation; parent completion cannot finish a background child row. Cold recovery repairs legacy missing rows only with proven anchors and never replays child execution. See docs/specs/rust-app-session-projection.md.
History actions validate canonical identity plus revision/epoch, preserve frozen attachments and Goal intent, and isolate late runs. Fork child creation and its parent ACK are atomic. Write/Edit record byte checkpoints before mutation; content-addressed blobs preserve bytes and modes. Rewind validates expected hashes and uses a durable compensating journal across file/database commits. Turn headers and the fileChanges RPC expose summaries and on-demand patches. Shell writes and arbitrary external-tool writes are not tracked. Legacy rows without proven canonical boundaries cannot be rewritten by guessing text. See rust-history-actions.md.