Agent Runtime¶
The AgentRuntime class (missy/agent/runtime.py) is the top-level orchestrator. It binds a provider, session, tool registry, and all supporting subsystems into a single execution loop.
Initialization¶
from missy.agent.runtime import AgentRuntime, AgentConfig
agent = AgentRuntime(AgentConfig(
provider="anthropic",
max_iterations=10,
temperature=0.7,
capability_mode="full",
max_spend_usd=5.0,
))
AgentConfig Fields¶
| Field | Type | Default | Description |
|---|---|---|---|
provider | str | "anthropic" | Registry key of the provider |
model | str\|None | None | Model override (provider default if None) |
system_prompt | str | (built-in) | System instructions prepended to every conversation |
max_iterations | int | 10 | Max provider calls per run() invocation |
temperature | float | 0.7 | Sampling temperature |
capability_mode | str | "full" | "full", "safe-chat", "discord", or "no-tools" |
max_spend_usd | float | 0.0 | Per-session cost cap (0 = unlimited) |
Lazy-Loaded Subsystems¶
The runtime initializes these subsystems lazily and fails gracefully if any are unavailable:
graph LR
Runtime[AgentRuntime] --> CB[CircuitBreaker]
Runtime --> RL[RateLimiter]
Runtime --> CM[ContextManager]
Runtime --> MS[MemoryStore]
Runtime --> CT[CostTracker]
Runtime --> SN[InputSanitizer]
Runtime --> PR[ProgressReporter]
Runtime --> CP[CheckpointManager] This design means the core agent loop always works, even if optional subsystems (memory, cost tracking, checkpoints) fail to initialize.
Session Management¶
Each run() call operates within a session:
- If
session_idis provided and a session with that ID exists, it is reused. - If
session_idis provided but no session exists, a new session is created with that ID stored in metadata. - If
session_idis omitted, a new session is created.
Sessions are managed by a SessionManager instance owned by the runtime. Session history is loaded from the SQLiteMemoryStore at the start of each run and persisted after completion.
Provider Resolution¶
The runtime resolves the provider through the ProviderRegistry:
- Look up the provider named in
AgentConfig.provider. - If unavailable (disabled, missing API key), fall back to the first available provider.
- If no provider is available, raise
ProviderError.
The runtime can switch providers at any time via switch_provider(name), which validates availability and rebuilds the circuit breaker.
Single-Turn vs. Multi-Step¶
The runtime supports two execution modes, determined automatically:
flowchart TD
A[run called] --> B{Tools registered AND max_iterations > 1?}
B -->|Yes| C[Multi-step tool loop]
B -->|No| D[Single-turn completion]
C --> E[Return response]
D --> E Single-Turn Mode¶
When no tools are registered or max_iterations is 1:
- Build messages via
ContextManager. - Call
provider.complete()through the circuit breaker. - Return the response text.
Multi-Step Tool Loop¶
When tools are available and max_iterations > 1:
sequenceDiagram
participant Runtime
participant Provider
participant Tools
participant Checkpoint
loop Up to max_iterations
Runtime->>Provider: complete_with_tools(messages, tools)
alt Model requests tool calls
Provider-->>Runtime: response.tool_calls
loop For each tool call
Runtime->>Tools: execute(tool_call)
Tools-->>Runtime: tool_result
Runtime->>Runtime: Scan for injection
Runtime->>Runtime: Truncate if oversized
end
Runtime->>Checkpoint: update(messages, tools_used)
Runtime->>Runtime: Inject verification prompt
else Model returns final text
Provider-->>Runtime: response.content
Runtime->>Checkpoint: complete()
Runtime-->>Runtime: Return response
end
end Each iteration:
- Call
provider.complete_with_tools()via the circuit breaker. - If the model requests tool calls, execute them through the
ToolRegistry. - Scan tool output for prompt injection (
InputSanitizer). - Truncate oversized tool results (> 200K chars hard limit, > 50K stored separately).
- Track tool failures and inject strategy rotation prompts after 3 consecutive failures to the same tool.
- Save a checkpoint after each round of tool results.
- Inject a verification prompt (
DoneCriteria) so the model can assess outcomes. - If the model returns a final text response (
finish_reason: "stop"), exit the loop.
Checkpoint and Recovery¶
The CheckpointManager provides crash recovery for the tool loop:
During Execution¶
- Create -- A checkpoint is created at the start of each tool loop with the session ID, task ID, and user input.
- Update -- After each round of tool results, the checkpoint is updated with the current message list, tools used, and iteration count.
- Complete -- When the loop finishes normally, the checkpoint is marked complete.
- Fail -- On unhandled exceptions, the checkpoint is marked failed.
On Startup¶
When the runtime initializes, it scans for incomplete checkpoints from previous runs:
agent = AgentRuntime(config)
for recovery in agent.pending_recovery:
print(f"Session {recovery.session_id}: {recovery.strategy}")
# strategy is "resume" or "abandon"
Manage recovery via the CLI:
missy recover # list incomplete checkpoints
missy recover --abandon-all # discard all incomplete checkpoints
Cost Tracking¶
When max_spend_usd > 0, the runtime tracks cumulative cost across provider calls within a session. After each completion, the cost is recorded and checked against the budget. If the budget is exceeded, subsequent calls are blocked.
Or per-runtime:
Check cost status:
Streaming¶
The run_stream() method provides token-by-token output for real-time CLI display:
- Single-turn: Yields text deltas from
provider.stream(). - Multi-step with tools: Falls back to
run()and yields the complete response as a single chunk (streaming during tool calls is not practical since tool results must be processed first).
Audit Events¶
The runtime emits structured audit events at each stage:
| Event | Stage |
|---|---|
agent.run.start | Run begins, input length recorded |
agent.run.error | Provider resolution or completion failure |
agent.run.complete | Run finishes, provider and tools used recorded |
agent.tool.strategy_rotation | Tool failure threshold hit, strategy prompt injected |
Integrated Intelligence Subsystems¶
The runtime integrates four intelligence subsystems that enhance the agent loop beyond basic tool execution:
Attention System¶
At the start of each iteration, the runtime runs AttentionSystem.process() on the user input:
- Urgency detection flags high-priority requests for immediate tool dispatch.
- Topic extraction identifies what the user is focused on.
- Focus continuity tracks whether the user has been on the same topic across turns.
- Tool prioritization suggests which tools to favor based on urgency and topics.
- Context filtering provides topic words to the memory synthesizer for selective retrieval.
See Attention System for the full five-subsystem breakdown.
Memory Consolidation (Sleep Mode)¶
Before context assembly, the runtime checks whether the token window is approaching capacity. If usage reaches 80% of max_tokens, the MemoryConsolidator compresses older messages:
- Keep the last 4 messages intact.
- Extract key facts (tool results, decisions, short instructions) from older messages.
- Replace older messages with a single summary.
This allows long-running sessions to continue without hitting token limits. See Sleep Mode.
AI Playbook¶
The playbook operates at two points in the loop:
- Before execution:
Playbook.get_relevant()retrieves proven patterns for the current task type, which the Memory Synthesizer injects into context. - After execution: When a tool-augmented run succeeds,
Playbook.record()captures the task type, tool sequence, and a prompt hint for future replay.
Patterns with 3+ successes are flagged for promotion to full skills. See AI Playbook.
Memory Synthesizer¶
During context assembly, the MemorySynthesizer replaces the previous approach of injecting learnings and playbook entries separately. It:
- Collects fragments from all memory sources (learnings at 0.7, playbook at 0.6, conversation at 0.5, summaries at 0.4).
- Scores each fragment against the current query using keyword overlap.
- Deduplicates near-identical fragments.
- Produces a single relevance-ranked block within a 4500-token budget.
See Memory Synthesizer.
Message Bus Integration¶
The runtime publishes lifecycle events via the Message Bus:
| Event | Topic | When |
|---|---|---|
| Run start | agent.run.start | run() is called |
| Run complete | agent.run.complete | Run finishes successfully |
| Run error | agent.run.error | Run fails with an error |
| Tool request | tool.request | Before executing a tool call |
| Tool result | tool.result | After a tool call completes |
Other subsystems (audit logger, attention system, custom handlers) subscribe to these events for decoupled observation.
Capability Modes¶
The capability_mode field controls which tools and system prompt are available:
| Mode | Tools | System Prompt | Use Case |
|---|---|---|---|
full | All tools | Full (desktop, browser, shell, etc.) | CLI interactive |
safe-chat | Limited set | Restricted | Edge nodes with limited trust |
discord | Discord-safe set | No desktop/GUI references | Discord bot |
no-tools | None | Conversational only | Pure chat |