Integrate Codex patterns: task scheduler, TUI, sessions, compaction

Add Codex-inspired patterns for improved agent architecture:

**Task Scheduler (Phase 1 & 4)**
- New Task enum with Job, ToolExec, Background variants
- TaskHandler trait for custom background tasks
- Scheduler.spawn_subtask() and spawn_batch() for parallel execution
- Worker executes multiple tools in parallel via futures::join_all

**Tool Approval System (Phase 6)**
- Tool.requires_approval() method (default false for sandboxed tools)
- HttpTool marked as requiring approval (external network)
- MCP protocol annotations: destructive_hint, side_effects_hint

**Ratatui TUI CLI (Phase 2)**
- Replace blocking stdin with event-driven TUI
- ChatComposer with history navigation and tab completion
- ApprovalOverlay modal with y/n/a keyboard shortcuts
- Raw mode with proper terminal cleanup

**Session/Turn Model (Phase 3)**
- Session, Thread, Turn structs for conversation tracking
- Submission enum for user input, approvals, undo, interrupt
- UndoManager with checkpoint-based undo/redo

**Context Compaction (Phase 5)**
- ContextMonitor with token estimation and threshold checks
- CompactionStrategy: Summarize, Truncate, MoveToWorkspace
- ContextCompactor writes summaries to workspace daily logs

Co-Authored-By: Claude Opus 4.5 <[email protected]>
This commit is contained in:
Illia Polosukhin
2026-02-02 23:41:57 -08:00
co-authored by Claude Opus 4.5
parent d047c23b2d
commit 09032d69cb
26 changed files with 3944 additions and 272 deletions
+4
View File
@@ -163,4 +163,8 @@ impl Tool for HttpTool {
fn requires_sanitization(&self) -> bool {
true // External data always needs sanitization
}
fn requires_approval(&self) -> bool {
true // HTTP requests go to external services, require user approval
}
}
+5
View File
@@ -203,6 +203,11 @@ impl Tool for McpToolWrapper {
fn requires_sanitization(&self) -> bool {
true // MCP tools are external, always sanitize
}
fn requires_approval(&self) -> bool {
// Check the destructive_hint annotation from the MCP server
self.tool.requires_approval()
}
}
#[cfg(test)]
+46
View File
@@ -11,6 +11,52 @@ pub struct McpTool {
pub description: String,
/// JSON Schema for input parameters.
pub input_schema: serde_json::Value,
/// Optional annotations from the MCP server.
#[serde(default)]
pub annotations: Option<McpToolAnnotations>,
}
/// Annotations for an MCP tool that provide hints about its behavior.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct McpToolAnnotations {
/// Hint that this tool performs destructive operations that cannot be undone.
/// Tools with this hint set to true should require user approval before execution.
#[serde(default)]
pub destructive_hint: bool,
/// Hint that this tool may have side effects beyond its return value.
#[serde(default)]
pub side_effects_hint: bool,
/// Hint that this tool performs read-only operations.
#[serde(default)]
pub read_only_hint: bool,
/// Hint about the expected execution time category.
#[serde(default)]
pub execution_time_hint: Option<ExecutionTimeHint>,
}
/// Hint about how long a tool typically takes to execute.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ExecutionTimeHint {
/// Typically completes in under 1 second.
Fast,
/// Typically completes in 1-10 seconds.
Medium,
/// Typically completes in more than 10 seconds.
Slow,
}
impl McpTool {
/// Check if this tool requires user approval based on its annotations.
pub fn requires_approval(&self) -> bool {
self.annotations
.as_ref()
.map(|a| a.destructive_hint)
.unwrap_or(false)
}
}
/// Request to an MCP server.
+12
View File
@@ -148,6 +148,18 @@ pub trait Tool: Send + Sync {
true
}
/// Whether this tool requires explicit user approval before execution.
///
/// Returns false by default since most tools run in a sandboxed/virtualized
/// environment. Only tools that make external network calls or perform
/// destructive operations should return true.
///
/// When true, the agent will prompt the user for confirmation before
/// executing this tool.
fn requires_approval(&self) -> bool {
false
}
/// Get the tool schema for LLM function calling.
fn schema(&self) -> ToolSchema {
ToolSchema {
+1 -3
View File
@@ -553,9 +553,7 @@ fn row_to_tool(row: &tokio_postgres::Row) -> Result<StoredWasmTool, WasmStorageE
trust_level: trust_level_str
.parse()
.map_err(WasmStorageError::InvalidData)?,
status: status_str
.parse()
.map_err(WasmStorageError::InvalidData)?,
status: status_str.parse().map_err(WasmStorageError::InvalidData)?,
created_at: row.get("created_at"),
updated_at: row.get("updated_at"),
})