Provider Runtime Notes
This document captures the practical runtime differences between Wardian's supported CLI providers: Antigravity, Claude, Codex, OpenCode, Pi, and Gemini (unmaintained). It is intended for maintainers working on spawn, resume, automation execution, skill projection, and status/approval handling.
Shared Wardian Invariants
Startup publishes the saved roster as Restoring placeholders and retains each agent's lifecycle claim until its provider or failure state is published. Settled but uncommitted Claude completions in <wardian-home>/agents/<agent-id>/claude/turn-verdicts/ are replayed under those existing claims before providers launch, including for saved Off agents. Replay must not reacquire a claimed lifecycle gate: doing so blocks startup before any provider can launch. Inbox completion IDs make replay idempotent; a prompt is recorded as committed only after its Inbox item is persisted.
- The Rust backend remains the source of truth for provider process lifecycle, session IDs, PTY ownership, and status telemetry.
- Every provider receives Wardian's
system_include_directories, which are resolved fromcommon,classes/<class>, andagents/<session_id>. - Headless execution and interactive execution use the same provider-specific assumptions where possible. Differences should stay explicit in
manager.rsinstead of being hidden in frontend state. - Provider-native instruction discovery matters more than Wardian's abstract model. The backend adapts Wardian's files and directories to each CLI instead of expecting the CLI to understand Wardian directly.
- Automation Agent nodes expose one run mode:
ephemeral,inherit_fresh, orinherit_resume. Provider resume flags are emitted only forinherit_resume. inherit_freshclones the selected agent's runtime configuration and scoped read context, but writes automation artifacts under an automation-run session ID and clears provider resume state.- Automation-spawned fresh runs skip interactive startup prompts. The automation node prompt is the first provider input.
- Regular visible agents use the global
Regular agent sessionssetting unless the agent config setssession_persistencetofreshorresume. The agent-leveldefaultvalue inherits the global setting. - The regular-agent context menu New Session action archives the closing provider log before forcing a fresh provider launch. It clears both the backend PTY output buffer and frontend terminal scrollback cache while retaining the Wardian agent, habitat, and saved history. If the archive cannot be updated safely, replacement stops and Wardian shows the failure detail. A repeated request while a New Session is running for the same agent is refused at once with an explanatory message, and every outcome (finished, failed, or refused) is written to
<wardian-home>/wardian_debug.log. - Claude provider-log archive ownership prefers the exact raw-line observation ID. A legacy alias can recover an older row only when no exact owner exists; multiple alias owners fail closed, preserving their distinct history rows and source positions instead of joining them by matching prompt text. Other providers retain their provider-specific alias reconciliation.
- Fresh Mock sessions retain their launch-owned session ID through registration so enabled conversation capture includes the first request. Prefix trust requires the matching runtime-only ID; restored sessions and disabled logging spans remain excluded from initial capture.
- Ordinary telemetry observations of eligible stopped agents schedule durable JSONL capture independently of the status parser's modification watermark. Background capture from telemetry, restored startup, and status changes shares an incarnation-bound coordinator; Chat reads retain one bounded pass and lifecycle boundaries retain their priority lane. Each background pass revalidates the agent, provider conversation, and opened source identity after acquiring the capture policy gate. Pending records, request roots, and incomplete sources keep their acquisition reasons. Errors and cancellation retry on a later eligible observation, while quiet serviced sources do not repeatedly drain. Disabled logging records its existing cutoff and excluded span, then suspends background draining until a later policy or source observation.
- Provider delivery profiles are responsible for translating Wardian input into the provider's native submit behavior, including short prompts, pasted multiline prompts, long prompts, slash-command-shaped text, and inputs that already end with a newline.
- Delivery recognizers must fail closed. If Wardian cannot recognize that a provider prompt is ready, that a paste bracket has settled, or that a command was submitted, it should avoid sending more input instead of guessing and corrupting the provider TUI state.
- Approval prompt state must be fresh. A stale recognizer hit, old transcript event, or previous terminal buffer line must not keep an agent in
action_requiredor trigger a delivery retry for a new turn. - On Windows, provider adapters should prefer direct native executables or a direct
node <script.js>launch resolved from an npm shim. Shell-wrap only when shell dispatch is required and no verified native launcher is available.
Native transport compatibility
OpenCode ACP starts with its own command arguments. Wardian applies the selected model through session/set_model and the configured agent through session/set_mode after binding either a new or resumed session; a rejected selection stops bootstrap before a prompt is submitted. Only ACP agent_message_chunk text contributes to the assistant answer. Thoughts, user echoes, and tool updates remain progress signals. Native answer chunks retain their whitespace, including whitespace-only chunks, so streaming preserves words and formatting. Codex app-server launch configuration may contain the saved model and reasoning-effort overrides. After a shared thread is bound, Wardian applies model and effort changes for future turns through the owned thread/settings/update operation as one pair. It requires both the request acknowledgement and a matching thread/settings/updated notification from the same thread before reporting the live update as applied. Its interactive --model flag does not establish the model used by an app-server thread.
The saved setting and the live runtime result remain separate. A saved null means that Codex should resolve its provider default. An unavailable default is persisted as deferred rather than replaced with a guessed catalog entry. The runtime result can be applied, deferred, failed, or unknown:
appliedmeans the shared runtime reported the requested future-turn pair.deferredmeans the saved pair awaits a usable runtime or authoritative default, or the update made no live change.failedmeans Codex rejected the settings operation.unknownmeans the request crossed the provider boundary without matching acknowledgement, or the bound runtime changed before the result was proven.
These results describe the settings operation and do not prove that a later completed provider turn used the pair. A provider replacement or other launch-only change still requires restart; Wardian does not interrupt the current provider process during a settings update.
Pi RPC streams typed text deltas and publishes the complete assistant message at message_end. Wardian replaces the accumulated answer for final messages and older full-message updates. User echoes, thinking, and tool events cannot become assistant answers. Bootstrap timeout diagnostics identify the protocol stage, such as initialize or thread/resume, without including the prompt. An unknown Codex resume identity remains a bootstrap error.
Quick Comparison
| Provider | Working root | Instruction file | Skill model | Session identity |
|---|---|---|---|---|
| Antigravity | Real target workspace | AGENTS.md | --add-dir roots expose Wardian context | Captured after the first real prompt |
| Claude | Real target workspace | AGENTS.md | .claude/skills points at Wardian's .agents/skills | Wardian assigns --session-id up front |
| Codex | Real target workspace via --cd; habitat-backed CODEX_HOME | AGENTS.md | Per-agent CODEX_HOME/skills under habitat | Fresh local rollout, then exact resume |
| OpenCode | Habitat command root; real workspace passed as a positional arg (interactive) or --dir (headless run) | AGENTS.md plus injected runtime config | Skills junctioned into the habitat .opencode config dir | Discovered from provider output (ses_…) |
| Pi | Real target workspace | AGENTS.md plus appended Wardian instruction files | Repeated --skill paths point at Wardian-managed skill roots | Wardian assigns --session-id up front |
| Gemini (unmaintained) | Projected habitat workspace for headless runs | Legacy GEMINI.md; not generated | Patched CLI can discover skills from include directories | Discovered from provider output |
Windows projected-habitat working directories
Interactive providers that use a projected habitat can receive a short Windows working-directory alias when their logical cwd exceeds the Windows path limit. The alias is an owned junction to the complete habitat, so the provider starts in the same instruction and skill ancestry. A workspace launch uses the corresponding h\workspace descendant; the provider arguments, configuration, archive identity, and Wardian records continue to use the logical habitat and workspace paths.
Alias allocation uses a private compact slot with an ownership record and identity checks. Preparation is serialized with the existing habitat lock. Removal happens after the provider process has been joined and removes only the exact owned junction and its slot records. A foreign, retargeted, or ambiguous slot is retained and reported as an error. Short paths and non-Windows launches keep their existing cwd behavior, and no insecure fallback is attempted when a safe alias cannot be published.
Antigravity
Model discovery
Wardian reads agy models from the installed CLI. Current releases return tab-separated model IDs and display names; both are preserved in the model picker. Progress messages are excluded. The selected ID is passed unchanged through --model for launches, including low-effort Flash variants. OpenCode's one-model-ID-per-line catalog remains supported by the shared parser.
Working-root model
Antigravity runs directly in the real target workspace. Wardian does not use a projected workspace for Antigravity.
Instruction and context discovery
- Antigravity reads
AGENTS.md. - Wardian passes common, class, and agent include roots as repeated
--add-dir <absolute-path>flags. - The provider adapter intentionally stays separate from Gemini even though Antigravity stores runtime files under
~/.gemini/antigravity-cli. - Hidden Wardian roots are projected through visible temp paths before they are passed to
agy. If a projected root contains.agents/skills, Wardian materializes that root and follows deployed skill links so Antigravity sees real skill directories instead of junctions or symlinks back into hidden storage. deploy_skillrefreshes live Antigravity projections after the canonical Wardian skill tree changes. The library skill watcher also refreshes projections after skill-file changes while it is active. Agent restart remains the full rebuild path for projections.
Session and telemetry behavior
- Visible launches use
agy --prompt-interactive. - Headless launches use
agy --print <prompt>. - Antigravity presents an interactive first-use folder-trust modal even with its tool-permission bypass flag. Wardian confirms only that exact startup modal once when managed permission bypass is enabled. This keeps Antigravity's normal workspace-to-conversation mapping intact for strict provider identity capture.
- Wardian-managed launches separately pass
--dangerously-skip-permissionsby default to auto-approve tool permission requests. Setdangerously_skip_permissionstofalseexplicitly to retain both folder-trust and per-tool operator approval prompts. - Resume launches pass
--conversation <conversation-id>. - Current Antigravity releases persist interactive user and assistant steps in
conversations/<conversation-id>.db; Wardian projects newly observed provider-authored rows into live watch state. The olderbrain/<conversation-id>/.system_generated/logs/transcript.jsonlremains a compatibility fallback. - New Session starts Antigravity fresh without sending a bootstrap prompt. Wardian first accepts Antigravity's changed workspace cache mapping when available. Antigravity 1.1.22 can leave that cache stale, so Wardian can instead bind the sole post-launch conversation DB whose provider-authored trajectory metadata contains the exact workspace URI. Ambiguous or pre-launch databases are rejected. The verified ID is then stored as
resume_session. - Until a real prompt creates that mapping, no provider identity exists to resume; a restart starts a fresh conversation again.
- Wardian verifies Antigravity's exact workspace-cache mapping against
conversation_metadata.json, then resumes that conversation with--conversation. A conversation explicitly detached by Clear is excluded from recovery. - Antigravity 1.1.7 and later persist interactive turns in
conversations/<conversation-id>.db; Wardian binds a known conversation's database for live status as soon as itsstepsschema exists, while Chat waits for a real user-message step before preferring it over the olderbrain/<conversation-id>/.system_generated/logs/transcript.jsonlfallback. Fresh identity discovery still requires exact post-launch workspace metadata and an unambiguous database candidate. Restored agents position the watch cursor after existing rows instead of replaying history as live output. - The Chat view also replays Wardian's durable conversation archive before the bounded live provider data, so already captured rows remain visible when a provider artifact is temporarily unavailable.
- SQLite Chat replay preserves the provider step source. Explicit user-message steps carry
human_input/requestprovenance and a stable request root; legacy user-message payloads without a source retain that fallback. A user-shaped step with an explicitly different source uses the System role andprovider_internal/internaland does not start a request.context_observation: "unreported"records that this decoder does not identify context-injection subtypes. Assistant messages retain their assistant role without input provenance. - SQLite Chat also projects planner tool calls and generic tool-result text from the layouts verified against paired Antigravity 1.1.27 SQLite/JSONL records. It retains the database log binding and existing message IDs, orders tools by provider step and call position, and identifies tool results with the Tool role. Completion alone does not establish success. Unknown step layouts are omitted; this does not establish coverage of every Antigravity tool type.
- The real-provider rendering audit uses a short exact marker prompt for Antigravity, submits it through Wardian's provider-aware prompt delivery path, and treats the post-clear respawn as marker-optional. This avoids mistaking echoed prompt text for the model response while still proving initial live rendering, resize, pause, and resume behavior.
Prompt delivery
- Antigravity's editor honors bracketed paste. Wardian wraps multiline prompts, and single-line prompts of 2048 bytes or more, in
ESC[200~…ESC[201~, then sends one carriage return as a separate write. Short single-line prompts keep the simple literal path. - This supersedes an earlier assumption that Antigravity did not support bracketed paste. That assumption made Wardian send long multiline prompts literally, so the editor treated the embedded newlines as submits and could retain the prompt unsent with no turn produced. A native protocol experiment against Antigravity 1.1.27 sent raw
ESC[200~ payload ESC[201~for a 6886-byte, 285-line prompt; the editor collapsed it into a single paste entry, and one carriage return produced a provider-native answer containing all three independent random labels placed at the payload's beginning, middle, and end. - The 500 ms submit settle delay is unchanged. The experiment's 267 ms editor-application time is one machine's measurement, not a guarantee, so delivery still depends on the existing bounded turn receipt and still fails closed with no automatic retry when that receipt does not arrive.
Practical implications
- Do not use Gemini's
--include-directories,--session-id, or stream output assumptions for Antigravity. - Empty stdout from
agy --printcan still be a successful run if the transcript contains the answer. - Auth or account polling warnings in Antigravity logs can be non-fatal; verify transcript output before declaring the run blocked.
Claude
Working-root model
Claude also runs directly in the real target workspace. Wardian does not use a projected workspace for Claude.
Session identity
- Fresh Claude spawns use an explicit Wardian-generated
--session-id. - This avoids a bootstrap phase just to discover the provider session ID.
- Resume launches use
--resume <session_id>and do not resend--session-idor--name. - Fresh resume of an existing Wardian agent uses a new transient Claude provider session ID while keeping the Wardian agent ID stable. After launch, Wardian stores the transient Claude ID as the next
resume_session.
Instruction and skill discovery
- Claude reads
AGENTS.mdnatively. - Wardian enables
CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1so Claude can discover instruction files from--add-dirroots. - Wardian generates only canonical
AGENTS.mdinstruction files. Home and habitat initialization retire exact legacy stubs and unchanged hash-marked Claude projections from real Wardian-managed roots. Customized files and linked files or directories are preserved. - Wardian also maintains
.claude/skills -> .agents/skillslinks where needed so provider-native skill discovery still works. - Wardian enables
CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN=1for Claude launches in Wardian-managed terminal surfaces so mobile and remote terminal scrollback remains native to xterm.
Approval handling
- Wardian-managed launches use Claude's
bypassPermissionsmode by default. Claude can present a separate first-run safety-consent selector for that mode; Wardian rejects the selector as readiness evidence and confirms only that exact startup modal once. Delivery remains queued until Claude's real compose surface is available and any visible/rc connecting…transition has completed. Set an explicit mode such asmanual,acceptEdits, orplanto opt back into provider approval behavior. - Claude permission requests are surfaced through a generated hook under
.wardian/agents/<session_id>/claude/. - The hook writes permission request events to a JSONL file that Wardian watches.
- If Claude appears stuck in approval state, inspect the hook output before changing status code.
Practical implications
- Claude depends heavily on the permission-hook path being writable and stable.
- Bugs here are usually about hook setup,
AGENTS.mddiscovery, or resume/session flags. - If mobile or remote drag scrolling fails only for Claude, verify that the managed launch environment still includes the alternate-screen opt-out before changing terminal gesture handling.
- On Windows, Claude may invoke both PowerShell and bash-family tool shells during one Wardian-managed session. Wardian therefore installs both
%USERPROFILE%\.wardian\bin\wardian.cmdand%USERPROFILE%\.wardian\bin\wardian, then prepends the active Wardianbindirectory to the managed provider process PATH. Verify shell parity from inside the managed runtime, not only from the parent Wardian process. - If
%USERPROFILE%\bin\wardianor%USERPROFILE%\bin\wardian.cmdis a Wardian-owned legacy launcher, the Windows installer rewrites it to forward to the active%USERPROFILE%\.wardian\bin\wardian-cli.exe. This protects Claude bash tool shells that prepend~/binahead of the inherited provider PATH. - Windows manual smoke:
powershell -NoProfile -Command "wardian --version"
bash -lc "wardian --version"Headless result output
Claude's verbose JSON output can contain an event array ending in a result. Wardian extracts that terminal answer and its provider session ID before passing the answer to automation. Intermediate messages remain diagnostic data. Existing single-object responses are also supported. Missing, ambiguous or unsuccessful terminal results fail the task instead of exposing event data as a successful answer.
Codex
Chat history
- Startup host-context records do not start work or increment the query count. The status parser and Chat share the native content-kind classifier; explicit user content, legacy string prompts, and canonical user events retain their normal activity behavior. See #1249.
- Codex emits a lightweight
agent_messageand a completedresponse_itemfor the same visible assistant response. The completed record can append an internal<oai-mem-citation>block. Wardian removes that block before storing or rendering the message, and applies the same normalization while replaying older archived rows, so one user-visible answer appears once. - For a same-turn final assistant mirror, Wardian coalesces the identityless
event_msgand identifiedresponse_itemin the chat projection while retaining both raw provider observation IDs. - A Codex
event_msg.user_messagemirror ID includes its nativeprovider_turn_id. If turn context is missing, the ID uses the persisted provider-log line sequence; that mirror stays separate from other text-identical requests and from the response-item projection. New mirrors do not carry the old text-derived ID as an alias, because that alias can name a different archived turn. Existing archive rows and their narrative references remain intact. - Wardian memory rows are filtered to the active conversation boundary before they are merged into Chat, then receive the same chronological sequence assignment as provider and watch events. Agent-wide memory history must not be replayed into a later conversation.
Working-root model
Codex must run with the real project workspace as its effective working root. Wardian now enforces this by passing --cd <real workspace> for interactive spawn, headless resume, and bootstrap session creation.
Normal managed provider-home preparation, periodic index publication, and rollout lookup use the Wardian application's standard CODEX_HOME when it names an existing directory. Wardian canonicalizes that explicit upstream directory. An unset or empty value retains the native user profile's .codex default. An invalid explicit home fails preparation and index publication; rollout lookup returns no source instead of falling back to the user profile.
Wardian still keeps Codex state in a per-agent habitat:
- final agent home:
.wardian/agents/<wardian-agent-id>/habitat/.codex - legacy fallback bootstrap home:
.wardian/provider-bootstrap/codex/session-*/.codex
During normal managed startup, each provider child receives its own managed home as CODEX_HOME. Its active and archived session links and Wardian's central index, history, and lock writes use the same application-selected upstream home. With an explicit home, Wardian checks both existing session links before migrating either namespace. A foreign link or projection failure stops preparation. The default home retains optional projection and its existing local-tree fallback.
The unchanged legacy bootstrap fallback still imports from the native user profile's .codex home. The explicit-upstream guarantee covers the normal managed preparation, periodic publication, and rollout lookup paths described above.
The critical rule is: trust should bind to the real workspace, not to the bootstrap directory or habitat path.
Skill discovery model
Codex does not treat --add-dir as a skill-discovery mechanism. Wardian therefore projects assigned skills into the agent-specific CODEX_HOME/skills tree.
Current model:
- shared Codex files copied into each agent home:
auth.jsoncap_sid
- the user's
config.tomlis a managed base, not a shared home. Wardian reconciles missing base policy values into the agent's ownconfig.tomland preserves agent model choices, project trust, and local overrides. - Codex projects
sessions/**andarchived_sessions/**from the native Codex home into each agent home through a directory link (a Windows junction on Windows). Existing local rollouts are copied first without changing their filenames. If link creation fails, that namespace's local tree is restored and the provider continues with the available roots. Active and archived files retain their original identity and metadata; archives are not converted to active sessions. - Telemetry resolves the shared Codex session catalog from an explicit absolute
CODEX_HOMEwhen the Wardian process has one. Without that override, it uses the native user profile's.codexhome. - The provider writes agent-local
history.jsonlandsession_index.jsonl. Wardian is the sole writer to the central copies: it publishes complete, validated records under a cross-process lock, atomically republishes the complete central file, repairs invalid central tails, and de-duplicates repeat observations. - Codex SQLite databases such as
state_5.sqlite*andlogs_2.sqlite*remain per-agent because SQLite journal/WAL files are path-sensitive and are never shared or hardlinked. Runtime logs, databases, and temporary files remain local, except for the provider-owned marketplace catalogs and plugin cache directories explicitly projected below. auth.jsonandcap_sidflow only from the native Codex home into an agent home; they are never copied back outward.- Codex runtime directories such as
log, temp, and generated database files remain per-agent. The provider plugin cache is the explicit exception described below. - On Windows, Codex elevated sandbox support is treated separately from session state:
.sandbox-secretsand.sandbox-binare projected from the user's Codex home so every Wardian-created Codex home sees the same elevated sandbox credentials and helpers..sandbox/setup_marker.jsonis copied when present so a new projected home can observe completed setup..sandboxitself is not projected. Runtime files such assandbox.logandsetup_error.jsonstay local to the agent or bootstrap home.
- Codex system skills remain under
CODEX_HOME/skills/.system - Wardian-assigned skills are projected into
CODEX_HOME/skills/<skill-name> - Native Codex marketplace catalogs under
.tmp/bundled-marketplacesand.tmp/plugins, plusplugins/cache, are projected into each agent home as directory links. These are provider-owned implementation assets, not shared agent state.
This preserves per-agent skill scope without forcing the project repo itself to hold agent-specific skill directories.
Plugin pass-through and diagnostics
Plugin enablement and agent-specific configuration remain entirely in each agent's CODEX_HOME. Wardian does not apply a class allowlist, does not alter installed or enabled plugin state, and does not pass global plugin/app disable flags. The native marketplace catalogs and plugin implementation cache are projected as provider-owned directory links so every agent can resolve the same current plugin surface without copying agent databases. Agent-local configuration is reconciled with current marketplace and MCP runtime records; agent-only MCP entries remain intact. A configuration or plugin change needs a new Codex session because an existing thread has a fixed tool list.
Use the provider-neutral control surface to inspect effective state without reading sensitive Codex files:
wardian agent doctor <agent-name-or-uuid>The response includes the effective home path, installed/enabled plugins read from that home through the provider-resolved Codex executable, and launch feature flags. It never changes plugin state.
Session identity and bootstrap
Codex fresh-session materialization does not require a model bootstrap turn.
Current sequence:
- Create or update the agent's projected
CODEX_HOMEunder.wardian/agents/<wardian-agent-id>/habitat/.codex. - Generate a distinct provider UUID and write a minimal
session_metarollout atsessions/<year>/<month>/<day>/rollout-<timestamp>-<provider-id>.jsonl. - Validate that Codex resolves the rollout from that same projected home.
- Start the Wardian-owned local daemon without loading a thread. The ordinary TUI resumes the selected provider UUID under the same home and real workspace. Wardian requires that exact thread to appear in the owned daemon before subscribing and enabling peer delivery. No model bootstrap turn is required.
App-server startup stderr
The owner drains app-server stderr from spawn onward and retains at most 16 KiB until startup completes. It adds only sanitized text, capped at 1,024 characters plus an optional three-character ellipsis when shortened, to the startup timing log. Credential-like environment values, token-shaped text, private paths, invalid text, and truncated captures are replaced with fixed omission markers. Raw provider stderr is never written to general logs. After successful startup the owner keeps draining and discards later stderr. Startup failure and owner shutdown terminate the process tree, then wait up to 500 ms for stderr EOF before aborting the reader if a descendant still holds the pipe open.
The startup timing line also carries socket_wait_resources. On Windows the owner retains a query-only duplicate of its daemon's process handle and samples CPU time and I/O counters around the socket wait. It logs elapsed sampling time, kernel and user CPU time in microseconds, and read/write/other operation and byte deltas. An unavailable query, changed identity, counter rollback, or unsupported platform produces null. Sampling preserves the original socket deadline and does not retry provider work. These are process accounting counters, including device and pipe I/O, not disk throughput or a backfill phase diagnosis. They exclude descendant processes and contain no paths or provider text.
The ordinary TUI startup overlay also sets check_for_update_on_startup = false so an interactive update notice cannot block attachment. The guarded journal restores the prior setting after the TUI has attached and the selected model and policy have been verified.
Legacy bootstrap migration remains available as a fallback when local rollout materialization is unavailable. It merges a new rollout into an existing projected sessions/** tree instead of discarding it.
For a fresh visible Codex launch, Wardian publishes the same PTY-backed runtime to the roster provisionally before native app-server attachment finishes. The terminal remains available for human input, while provider input readiness stays Booting, so automatic delivery waits for the attachment finalizer. The finalizer checks the exact runtime generation, persists the discovered provider identity, then promotes readiness to Ready. Attachment cancellation or a stale generation disposes the exact native owner and removes only the current provisional roster entry; a durable-state failure leaves that entry visible in Error behind the retained stop fence. Restored agents and other providers keep their existing synchronous publication path. A restored Codex runtime replaces its Restoring placeholder only after owner attachment succeeds. That publication reconciles the latest accepted status transition for that exact attached runtime, including Off, Error, or Action Needed reserved before publication. With no accepted transition, the attached runtime becomes Idle. A missing attachment, missing runtime generation, or already committed terminal state does not become Idle. Queued status changes reserve an intent revision for the exact runtime status Arc after attachment admission. A newer accepted intent supersedes an older queue entry; rejected attempts do not. Value commits have a separate revision so a same-value status report does not invalidate an already staged observation.
Private thread indexes
Codex owns each agent home's private thread database. Wardian projects both active and archived rollout roots but does not clone thread databases between homes. The former shared-index optimization published snapshots under <wardian-home>/codex/thread-state/; the runtime no longer reads or writes those snapshots. Existing cache files and private provider databases are left intact.
The optimization was removed because the SDK does not provide a supported checkpoint that certifies shared snapshot metadata against current rollout contents. Any future cloning implementation requires that coherence contract.
A new home lets the SDK build its own index through normal startup. Existing provider-owned indexes remain unchanged. This correctness change does not establish the cold-start latency target; the startup performance work remains open and requires measurement of the normal path.
Source lookup checks the original session metadata ID in both rollout roots. Archived discovery preserves archive classification and retains the SDK's normal unarchive prerequisite before resume.
If Codex starts asking for trust every launch again, first verify that the session was born with the real workspace as cwd, not the bootstrap path.
Wardian also exposes an off-by-default global Trust launch workspaces Codex runtime setting. When enabled, Wardian passes a launch-scoped config override for the agent workspace:
codex -c 'projects."<absolute-agent-workspace-path>".trust_level="trusted"'PowerShell:
codex -c 'projects."<absolute-agent-workspace-path>".trust_level="trusted"'This uses Codex's project trust table without editing the user's global Codex config file. Keep it separate from Codex autonomous mode: autonomous mode bypasses approvals and sandboxing, while workspace trust only marks the launch folder as trusted.
Approval and status handling
Wardian's Codex approval setting is shared by global runtime defaults and explicit per-agent overrides. on-request, untrusted, and never are passed through --ask-for-approval; Approve for me is translated to Codex's --approve-for-me flag, which selects the workspace-write sandbox and automatic review. It is never passed as an argument value to --ask-for-approval.
Shared local Codex launches capture effective policy once for their runtime generation. The ordinary TUI receives direct --sandbox and --ask-for-approval choices, or the equivalent bypass flag, before its first thread load. Cold background starts and resumes send the same captured policy as explicit per-thread parameters alongside model and reasoning effort. Supported policies explicitly capture the stock user approval-review route. Both cold background requests carry that reviewer. The ordinary TUI has no direct reviewer override. Initial saved resumes therefore prepare the captured policy in the fresh private daemon before starting a TUI or registering its PTY/input runtime. This preparatory resume alone omits the reviewer to inspect the saved route. Saved user needs no setter; saved auto_review requires a typed thread/settings/update to user, its applied notification and strict policy readback. Other saved reviewers and busy or mismatched threads reject. This checkpoint requires the initialized stable CLI version to be at least 0.160.0, the qualified compatibility floor for this path. Earlier or unknown versions reject before its preload/settings requests. Fresh, background and ready warm paths retain the existing shared-version floor; eligibility alone does not establish provider acceptance.
The owned startup overlay sets stock thread_unload_delay_secs=0 before daemon start. Preparation unsubscribes, requires the actual matching thread/closed event and verifies an empty loaded set before ordinary TUI cold resume of the same native ID. An unsubscribe acknowledgement is insufficient. Closure alone does not prove successful persistence: stock rejects a retained live writer on new cold load, and final attachment still requires the persisted user policy. The checkpoint shares the initialization deadline and records its elapsed time; it adds no model turn or input replay. Fresh sessions and ready warm rejoins retain their existing paths. Overlay recovery restores the prior unload value. Attachment still rejects missing or mismatched effective policy and unavailable direct-input capability. Rejoining a loaded TUI does not rewrite its policy. Rejection diagnostics report only whitelisted policy words.
The shared local path currently rejects a non-user approval reviewer, including Approve for me, before owner home preparation or process launch. Its stock preset produces config overrides that prevent ordinary local-daemon adoption, and equivalence between Wardian's guardian_subagent setting and stock automatic review has not been established. The standalone codex exec adapter retains its preset translation.
Codex emits several different event shapes across live PTY output and persisted session logs.
Wardian treats these as the important lifecycle markers:
thread.started: session identity availableturn.started: processing beginsexec_approval_requestor escalatedfunction_call: action requiredexec_command_begin,exec_command_start,function_call_output: processing resumes after approvaltask_complete/turn.completed: idle
Codex commentary events like agent_message should not be used as hard status transitions.
The shared app-server event reader observes turns started through either the TUI or Wardian. A named Wardian inbox output without an originating model call is non-waking context; it must not mark an idle agent as processing. V2 peer information, follow-up work, and interruption use native WebSocket operations through a private local socket and the configured Codex executable's app-server proxy tunnel. MCP exposes the model-facing tools and explicit receiver. See agent messaging tools.
After an unclean exit, startup recovers an abandoned default socket only when a nonblocking connection is refused. It holds Codex's native startup lock, checks that the entry is a Unix socket, and verifies its filesystem identity before removal. Live listeners, busy startup locks, and ambiguous connection results remain untouched and produce a startup error.
Owner attachment requests thread metadata and live input capability without hydrating archived turns, so large conversations do not exceed the control transport's frame limit. The ordinary TUI still owns history display. Its launch explicitly selects the configured workspace with --cd, avoiding Codex's resume directory picker when a saved conversation records another working directory. Wardian does not force Codex's terminal screen mode with a CLI flag. When agent-local [tui].fullscreen_transcript is absent and alternate screen is allowed, home projection defaults it to true. Explicit false and alternate_screen = "never" are retained. /tui changes the preference for the next provider launch. Fullscreen uses Codex's own scrollable transcript, while inline mode uses xterm scrollback. Attachment failures retain the transport reason and a bounded terminal tail.
On Windows, replacing or removing a launch configuration file can briefly fail when another reader did not allow delete sharing. Wardian retries these atomic operations for at most 200 ms, rechecking the destination and any paired journal before each attempt. Persistent denial still fails startup and retains recovery evidence. Observed file changes stop publication; file permissions are unchanged. The error identifies the file operation and whether it involved config.toml or the launch journal, without exposing the private home path.
For long canonical homes, owner startup recovers pending launch settings and then prepares a private compact physical home before config/MCP projection. The logical habitat path remains an owned directory link. Matching agent and slot records authorize that link; arbitrary links remain invalid. A separate preparation lock fences migration against ordinary refresh and index writes. Refresh resolves completed mappings without moving homes or recovering a live startup overlay. The provider executable, normal TUI invocation and shell HOME are unchanged. See the removal criteria.
On Windows, managed Codex MCP publication uses the existing parent's canonical verbatim path for both the temporary file and destination, including before compact-home mapping. It resolves only the parent. Configuration is written before ownership; interrupted publication leaves an unowned entry that retries preserve as a collision. See the regression.
Managed Codex registration uses the staged wardian-mcp.exe on Windows. This GUI-subsystem launcher starts wardian-cli.exe mcp serve with CREATE_NO_WINDOW, preserving the MCP stdio handles while avoiding a console window. A Windows job object ties the server lifetime to the registered launcher. The normal wardian-cli.exe keeps its console subsystem and remains the target of interactive CLI launchers.
Codex compact-home ownership records and interactive launch configuration and journal files use the same parent-only canonicalization for long paths. Their ownership, link, and compare-before-publish checks continue to use the caller's logical destination; the destination leaf is never canonicalized. See the regression.
Known operational edge cases
- Codex skill discovery can be correct while shell execution is still blocked by the CLI sandbox. In that case, the agent sees the skill but fails when the skill tries to invoke shell tools.
- On Windows, those failures may surface as
CreateProcessAsUserW failed: 5or setup-helper launch errors. - When debugging Codex, separate these questions explicitly:
- Did Codex discover the skill?
- Did Codex trust the workspace?
- Did Codex succeed in spawning a shell command under its sandbox?
OpenCode
Working-root model
OpenCode uses a Wardian habitat as the provider command root when projected context is available. The real target workspace is passed explicitly: as a positional directory argument for interactive TUI launches, and as --dir for headless opencode run invocations.
Instruction and skill discovery
- OpenCode reads
AGENTS.mdnatively when it exists in the working tree. - Wardian writes a runtime config file to
<habitat>/.opencode/opencode.jsonand points OpenCode at it throughOPENCODE_CONFIG(plusOPENCODE_CONFIG_DIR). - That injected config adds extra
AGENTS.mdfiles from Wardian include roots toinstructions. - Skills from Wardian include roots are junctioned into the config dir's
skills/folder. There is noskills.pathsconfig key: OpenCode 1.4.3 dropped it, so Wardian omits anyskillskey entirely.
This is how OpenCode sees Wardian-managed class and agent context without forcing those files into the user repository.
Session identity
- OpenCode session IDs are discovered from JSON output during
opencode run --format json. For interactive TUI launches, Wardian binds the provider'screatedlog record to the same OpenCode run that loaded this agent's generated.opencode/opencode.json; if that ownership evidence is absent or ambiguous, Wardian leaves the session identity unset rather than adopting a global session-list match. - Fresh interactive OpenCode launches reserve an authenticated loopback HTTP owner before the TUI starts. After one unique launch-scoped
ses_...identity is discovered, Wardian rebinds that same listener to the provider session only when the process, runtime generation, workspace, and configuration fingerprint still match. A missing or ambiguous identity fails closed and does not select a global or latest session. - Resumed interactive OpenCode launches bind the configured
ses_...identity during startup. Fresh and resumed launches therefore share the same native owner checks, while a fresh first turn remains the operation that creates the provider session; the listener reservation does not submit a bootstrap prompt. - Interactive identity discovery also runs when the title remains idle. It checks log metadata between polls and reads the ownership evidence only when the source changes; a Processing title is not required to resume or link an already completed conversation.
- Valid IDs match
ses_…; Wardian never substitutes its own UUIDs into--session. - Resume uses
--session <session_id>. - Wardian resolves OpenCode's database and rolling log from
XDG_DATA_HOMEfirst, then the platform data directories. This matters on Windows as well: OpenCode honors the XDG override, so using only%LOCALAPPDATA%can associate a session with another installation's transcript. - Once that launch-scoped identity is available, transcript refresh reads OpenCode's SQLite
message/partrows and retains the database path assource_pathprovenance metadata; later rows are visible on the next refresh without restarting the agent. - SQLite tool parts expose native call IDs, names, structured input, and completed or failed output. Pending parts do not create placeholder results. Tool output has the Tool role; tool events never create human prompts. Tool IDs remain stable across refreshes, and adding previously omitted tools preserves existing text-event IDs. The retained fixture covers OpenCode 1.18.29; unknown states and conflicting native identities are omitted.
Headless prompt input
Wardian sends the complete headless OpenCode prompt as UTF-8 on stdin, then closes the pipe to signal EOF. It supplies no positional message: OpenCode's run parser reconstructs positional messages with literal quotes. Whitespace, line endings, quotes, backslashes, and Unicode therefore remain part of the original prompt. Model, agent, session, output-format, and directory flags still use the ordinary argument path.
The execution deadline and conversation-lease heartbeat also cover blocked stdin writes. Failed or cancelled delivery terminates the owned process tree; a failed write can represent partial delivery and is never automatically retried by this transport. OpenCode failure errors retain the exit code but omit raw provider stderr, which can echo private input. Input fidelity does not guarantee that the provider's answer satisfies the requested task.
Practical implications
- OpenCode is closer to Gemini than Codex on workspace handling: Wardian launches from the habitat command root while passing the real repo as the project directory.
- OpenCode is closer to Codex than Gemini on instruction naming: it consumes
AGENTS.mddirectly. - If OpenCode stops seeing Wardian skills or class instructions, inspect the generated
<habitat>/.opencode/opencode.json(OPENCODE_CONFIG) first, then verify the junctionedskills/entries resolve. - Interactive startup remains Starting until the current runtime's canonical screen shows the OpenCode composer and command footer without a loading or consent screen. A generic window title or an older completion in the rolling log cannot release queued input. Later screen repaints can establish readiness without another title event. Once startup completes, TUI window titles report turn status ("OpenCode" idle, "OC | …" processing), while token/cost telemetry comes from OpenCode's shared SQLite store via wardian-core. Composer readiness authorizes submission; provider acceptance still requires the delivery transport's receipt.
- OpenCode turn receipts for Wardian-delivered prompts come only from a new, session-bound user text part in OpenCode's read-only SQLite store. Title changes, including restored and approval titles, are status evidence rather than acceptance receipts.
- The chat-log link and transcript source must resolve from the same provider data root as the running OpenCode process. If an isolated harness sets
XDG_DATA_HOME, keep that setting for the Wardian process that reads telemetry and chat history as well. - TUI "Permission required" prompts never appear in the window title. Wardian detects them from the provider log (
message=asking id=per_…) and raises Action Needed; the ask is attributed to a session only while its prompt loop is the sole open loop in the log, and clears once loop activity resumes after the prompt is answered. - On Windows, Wardian resolves the
opencodecommand from PATH. When an npm-generated.cmdor.ps1shim points at the package's top-level native launcher, Wardian launches that executable directly so the provider does not acquire an avoidable configured-shell wrapper. Unrecognized or incomplete shims still run through the configured shell, preserving compatibility with the PATH command behavior described in #568.
Pi
Working-root and identity model
Pi normally starts in the real workspace. On resume, Wardian uses the saved session header's project directory when it matches either the real workspace or that agent's habitat workspace; switching between those two paths can make Pi prompt to fork even when they resolve to the same files. On Windows, a fresh workspace path beyond the PTY launch limit starts through the habitat projection (or its validated short alias) and retains that identity on resume. A saved project path beyond that limit cannot be restored through a shorter alias without changing Pi's project identity, so Wardian reports the limitation. Wardian passes a private --session-dir under the Wardian agent directory, assigns a distinct UUID with --session-id, and uses --session for exact resume. Pi writes the JSONL lazily after its first persisted entry, so discovery validates the session header ID and never falls back to the newest file.
Instruction and skill model
Pi discovers the workspace's parent/project AGENTS.md chain itself. Wardian adds common, class, and agent instructions with repeated --append-system-prompt <absolute-file-path> arguments and their Agent Skills directories with repeated --skill <absolute-directory-path> arguments. Do not redirect PI_CODING_AGENT_DIR: it would replace the user's authentication, packages, extensions, settings, and themes rather than isolate only sessions.
Output and status model
The interactive regular TUI stays attached to the PTY. Wardian tails the version 3 session JSONL for user messages, assistant stop reasons, tool calls, tool results, and definitive completion. Headless automations use --mode json; agent_end, not a transient assistant message, is the final automation boundary.
Startup readiness uses the current generation's visible terminal frame. The Pi footer may wrap or truncate a long workspace path; readiness still requires its workspace and model context. Loading, error, and stale historical frames cannot release queued input.
For resumed TUI sessions with the Wardian messaging bridge, startup conversation exclusion also waits for the authenticated bridge handshake. Its 35-second overall startup budget starts at the first registration of the actual child and ends at authenticated readiness. Preparing the listener does not arm this timer. HELLO and READY each retain a five-second maximum, capped by the remaining overall budget; connection, welcome writing, and other authentication awaits cannot extend that budget. Repeated registration cannot change the child or reset its deadline. Cancellation and failed spawn dispose an unregistered listener promptly. This allowance is a bounded startup policy, not a measured provider performance guarantee. A terminal bridge failure transfers the exact runtime into retained child cleanup. Successful termination requests and terminal EOF do not prove exit. Wardian publishes Error and releases the exact lease acquisition only after observing every captured child exit. Failed or uncertain cleanup publishes Action Needed and keeps the retained runtime fence and lease; later exit observation may complete cleanup without another termination request. Cancelling a startup observer does not cancel cleanup. Saved session identity and history are preserved.
Pi's --approve and --no-approve flags control project-local configuration, extensions, and skills. They do not sandbox the shell tool or extensions.
Gemini (Unmaintained)
Unmaintained. Consumer/free Gemini CLI access ended June 18, 2026. Use Antigravity for Google-model access — it is the preferred replacement.
Working-root model
Gemini headless runs use a projected habitat workspace so shared, class, and agent instructions and skills can be materialized outside Wardian's hidden state tree. The real target workspace remains the author-facing workspace, but the provider process runs from the habitat workspace path during headless execution.
Instruction and skill discovery
- Gemini's legacy contract reads
GEMINI.md, but Wardian no longer generates it. - Wardian passes include roots through
--include-directories. - Skill discovery depends on Wardian's Gemini patching flow; see Gemini CLI Patches.
- If Gemini stops seeing Wardian-managed skills, check the patched CLI bundle before changing spawn logic.
Session and telemetry behavior
- Gemini session identity is learned from provider output rather than assigned before launch.
- Wardian parses Gemini JSON events into
Init,UserQuery,Generating, andTurnCompletedstates.
Practical implications
- Gemini regressions are usually about habitat projection, CLI patch drift, include-directory handling, or event parsing.
Choosing Where to Debug
When provider behavior breaks, start with the provider-specific seam instead of the generic agent UI.
- Antigravity problems: inspect visible include-root projections, the exact workspace conversation mapping, and the conversation transcript.
- Claude problems: inspect
AGENTS.mddiscovery, permission hooks, and explicit session flags. - Codex problems: inspect
CODEX_HOME,--cd, bootstrap migration, and sandbox approval transitions. - OpenCode problems: inspect the generated
OPENCODE_CONFIGfile, junctioned skills, real-workspace directory argument, andses_…session discovery. - Pi problems: inspect appended system-prompt and skill arguments, the private session directory, and the exact Pi session ID and JSONL boundary.
- Gemini problems (unmaintained): inspect patching, include directories, and JSON event parsing.