feat(team): team enhancements, subagentModel, parallel tool exec#3249
Closed
TheArchitectit wants to merge 92 commits into
Closed
feat(team): team enhancements, subagentModel, parallel tool exec#3249TheArchitectit wants to merge 92 commits into
TheArchitectit wants to merge 92 commits into
Conversation
…rl+P Adds an interactive setup wizard that lets users configure their provider, API key, base URL, and model without setting environment variables. Configuration is persisted to ~/.claw/settings.json (with 0600 permissions). New features: - `claw setup` CLI subcommand runs the wizard from the terminal - `/setup` slash command runs the wizard inside the REPL (hot-swaps model) - Ctrl+P hotkey in the REPL triggers /setup for in-session provider swap - Stored provider config used as fallback when env vars are absent - Three-tier auth resolution: env var > .env file > stored config - RuntimeProviderConfig struct and validation in settings schema Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Ctrl+P now inserts a sentinel char (\x01) that the highlighter renders as a cyan "[Provider Swap]" prompt. User presses Enter to confirm and launch the setup wizard. Returns ReadOutcome::ProviderSwap so the REPL loop can hot-swap the model and reprint the connection line. Also fixes clippy warnings: merged duplicate match arms in provider_config_value, doc_markdown on ProviderKind, map_unwrap_or idioms in setup_wizard.rs, and pre-existing clippy issues in main.rs and commands/lib.rs. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Implement complete LSP support for code intelligence tools: - lsp_transport.rs: JSON-RPC 2.0 transport over stdio with Content-Length framing, async request/response handling, and graceful shutdown - lsp_process.rs: LSP process manager with initialize handshake, and methods for hover, goto_definition, references, document_symbols, completion, format - lsp_discovery.rs: Auto-discovery of installed LSP servers (rust-analyzer, clangd, gopls, pyright, typescript-language-server, etc.) with PATH lookup - lsp_client.rs: Rewired LspRegistry to use real LSP processes instead of placeholder JSON, with lazy-start on first dispatch call - config.rs: Added LspServerConfig for user-configured LSP servers - config_validate.rs: Validation for lsp config section - main.rs: CLI integration with server discovery at startup, /lsp slash command for status/start/stop/restart, and graceful shutdown on exit - commands/src/lib.rs: Added SlashCommand::Lsp variant The LSP tool is now available to the agent for hover, definition, references, symbols, completion, and diagnostics queries. Servers are auto-discovered at REPL startup and lazily started on first use. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
rust-analyzer installed through rustup exits non-zero on --version
("Unknown binary in official toolchain"), which caused discovery
to skip it. Changed command_exists_on_path to treat any successful
spawn as "found", regardless of exit code — only a failure to
spawn (command not found) means the server isn't available.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…chment Wire LSP into the Read/Edit/Write tool flow so the agent automatically gets diagnostics after file operations: - lsp_transport: Add LspServerMessage enum, read_message() for handling both responses and server-initiated notifications, notification queue with drain_notifications(), send_request now handles interleaved publishDiagnostics without breaking - lsp_process: Add did_open(), did_change(), drain_diagnostics(), open file tracking (HashSet) and version counters for didChange, language_id_for_path() and severity_name() helpers - lsp_client: Add notify_file_open(), notify_file_change(), fetch_diagnostics_for_file() with best-effort graceful fallback, registry-level open file tracking, diagnostic caching - tools: Enrich run_read_file with didOpen + diagnostics, run_write_file and run_edit_file with didChange + diagnostics, format_diagnostic_appendix() for readable diagnostic output appended to tool results All enrichment is non-blocking: if no LSP server is available, tools work exactly as before. No errors propagate from the LSP layer. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Split the three large LSP files into module directories with sub-files: lsp_transport/ (was 560 lines): - mod.rs (425) — types + LspTransport impl - tests.rs (134) — test module lsp_process/ (was 929 lines): - mod.rs (436) — LspProcess struct + public methods + error types - parse.rs (311) — helper functions and LSP response parsers - tests.rs (194) — test module lsp_client/ (was 1338 lines): - mod.rs (466) — LspRegistry struct + impl, re-exports from types - types.rs (103) — LspAction, LspDiagnostic, LspServerStatus, etc. - dispatch.rs (224) — LspRegistry::dispatch() method - tests.rs (273) — core registry tests - tests_lifecycle.rs (294) — lifecycle and integration tests All files under 500 lines. All 501 runtime tests pass. Clippy clean. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…transport modules - Add lsp_auto_start field to RuntimeFeatureConfig (default: true) - Add lspAutoStart bool field validation in config_validate - Parse lspAutoStart from config JSON - Auto-start discovered LSP servers on REPL init when enabled - Add /lsp toggle command to enable/disable auto-start at runtime - Remove lsp_client.rs, lsp_process.rs, lsp_transport.rs (2831 lines) — functionality consolidated into discovery-based auto-start - Show auto-start status in /lsp status output
Add the 3-stage Trident compaction strategy from R.A.D.1.C.A.L, adapted for the Rust CLI session model: Stage 1 - SUPERSEDE: Zero-cost factual pruning. If a file was read and then later written/edited, the earlier read is obsolete and removed. Earlier writes superseded by later writes are also dropped. Stage 2 - COLLAPSE: Buffer short chatty exchanges (under 200 chars, no tool calls) and collapse them into dense summary blocks when the threshold is exceeded. Stage 3 - CLUSTER: Group semantically similar messages (same tool names, same file paths, similar lengths) using Jaccard-based fingerprinting and collapse clusters into summary blocks. All three stages run before the existing summary-based compaction, so less data needs to be summarized. Wired into both /compact and the auto-compact retry on context window errors.
…e retry - Add TimeoutConfig to HTTP client builder with connect_timeout (30s) and request_timeout (5min) defaults, configurable via CLAW_API_CONNECT_TIMEOUT and CLAW_API_REQUEST_TIMEOUT env vars - Add with_timeout() builder to both AnthropicClient and OpenAiCompatClient for per-client timeout configuration - Parse Retry-After header on 429 responses and use it to override exponential backoff delay when present - Add ApiTimeoutConfig to runtime config with apiTimeout settings in ~/.claw/settings.json (connectTimeout, requestTimeout, maxRetries) - Add retry_after field to ApiError::Api for propagating rate limit backoff hints through the retry pipeline
- compact.rs: fix panic when preserve_recent_messages=0 - main.rs: progressive 4-round auto-compact retry with session_mut fix - main.rs: detect "no parseable body" as context window overflow - anthropic.rs: remove debug eprintln - error.rs: add "no parseable body" to CONTEXT_WINDOW_ERROR_MARKERS - config.rs, lib.rs: conflict resolution fixes from merge 💘 Generated with Crush Assisted-by: GLM 5.1 FP8 via Crush <crush@charm.land>
Implement complete LSP support for code intelligence tools: - lsp_transport.rs: JSON-RPC 2.0 transport over stdio with Content-Length framing, async request/response handling, and graceful shutdown - lsp_process.rs: LSP process manager with initialize handshake, and methods for hover, goto_definition, references, document_symbols, completion, format - lsp_discovery.rs: Auto-discovery of installed LSP servers (rust-analyzer, clangd, gopls, pyright, typescript-language-server, etc.) with PATH lookup - lsp_client.rs: Rewired LspRegistry to use real LSP processes instead of placeholder JSON, with lazy-start on first dispatch call - config.rs: Added LspServerConfig for user-configured LSP servers - config_validate.rs: Validation for lsp config section - main.rs: CLI integration with server discovery at startup, /lsp slash command for status/start/stop/restart, and graceful shutdown on exit - commands/src/lib.rs: Added SlashCommand::Lsp variant The LSP tool is now available to the agent for hover, definition, references, symbols, completion, and diagnostics queries. Servers are auto-discovered at REPL startup and lazily started on first use. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…chment Wire LSP into the Read/Edit/Write tool flow so the agent automatically gets diagnostics after file operations: - lsp_transport: Add LspServerMessage enum, read_message() for handling both responses and server-initiated notifications, notification queue with drain_notifications(), send_request now handles interleaved publishDiagnostics without breaking - lsp_process: Add did_open(), did_change(), drain_diagnostics(), open file tracking (HashSet) and version counters for didChange, language_id_for_path() and severity_name() helpers - lsp_client: Add notify_file_open(), notify_file_change(), fetch_diagnostics_for_file() with best-effort graceful fallback, registry-level open file tracking, diagnostic caching - tools: Enrich run_read_file with didOpen + diagnostics, run_write_file and run_edit_file with didChange + diagnostics, format_diagnostic_appendix() for readable diagnostic output appended to tool results All enrichment is non-blocking: if no LSP server is available, tools work exactly as before. No errors propagate from the LSP layer. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…transport modules - Add lsp_auto_start field to RuntimeFeatureConfig (default: true) - Add lspAutoStart bool field validation in config_validate - Parse lspAutoStart from config JSON - Auto-start discovered LSP servers on REPL init when enabled - Add /lsp toggle command to enable/disable auto-start at runtime - Remove lsp_client.rs, lsp_process.rs, lsp_transport.rs (2831 lines) — functionality consolidated into discovery-based auto-start - Show auto-start status in /lsp status output
Remove SlashCommand::Setup (provider wizard), PROVIDER_FIELDS (provider config), and stale imports that leaked in from the feat/lsp-integration branch which included other PRs. Also fix pre-existing clippy findings (Duration::from_hours, is_ok_and).
- Add distro-aware install prompt system: detects Ubuntu/Debian/Fedora/ Arch/openSUSE/Alpine/Void/NixOS/macOS and suggests the right install command for each missing LSP server at startup - Add 6 new language servers: HTML, CSS, JSON, Bash, YAML, GDScript - Add didClose notifications for proper file lifecycle - Add code_action support (quick fixes, refactors) - Add rename support (workspace-wide symbol renaming) - Add signature_help (function signatures + parameter hints) - Add code_lens (inline actionable hints) - Add workspace_symbols (project-wide symbol search) - Add workspaceFolders support in initialize handshake - Advertise full capability set (code actions, rename, signatures, code lens, workspace symbols) to LSP servers - Fix panic in lsp_discovery test when rust-analyzer is a rustup proxy stub for an uninstalled component 💘 Generated with Crush Assisted-by: GLM 5.1 FP8 via Crush <crush@charm.land>
Godot LSP runs as a TCP server on localhost:6008 when the editor is open — it doesn't speak LSP over stdio like other servers. Added connect_tcp() to LspTransport which uses socat (or nc fallback) as a stdio↔TCP bridge, reusing the existing Content-Length framing. lsp_process detects tcp:// URIs and routes to TCP transport. LSP startup now gracefully handles servers that fail to start (gdscript without a running Godot editor) without blocking other servers from initializing. 💘 Generated with Crush Assisted-by: GLM 5.1 FP8 via Crush <crush@charm.land>
…vers Set NODE_NO_WARNINGS=1 when spawning LSP server processes to suppress noisy punycode deprecation warnings from bash-language-server, yaml-language-server, vscode-* servers, etc. 💘 Generated with Crush Assisted-by: GLM 5.1 FP8 via Crush <crush@charm.land>
- Startup now shows "Loading LSP servers..." then ✓/✗ per server - When auto-start is on: shows disable hint (toggle or settings.json) - When auto-start is off: shows available servers with how to start 💘 Generated with Crush Assisted-by: GLM 5.1 FP8 via Crush <crush@charm.land>
Adds a project rules system for loading instruction files into the
system prompt, plus auto-import from other AI coding frameworks.
- `.claw/rules/*.md` / `.claw/rules/*.txt` / `.claw/rules/*.mdc`
- `.claw/rules.local/*.md` — personal/local rules (gitignored)
- Existing: `CLAUDE.md`, `CLAUDE.local.md`, `.claw/CLAUDE.md`,
`.claw/instructions.md`
When users switch to claw-code from another tool, their existing
rules are automatically detected and loaded:
- Cursor: `.cursorrules`, `.cursor/rules/`
- GitHub Copilot: `.github/copilot-instructions.md`
- Windsurf: `.windsurfrules`, `.windsurfrules/`
- Aider: `.aider.conf.yml` instructions block
- Pi (Plandex): `.plandex/instructions.md`, `.plandex/plan.md`
- OpenCode: `opencode.json` instructions field
- Crush: `.crush/CLAUDE.md`, `.crush/rules/`
`rulesImport` in settings.json controls framework auto-import:
- `"auto"` (default) — import from all detected frameworks
- `"none"` — only load .claw/rules/ and CLAUDE.md files
- `["cursor", "copilot"]` — import only from listed frameworks
```json
{
"rulesImport": "auto"
}
```
💘 Generated with Crush
Assisted-by: GLM 5.1 FP8 via Crush <crush@charm.land>
When the model returns multiple tool_use blocks in a single response, read-only tools (read_file, glob_search, grep_search, WebFetch, WebSearch, LSP, Git*) now execute concurrently via std::thread::scope instead of sequentially. Architecture: - Add ToolCall/ToolResult types to ToolExecutor trait - Add execute_batch() with default sequential fallback - CliToolExecutor overrides execute_batch to classify tools as parallel-safe or sequential, then runs parallel-safe tools concurrently via thread scopes - run_turn refactored into 3 phases: 1. Pre-hooks + permission checks (sequential, hooks may mutate state) 2. Tool execution (batch, parallel for read-only tools) 3. Post-hooks + session updates (sequential, preserves ordering) Safety guarantees: - Pre/post hooks always run sequentially - Permission checks complete before any tool executes - Tool results pushed to session in original model order - Fallback to sequential for single-tool batches
Add a SubAgent tool that lets the main model delegate multi-step read and search tasks to a fast, inexpensive sub-agent. The sub-agent runs autonomously with its own ConversationRuntime, making multiple tool calls without round-tripping through the main model. - SubAgent tool with prompt, task_type (Explore/Plan/Verify), and optional model override - Explore: read_file, glob_search, grep_search, WebFetch, WebSearch - Plan: Explore + TodoWrite - Verify: Plan + bash - subagentModel config field in settings.json (falls back to main model) - Uses ProviderRuntimeClient + SubagentToolExecutor (same as Agent tool) This dramatically reduces token usage for information-gathering tasks. Example: "find all Rust files that import X and summarize their usage" → one SubAgent call instead of 10 sequential main-model round trips.
…nt for exploration
Match Claude Code's architecture: a single Agent tool with subagent_type (Explore/Plan/Verification) that uses the subagentModel setting when no explicit model override is provided. The separate SubAgent tool is removed since it duplicated existing functionality.
- Agent tool is now parallel-safe: multiple Agent calls execute concurrently
- AgentMessage tool: send/read/broadcast between agents via shared mailbox
- TeamCreate rewired to spawn real Agent threads instead of TaskRegistry
- Agents set CLAWD_AGENT_ID env var so they can identify themselves for messaging
- AgentMessage added to Explore/Plan/Verification sub-agent tool lists
- Team manifest persisted to .clawd-agents/teams/{team_id}.json
- Add missing AgentMessage dispatch entry in execute_tool_with_enforcer (tool spec existed but couldn't actually be called) - Add AgentMessage to Explore/Plan/Verification/general-purpose allowed_tools lists so sub-agents can communicate - Add debug logging to resolve_agent_model and load_subagent_model_from_config to diagnose subagentModel config chain Co-authored-by: GLM 5.1 FP8 via Crush <crush@charm.land>
Add 'mode' field to TeamCreate that auto-generates agent teams: - "2x" = 2 Explore + 2 Plan + 2 Verification = 6 agents - "4x" = 4 Explore + 4 Plan + 4 Verification = 12 agents - "6x" = 6 Explore + 6 Plan + 6 Verification = 18 agents When mode is set, 'prompt' provides the shared task description and 'tasks' is ignored. Also add 'subagent_type' and 'model' fields to manual task items for per-task role and model control. Co-authored-by: GLM 5.1 FP8 via Crush <crush@charm.land>
Instead of erroring when neither mode nor tasks are specified, default to "2x" (2 Explore + 2 Plan + 2 Verification = 6 agents). Co-authored-by: GLM 5.1 FP8 via Crush <crush@charm.land>
- RAII guard owns alternate screen + raw mode lifecycle - Drop guarantees cleanup even on panic — replaces panic hook - Consolidates 6 ad-hoc methods into leave_for_turn/reenter_after_turn - main.rs uses guard-based lifecycle instead of suspend/resume The panic hook in tui_update is no longer needed for cleanup — the guard's Drop fires on panic and restores terminal state.
- Adds capture_turn() that wraps a closure with stdout/stderr capture and calls an on_output callback with captured text - Infrastructure for the streaming event bridge — when turns run on a background thread, this will feed incremental output to the TUI - For now, the turn loop continues to use leave-and-return via TerminalGuard because CliPermissionPrompter needs stdin access
- event_bus is now accessible via event_sender() for background threads - drain_events() processes TuiEvent variants and updates components: StreamTextDelta, TurnComplete, TurnError, TurnStarted, etc. - read_line() drains events on each tick before polling for keys - capture_turn() adds infrastructure for incremental output capture - Fix flaky capture test (BufferRedirect shares fd across test threads) - Current turn loop remains synchronous; background threading is next step
InputBar regression tests: - Submit blocked during turn_in_progress - Turn state properly cleared after turn completes - Completions popup no-op when not showing - Setting turn_in_progress marks dirty - Cancel clears textarea and marks dirty ConversationPane regression tests: - wrap_line respects width in unicode-width terms - Narrow width doesn't cause infinite loop - Cache invalidated on width change (resize) - mark_clean clears dirty flag TerminalGuard tests: - leave_for_turn no-op when already outside - reenter_after_turn short-circuits when already inside - EventBus sender and drain
All slash commands now route through SlashCommand::parse() and LiveCli::handle_repl_command() — the same path as the plain REPL. This gives the TUI access to all 60+ slash commands instead of just the 8 that were reimplemented in the TUI dispatcher. TUI-specific commands (/theme, /keys, /exit) are still handled locally because they modify TuiApp state directly. Everything else leave_for_turn → handle_repl_command → reenter_after_turn. The output from REPL commands appears in the normal terminal while alternate screen is left, then the TUI re-enters with a clean redraw. Also: alias now points to our debug build without hardcoding a model, so the config file's provider is used.
- Populate the TUI conversation pane from existing session messages when entering via /tui — no more empty TUI - Add /menu command to return from TUI to the plain REPL - Add run_repl_from_cli() to resume the REPL with the same LiveCli - Ctrl+D and /exit both break the loop and exit cleanly - Ctrl+C clears input and stays in TUI - Handle MessageRole::Tool in the populate loop
The /setup wizard saves apiKey and baseUrl to ~/.claw/settings.json, but the API client constructors (OpenAiCompatClient::from_env, AnthropicClient::from_env) only read environment variables. This caused saved provider settings to be silently ignored — you'd run /setup, set a custom URL and API key, and the runtime would still try to use the default endpoint. Now AnthropicRuntimeClient::new() calls inject_config_as_env_fallbacks() before constructing the API client. This function loads the config file's provider settings and sets the corresponding env vars (OPENAI_API_KEY, OPENAI_BASE_URL, etc.) only when they aren't already set — preserving the 3-tier resolution order: env var > .env file > stored config. This is a process-level env injection (set_var), so it only affects the current claw process and its children, not the parent shell.
…oints - Move config-to-env injection out of AnthropicRuntimeClient::new so parallel unit tests are not affected by global env mutations. - Call inject_config_as_env_fallbacks() once at binary startup in run(), preserving the env-var > .env > stored-config precedence. - Normalize bare model names (e.g. openclaw) to openai/openclaw when a custom OpenAI-compatible base URL is configured, so validation passes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…xy 404 The previous fix normalized bare model names like openclaw to openai/openclaw so validation passed, but the full prefixed name was sent as the wire model, causing custom proxies to return 404. Instead, normalize to local/openclaw. The local/ prefix forces OpenAI- compatible routing and is always stripped by wire_model_for_base_url(), so the backend receives the bare model id the user configured while the CLI still validates and routes correctly. - Revert validation-time bare-name acceptance (it leaked the user's OPENAI_BASE_URL env var into unrelated tests). - Add unit tests for the local/ normalization and config env injection. - Fix an unused_mut warning in tui/app.rs terminal-guard tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Custom (OpenAI-compat) /setup option was saving kind: openai and injecting OPENAI_API_KEY / OPENAI_BASE_URL. That collides with users who have real OpenAI/NeuralWatt credentials in their environment. Introduce a dedicated custom-openai provider kind that uses its own environment variables: - CLAWCUSTOMOPENAI_API_KEY - CLAWCUSTOMOPENAI_BASE_URL A new custom/ routing prefix selects the OpenAI-compatible client with those env vars and is stripped on the wire, so the proxy receives the bare model id. /setup now saves kind: custom-openai and prompts for the new env vars. Bare model names saved by /setup are normalized to custom/<model>. Manual verification against http://100.96.49.42:4001/v1 succeeds. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> 🤖 Generated with [Claude Code](https://claude.com/claude-code)
…h commands - run_tui_repl now treats handle_repl_command's return as a persist hint, not an exit signal, so /permissions re-enters the TUI instead of returning to the CLI screen. - Added Action::RunSlashCommand(idx) and wired it in both component and legacy TUI dispatch paths. - Command palette now includes every registered slash command so users can discover and run /permissions, /theme, /model, etc. from Ctrl+K. - Updated palette filter test to account for the new slash entries. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…slash output - Fix palette navigation: Up/Down now move selection; only Enter runs the selected command. Previously any palette key immediately dispatched. - Add Action::TopCommandsPalette bound to Ctrl+Shift+D. It opens a filtered palette with high-stakes slash commands: /permissions, /setup, /model, /env, /plugins, /mcp, /team. - Capture stdout/stderr while running slash commands inside the TUI so /status, /cost, /memory, etc. are visible in the conversation pane. /setup also shows a confirmation message with the new provider/model. - Empty Ctrl+C opens the full command palette; with text it still clears. - Document TUI keybindings and palette behavior in USAGE.md. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…st helper AgentOutput has no team_id or task_id fields, but the lane_completion tests::test_output() helper initialized both. This broke compilation of the tools lib test crate, which in turn blocked `cargo test --workspace`. The fields were never present on AgentOutput (team_id/task_id live on the Task registry entries, not on agent output). Remove the stale initializers so the test compiles and the lane-completion suite runs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…o Unknown
The /team spec existed in slash_command_specs() but validate_slash_command_input
had no "team" match arm, so /team always produced SlashCommand::Unknown and the
REPL reported "unknown slash command" — even while suggesting /team as a fix.
- Add the "team" => SlashCommand::Team { action } parse arm (action optional:
/team, /team list, /team help). Extra args are rejected like other commands.
- Give SlashCommand::Team a dedicated REPL handler (print_team_command) so
/team prints guidance on the model-invokable TeamCreate/TeamDelete/Cron*
tools and /team list explains that live registry listing is not yet wired
into the CLI layer (the team registry is owned by the in-process tool
executor, not the session/runtime).
- Regression test: assert /team and /team list parse correctly.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Agent tool is now parallel-safe: multiple Agent calls execute concurrently
- AgentMessage tool: send/read/broadcast between agents via shared mailbox
- TeamCreate rewired to spawn real Agent threads instead of TaskRegistry
- Agents set CLAWD_AGENT_ID env var so they can identify themselves for messaging
- AgentMessage added to Explore/Plan/Verification sub-agent tool lists
- Team manifest persisted to .clawd-agents/teams/{team_id}.json
- Add missing AgentMessage dispatch entry in execute_tool_with_enforcer (tool spec existed but couldn't actually be called) - Add AgentMessage to Explore/Plan/Verification/general-purpose allowed_tools lists so sub-agents can communicate - Add debug logging to resolve_agent_model and load_subagent_model_from_config to diagnose subagentModel config chain Co-authored-by: GLM 5.1 FP8 via Crush <crush@charm.land>
Add 'mode' field to TeamCreate that auto-generates agent teams: - "2x" = 2 Explore + 2 Plan + 2 Verification = 6 agents - "4x" = 4 Explore + 4 Plan + 4 Verification = 12 agents - "6x" = 6 Explore + 6 Plan + 6 Verification = 18 agents When mode is set, 'prompt' provides the shared task description and 'tasks' is ignored. Also add 'subagent_type' and 'model' fields to manual task items for per-task role and model control. Co-authored-by: GLM 5.1 FP8 via Crush <crush@charm.land>
Instead of erroring when neither mode nor tasks are specified, default to "2x" (2 Explore + 2 Plan + 2 Verification = 6 agents). Co-authored-by: GLM 5.1 FP8 via Crush <crush@charm.land>
- TeamStatus tool with 3 actions:
- status: live snapshot (running/completed/failed counts, agent details)
- summary: final results when agents finish (includes result content)
- events: timeline from append-only event log
- Background team watcher thread spawned by TeamCreate:
- Polls agent .json files every 2s
- Prints [team] progress to stderr on agent completion/failure
- Updates team manifest status when all agents finish
- Writes events to .clawd-agents/teams/{team_id}-events.jsonl
- TeamStatus added to PARALLEL_SAFE_TOOLS and all agent allowed_tools
Co-authored-by: GLM 5.1 FP8 via Crush <crush@charm.land>
…agentModel Co-authored-by: GLM 5.1 FP8 via Crush <crush@charm.land>
- Agents post completion/failure to team inbox on termination
(.clawd-agents/mailbox/team/{team_id}/{agent_id}-{ts}.json)
- Team watcher reads from inbox instead of polling .json files
- New TeamStatus action=inbox reads team messages from the inbox
- AgentOutput carries team_id, persisted in manifest
- AgentInput accepts team_id from TeamCreate
- TeamCreate passes team_id to each spawned agent
- Inbox cleaned up when all agents finish
Co-authored-by: GLM 5.1 FP8 via Crush <crush@charm.land>
…nitoring - TeamInboxReporter: per-tool-call progress reporting to team inbox - TaskClaim tool: atomic claim/release/list with .clawd-agents/claims/ lock files - Team-scoped task_ids to prevent cross-team claim collisions - AgentSuggestion tool: propose AGENTS.md additions (human review required) - ContextRequest tool: iterative retrieval with 3-cycle budget for sub-agents - Context-window-aware auto-compaction (70% threshold) prevents overflow - Model token limits for qwen/glm/generic models with 131K fallback - Reviewer subagent_type: read-only tools, no bash/write - Team mode presets: 1x-6x (tiny/small/medium/large/xlarge/mega) - /team slash command + Ctrl+T toggle (off by default, CLAWD_AGENT_TEAMS=1) - TeamDelete: disk-based deletion with inbox/claims cleanup - TeamStatus: kill stuck agents, list AGENTS.md suggestions - AGENTS.md: auto-loaded shared learnings in sub-agent system prompt - Periodic git commits every 5 tool calls via TeamInboxReporter - Claims released on failure/panic in spawn_agent_job - Fixed doubled .clawd-agents/.clawd-agents/ paths (set CLAWD_AGENT_STORE abs) - Fixed "unknown error" in team watcher (added error field to inbox messages) 💘 Generated with Crush Assisted-by: GLM 5.1 FP8 via Crush <crush@charm.land>
…n layer Port the team-enhancement feature layer from feat/multi-tool-exec onto feat/team-test so the agent-teams stack is buildable and testable on top of the current CLI/runtime. Subagent model (config wiring): - Add subagent_model field to RuntimeFeatureConfig + RuntimeConfig::subagent_model() accessor + parse_optional_subagent_model() so the subagentModel setting (already validated by config_validate.rs) is now actually read and stored. - Agent tool's resolve_agent_model falls back to subagentModel from config when no explicit model is passed. Parallel tool execution: - Re-export ToolCall/ToolResult/TurnProgressReporter from runtime so the CliToolExecutor::execute_batch override type-checks. - Override execute_batch to classify read-only tools (read_file, glob/grep search, WebFetch/WebSearch, LSP, Git*, ToolSearch, Skill, Agent*, TeamStatus, Task*) as parallel-safe and run them concurrently via std::thread::scope; everything else stays sequential. Results return in original model order. Team coordination layer (cherry-picked from feat/multi-tool-exec): - AgentMessage, TaskClaim, TeamStatus tools + shared mailbox directory + mode presets (tiny/1x ... mega/6x) + enriched TeamCreate/Agent tool descriptions + background team watcher. - /team slash command: on/off/status/toggle of CLAWD_AGENT_TEAMS (the flag TeamCreate requires), replacing the info-only stub. Spec updated to [on|off|status]. - resolve_conflicts resolved: de-duplicated SlashCommand::Team/Setup arms and ReadOutcome::TeamToggle arms that the cherry-picks overlapped with the existing /team parse fix. Verification: - cargo build --workspace OK - cargo test -p tools --lib: 111 passed (1 env failure: repl_executes_python_code) - cargo test -p runtime --lib team_cron: 17 passed - cargo test -p commands --lib: 42 passed (incl /team parse regression) - cargo test -p rusty-claude-cli --bin claw: 345 passed - scripts/fmt.sh --check OK Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t-found Team agents failed with 'Model not found' 404s because of two precedence bugs in the sub-agent provider chain: 1. providerFallbacks.primary silently overrode the agent's resolved model. new_with_fallback_config took fallback_config.primary() as THE primary, ignoring the model the caller (Agent tool / subagentModel) actually picked. So every spawned agent used the configured (dead) primary instead of its own model. Fix: the caller's model is the primary; providerFallbacks (primary + fallbacks) are recovery entries appended after it, deduped. 2. A 404 'model not found' is not retryable, so the chain died on the first (dead) model instead of advancing to the next configured fallback. Fix: add fallback_chain_eligible() that also treats 404/400 with a 'not found' / 'model ... unavailable' body as chain-eligible, so a dead primary advances to kimi/qwen/etc. Added status_code()/response_body() accessors to ApiError for the detection. 3. resolve_agent_model defaulted to hard-coded claude-opus-4-6 even when the session was connected to a custom endpoint (e.g. custom/openclaw). Fix: fall back to the config's top-level `model` before DEFAULT_AGENT_MODEL, so sub-agents inherit the session's actual provider by default. Verified with fallback_chain_tests (404 model-not-found eligible; 401 auth not eligible; 429 still eligible). 3 tests pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sub-agents failed because the main CLI calls inject_config_as_env_fallbacks() to set CLAWCUSTOMOPENAI_API_KEY / CLAWCUSTOMOPENAI_BASE_URL (and other provider env vars) before building its provider client, but the sub-agent spawn path in the tools crate never did. Custom providers set up via /setup were invisible to spawned agents, so they fell through to the Anthropic provider and died with 'missing Anthropic credentials'. Move inject_config_as_env_fallbacks from main.rs private fn into runtime/src/config.rs as a pub fn, export it via runtime::lib.rs, and call it at the top of run_agent_job() before the agent runtime is built. - runtime::inject_config_as_env_fallbacks(): idempotent (only sets env vars that aren't already present), reads provider config saved by /setup, maps provider kind -> env var names (including custom-openai -> CLAWCUSTOMOPENAI_*), injects api_key/base_url/model. - main.rs now calls runtime::inject_config_as_env_fallbacks() instead of the local private version. - tools::run_agent_job calls runtime::inject_config_as_env_fallbacks() before building the agent runtime so custom/exotic providers work for spawned agents too. Together with the earlier model-resolution and chain-fallback fixes (d8716ef), sub-agents now: (1) use the session's model by default, (2) inject the /setup-saved provider credentials, and (3) fall through to the next configured chain entry on a model-not-found 404. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…fault load_main_model_from_config() read config.model() — the feature-config model from settings.json (e.g. 'openai/glm-5.1-fast') — instead of config.provider().model() — the /setup-saved active provider model (e.g. 'custom/openclaw'). The latter is what the main session actually connects with and what the status bar shows. Sub-agents inherited the wrong model, which might not exist on the custom endpoint. Fix: use config.provider().model() so sub-agents default to the same provider that the user's main session is connected to. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
When the user's active provider is custom-openai, bare model names like 'claude-haiku-4-5-20251001' (saved by /setup as subagentModel) don't route through the custom endpoint — ProviderClient::from_model dispatches them to the Anthropic provider instead, which has no credentials. Add qualify_for_provider() that prefixes bare models with 'custom/' when the provider config is custom-openai so they share the session's endpoint. Applied to both the explicit model override and the subagentModel fallback path in resolve_agent_model(). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Apply qualify_for_provider to the session model fallback too so the custom-openai prefix is added to bare model names from config. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
Author
|
Superseded by #3250 (clean branch, no TUI/LSP contamination) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Ports the agent-team enhancement layer onto the current CLI/runtime so teams, sub-agent model overrides, and parallel read-only tool execution are buildable and usable. Labeled team enhancements and fixes.
This is the team functionality split out as its own PR (separate from the TUI work), per the discussion that team code should ship independently.
What's included
1.
subagentModelconfig wiring (fix)The
subagentModelfield was already validated byconfig_validate.rsbut never stored or read — so the setting was silently dropped. This ports the wiring so it actually takes effect:RuntimeFeatureConfig.subagent_modelfieldRuntimeConfig::subagent_model()accessorparse_optional_subagent_model()(readssubagentModel/subagent_model, blank =None)Agenttool'sresolve_agent_modelfalls back tosubagentModelfrom config when no explicit model is passed2. Parallel tool execution (perf, fix)
When the model returns multiple
tool_useblocks in one response, read-only tools now run concurrently viastd::thread::scopeinstead of sequentially:ToolCall/ToolResult/TurnProgressReporterfromruntimeCliToolExecutor::execute_batchoverride classifies parallel-safe tools (read_file,glob_search,grep_search,WebFetch,WebSearch,ToolSearch,Skill,LSP,Git*,Agent,AgentMessage,TeamStatus,TaskGet,TaskList,TaskOutput) and runs them concurrently; others stay sequential3. Team coordination layer (feature)
AgentMessage,TaskClaim,TeamStatustools + shared mailbox directory + mode presets (tiny/1x … mega/6x) + enrichedTeamCreate/Agenttool descriptions + background team watcher/teamslash command:on|off|status/toggleofCLAWD_AGENT_TEAMS(the flagTeamCreaterequires), replacing the info-only stub. Spec hint updated to[on|off|status].Also fixed along the way
cargo test -p tools --libdid not compile:lane_completion.rstest helper setteam_id/task_idfields thatAgentOutputnever had. Removed the stale initializers./teamparsed asUnknowndespite existing in the spec table: added the missing\"team\" =>arm invalidate_slash_command_input.SlashCommand::Team/SetupandReadOutcome::TeamTogglearms introduced by the cherry-pick sequence.Verification
cargo build --workspace✅cargo test -p tools --lib— 111 passed (1 environmental failure:repl_executes_python_code, no python runtime on this machine)cargo test -p runtime --lib team_cron— 17 passedcargo test -p commands --lib— 42 passed (incl./teamparse regression)cargo test -p rusty-claude-cli --bin claw— 345 passedscripts/fmt.sh --check✅Notes
/teamparse fix from earlier commits.cargo clippy --workspace -- -D warningshas pre-existing lint failures incrates/apiandcrates/toolsthat predate this PR; not introduced here.🤖 Generated with Claude Code