S240 Unified Autonomy Engine
Documentation for Sessions
S240: Unified Autonomy Engine — Technical Session Documentation
Session: 240 Date: 2026-03-19 Branch: session-238-239 (concurrent with S239 Memory v2) Status: Complete — 65/65 E2E tests, 0 type errors
Executive Summary
Session 240 built the Unified Autonomy Engine — the system that makes Vitto act like a 24/7 intelligent partner rather than a reactive chatbot. It fuses three previously disconnected concerns into one coherent decision pipeline:
- VM Execution — Vitto can now run commands, install packages, and manage files on isolated Firecracker VMs per user
- Adaptive Trust — Vitto earns autonomy through proven reliability, not static role assignments
- Proactive Intelligence — Vitto learns recurring patterns and acts before being asked
Architecture Overview
The Autonomy Gate (Central Innovation)
Every Vitto action flows through an AutonomyGateService that assembles a read-only snapshot:
User message → AutonomyGate.evaluate(task) → {
trust: TrustScoringService.getScore(userId, domain) // independent
tier: TierResolverService.resolve(task, trust) // reads trust
action: ActionDecider.decide(trust, tier, task) // reads both
} → Route to execution tier → Execute → Outcome feeds trust
Design principle: Three independent services, one gate. No god object. Each concern evolves independently. The gate is the only place they meet.
Key Files Created
| Service | Path | Purpose |
|---|---|---|
| AutonomyGateService | engine/autonomy-gate.service.ts |
Assembles read-only AutonomyContext per task |
| TrustScoringService | engine/trust-scoring.service.ts |
Adaptive 0-100 scoring per (userId, domain) |
| TierResolverService | engine/tier-resolver.service.ts |
Dynamic in-process → VM → browser selection |
| VmApiClientService | engine/vm-api-client.service.ts |
HTTP client to ai-api sandbox proxy |
| VmSessionManagerService | engine/vm-session-manager.service.ts |
Per-user VM lifecycle with snapshot restore |
| SandboxedToolProxyService | engine/sandboxed-tool-proxy.service.ts |
Routes tool calls to VM when tier=vm |
| VmCredentialInjectorService | engine/vm-credential-injector.service.ts |
First-time deploy key + credential setup |
| TriggerEngineService | engine/trigger-engine.service.ts |
Frequency-based proactive trigger detection |
| TaskRecoveryService | engine/task-recovery.service.ts |
Genome-based checkpoint + resume |
Schemas Created
| Schema | Collection | Purpose |
|---|---|---|
| TrustScore | vitto_trust_scores |
Materialized trust per (userId, domain) |
| TriggerPattern | vitto_triggers |
Learned proactive patterns with confidence |
| TaskCheckpoint | vitto_checkpoints |
Genome + tool results, TTL 24h |
| ChainCheckpoint | vitto_chain_checkpoints |
Multi-step background task state |
Phase-by-Phase Breakdown
Phase 0: Configuration (Task 0.1)
- Added environment variables:
VM_WARM_POOL_SIZE,VM_SESSION_TTL_MINUTES,TRUST_ESCALATION_CAP_DAYS - Created MongoDB collections with indexes: compound on
(userId, domain)for trust, TTL on checkpoints
Phase 1-2: Infrastructure Layer (Tasks 1.1, 2.1)
- Sandbox-Orchestrator: Added structured file operations controller (read/write/list files on VMs via HTTP)
- Backend-AI-API: Added 9-endpoint sandbox proxy controller that relays requests from vitto-api through ai-api to sandbox-orchestrator, preserving the existing SandboxProvider abstraction and circuit breaker
Phase 3: VM Execution Layer (Tasks 3.1–3.5)
VmApiClientService — HTTP client with retry + circuit breaker to ai-api's sandbox proxy. Handles:
createSession,restoreSession,snapshotSession,destroySessionexecuteCommand,readFile,writeFile,listFiles
VmSessionManagerService — Per-user VM lifecycle:
- Each user gets a snapshot-based 2GB micro-space
- First use: cold boot (~10s) + credential injection
- Subsequent uses: snapshot restore (~500ms) — credentials persist in snapshot
- Idle timeout: snapshot + suspend (configurable, default 15min)
- 10x cost reduction vs always-on (20GB peak vs 200GB)
SandboxedToolProxyService — The #1 blocker that was closed:
- When a sub-Vitto tool call needs VM tier, intercepts
run_command/write_file/read_file - Routes to VmSessionManagerService → VmApiClientService → ai-api → sandbox-orchestrator → VM
- Sticky claim: first tool pays ~500ms, subsequent tools reuse same VM
- Release on task completion
VmCredentialInjectorService — First-time setup:
- Injects deploy keys, git config, npm tokens, environment variables
- Only runs once per user snapshot — subsequent restores have credentials
Wiring (Task 3.5):
- SubVittoOrchestratorService reads tier from AutonomyContext
- AgentLoopService tool execution checks SandboxedToolProxyService before local ProcessSandboxService
- Fallback chain: if VM unavailable, degrade to in-process for safe tools
Phase 4: Adaptive Trust (Tasks 4.1–4.5)
TrustScore schema: Materialized in MongoDB, updated event-driven (not computed live):
{ userId, domain, score: 0-100, level: OBSERVE|SUGGEST|DIRECT|AUTONOMOUS,
recentOutcomes: [...last 50], lastEscalation, trajectory }
Scoring algorithm:
- Complex success: +3 points, simple success: +1
- Recency decay: outcomes >30 days weighted at 0.5x
- Critical failure: immediate drop (destructive action user had to undo)
- Escalation cap: max +1 level per 7 days (trust earned slowly, lost quickly)
- User's
preferredAutonomyLevelis a floor, not a ceiling
Domain isolation: Trust for code_execution ≠ trust for search_docs ≠ trust for send_message. A user can be AUTONOMOUS for doc search but SUGGEST for code execution.
Wiring:
- OutcomeFeedbackService → TrustScoringService (event-driven update after each outcome)
- AutonomyGateService → TrustScoringService (reads materialized score, ~1ms)
- SubVittoOrchestratorService → reports sub-Vitto outcomes (cross-agent learning)
Phase 5: Proactive Intelligence (Tasks 5.1–5.2)
TriggerPattern schema (stored in vitto_triggers, NOT VittoMemory):
{ pattern, action, confidence: 0-1, userId, spaceId,
activations, suppressedCount, lastTriggered }
Detection: After each task completion, checks: "has this user asked for similar things 3+ times in 14 days?" If yes, creates pattern with confidence 0.5.
Lifecycle:
- Confirmed by user engagement → confidence increases
- Dismissed by user → confidence decreases
- Below 0.3 → auto-deleted
- Above 0.8 → eligible for AUTONOMOUS execution (if trust allows)
Why not VittoMemory: S239's admission control scores on factualConfidence and semanticNovelty. Trigger patterns are predictions (low factual) that repeat (low novelty) — they'd be quarantined.
Phase 6: Resilience (Tasks 6.1–6.3)
TaskRecoveryService — Genome-based checkpoints:
- After each tool execution: persist ConversationGenome (~500 tokens) + last 3 tool results + VM snapshot ID
- On crash recovery: BullMQ picks up task → detect checkpoint → inject genome as resume context
- TTL 24h auto-cleanup (no checkpoint accumulation)
- 100x smaller than raw message replay (~500 tokens vs ~50K)
Agent Nesting — Depth-2:
- MAX_DEPTH bumped from 1 to 2
- Depth-2 constraints: in-process only, 50% parent budget, max 3 tools
- Prevents resource explosion: 8 × 8 = 64 VMs impossible with in-process constraint
- Enables real decomposition: code reviewer spawns test runner, architect spawns linter
S239 Memory v2 Compatibility
Analyzed concurrently running S239 spec. Key integration points:
| S240 Component | S239 Impact | Resolution |
|---|---|---|
| Trust scoring | None — reads outcomes, not memory | Safe |
| Trigger patterns | Would conflict with admission control | Separate collection |
| Sub-Vitto context | Must use new WindowManager + Genome | Wired through new APIs |
| Checkpoint recovery | Benefits from genome service | Uses genome instead of raw replay |
| VM lifecycle | Fully independent infrastructure layer | No conflict |
S239 features that benefit S240 for free:
- Dopamine-gated recall: Sub-Vittos get <5ms fast-route recall
- TOON compression: Sub-Vitto prompts use 30-60% fewer tokens
- Cognitive plugins: Trust updates implementable as
onAfterResponseplugin - Prospective indexing: VM task outcomes pre-indexed for future recall
Test Coverage
| Suite | Tests | Coverage |
|---|---|---|
| VM Execution E2E | 7 | Session create/restore/snapshot, tool proxy routing, degradation fallback, credential injection |
| Trust Scoring E2E | 9 | Score computation, domain isolation, escalation cap, critical drop, floor enforcement, trajectory |
| Trigger Engine E2E | 12 | Pattern detection, confidence growth/decay, auto-delete, dispatch integration, suppression |
| Task Recovery E2E | 8 | Checkpoint create/restore, genome resume injection, TTL cleanup, VM snapshot restore |
| Agent Nesting E2E | 5 | Depth-2 spawn, in-process constraint, budget inheritance, tool cap, depth-3 rejection |
| Background Tasks E2E | 15 | Chain checkpoint CRUD, pause/resume/cancel, step sequencing |
| Mind Service | 9 | Virtual context, genome aggregation, stale topic eviction |
| Total | 65 | All passing |
Decisions Logged (MCP)
| ID | Type | Question | Confidence |
|---|---|---|---|
dev_..4051slx |
architecture | Unified vs incremental vs top-down | 0.93 |
dev_..8b11um0 |
architecture | Always-on vs snapshot vs ephemeral VMs | 0.95 |
dev_..z4mge7c |
architecture | Static vs dynamic trust scoring | 0.92 |
dev_..3qxz9w5 |
architecture | Trigger storage: memory vs dedicated collection | 0.91 |
dev_..3xo53t5 |
architecture | Checkpoint: event sourcing vs genome-based | 0.94 |
dev_..veqh6fq |
architecture | Agent nesting depth and resource constraints | 0.93 |
All 6 outcomes reported as successful. 4 conventions added. 7 lessons reflected.
Conventions Established
- Trust domain scoping: Scores are per
(userId, actionDomain)— never assume cross-domain trust - Trigger storage isolation: Proactive triggers go in
vitto_triggers, never VittoMemory - VM sticky claims: Per-tool-call claiming with sticky sessions — release on task completion
- Nesting resource guards: Depth-2 is in-process only, 50% budget, 3-tool cap
Known Limitations & Future Work
| Limitation | Severity | Future Fix |
|---|---|---|
| Frequency-based triggers miss one-shot critical patterns | Medium | Phase 2: semantic similarity detection |
| 4-hop VM latency (vitto → ai-api → orchestrator → VM) | Medium | Evaluate direct connection once stability proven |
| LLM resume after checkpoint may diverge on complex tasks | Medium | Limit recovery to tasks <20 iterations |
| Trust gaming via trivial task spam | Low | Complexity weighting already mitigates; monitor |
| Warm pool costs money when idle | Low | TTL eviction (15min default), max 2-3 VMs |
Files Modified (Existing)
| File | Change |
|---|---|
sub-vitto-orchestrator.service.ts |
MAX_DEPTH=2, reads tier from AutonomyContext, reports sub-Vitto outcomes |
autonomy.service.ts |
Extended with ActionDecider role, reads trust from TrustScoringService |
background-task.service.ts |
Fixed Mongoose FlattenMaps type casts on ChainCheckpoint queries |
user-genome.service.ts |
Fixed Mongoose lean() type cast on getUserGenome |
.env.local |
Added VM_WARM_POOL_SIZE, VM_SESSION_TTL_MINUTES, TRUST_ESCALATION_CAP_DAYS |