Skip to content

refactor(agent): unify chat loops and enrich Tool trait#24

Merged
odysa merged 1 commit into
mainfrom
refactor/unify-agent-loops-and-tool-metadata
Apr 21, 2026
Merged

refactor(agent): unify chat loops and enrich Tool trait#24
odysa merged 1 commit into
mainfrom
refactor/unify-agent-loops-and-tool-metadata

Conversation

@odysa

@odysa odysa commented Apr 20, 2026

Copy link
Copy Markdown
Owner

Summary

  • Collapse four near-identical agent loops (SimpleAgent, StreamingAgent, SubagentTool, PlanAgent) into two shared drivers: chat_loop (non-streaming) and stream_chat_loop (streaming).
  • Enrich the Tool trait with metadata — is_read_only, is_concurrent_safe, is_destructive, summary, validate_input — and change Tool::call to return ToolResult { content, is_truncated } instead of String.
  • Plan mode now picks visible tools via Tool::is_read_only() rather than a hardcoded ["bash","read","ask_user"] allowlist; plan_tool_names(...) is available when the read-only-ness depends on arguments.
  • Bound every loop with QueryConfig { max_turns, max_result_chars }; UTF-8-safe truncation happens once in ToolSet::execute_calls.
  • Split AgentEvent::ToolCall into ToolStart (summary) + ToolEnd (result preview) so UIs can render tool outputs.

Test plan

  • cargo x solution-check passes (fmt + clippy + tests)
  • cargo test -p mini-claw-code — all unit tests green
  • cargo run -p mini-claw-code --example tui — manual smoke of ToolStart/ToolEnd rendering
  • Plan-mode smoke: read-only tools visible, destructive tools blocked with a friendly error surfaced as tool output
  • Sub-agent smoke: child agent honors QueryConfig::max_turns = 10 default

Collapse the four near-identical agent loops (simple, streaming,
subagent, plan) into two shared drivers (chat_loop / stream_chat_loop),
and give the Tool trait metadata (is_read_only, is_concurrent_safe,
is_destructive, summary, validate_input) plus a ToolResult return type.

- Plan mode now decides visible tools via Tool::is_read_only() instead
  of a hardcoded ["bash","read","ask_user"] allowlist, with an opt-in
  plan_tool_names override for arg-dependent cases.
- QueryConfig bounds every loop (max_turns, max_result_chars) and
  UTF-8-safe truncation happens once in ToolSet::execute_calls.
- AgentEvent::ToolCall splits into ToolStart + ToolEnd so UIs can
  render tool results.
@odysa

odysa commented Apr 21, 2026

Copy link
Copy Markdown
Owner Author

Test plan results

  • cargo x solution-check — exit 0. Runs cargo fmt --check, cargo clippy -- -D warnings, and cargo test. All pass.
  • cargo test -p mini-claw-code268 unit tests + 2 doctests, all green. (covered by the solution-check run above)
  • TUI examplecargo check -p mini-claw-code --example tui compiles cleanly against the refactored AgentEvent::ToolStart / ToolEnd split (mini-claw-code/examples/tui.rs:146-162). Full interactive smoke (OpenRouter + TTY) not run from CI; leaving to the author for visual confirmation.
  • Plan-mode smoke — covered by the existing test suite, all passing on this branch:
    • test_plan_agent_plan_with_read_tool — read-only tools visible during planning
    • test_plan_agent_plan_blocks_write_tool / test_plan_agent_plan_blocks_edit_tool — destructive tools blocked, error surfaced as a ToolResult::error the model can see
    • test_plan_agent_read_only_overrideplan_tool_names(...) override works
    • test_plan_agent_full_plan_then_execute — plan→exit_plan→execute flow intact
  • Sub-agent smoke — covered by:
    • test_subagent_builder_pattern — default QueryConfig::max_turns = 10 on SubagentTool
    • test_subagent_max_turns_exceeded — child bails with an error result (not a panic) when the bound is hit

@odysa odysa merged commit 3d92fe8 into main Apr 21, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant