feat(delegate): named delegation profiles for delegate_task#34754
Open
gutleib wants to merge 14 commits into
Open
feat(delegate): named delegation profiles for delegate_task#34754gutleib wants to merge 14 commits into
gutleib wants to merge 14 commits into
Conversation
…nstraints added before task instruction
… empty persona line
…xy=None stub to _build_keepalive_http_client
…ugh, toolset priority, early batch validation
…ts [] bug; cache per-profile creds
…ks.items) Adds static 'profile' field to the schema at both root level and per-task level. Both fields remain optional (not in required array). Description mentions config.yaml delegation.profiles and that this will be overridden with dynamic values in get_definitions().
…s priority
- Move _resolve_delegation_credentials to lazy evaluation so ACP-only
calls (no API credentials needed) no longer fail when delegation.provider
is absent or misconfigured
- Use key-presence check ("toolsets" in _profile_cfg) instead of `or` so
profile.toolsets=[] is passed through rather than silently falling back
to the top-level toolsets arg
- Honor top-level acp_args as fallback in ACP profile branch (per-task >
top-level > profile), matching existing API-mode precedence semantics
- Add assert_not_called() to ACP test (verifies lazy-creds fix holds)
- Add tests: ACP-only batch skips provider lookup, profile toolsets []
key-presence, top-level acp_args fallback in ACP mode
…ample Shows API mode, ACP mode, proxy/toolsets/constraints usage in commented example block under the existing delegation: section.
Prevents accidental staging of ~/.hermes config, sessions, and keys when developing from a checkout that sits alongside a local Hermes install.
Collaborator
Author
|
Thanks for the triage, @alt-glitch. Worth flagging that the ACP routing mode (acp_command — launching a local agent runner instead of an API call) isn't addressed by any of the open per-call override PRs. For users who need to route tasks to a local Claude Code / aider instance rather than a remote API, it's more of a hard blocker than a cosmetic gap. But I respect your call. |
23 tasks
Profiles can now declare their own iteration budget via max_iterations, overriding the global delegation.max_iterations for that subagent only. Falls back to the global value when the field is absent.
9495f23 to
9a96fdc
Compare
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.
What does this PR do?
Implements named delegation profiles for
delegate_task: users definespecialised subagent roles in
cli-config.yamland route tasks to them byname — selecting a different model, provider, toolset, iteration budget, and
system prompt per role without touching application code.
Full design details in the sections below.
Related Issue
Closes #9459
Also addresses: #10995, #21827, #31793, #30652
Type of Change
Changes Made
tools/delegate_tool.py—_resolve_profile(), ACP + API routing, toolset priority chain, per-profilemax_iterations, system prompt / constraints injection, schema enum injection from configured profilesrun_agent.py— forwardprofilearg through_dispatch_delegate_task()cli-config.yaml.example— fulldelegation.profilesusage example (API + ACP modes, all fields)tests/tools/test_delegate_profiles.py— 37 new tests (new file)How to Test
delegation.profilesblock to~/.hermes/config.yaml(see example incli-config.yaml.example)delegate_task(goal="...", profile="<name>")acp_command: "claude"in a profile and verify the local runner is spawned via the terminal toolUnit tests:
scripts/run_tests.sh tests/tools/test_delegate_profiles.pyImplementation details
New
delegation.profilesconfig section (nested under the existingdelegationblock — see rationale below):Usage:
Two routing modes per profile (mutually exclusive):
model+provider(+ optionalbase_url,api_key,api_mode)acp_command(+ optionalacp_args)Toolset priority chain:
explicit call arg>profile.toolsets>inherited from parentIteration budget:
profile.max_iterations>delegation.max_iterations(global). Theorchestrating LLM cannot override this — the budget is always config-controlled
to keep costs predictable.
System prompt injection:
system_prompt— replaces the first line of the child's system prompt (persona layer)constraints— prepended before the task instruction blockSchema enum injection:
Configured profiles surface in the
delegate_taskJSON schema as an enum withsummarydescriptions, so the orchestrating LLM can choose the right profileautonomously.
How this differs from other open proposals
There are several open PRs in this space (#18522, #34650, #34681, #34752, and
others). This PR takes a different position on two points that the others don't
cover:
ACP routing. Most proposals only address API-based subagents (model +
provider overrides). This PR also lets a profile launch a local agent runner
— Claude Code, aider, a second Hermes instance — via a terminal command wrapper
(
acp_command+acp_args). That covers the cloud-local hybrid pattern from#31793 and the "right tool for the right job" use case from #21827 without
requiring a second gateway process.
LLM-autonomous profile selection. Configured profiles are injected into the
delegate_taskJSON schema as a typed enum withsummarylabels. Theorchestrator LLM sees the available profiles and their descriptions at call
time, and can select one based on task content — no hard-coded routing logic
required in the skill or system prompt.
Both features are absent from the per-call
model/provideroverride approach(#34681, #34752) and from the fixed-tier approach (#34650). They're also absent
from the closest structural cousin (#18522), which uses
agent_profiles:as atop-level key and does not implement ACP mode, schema enum injection, or
per-profile iteration budgets.
Why
delegation.profilesand notagent_profilesThe issue proposes a top-level
agent_profileskey. Nesting underdelegationis a better fit because:
max_iterations,max_spawn_depth,subagent_auto_approve) without repeating them.delegationblock already owns all subagent configuration — profilesare a natural extension, not a parallel concept.
What's not in v1 (explicit stub)
proxyfield is accepted and logged as a warning but not applied.Routing through a local proxy requires additional client wiring (v2).
Checklist
Code
scripts/run_tests.shand all tests pass (37/37)tests/tools/test_delegate_profiles.py)Documentation & Housekeeping
cli-config.yaml.examplewith the newdelegation.profilesconfig keysscripts/check-windows-footguns.pyclean, no POSIX-only syscalls introduceddelegate_taskschema)