Skip to content

feat(delegate): named delegation profiles for delegate_task#34754

Open
gutleib wants to merge 14 commits into
NousResearch:mainfrom
gutleib:feat/delegate-profiles
Open

feat(delegate): named delegation profiles for delegate_task#34754
gutleib wants to merge 14 commits into
NousResearch:mainfrom
gutleib:feat/delegate-profiles

Conversation

@gutleib

@gutleib gutleib commented May 29, 2026

Copy link
Copy Markdown

What does this PR do?

Implements named delegation profiles for delegate_task: users define
specialised subagent roles in cli-config.yaml and route tasks to them by
name — 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

  • ✨ New feature (non-breaking change that adds functionality)

Changes Made

  • tools/delegate_tool.py_resolve_profile(), ACP + API routing, toolset priority chain, per-profile max_iterations, system prompt / constraints injection, schema enum injection from configured profiles
  • run_agent.py — forward profile arg through _dispatch_delegate_task()
  • cli-config.yaml.example — full delegation.profiles usage example (API + ACP modes, all fields)
  • tests/tools/test_delegate_profiles.py — 37 new tests (new file)

How to Test

  1. Add a delegation.profiles block to ~/.hermes/config.yaml (see example in cli-config.yaml.example)
  2. Start a chat session and call delegate_task(goal="...", profile="<name>")
  3. Verify the child agent runs with the configured model / toolsets / iteration budget
  4. For ACP mode: set acp_command: "claude" in a profile and verify the local runner is spawned via the terminal tool

Unit tests: scripts/run_tests.sh tests/tools/test_delegate_profiles.py

Implementation details

New delegation.profiles config section (nested under the existing
delegation block — see rationale below):

delegation:
  profiles:
    coder:
      nickname: "👷 Coder"          # shown in tool description (informational)
      summary: "Writes code, TDD"   # shown to LLM orchestrator in the schema enum
      model: deepseek-v4-flash
      provider: deepseek
      toolsets: [terminal, file]
      max_iterations: 80            # overrides delegation.max_iterations for this profile
      system_prompt: |
        You are a focused developer. Follow TDD strictly.
      constraints: |
        - No changes outside the assigned task scope

    critic:
      nickname: "🔍 Critic"
      summary: "Code review, bug hunting"
      model: claude-sonnet-4-6
      provider: openrouter
      toolsets: [file]
      max_iterations: 20

Usage:

delegate_task(goal="implement auth module", profile="coder")
delegate_task(tasks=[
    {"goal": "write tests", "profile": "coder"},
    {"goal": "review PR",   "profile": "critic"},
])

Two routing modes per profile (mutually exclusive):

Mode Fields Use case
API model + provider (+ optional base_url, api_key, api_mode) Any OpenAI-compatible endpoint
ACP acp_command (+ optional acp_args) Local agent runners (Claude Code, aider, etc.)

Toolset priority chain:
explicit call arg > profile.toolsets > inherited from parent

Iteration budget:
profile.max_iterations > delegation.max_iterations (global). The
orchestrating 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 block

Schema enum injection:
Configured profiles surface in the delegate_task JSON schema as an enum with
summary descriptions, so the orchestrating LLM can choose the right profile
autonomously.

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_task JSON schema as a typed enum with summary labels. The
orchestrator 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/provider override 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 a
top-level key and does not implement ACP mode, schema enum injection, or
per-profile iteration budgets.

Why delegation.profiles and not agent_profiles

The issue proposes a top-level agent_profiles key. Nesting under delegation
is a better fit because:

  1. Profiles inherit the delegation family settings (max_iterations,
    max_spawn_depth, subagent_auto_approve) without repeating them.
  2. The delegation block already owns all subagent configuration — profiles
    are a natural extension, not a parallel concept.
  3. Keeps the root config namespace flat.

What's not in v1 (explicit stub)

  • proxy field is accepted and logged as a warning but not applied.
    Routing through a local proxy requires additional client wiring (v2).

Checklist

Code

Documentation & Housekeeping

  • I've updated cli-config.yaml.example with the new delegation.profiles config keys
  • I've considered cross-platform impact — scripts/check-windows-footguns.py clean, no POSIX-only syscalls introduced
  • I've updated tool descriptions/schemas (schema enum injection adds profiles to delegate_task schema)
  • README / docs updates — N/A (config example is self-documenting)
  • CONTRIBUTING.md / AGENTS.md — N/A

gutleib added 12 commits May 29, 2026 20:30
…xy=None stub to _build_keepalive_http_client
…ugh, toolset priority, early batch validation
…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.
@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have comp/agent Core agent loop, run_agent.py, prompt builder tool/delegate Subagent delegation labels May 29, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Competing implementation with #18522 (open) — both implement named delegation profiles for #9459. Also competes with #34752 (Phase 7 delegation overrides). Different approach: nests profiles under delegation.profiles config key vs. top-level agent_profiles.

@gutleib gutleib marked this pull request as ready for review May 29, 2026 18:16
@gutleib

gutleib commented May 29, 2026

Copy link
Copy Markdown
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.

gutleib added 2 commits May 29, 2026 22:03
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent loop, run_agent.py, prompt builder P3 Low — cosmetic, nice to have tool/delegate Subagent delegation type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(delegation): agent profiles for delegate_task — custom orchestration harness support

2 participants