feat(team): team enhancements, subagentModel, parallel tool exec - #3250
TheArchitectit wants to merge 775 commits into
Conversation
…pty success; now returns unknown_option error
… not-found hint:null
…lugin_source_not_found instead of unknown+null
…now return non-null hints via fallback table
…ng not-found instead of unexpected_extra_args
…limited usage string Parity with ultraworkers#791 (config extra-arg fix). The plugins arg parser emitted 'unexpected extra arguments after claw plugins show ...' with no newline delimiter, so split_error_hint returned None. Added usage hint after newline. 60 CLI contract tests pass.
…ed usage string claw '' and claw ' ' returned empty_prompt + hint:null because the error message had no newline delimiter. Added usage hint. 61 CLI contract tests pass.
…tion classifier arms Two classifier arms had no corresponding assert_eq! in test_classify_error_kind_returns_correct_discriminants: invalid_history_count (both prefix and contains paths) and unknown_option (ultraworkers#790). Now 49/39 = full coverage of all classify_error_kind return values.
…(plugins extra-arg, empty-prompt, classifier coverage)
…rror_kind, hint, and message fields
… now include hint field
… silently returned empty success
… returned wrong error instead of unexpected_extra_args
…returned empty success instead of error
…ltraworkers#3161) Keep malformed diff invocations with trailing JSON format flags on the parser error path and lock the contract with focused output-format regressions. Constraint: Do not touch tracked .omx state files. Rejected: Repeating direct binary smoke loops | local auth/provider configuration intercepts those invocations and obscures parser behavior. Confidence: high Scope-risk: narrow Tested: git diff --check; cargo fmt --check; cargo test -p rusty-claude-cli diff_extra_args_have_typed_error_kind_and_hint_766 --test output_format_contract; cargo test -p rusty-claude-cli diff_trailing_json_after_malformed_args_is_bounded_json_3129 --test output_format_contract; cargo test -p rusty-claude-cli diff_non_git_dir_has_error_kind_and_hint_801 --test output_format_contract
… empty success instead of error
…ailures Extend auto-compaction error detection to handle additional error patterns from llama.cpp backends: 'Context size has been exceeded', 'exceed_context_size_error', 'exceeds the available context size'. Also recover from reqwest 'error decoding response body' errors — some llama.cpp instances return a non-SSE plaintext HTTP 500 on context overflow, causing the SSE deserializer to fail. Add dynamic threshold adaptation: parse server-reported context window size from error messages (e.g., '(81920 tokens)') and set the auto- compaction trigger at 70% of that value. This replaces the need for a hardcoded threshold, adapting automatically to any backend's limits. This patch was developed with assistance from OpenCode and local Qwen 3.6 API server.
|
Team enhancements with subagentModel and parallel tool execution is a solid feature addition. The ability to configure per-team subagent models will help with cost optimization and specialization. |
No behavior change. Move the inline probe out of unshare_user_namespace_works into a reusable unshare_probe helper and a cached working_unshare_mapping() that picks the first working candidate from UNSHARE_MAPPING_CANDIDATES, so the launcher and the capability probe share one code path.
…icted Plain `unshare --user --map-root-user` fails on kernels and containers that block unprivileged writes to /proc/self/uid_map (e.g. GitHub Actions, restricted AppArmor profiles). On those systems util-linux delegates to the setuid newuidmap/newgidmap helpers when --map-auto is also present. Add the combined form as a fallback candidate and build the launcher args from the probed mapping, so systems without newuidmap/newgidmap or a /etc/subuid range keep using the plain form.
… fallback The fallback candidate relies on the setuid newuidmap/newgidmap helpers (uidmap package) plus a subuid/subgid range for the current user. Note in the candidate docs that the startup probe rejects the candidate when those are missing, so the plain --map-root-user form is used instead.
The startup probe validated only the mapping flags against the trivial program `true`, but the real launcher always adds --mount --ipc --pid --uts --fork. On environments where the user namespace is created but mount propagation inside it is restricted (e.g. AppArmor-restricted CI runners), the fallback mapping passed the probe and the sandbox activated, yet every sandboxed command died with "cannot change root filesystem propagation: Permission denied", silently returning empty tool output and breaking the mock parity suite. The candidates now define the complete static launcher shape (mapping flags + namespace flags), so probe success implies launch success; the launcher reuses the candidate instead of re-appending the namespace flags, keeping probe and launch as one source of truth. The order-guarding test asserts the namespace flags are present in every candidate. Co-authored-by: linkst <2024023709@m.scnu.edu.cn>
…ap-auto-fallback fix(sandbox): fall back to --map-auto when root-user mapping is restricted
Root knowledge base plus complexity-scored subdirectory files for the rust/ workspace, its five highest-mass crates (runtime, rusty-claude-cli, api, tools, commands, plugins), and the src/ Python porting workspace. Generated via init-deep: 13 parallel explore agents, LSP/ast-grep code map, centrality-scored placement. Snapshot in .omo/init-deep.json (local).
|
Two details worth pinning down for the parallel read-only execution: (1) result ordering — if tool results are appended in completion order rather than call order, the transcript becomes non-deterministic across runs for the same input, which hurts reproducibility and makes failures hard to replay; keying results to the original call index would keep it stable. (2) a concurrency bound — unbounded fan-out against a local model server will queue or thrash, so a configurable cap with a sane default (and a per-team override alongside subagentModel) would be safer. Also worth confirming the workspace-scope check runs per tool call rather than once for the whole batch, since parallelism makes it easy to smuggle one out-of-scope path among several valid ones. |
|
All three points addressed in 6ee9e03..697911b ( 1. Deterministic ordering — was already index-keyed, now airtight + tested Documented parity caveat in-code: parallel-safe calls execute before sequential ones within a batch (slots still call-order), so models must not emit intra-batch dependent calls. 2. Bounded fan-out — cap + per-team override
3. Workspace-scope check runs per call — confirmed Validation: |
|
Thanks for the thorough follow-up — all three points are properly closed:
This is ready once #3248 lands and the base commits are rebased away. No further blocking feedback from my side. |
…ve TUI deps Review fixes for PR ultraworkers#3250: 1. Restore workspace-level unsafe_code = "forbid" (was downgraded to "warn" for TUI support). The per-crate [lints.rust] unsafe_code = "allow" on rusty-claude-cli already overrides it where needed. 2. Fix TaskClaim TOCTOU race in claim_task(). Replace exists()-then-write with OpenOptions::create_new(true) which atomically fails if the lock file already exists, eliminating the race window between the check and the write. 3. Remove TUI scaffolding deps (ratatui, tui-textarea, gag, crossbeam-channel, libc, unicode-width) and the /tui slash command spec — these belong in a future TUI PR and are unused in the current source. Also updates the provider chain precedence test to reflect the new behavior where the caller's resolved model is the chain primary. Co-Authored-By: Claw <noreply@openclaw.ai>
697911b to
47ec647
Compare
…ve TUI deps Review fixes for PR ultraworkers#3250: 1. Restore workspace-level unsafe_code = "forbid" (was downgraded to "warn" for TUI support). The per-crate [lints.rust] unsafe_code = "allow" on rusty-claude-cli already overrides it where needed. 2. Fix TaskClaim TOCTOU race in claim_task(). Replace exists()-then-write with OpenOptions::create_new(true) which atomically fails if the lock file already exists, eliminating the race window between the check and the write. 3. Remove TUI scaffolding deps (ratatui, tui-textarea, gag, crossbeam-channel, libc, unicode-width) and the /tui slash command spec — these belong in a future TUI PR and are unused in the current source. Also updates the provider chain precedence test to reflect the new behavior where the caller's resolved model is the chain primary. Co-Authored-By: Claw <noreply@openclaw.ai>
47ec647 to
77a23db
Compare
|
Resubmitted as requested (re: #3248 comment): rebased onto current Review note: with the trio dropped, the test FYI: #3248 still shows OPEN / no merge commit on main as of 13:18 UTC on our side, so the dropped trio is not yet in main — nothing further needed from this PR when it lands. |
Port the agent-team enhancement layer onto upstream/main so the model can spin up coordinated sub-agent teams for parallel work. subagentModel config wiring (fix): - Add subagent_model field to RuntimeFeatureConfig + RuntimeConfig::subagent_model() accessor 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. Provider namespace separation: - New 'custom-openai' provider kind with dedicated env vars (CLAWCUSTOMOPENAI_API_KEY / CLAWCUSTOMOPENAI_BASE_URL) - /setup wizard saves kind: 'custom-openai' for option 5 - Bare model name normalized to 'custom/' prefix to avoid proxy 404s - Sub-agents inherit /setup-saved provider config via inject_config_as_env_fallbacks Parallel tool execution: - Override execute_batch to classify read-only tools as parallel-safe and run them concurrently via std::thread::scope - Results return in original model order Team coordination layer: - AgentMessage, TaskClaim, TeamStatus tools + shared mailbox directory - Mode presets (tiny/1x ... mega/6x) + enriched TeamCreate/Agent descriptions - Background team watcher - /team slash command: on/off/status/toggle Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Restores the fix from @Offwhite-Del that was lost during rebase. Unknown models now return None so preflight skips the context-window check and lets the API enforce its own limits.
Adds 3 unit tests covering the original silent-drop bug: - camelCase 'subagentModel' key reads end-to-end - snake_case 'subagent_model' key reads end-to-end - blank value returns None (falls back to default model) Requested by @1716775457damn in PR review.
…ve TUI deps Review fixes for PR ultraworkers#3250: 1. Restore workspace-level unsafe_code = "forbid" (was downgraded to "warn" for TUI support). The per-crate [lints.rust] unsafe_code = "allow" on rusty-claude-cli already overrides it where needed. 2. Fix TaskClaim TOCTOU race in claim_task(). Replace exists()-then-write with OpenOptions::create_new(true) which atomically fails if the lock file already exists, eliminating the race window between the check and the write. 3. Remove TUI scaffolding deps (ratatui, tui-textarea, gag, crossbeam-channel, libc, unicode-width) and the /tui slash command spec — these belong in a future TUI PR and are unused in the current source. Also updates the provider chain precedence test to reflect the new behavior where the caller's resolved model is the chain primary. Co-Authored-By: Claw <noreply@openclaw.ai>
…ings Adds optional positive-integer parsing for the two new fan-out caps next to subagentModel, in both camelCase and snake_case forms. Zero parses as unset; non-numeric values are a named config error. Covered by three regression tests mirroring the subagentModel test style.
TeamCreate resolves maxConcurrentAgents from the per-team input, then the maxConcurrentAgents setting, then unlimited (pre-fix behavior). The cap is persisted in the manifest and registered on a process-wide gate; agent threads still spawn immediately but block on an RAII slot before entering their run loop, so manifest/task-claim semantics are unchanged. TeamDelete forgets the gate so queued agents drain and no stale team entries accumulate. Includes a gate regression test.
execute_batch already keyed results to original call slots; this makes that airtight: panicking worker threads resolve to their own index via a fallback tuple instead of colliding with slot 0, and results are asserted to follow call order with a regression test that mixes a workspace-escape read among valid reads at cap=2. Adds the fan-out bound: parallel-safe calls run in waves of at most maxParallelToolCalls (default 8, CLAWD_PARALLEL_TOOL_CALLS env, or the new setting applied via with_max_parallel_tool_calls at runtime build). Sequential behavior is preserved: allowed_tools checks still run before dispatch, and ToolSearch stays off the parallel path.
…ve TUI deps Review fixes for PR ultraworkers#3250: 1. Restore workspace-level unsafe_code = "forbid" (was downgraded to "warn" for TUI support). The per-crate [lints.rust] unsafe_code = "allow" on rusty-claude-cli already overrides it where needed. 2. Fix TaskClaim TOCTOU race in claim_task(). Replace exists()-then-write with OpenOptions::create_new(true) which atomically fails if the lock file already exists, eliminating the race window between the check and the write. 3. Remove TUI scaffolding deps (ratatui, tui-textarea, gag, crossbeam-channel, libc, unicode-width) and the /tui slash command spec — these belong in a future TUI PR and are unused in the current source. Also updates the provider chain precedence test to reflect the new behavior where the caller's resolved model is the chain primary. Co-Authored-By: Claw <noreply@openclaw.ai>
77a23db to
83b201f
Compare
|
Thanks for the cleanup — splitting the three OpenAI-compat namespace commits out and keeping this PR focused on the 8 team commits makes review much easier, especially once #3248 lands. The deterministic per-call ordering (panic-safe slot fallback) and the cap=2 ordering regression test look solid. Will re-review head-to-head and merge after CI passes. |
|
Confirmed head 77a23db now carries just the 8 team commits — much cleaner to review than the previous 11-commit history. Alignment on the handoff: merge #3248 first, then restore the custom/ model-normalization test (config_model_normalizes_bare_name_to_custom_prefix_for_custom_openai_provider) and validate it against the merged base so we don't lose that coverage, then this PR gets its final pass. Once CI on the cleaned head is green, it's ready to merge from my side. |
83b201f to
4ff9750
Compare
Summary
Ports the agent-team enhancement layer onto main so the model can spin up
coordinated sub-agent teams for parallel work. 2,095 lines added across 11 files
— zero deletions of existing upstream functionality.
Single clean commit built from
upstream/main. No TUI, LSP, or setup-wizardcontamination from other in-flight work.
Motivation
The
TeamCreate,TeamDelete, andAgenttools already existed, but theydidn't work end-to-end because:
subagentModelwas silently dropped —/setupsaved the sub-agent modelinto
settings.jsonbutRuntimeConfignever stored or exposed it, soagents always fell back to a hard-coded default.
inject_config_as_env_fallbacks()to setCLAWCUSTOMOPENAI_API_KEY/CLAWCUSTOMOPENAI_BASE_URLfrom/setup-saved config, but the sub-agentspawn path in
toolsnever did, so custom providers were invisible.providerFallbacks.primarysilently overrode the agent's model — theconfigured fallback primary replaced whatever model
resolve_agent_modelpicked, routing agents to a dead model instead of the session's provider.
so the fallback chain never advanced to working models.
This PR fixes all four and adds the team coordination tools the model needs
to orchestrate multi-agent work.
What's included
1.
subagentModelconfig wiring (bug fix)The
subagentModelfield was validated byconfig_validate.rsbut neverstored or read — the setting from
/setupwas silently dropped.RuntimeFeatureConfig.subagent_modelfieldruntime/src/config.rsRuntimeConfig::subagent_model()accessorruntime/src/config.rsparse_optional_subagent_model()(readssubagentModel/subagent_model, blank →None)runtime/src/config.rsruntime::inject_config_as_env_fallbacks()— moved from privatemain.rsfn topub fnin runtime so sub-agents can call itruntime/src/config.rs+runtime/src/lib.rsresolve_agent_modelfallback chain: explicit model →subagentModel→ session provider model →DEFAULT_AGENT_MODELtools/src/lib.rs2. Parallel tool execution (perf)
When the model emits multiple
tool_useblocks in one response, read-onlytools now run concurrently via
std::thread::scopeinstead ofsequentially.
Parallel-safe tools:
read_file,glob_search,grep_search,WebFetch,WebSearch,ToolSearch,Skill,LSP,GitStatus,GitDiff,GitLog,GitShow,Agent,AgentMessage,AgentSuggestion,TeamStatus,TeamInfo,TaskGet,TaskList,TaskOutputSequential tools: everything else (writes, side-effects)
ToolCall,ToolResult,TurnProgressReporterre-exported from runtimeruntime/src/conversation.rs+runtime/src/lib.rsToolExecutor::execute_batchdefault impl on traitruntime/src/conversation.rsCliToolExecutor::execute_batchoverride with parallel-safe classificationrusty-claude-cli/src/main.rs3. Team coordination layer (feature)
New tools and capabilities for multi-agent work:
TeamCreateTeamDeleteTeamStatusAgentMessageTaskClaimAgentSuggestionSupporting infrastructure:
~/.clawd-agents/mailbox/team/{team_id}/.jsonfiles every 2s,prints
[team]progress to stderr, writes events to-events.jsonltiny/1xthroughmega/6xcontrolling agent countand role distribution (Explore, Plan, Verification, Reviewer)
/teamslash command —on|off|statusto toggleCLAWD_AGENT_TEAMS(required by
TeamCreate)TeamCreateandAgentsteering themodel toward correct usage
4. Model-resolution robustness fixes (bug fix)
qualify_for_provider()claude-haiku-4-5-20251001) getcustom/prefix when the active provider iscustom-openai, so sub-agents route through the same endpoint as the main sessionproviderFallbacks.primaryprecedenceproviderFallbacks.primary+fallbacksare appended as recovery entries, deduped. Previously the config primary silently replaced the caller's modelfallback_chain_eligible()that treats 404/400 "not found" as chain-eligible, so a dead configured primary advances tokimi/qwen/etc. AddedApiError::status_code()andresponse_body()accessors for the detection/setupcredentialsrun_agent_job()callsruntime::inject_config_as_env_fallbacks()before building the agent runtime, so custom providers set up via/setupwork for spawned agents5. Also fixed
lane_completion.rstest helper set staleteam_id/task_idfieldstools/src/lane_completion.rs/teamslash command had novalidate_slash_command_inputmatch arm (parsed asUnknown)commands/src/lib.rsApiErrorgainsstatus_code()andresponse_body()public accessorsapi/src/error.rsmodel_token_limitcatch-all:_ => Some(...)→_ => None— unknown models skip preflight, let API enforce limits (caught by @Offwhite-Del)api/src/providers/mod.rsLspvariant added to match armscommands/src/lib.rs+rusty-claude-cli/src/main.rsDEFAULT_CUSTOM_OPENAI_BASE_URLconstant addedapi/src/providers/openai_compat.rstools/src/lib.rsVerification
Build
Tests
Live testing (IdleDefense repo, Godot 4.6 game)
/team on→ TeamCreate with 4 agents → all agents spawned, claimed tasks, read filescustom/openclawprovider (viaqualify_for_provider)TeamStatusshowed live progress, inbox messages, task claimsmax_iterationsor simplify prompts for large repos)Diff stats
Key files
tools/src/lib.rsruntime/src/conversation.rsruntime/src/config.rsapi/src/error.rsapi/src/providers/mod.rsWhat this does NOT include
This PR is purely additive on top of upstream/main. It does not include:
🤖 Generated with Claude Code