Repository navigation
agent tool
Source: src/tools/agent.rs · Added in 4.1.0 (#112); cost controls in 4.2.0 (#113).
An AI agent step implemented in Rust. No Python, no persistent agent process. Two frameworks:
framework |
Backend | Billing | Tools |
|---|---|---|---|
claude |
Anthropic Messages API (POST {endpoint}/v1/messages, anthropic-version: 2023-06-01) |
API key from auth:
|
declared in tools:, run by the host
|
claude-code |
Claude Code CLI subprocess: claude -p --output-format json --no-session-persistence --model <m>
|
the CLI's signed-in account | Claude Code's own, granted by allowed_tools / mcp_config
|
pub struct AgentSession { /* config, api_key, client, messages, turns, usage counters */ }
impl AgentSession {
pub async fn start(config: &ToolConfig, ctx: &ExecutionContext) -> Result<Self, ToolError>;
pub async fn next_turn(&mut self) -> Result<AgentTurn, ToolError>;
pub fn submit_tool_results(&mut self, outputs: Vec<AgentToolOutput>);
pub fn tool_spec(&self, name: &str) -> Option<&AgentToolSpec>;
pub fn config(&self) -> &AgentConfig;
pub fn turns(&self) -> u32;
}
pub enum AgentTurn { ToolCalls(Vec<AgentToolCall>), Final(serde_json::Value) }
pub struct AgentTool; // registry kind "agent"Also exported: AgentConfig, AgentFramework, AgentToolSpec, AgentToolCall, AgentToolOutput.
The agent's tools are run by the host, not by this crate. The CLI runs each tool as a sub-playbook, and its runner is synchronous and enters tokio itself, so running tools inside one async loop here would nest block_on. The host drives next_turn → runs the requested tools → submit_tool_results → next_turn. The session's whole state is its JSON message history, which is also the shape a callback-driven distributed runner would persist between blocks.
AgentTool (the registry entry) runs tool-free agents only. It renders config with TemplateEngine, starts a session, and takes one turn. A model that asks for a tool there is an error rather than a silent empty answer.
| Field | Applies to | Default | Notes |
|---|---|---|---|
framework |
both | required |
claude | claude-code; anything else fails to parse |
model, prompt
|
both | required | prompt is the first user message (stdin for claude-code) |
system |
both | — |
claude: system; claude-code: --append-system-prompt (keeps Claude Code's own) |
max_turns |
claude | 8 | must be ≥ 1 |
max_tokens |
claude | 4096 | per call |
temperature, endpoint
|
claude | — |
endpoint is the base URL (tests / compatible gateways) |
tools |
claude | [] |
{name, description, input_schema, …binding}; extra keys are a host binding, never sent to the model |
thinking |
claude | — | 4.2.0. disabled | adaptive → thinking: {type}
|
effort |
claude | — | 4.2.0. low | medium | high → output_config.effort
|
prompt_cache |
claude | true |
4.2.0. Cache breakpoint on the last block of the request |
claude_bin, allowed_tools, mcp_config, workdir
|
claude-code |
claude, none, none, inherited |
mcp_config is passed with --strict-mcp-config
|
ToolConfig.auth is required for claude and resolves through AuthResolver: ApiKey or Bearer → x-api-key. It is refused for claude-code, as is tools:. ToolConfig.timeout (default 300 s) bounds each HTTP call (claude) or the whole subprocess (claude-code).
{ "text": "...", "stop_reason": "end_turn", "truncated": false, "model": "...", "turns": 2,
"usage": { "input_tokens": 8, "output_tokens": 2667,
"cache_read_input_tokens": 2126, "cache_creation_input_tokens": 8158 } }claude-code returns text (the CLI's result), turns (num_turns), usage, "framework": "claude-code" and permission_denials.
| Guard | Prevents |
|---|---|
auth required for claude; no environment fallback |
a step spending credits it never declared |
max_turns checked before every call |
an agent looping without bound |
| max_tokens truncation is an error when the turn has no text or has tool calls; error names the content block types | an empty or half-written run reported as success. Haiku spent a whole summary turn on a thinking block and returned [thinking] only |
| API errors carry status + message, never the key | keys in logs |
claude-code removes ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKEN from the child env |
a subscription run silently billing API credits |
claude-code prompt on stdin, never argv |
variadic --allowed-tools / --mcp-config swallowing the prompt |
tracing spans use .instrument(), not a held enter() guard |
wrong span attribution across .await
|
| prompt cache marks only the request copy, not stored history | breakpoints accumulating past the API's limit |
-
noetl CLI ≥ 5.1.0 (
--runtime local) drivesAgentSessionfromPlaybookRunnerand runs each tool'splaybook:binding as a sub-playbook. See the cli wiki: kind: agent. -
worker / server: not wired. The server rejects
agentat registration (ai-meta#256).
Home · showcase and tutorials: noetl/agent-playbooks wiki · tracking: noetl/ai-meta#474