VT Code follows a modular architecture designed for maintainability,
extensibility, and performance. Internally, VT Code uses Rust traits for
runtime composition. Externally, VT Code prefers protocol- and manifest-driven
extension seams such as MCP, skills, plugin manifests, and
[[custom_providers]] so third-party integrations do not need to land new Rust
impls in the core workspace. This is deliberate: Rust coherence and orphan-rule
constraints make public trait APIs a poor default extension boundary for an
ecosystem-style project.
For internal composition, prefer explicit provider selection over "clever"
trait resolution when a boundary starts depending on many overlapping impls,
associated types, or context-specific behavior. In practice, VT Code treats
HasComponent<Name>::Provider as an elaborated dictionary: the context chooses
the provider once, and blanket consumer traits delegate through that explicit
selection. See vtcode-core/src/components.rs and vtcode-core/src/llm/cgp.rs.
VT Code treats the model as the reasoning engine and the harness as the runtime that makes that reasoning useful. Phase 1 of the exec/full-auto runtime is built around that split rather than around a separate agent subsystem.
Harness primitives in VT Code map to the runtime like this:
- Instruction memory: AGENTS.md loading, project docs, prompt assembly, onboarding guidance, and session bootstrap live in
vtcode-core/src/prompts/,vtcode-core/src/core/agent/, and the workspace instruction loaders. - Tools:
vtcode-tools/,vtcode-core/src/tools/, MCP integration, slash commands, and the tool registry expose filesystem, search, edit, exec, and protocol-backed capabilities to the model. - Sandbox / execution environment:
vtcode-bash-runner/, unified exec, workspace trust, command policies, and tool allow-lists define where generated code runs and what it can touch. - Dynamic context: context assembly, instruction merging, task tracker state, history, plan sidecars, and spooled tool outputs let VT Code rehydrate long-running work without keeping every token in the live window. The persisted
SessionMemoryEnvelopeis the harness working-memory artifact: it summarizes objective, constraints, touched files, grounded facts, verification TODOs, and delegated findings for resume and summarized-fork handoff. - Compaction / offloading: split tool results, spool files, archive transcripts, and provider-aware auto-compaction reduce context rot while preserving recoverable state on disk. On VT Code's local compaction path, older repeated single-file reads are deduplicated before summarization so the summary prompt keeps the newest copy and avoids re-injecting stale file payloads.
- Hooks / middleware: lifecycle hooks, tool middleware, guard rails, duplicate-call protection, and plan-mode enforcement add deterministic control around the model loop.
- Continuation: exec/full-auto now uses a harness-managed continuation controller that accepts completion only when tracker state is complete and verification commands pass.
- Scheduling: session-scoped
/looptasks live on the interactive runtime, while durablevtcode schedulejobs persist definitions under the VT Code config/data directories and launch freshvtcode execruns through a local daemon. - Traces / archives: thread events, session archives, checkpoints, Open Responses emission, ATIF trajectory export, and optional harness event logs capture what happened for resume, audit, and downstream tooling. The ATIF exporter (
vtcode-exec-events::atif) converts liveThreadEventstreams into the standardized Agent Trajectory Interchange Format for SFT/RL pipelines, debugging, and visualization.
vtcode-exec-events::ThreadEvent is the authoritative runtime event contract across exec mode, harness logs, and interactive lifecycle emission. Item lifecycle events come from the shared runtime/event builders, while outer TurnStarted / TurnCompleted / TurnFailed events remain wrapper-owned submission boundaries. Follow-up inputs are queued in the runtime and injected one-at-a-time only after a turn reaches an idle boundary.
For the public loop semantics and the Agent SDK concept mapping, see Agent Loop Contract.
The harness configuration is intentionally split across three existing surfaces instead of a new top-level subsystem:
agent.harnesscontrols continuation, event logging, and per-turn safety limits.automation.full_autocontrols autonomous execution and turn limits.context.dynamiccontrols how much state and history are reintroduced on each turn.
That split keeps VT Code aligned with the current runtime while making the harness explicit enough to evolve.
When agent.harness.orchestration_mode = "plan_build_evaluate" is enabled for exec/full-auto, VT Code adds a lightweight
planner/evaluator loop on top of the single-agent runtime instead of introducing a separate always-on agent subsystem:
- The planner writes
.vtcode/tasks/current_spec.mdand.vtcode/tasks/current_contract.md, then seedscurrent_task.md. - The generator still runs on the main session, but it is constrained by those artifacts plus tracker completion and verification.
- The evaluator performs a skeptical post-build pass over the spec, contract, tracker, verification results, warnings, and changed files, then writes
.vtcode/tasks/current_evaluation.md. - Failed evaluation triggers bounded revision rounds rather than silent acceptance, and the artifacts survive blocked handoff, resume, and local compaction.
Broader multi-agent orchestration, dynamic tool assembly, and harness self-analysis remain follow-on work after this single-threaded plan/build/evaluate path is stable.
VT Code treats delegation like explicit thread spawning rather than ambient background concurrency.
- The main session stays responsible for the current control flow, user-visible plan, and final integration.
- Child agents are for bounded sidecar work: focused exploration, isolated verification, or disjoint implementation slices.
- If the next local action depends on a result, the main session should usually do that work itself instead of delegating and waiting.
- Child output is advisory until the parent thread reads it, validates it, and folds it back into the main task state.
- Completed child results are merged back into the parent
SessionMemoryEnvelopeat turn boundaries, so delegated facts, touched files, open questions, and verification follow-ups survive resume and summarized forks.
VT Code borrows selected request-shaping patterns from the Rig ecosystem without delegating runtime control to rig-core agents.
- Rig-backed usage in VT Code is limited to provider/model validation and provider-specific reasoning payload shaping.
- The VT Code harness remains the execution engine for turns, tool routing, continuation, compaction, and resume flows.
- Session continuity stays on VT Code primitives:
.vtcode/history/artifacts, local/server compaction, and prompt-side memory injection. - This keeps VT Code’s existing tool registry, sandboxing, and resume semantics intact while still adopting Rig’s provider-facing patterns where they reduce duplication.
The command-line interface is built on specific principles for robustness and interoperability:
- Strict Output Separation: Data goes to
stdout, diagnostics/logs go tostderr. This enables clean piping of machine-readable output. - Standard Argument Parsing: Uses
clapfor POSIX/GNU compliance, supporting standard flags and behavior. - Command Isolation: Each sub-command (
ask,exec,chat) is handled by a dedicated module insrc/cli/, sharing core logic viavtcode-core. - Signal Handling: Graceful handling of
SIGINT/SIGTERMto ensure resource cleanup (e.g., restoring terminal state).
The terminal UI now has a dedicated crate boundary:
vtcode-tui: public TUI-facing API surface for downstream consumersvtcode-core::ui::tui: canonical runtime type surface for VT Code internals
This separation allows external code to import TUI types and session APIs from
vtcode-tui while keeping host-specific integrations inside vtcode-core.
vtcode-tui now exposes standalone launch primitives (SessionOptions,
SessionSurface, KeyboardProtocolSettings) plus host adapters
(host::HostAdapter) so downstream projects can start sessions without
importing vtcode_core::config types directly.
The full TUI source tree is now located in:
vtcode-tui/src/core_tui/
vtcode-core/src/ui/tui.rs is a compatibility shim that compiles this migrated
source tree to preserve existing vtcode_core::ui::tui paths.
The TUI runner is organized into focused modules:
vtcode-tui/src/core_tui/runner/
mod.rs # Orchestration entrypoint (`run_tui`)
drive.rs # Main terminal/event loop drive logic
events.rs # Async event stream + tick scheduling
signal.rs # SIGINT/SIGTERM cleanup guard
surface.rs # Inline vs alternate screen detection
terminal_io.rs # Cursor/screen prep + drain helpers
terminal_modes.rs# Raw/mouse/focus/keyboard mode management
tools/
mod.rs # Module coordination & exports
traits.rs # Core composability traits
types.rs # Common types & structures
cache.rs # Enhanced caching system
grep_file.rs # Ripgrep-backed search manager
file_ops.rs # File operations tool (async + cached)
command.rs # Command execution tool (3 modes)
registry.rs # Tool coordination & function declarations
// Base tool interface
pub trait Tool: Send + Sync {
async fn execute(&self, args: Value) -> Result<Value>;
fn name(&self) -> &'static str;
fn description(&self) -> &'static str;
}
// Multi-mode execution
pub trait ModeTool: Tool {
fn supported_modes(&self) -> Vec<&'static str>;
async fn execute_mode(&self, mode: &str, args: Value) -> Result<Value>;
}
// Intelligent caching
pub trait CacheableTool: Tool {
fn cache_key(&self, args: &Value) -> String;
fn should_cache(&self, args: &Value) -> bool;
}- Debounce and cancellation pipeline for responsive searches
- Ripgrep primary backend with perg fallback when unavailable
- Supports glob filters, hidden file handling, and context lines
- Enforces workspace boundaries with robust path validation
grep_file.rsis the single source of truth for content search- Higher-level helpers were removed; use
ToolRegistry::grep_file_executor list_filesremains a discovery/metadata tool; defer all content scanning togrep_file
- Workspace-scoped file listing, metadata inspection, and traversal
- Async directory walking with cache integration for large trees
- Path policy enforcement shared with the command subsystem
- Standard command execution with exit-code and output capture
- PTY session management for interactive commands
- Streaming support for long-lived shell tasks with cancellation
- Internal Traits, External Protocols - Keep Rust traits as internal composition seams; prefer config, manifests, and protocols for third-party extension points so external integrations do not depend on compile-time impl slots in VT Code
- Prefer Explicit Provider Dictionaries - When internal Rust abstractions become coherence-sensitive, move behavior behind context-selected providers instead of adding more blanket impl magic
- Mode-based Execution - Single tools support multiple execution modes
- Simplicity First - Prefer simple algorithms and control flow until real workload data justifies more complexity
- Data-Oriented Design - Choose data structures and boundaries so the right algorithm is obvious
- Backward Compatibility - All existing APIs remain functional
- Measured Optimization - Profile and benchmark before keeping performance-motivated complexity
- Clear Separation - Each module has single responsibility
- Handle/Context Before Clever Ownership - When modeling complex Rust state, start with explicit handles/IDs and an owning context. Reach for self-referential layouts, raw pointers, lifetime-branding tricks, or custom
Send/Synconly when a simpler handle-based design is demonstrably insufficient, and document the invariant at the boundary
use super::traits::{Tool, ModeTool};
use async_trait::async_trait;
pub struct MyTool;
#[async_trait]
impl Tool for MyTool {
async fn execute(&self, args: Value) -> Result<Value> {
// Implementation
}
fn name(&self) -> &'static str { "my_tool" }
fn description(&self) -> &'static str { "My custom tool" }
}
#[async_trait]
impl ModeTool for MyTool {
fn supported_modes(&self) -> Vec<&'static str> {
vec!["mode1", "mode2"]
}
async fn execute_mode(&self, mode: &str, args: Value) -> Result<Value> {
match mode {
"mode1" => self.execute_mode1(args).await,
"mode2" => self.execute_mode2(args).await,
_ => Err(anyhow::anyhow!("Unsupported mode: {}", mode))
}
}
}- 77% complexity reduction from monolithic structure
- Enhanced functionality through mode-based execution
- 100% backward compatibility maintained
- Protocol-friendly extension model for external development
- Performance optimized with intelligent caching
- Module:
vtcode-core/src/llm/rl(bandit and actor-critic implementations) - Config:
[optimization]withstrategy = "bandit" | "actor_critic"plus reward shaping knobs - Signals: Success/timeout + latency feed
RewardSignal, stored in a rollingRewardLedger - Usage: Construct
RlEngine::from_config(&VTCodeConfig::optimization)and callselect(actions, PolicyContext); on completion emitapply_reward - Goal: Prefer low-latency, high-success actions (e.g., choose edge vs cloud executors) while remaining pluggable for future policies
To operationalize the staged training paradigm introduced in docs/research/kimi_dev_agentless_training.md, VT Code couples its
modular runtime with a data and evaluation strategy designed for agentless skill priors:
- Dual Roles – Prompt templates for
BugFixerandTestWritershare the same tool registry, enabling deterministic skill acquisition before agentic orchestration. - Execution Telemetry – Command and sandbox outputs are captured through the existing
bash_runnerand PTY subsystems, allowing outcome-based rewards for RL without extra instrumentation. The ATIF trajectory exporter provides standardized per-step metrics (token usage, costs, logprobs) directly consumable by SFT and RL training pipelines. - Self-Play Hooks – The tool layer exposes high-signal search, diff, and patching capabilities that feed directly into the multi-rollout evaluation loop defined in the training roadmap.
- Context Capacity – Long-context support in the LLM provider abstraction ensures that multi-turn reasoning traces and aggregated rollouts remain accessible during both SFT and inference.
See the research note for the full pipeline (mid-training data curation, SFT cold start, RL curriculum, and test-time self-play).