Skip to content

Latest commit

 

History

History
267 lines (200 loc) · 15.5 KB

File metadata and controls

267 lines (200 loc) · 15.5 KB

VT Code Architecture Guide

Overview

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.

Model + Harness

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 SessionMemoryEnvelope is 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 /loop tasks live on the interactive runtime, while durable vtcode schedule jobs persist definitions under the VT Code config/data directories and launch fresh vtcode exec runs 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 live ThreadEvent streams 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.harness controls continuation, event logging, and per-turn safety limits.
  • automation.full_auto controls autonomous execution and turn limits.
  • context.dynamic controls 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.md and .vtcode/tasks/current_contract.md, then seeds current_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.

Explicit Delegation Model

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 SessionMemoryEnvelope at turn boundaries, so delegated facts, touched files, open questions, and verification follow-ups survive resume and summarized forks.

Rig Alignment Boundary

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.

CLI Architecture

The command-line interface is built on specific principles for robustness and interoperability:

  1. Strict Output Separation: Data goes to stdout, diagnostics/logs go to stderr. This enables clean piping of machine-readable output.
  2. Standard Argument Parsing: Uses clap for POSIX/GNU compliance, supporting standard flags and behavior.
  3. Command Isolation: Each sub-command (ask, exec, chat) is handled by a dedicated module in src/cli/, sharing core logic via vtcode-core.
  4. Signal Handling: Graceful handling of SIGINT/SIGTERM to ensure resource cleanup (e.g., restoring terminal state).

Core Architecture

TUI Architecture

The terminal UI now has a dedicated crate boundary:

  • vtcode-tui: public TUI-facing API surface for downstream consumers
  • vtcode-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

Modular Tools System

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

Core Traits

// 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;
}

Tool Implementations

GrepSearchManager (tools::grep_file)

  • 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

Search Stack Consolidation

  • grep_file.rs is the single source of truth for content search
  • Higher-level helpers were removed; use ToolRegistry::grep_file_executor
  • list_files remains a discovery/metadata tool; defer all content scanning to grep_file

FileOpsTool

  • 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

CommandTool

  • Standard command execution with exit-code and output capture
  • PTY session management for interactive commands
  • Streaming support for long-lived shell tasks with cancellation

Design Principles

  1. 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
  2. Prefer Explicit Provider Dictionaries - When internal Rust abstractions become coherence-sensitive, move behavior behind context-selected providers instead of adding more blanket impl magic
  3. Mode-based Execution - Single tools support multiple execution modes
  4. Simplicity First - Prefer simple algorithms and control flow until real workload data justifies more complexity
  5. Data-Oriented Design - Choose data structures and boundaries so the right algorithm is obvious
  6. Backward Compatibility - All existing APIs remain functional
  7. Measured Optimization - Profile and benchmark before keeping performance-motivated complexity
  8. Clear Separation - Each module has single responsibility
  9. 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/Sync only when a simpler handle-based design is demonstrably insufficient, and document the invariant at the boundary

Adding New Tools

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))
        }
    }
}

Benefits

  • 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

RL Optimization Loop (Adaptive Action Selection)

  • Module: vtcode-core/src/llm/rl (bandit and actor-critic implementations)
  • Config: [optimization] with strategy = "bandit" | "actor_critic" plus reward shaping knobs
  • Signals: Success/timeout + latency feed RewardSignal, stored in a rolling RewardLedger
  • Usage: Construct RlEngine::from_config(&VTCodeConfig::optimization) and call select(actions, PolicyContext); on completion emit apply_reward
  • Goal: Prefer low-latency, high-success actions (e.g., choose edge vs cloud executors) while remaining pluggable for future policies

Training & Evaluation Alignment

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 BugFixer and TestWriter share the same tool registry, enabling deterministic skill acquisition before agentic orchestration.
  • Execution Telemetry – Command and sandbox outputs are captured through the existing bash_runner and 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).