# Agent Loop & Tools The ZeroClaw agent architecture is built around a recursive execution loop that allows an LLM to invoke external capabilities, observe their results, and refine its response until a final answer is reached. *** ## Tool Trait The `Tool` trait in `tools/traits.rs` defines the interface for any capability exposed to the agent. It provides both the metadata needed for LLM registration and the execution logic for the tool itself. ```rust /// Result of a tool execution #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ToolResult { pub success: bool, pub output: String, pub error: Option, } /// Description of a tool for the LLM #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ToolSpec { pub name: String, pub description: String, pub parameters: serde_json::Value, } /// Core tool trait — implement for any capability #[async_trait] pub trait Tool: Send + Sync { /// Tool name (used in LLM function calling) fn name(&self) -> &str; /// Human-readable description fn description(&self) -> &str; /// JSON schema for parameters fn parameters_schema(&self) -> serde_json::Value; /// Execute the tool with given arguments async fn execute(&self, args: serde_json::Value) -> anyhow::Result; /// Get the full spec for LLM registration fn spec(&self) -> ToolSpec { ToolSpec { name: self.name().to_string(), description: self.description().to_string(), parameters: self.parameters_schema(), } } } ``` *** ## Agent Loop The core logic resides in `agent/loop_.rs` within `run_tool_call_loop`. This function orchestrates the interaction between the provider and the tool registry. The process follows a repeating cycle: 1. Send the current conversation history and tool definitions to the LLM. 2. Receive a response. 3. Parse the response for tool calls (native or prompt-guided). 4. If tool calls exist, execute them and append the results to the history, then repeat. 5. If no tool calls exist, return the final text response. ### Iteration Safety To prevent infinite loops or runaway execution, the system enforces a strict iteration cap: * `DEFAULT_MAX_TOOL_ITERATIONS` = 10 ### Streaming and Progress Streaming responses are handled by accumulating chunks before relaying them to the draft channel to minimize noise: * `STREAM_CHUNK_MIN_CHARS` = 80 * `PROGRESS_MIN_INTERVAL_MS` = 500 (minimum time between progress updates) * `DRAFT_CLEAR_SENTINEL` = `\x00CLEAR\x00` (used to clear progress lines before showing the final answer) *** ## Auto-Compaction Conversation history is managed through an auto-compaction mechanism to stay within context window limits while preserving essential information. * **Threshold**: Triggered when non-system message count exceeds 50 (`DEFAULT_MAX_HISTORY_MESSAGES`). * **Retention**: Keeps the 20 most recent messages (`COMPACTION_KEEP_RECENT_MESSAGES`). * **Summarization**: Older messages are summarized by the LLM. * **Caps**: Summarization source is capped at 12,000 characters; the resulting summary is capped at 2,000 characters. *** ## Security & Pattern Matching ### Credential Scrubbing ZeroClaw proactively redacts sensitive information from tool outputs before they are fed back into the LLM history. ```rust static SENSITIVE_KV_REGEX: LazyLock = LazyLock::new(|| { Regex::new(r#"(?i)(token|api[_-]?key|password|secret|user[_-]?key|bearer|credential)["']?\s*[:=]\s*(?:"([^"]{8,})"|'([^']{8,})'|([a-zA-Z0-9_\-\.]{8,}))"#).unwrap() }); ``` The scrubbing logic preserves the first 4 characters for context then appends `*[REDACTED]`. ### Autosave Meaningful exchanges are automatically persisted to memory based on message length: * `AUTOSAVE_MIN_MESSAGE_CHARS` = 20 *** ## Tool Serialization & Formats Tools are converted into the appropriate format for the specific LLM provider. ### tools_to_openai_format() This function serializes the tool registry into the standard OpenAI function-calling schema used by many providers. ```rust fn tools_to_openai_format(tools_registry: &[Box]) -> Vec { tools_registry .iter() .map(|tool| { serde_json::json!({ "type": "function", "function": { "name": tool.name(), "description": tool.description(), "parameters": tool.parameters_schema() } }) }) .collect() } ``` ### Parsing Priority The system handles multiple response formats to support different providers: 1. Native structured JSON (OpenAI style) 2. XML tags (``, ``) 3. Markdown code blocks with `tool_call` identifiers 4. Provider-specific shortened formats (GLM, etc.) *** ## Relevance to Luna Luna currently operates as a pure chat relay, streaming LLM responses without any capability to interact with the system or external APIs. This lack of a tool execution layer is the primary bottleneck preventing Luna from becoming an autonomous assistant. ### Bridging the Gap Implementing the agent loop pattern in Luna would involve: * Defining an `ITool` interface in C# that mirrors the Rust `Tool` trait. * Transitioning from a single request-response model to a recursive loop in the [[Core]] engine. * Integrating the tool loop with [[Skills]] to allow dynamic capability discovery. The implementation of this protocol will turn Luna from a passive interface into an active participant capable of executing commands and managing its own [[Memory]].