<!-- Compact maintainer rules retain the repository instruction line budget. -->
<!-- markdownlint-disable MD013 -->
# vtcode (binary)
[Root AGENTS.md](../AGENTS.md) | CLI entrypoint, session bootstrap, and agent runloop wiring. Detailed notes: [binary gotchas](../docs/development/vtcode-binary-gotchas.md), [session orchestration ownership](../docs/development/session-orchestration-ownership.md), and [Copilot runtime ownership](../docs/development/copilot-runtime-ownership.md). Keep thread/archive preparation and config-reload polling in the existing bootstrap helper and activation/checkpoint/event ownership in the loop; observed state returns full host snapshots and PTY deltas, while progress/status mapping stays caller-owned.
## Modules (active bridge: `agent/runloop/unified/webmcp.rs`)
`main.rs` binary entry | `agent/` runloop + subagent dispatch | `cli/` handlers including opt-in WebMCP serving | `startup/` onboarding | `updater/` downloads and self-replacement | `codex_app_server/` bridge | `main_helpers/` tracing and runtime init | `allocator.rs` global allocator selection | `process_hardening.rs` early-process lockdown | `agent/runloop/unified/planning_workflow/tracker_response.rs` model-facing path boundary | `agent/runloop/unified/turn/session/interaction_loop_runner/status_refresh.rs` status/IDE/title cadence | `agent/runloop/unified/session_setup/hook_approval.rs` workspace lifecycle-hook approval overlay
## Rules
- Verifier responses require a terminal exit code before clearing the gate; track the exact live session, reuse it across internal turns, and never count rejected or running calls as verification failures. Completed checkers in unverified shell sequences get one advisory per turn even without a pending gate; diagnostic detection never grants permissions, verification credit, or repair edits; retain separate checker session identities through polling and discard them on completion, loss, or cleanup. Returned-text evidence drives navigation progress across queries/ranges; concentrated-search coaching is not a cap, and mutations invalidate evidence without resetting notice latches.
- Keep the binary thin; runtime logic belongs in `vtcode-core`. Build `LifecycleHookEngine` with `new_with_session_gated`, passing `workspace_gated = vt_cfg.workspace_lifecycle_hooks` non-empty OR `active_primary_agent.contributes_workspace_controlled_hooks()`; the approval overlay lives in `session_setup/hook_approval.rs`. See the binary gotchas guide for WebMCP bridge and prompt-boundary details. Spool preview generation and shell activity classification belong to `vtcode-core`; the binary only serializes the typed reference. Internal harness follow-ups preserve recovery episode budgets; never classify them as fresh user requests. Tracker retry resets require a new completed-count high-water mark. Preview limits apply per result, never as an aggregate exhaustion gate; history compaction bounds accumulation. Fresh turns supersede historical preview/tool-free guidance once per recovery episode without rewriting replayed history or relaxing current gates. `mimalloc` is the default allocator; `allocator-jemalloc` opts into `tikv-jemalloc`. Measure with `vtcode bench-allocator` before changing it; see the [allocator guide](../docs/development/ALLOCATOR_MEMORY.md). Install `vtcode_ui::tui::panic_hook` before producing output. `agent/runloop/` is the single-agent loop; `unified/turn/session_loop_runner/mod.rs` is its facade and `orchestration/` owns the loop body (`mod.rs`; `session_bootstrap.rs` holds bootstrap helpers and completion-status resolution; `session_teardown.rs` owns bounded drains, completed-artifact cleanup, persistent-memory finalization, and subagent shutdown). `session_loop_runner/blocked_handoff.rs` owns forced blocked-turn archive checkpoints and verified resume handoffs; `session_loop_runner/background_completion.rs` owns bounded completion queuing, raw exec-session fan-in, and idle-only continuation. Completion delivery refreshes Local Agents immediately so terminal status, retained output, and shimmer agree before the follow-up boundary; direct-command suppression must use the exact task/session identity, never a sticky or whole-drain flag that can consume unrelated completions. `session_loop_runner/harness.rs` opens and closes canonical session persistence with shared one-shot finalization; unexpected exits must emit terminal lifecycle events before draining. Keep status-line command input, persistence, preview, and action policy behind the private child modules of `turn/session/slash_commands/ui/statusline.rs`; workspace-local execution summaries use the shared relative-path formatter. Quiet blocked handoffs must preserve verified checkpoint resume metadata. Keep transcript/modal editor opens on the bounded runtime coordinator, not queued `/edit` submissions. Keep `/secret` provider/key validation and storage selection, including gateway providers, behind `turn/session/slash_commands/secrets/storage.rs` and the central resolver. Parallel calls consume streamed invocation IDs and emit canonical invocation/output completions inside execution futures, including interruption drains. Route batched tool metrics through the shared execution helper so every invocation emits exactly one terminal outcome observation, including fallible post-processing paths; checkpoint diagnostics use canonical `Usage` plus saturating per-turn counters.
- Tracker rows, panel metadata, and rendering live in `agent/runloop/tool_output/tracker.rs`; `tool_output/mod.rs` owns dispatch and the shared Git-diff payload predicate. Keep PTY status handoff during stream shutdown separate from output rendering; preserve complete current-session tool output for the fullscreen `Ctrl+T` viewer, group only contiguous successful command activity in compact presentation, suppress transient PTY rows in compact mode while retaining expanded live previews, label distinct pipe streams, and keep bounded queue-pressure diagnostics visible without duplicate aliases. Ctrl+B is wired through the UI action callback to reserve foreground-session promotion in the core registry before the async wait loop.
- `agent/runloop/unified/turn/compaction/` delegates to `vtcode-core::compaction`; reserve segment boundaries with the shared transition helper and preserve native providers' canonical replay windows when persisting envelopes.
- Updates own asset selection, checksum verification, safe extraction, and `self_replace`; TUI installs thread `UpdateProgress` callbacks through `install_update_reported` for real-time download/extract feedback; `main_helpers` owns relaunch context, pre-config legacy migration, and runtime initialization.
- Centralize provider-noise sanitization in `turn::provider_noise` and `stream_sanitization::StreamSanitizer`.
- Preserve prompt-section ordering and wire-tool shaping invariants; see the detailed guide before changing request assembly. Keep clean request and continuation history Arc-shared; route shaping of persisted editor/few-shot context (`llm_request/request_context.rs`) and provider compaction are intentional copy boundaries. Request context is persisted once at a user-turn boundary and never rewritten afterwards, so every request only appends to the previous one. The tool-free recovery contract rides as a request-only tail-position turn-scoped system message (`recovery_mode_directive` pushed in `build_turn_request`) — never appended to the system prompt, never persisted — so recovery dispatches keep the cached prefix byte-identical.
- Approved-plan turns apply one bounded internal loop allowance at initialization and schedule implementation through an explicit internal next-turn trigger; auto-permission probe warnings stay queued until the assistant tool batch is complete, then flush before recovery directives.
- Plan approval carries one typed destination/policy/context target through current and fresh handoffs; ordinary approval selects Build, explicit Auto or configured full-auto selects Auto, and queued mode input must not overwrite it.
- Preserve planning recovery, approval, interview, and budget-synthesis invariants; use the shared `ThreadEvent` contract for telemetry. Ordinary loop-limit refusal gets one tool-free synthesis pass, while the absolute hard cap remains terminal. Failed synthesis preserves the runtime budget cause and limit in the final response and blocked outcome. Planning gets one deterministic canonical-plan synthesis after two empty responses; failures remain resumable and must not request more input or advertise implementation without a validated persisted plan. Optional event exporters are best effort and must not prevent canonical finalization.
- The model picker must derive custom-provider metadata from exact profiles while keeping `model`/`models` as the availability allowlist. `/model` also works mid-turn (reasoning effort rides along in the picker selection): `session_setup/ui/active_settings.rs` sends `SessionSettingsControl` (`unified/session_settings.rs`) and `turn/turn_loop/settings.rs` applies it at the next request boundary; the in-flight request keeps its original settings.
- Natural-language persistent-memory saves are handled before the current prompt is appended; `remember it`/`this`/`that` may use only the latest non-empty assistant answer, never tool output or an older conversation window, and still require planner validation plus inline confirmation.
- Ordinary completed turns must publish a non-empty final response through both renderer and harness paths; the approved-plan handoff is the explicit control-flow exception because its outer loop creates the implementation turn. Blocked recovery remains visible. Completed planning turns clear live activity even while Plan remains selected. Legacy `preview_budget_exhausted` markers count diagnostics once per tool-call id without gating tools; balancer recovery must preserve anti-blind mutation and verification state. Transient post-tool follow-up failure compacts the older prefix, while context-capacity recovery is recognized only at `execute_llm_request`, then permits one tool-enabled retry before a resumable blocked handoff. Async checkpointing acknowledges consumed steering intents only after a `Persisted` history result, never after a throttled checkpoint; archive-disabled sessions release in-flight intents without marking them durable. Archive-less runner handoffs must omit resume commands.
## Gotchas
The detailed maintainer notes are in [vtcode-binary-gotchas.md](../docs/development/vtcode-binary-gotchas.md); startup timing must initialize before tracing and remain opt-in; `startup::StartupPolicy` keeps metadata read-only/no-auth, ask/`--print` auth-only, app-server security-only, and theme preference I/O interactive-only; live status config reloads must invalidate Git/command refresh gates, and malformed live config must retain the last valid snapshot; blocked-tool fuses drain the current assistant batch then schedule one tool-free synthesis pass, blocker live pointers are cleared only by the owning session after the archive is marked resolved, final session archives retain lightweight last-turn diagnostics while full progress remains checkpoint-only, direct idle/error status clears must mark cached status for resynchronization, and DSML parsing must tolerate whitespace around full-width token separators; anti-blind-editing counts 6 consecutive successful mutations only (docs-only prose stays allowed), carries pending verification across resumed turns, grants 2 fix-up edits plus one diagnostic text allowance after a failed verifier run (verifier-level Failure/Timeout or lost exec-session results grant the same window only while the gate is pending, as lost-result recovery; argument-level rejections when idle grant none), grants 2 in-turn auto-recovery attempts on the text cap (project-aware verifier directive via `default_verifier_for_workspace`/`default_verifier_override` + fresh streak; completion claims skip straight to execution), then one harness-executed verifier per turn through the normal pipeline (exit 0 clears, failure grants the fix window, denial falls through; 3 consecutive harness failures escalate to a handoff with the failure tail), plus up to 2 autonomous cross-turn recovery turns (skipped once escalated; tracker step completion resets turn budget with failures preserved) before manual `continue` is required — bounds/override/kill-switch under `[agent.harness.verification]`; tool-free recovery texts bypass verification accounting and user-typed `continue` after a verification stall resumes verifier-first (stall reason naming the gate also resumes verifier-first), exhausted handoffs report attempt/max with the exact verifier and lead verifier-first, clears on standalone or pure-`&&`-chained verifiers including `cargo fmt --check` while `;`/`||` joins never clear, elides pure-`head`/`tail` piped verifiers into standalone runs (truthful status; file redirects preserved) while static read-only filtering tails use fail-closed pipefail and `;`/`||` joins run as typed without clearing the gate and chained mutations behind a verifier prefix stay blocked, always re-executes verifiers (no read-only fast-reuse), exempts planning synthesis from the text block, does not treat `git diff` or status-masking checks as verification, and keeps Copilot/batch tracker persistence in sync; cross-turn no-progress tracking resets on workspace mutations and command execution; a user exit after a completed non-fallback turn is successful thread completion, while mid-turn exit remains cancellation; replacing a session preserves its last turn outcome, failed plan summaries are terminal regardless of fallback wording, and thread budget exhaustion takes precedence; streamed plan markup is display-suppressed, accepts one final validated `<proposed_plan>` or `<plan>` marker, and leaves persistence runtime-owned; validation-repair follow-ups use a bounded pending queue independent of prior text-response streaks; response-cap stops apply to consecutive text-only responses, use authoritative compaction-safe turn state, reset only after tool admission (including Copilot inline execution), remain blocked outcomes, and promote substantive commentary to the final phase without duplicating renderer or `ThreadEvent` output; successful tracker rendering splits user-facing surfaces: transcript uses `tracker_transcript_lines` (compact header+current `▶` row, expanded header+truncated tree; glyphless text with per-status styling — done struck-through/italic/dimmed, current bold `primary`, blocked `warning`) via single-writer `write_tracker_progress_transcript`; the TODO panel body uses `tracker_panel_rows` (texts plus parallel statuses plus focused index) with per-row theme styles; plan mode auto-continues only recoverable blocked planning ends when no plan is approval-ready — never completed planning turns; model-picker discovery must preserve legacy-cache recovery and use bounded concurrent provider probes; active WebMCP pairing displays the exact origin, can issue a non-replacing code for another configured origin, and reserves `--replace` for revocation; updater asset URLs must stay on HTTPS GitHub release paths, asset downloads must never use API credentials, and missing or invalid checksum metadata must abort installation; interactive palette probes must finish before startup errors return so OSC replies cannot leak into the shell; settings palette mutations are field-level writes, and custom-provider or provider endpoint/credential edits belong to the trusted user layer unless an explicit config file is selected; failure-like tool outcomes include non-zero commands, which retain evidence but require bounded diagnosis and a `diagnosis` ReasoningItem; proven standalone grep no-match results use completed-search event presentation and deterministic diagnosis without changing raw exit codes or execution accounting; typed missing-session errors use the same deterministic path, and lost-verifier recovery prefers their code while retaining legacy text matching; collapsed output uses one provider-neutral typed turn-scoped notice, cleans duplicate legacy copies before request assembly, and uses Anthropic-native lifecycle fields only where supported; persisted reasoning effort is best-effort across provider/model route changes, while explicit route capability validation remains strict; normalized UI streams render only provider-public reasoning summaries, and structured tool events own status; compact file-operation previews aggregate multi-file edits into one total row with `├`/`└` children while single-file and expanded output remain unchanged; TUI Esc keeps local cancellation semantics (single press cancels; double press on an empty composer submits `/rewind`, with content it clears line/all), raw-mode Ctrl+C exits on the second press within the existing window, and emergency double-SIGINT exit remains signal-handler-only; ANSI diff styles in `agent/runloop/tool_output/` use soft add/delete row bands with stronger changed-span chips, hide the visible gutter when measured content width is tight, and fall back from side-by-side below the shared threshold; ANSI16 and no-color remain foreground-only; keep this file focused and under 30 lines.