Enhance tool documentation and add new features
- Added module-level documentation for memory tools (`remember`, `recall`, `forget`) to clarify their purpose. - Improved documentation in `recall.rs` and `remember.rs` to describe the functionality and flow of memory entry operations. - Updated `mod.rs` to include descriptions for the tool trait and execution context. - Enhanced `plan.rs` with detailed comments on plan-mode signaling tools. - Documented text search tools in `search.rs` to explain their functionality. - Improved sequential-thinking tool documentation in `seqthink.rs`. - Added safety filter documentation in `shell_filter` for credential and git operations. - Enhanced utility tools documentation, including `cd`, `dir_cache_update`, and `todowrite`. - Improved rendering documentation in view modules (`chat`, `markdown`, `status`, `workflow`) to clarify rendering flows and purposes.
This commit is contained in:
@@ -1,8 +1,14 @@
|
||||
//! Construction of a `SubagentContext` from an `AgentDefinition`,
|
||||
//! including the default read-only tool set for reviewer agents.
|
||||
|
||||
use std::path::PathBuf;
|
||||
use super::spawn::AgentDefinition;
|
||||
|
||||
/// Default read-only tool names granted to `role == "reviewer"` agents.
|
||||
pub const REVIEWER_ALLOWED: &[&str] = &["read", "grep", "glob", "recall", "remember"];
|
||||
|
||||
/// Per-invocation configuration for a subagent: prompt, allowed tools,
|
||||
/// step budget, and the session directory it should operate against.
|
||||
pub struct SubagentContext {
|
||||
pub system_prompt: String,
|
||||
pub allowed_tools: Vec<String>,
|
||||
@@ -10,6 +16,14 @@ pub struct SubagentContext {
|
||||
pub session_dir: PathBuf,
|
||||
}
|
||||
|
||||
/// Build a `SubagentContext` from an `AgentDefinition`.
|
||||
///
|
||||
/// Flow: copy optional `allowed_tools` from the def -> fall back to the
|
||||
/// reviewer-allowlist when the def has none and the role is "reviewer" ->
|
||||
/// fall back to an empty list (i.e. "all tools allowed") for other roles.
|
||||
///
|
||||
/// Return: a context with empty `system_prompt` and `session_dir`,
|
||||
/// `max_steps = 25`, and the resolved allowed-tool list.
|
||||
pub fn build_subagent_context(def: AgentDefinition) -> SubagentContext {
|
||||
let allowed_tools = def.allowed_tools.clone().unwrap_or_else(|| {
|
||||
if def.role == "reviewer" {
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
//! Subagent execution loop: drive an LLM conversation, gate tool calls
|
||||
//! against the context's allowlist, run tools, and stream progress events
|
||||
//! to the parent via an mpsc channel.
|
||||
|
||||
use tokio::sync::mpsc;
|
||||
use crate::dto::chat::message::ChatMessage;
|
||||
use crate::dto::provider::request::ToolDef;
|
||||
@@ -5,12 +9,20 @@ use crate::tool::{all_tools, tool_defs, tool_is_risky};
|
||||
use super::context::SubagentContext;
|
||||
use super::event::SubagentEvent;
|
||||
|
||||
/// Upper bound on agent loop steps; effectively unbounded (`usize::MAX`).
|
||||
#[allow(dead_code)]
|
||||
pub const MAX_AGENT_STEPS: usize = usize::MAX;
|
||||
|
||||
/// Maps a subagent's allowed tool names to concrete Tool trait objects and
|
||||
/// OpenAI-style tool definitions. When `allowed_tools` is empty every tool is
|
||||
/// available; otherwise only explicitly allowed ones are included.
|
||||
/// OpenAI-style tool definitions.
|
||||
///
|
||||
/// Flow: load `all_tools()` → if `allowed_tools` is empty, use all; else
|
||||
/// filter by membership → derive `ToolDef`s for the LLM.
|
||||
///
|
||||
/// Why: an empty allowlist means "no restriction" (matches
|
||||
/// `build_subagent_context`'s default for non-reviewer roles).
|
||||
///
|
||||
/// Return: `(tool impls, schema defs)` for the subagent to use.
|
||||
fn build_subagent_tools(allowed_tools: &[String]) -> (Vec<Box<dyn crate::tool::Tool>>, Vec<ToolDef>) {
|
||||
let all = all_tools();
|
||||
let filtered: Vec<Box<dyn crate::tool::Tool>> = if allowed_tools.is_empty() {
|
||||
@@ -24,9 +36,16 @@ fn build_subagent_tools(allowed_tools: &[String]) -> (Vec<Box<dyn crate::tool::T
|
||||
(filtered, defs)
|
||||
}
|
||||
|
||||
/// Resolves the API key, model, and base URL from the persisted application
|
||||
/// configuration rather than environment variables, matching how the main agent
|
||||
/// resolves its credentials.
|
||||
/// Resolve the API key, model, and base URL from persisted app config.
|
||||
///
|
||||
/// Flow: try the settings key for the active provider → fall back to the
|
||||
/// provider's `api_key_env` env-var → fall back to the provider's
|
||||
/// `default_api_key` → fall back to an empty string.
|
||||
///
|
||||
/// Why: matches the main agent's credential resolution exactly, so
|
||||
/// subagents automatically inherit the same provider settings.
|
||||
///
|
||||
/// Return: `(api_key, model, optional_base_url)`.
|
||||
fn resolve_provider_config() -> (String, String, Option<String>) {
|
||||
let settings = crate::model::settings::Settings::load();
|
||||
let app_config = crate::model::app_config::AppConfig::load();
|
||||
@@ -54,6 +73,20 @@ fn resolve_provider_config() -> (String, String, Option<String>) {
|
||||
(api_key, model, base_url)
|
||||
}
|
||||
|
||||
/// Synchronous subagent entry point: run up to `ctx.max_steps` iterations
|
||||
/// of the LLM tool loop.
|
||||
///
|
||||
/// Flow: inject system prompt → for each step: resolve provider config,
|
||||
/// build an LLM client, call `chat_with_tools_non_streaming`, process tool
|
||||
/// calls or collect text output → send `SubagentEvent`s on `tx` → break on
|
||||
/// first text-only (non-empty) response.
|
||||
///
|
||||
/// Why: runs synchronously on a dedicated thread so the main async event
|
||||
/// loop is not blocked. Tool gating prevents restricted or risky tools from
|
||||
/// executing unless explicitly allowed.
|
||||
///
|
||||
/// Return: the concatenated text output, or an `anyhow::Error` if the LLM
|
||||
/// call fails at any step.
|
||||
pub fn run_subagent(ctx: SubagentContext, tx: mpsc::Sender<SubagentEvent>) -> anyhow::Result<String> {
|
||||
let mut output = String::new();
|
||||
let mut messages: Vec<ChatMessage> = Vec::new();
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
//! Event variants that a running subagent can emit to its parent via the
|
||||
//! shared mpsc channel.
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
/// Progress and outcome events emitted by `run_subagent` as it processes
|
||||
/// LLM responses and tool calls.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum SubagentEvent {
|
||||
StepCompleted {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
//! Subagent management: spawning, context building, engine loop, and
|
||||
//! progress events.
|
||||
|
||||
pub mod context;
|
||||
pub mod engine;
|
||||
pub mod event;
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
//! AgentDefinition -- declarative specification for instantiating a
|
||||
//! subagent from workflow scripts or programmatic calls.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Declarative specification for instantiating a subagent: name, role,
|
||||
/// optional system prompt, allowed tools, step budget, and temperature.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AgentDefinition {
|
||||
pub name: String,
|
||||
@@ -11,6 +16,8 @@ pub struct AgentDefinition {
|
||||
}
|
||||
|
||||
impl AgentDefinition {
|
||||
/// Create an agent definition with the required name and role; all
|
||||
/// optional fields start as `None`.
|
||||
pub fn new(name: String, role: String) -> Self {
|
||||
AgentDefinition {
|
||||
name,
|
||||
@@ -22,6 +29,7 @@ impl AgentDefinition {
|
||||
}
|
||||
}
|
||||
|
||||
/// Builder method: limit this agent to at most `steps` LLM calls.
|
||||
pub fn with_max_steps(mut self, steps: usize) -> Self {
|
||||
self.max_steps = Some(steps);
|
||||
self
|
||||
|
||||
Reference in New Issue
Block a user