Feature request: programmatic /effort and /model switching for multi-agent orchestration

Resolved 💬 4 comments Opened May 15, 2026 by fsocietydisobey Closed Jun 18, 2026

Context

I've been building khimaira, a multi-agent orchestration framework for Claude Code that uses the claude/channel research-preview capability. We coordinate master/agent/observer/critic role-shaped sessions through a shared chat primitive.

The feature request

Provide some programmatic surface to switch the active session's model and effort level mid-conversation. Today both are user-typed-only (/model, /effort); we need a non-user-typed pathway to make role-based budget routing seamless.

Use case

Role-based budget routing for multi-agent workflows:

| Role | Recommended | Why |
|----------|-------------|-----|
| Master / orchestrator | Opus 4.7 + ultrathink | Design decisions, multi-lane review |
| Implementing agent | Sonnet 4.6 + medium effort | Bounded implementation lanes |
| Observer / peer reviewer | Haiku 4.5 + default effort | Sanity checks, ack pass-through |

The model gap (~5× Opus→Sonnet, ~10-20× Opus→Haiku) compounds with the thinking gap (~10-50× ultrathink↔off at the SAME model). A naive all-Opus-ultrathink four-peer chat burns through a daily usage cap in a single coordination round. Role-based routing is the fix; without it, multi-agent orchestration is rate-limit-fragile.

See docs/khimaira-chat.md#token-cost-budgeting for our full rationale.

What's available today (all verified)

  • /model and /effort slash commands — user-typed only
  • ~/.claude/settings.json effortLevel + alwaysThinkingEnabledread at session start only
  • Agent tool's model parameter — works programmatically, but subagent-only (ephemeral, one-task)
  • Skill / subagent frontmatter model + effort — works programmatically, but per-invocation only (reverts after)

What we verified empirically does NOT work

A UserPromptSubmit hook outputting {"hookSpecificOutput": {"hookEventName": "UserPromptSubmit", "additionalContext": "/effort low\n"}} — the injected text is read as model-readable context, not parsed as a slash command. Effort remains at the session-start value.

What would help

Any one of:

  1. Hook output schema field — e.g., {"sessionUpdate": {"model": "sonnet", "effort": "medium"}} from a UserPromptSubmit or SessionStart hook, applied between user prompt and assistant turn.
  1. MCP tool surface — e.g., set_session_model(model) / set_session_effort(level) callable by an MCP server. Could be gated by an explicit user opt-in (settings.json key) to address the "agent shouldn't unilaterally decide" concern.
  1. claude/channel message kind — e.g., a notification with kind=session-control that runs the switch on the receiving session. Composes naturally with our existing channel-block-based orchestration.
  1. Some other surface we haven't considered — open to whatever shape fits the design philosophy.

Our workaround (today)

We ship a convention-only layer: when a role changes via our chat primitive, the receiving session sees a channel block with 🎚️ Role updated: you are now master. Recommended budget: /model opus, /effort max. The user reads it and types the commands. Works, but requires human-in-the-loop application — not seamless, and breaks if the user steps away.

Why this matters for the channels primitive trajectory

The pattern of "different agents at different price/capability tiers for the same workflow" is the basis of multi-agent peer review and division of labor. Solo agents at single-tier are well-supported today; multi-tier orchestration hits this wall.

We're shipping our orchestration framework open-source — happy to share usage data, specific dogfood examples, or the cost math behind the role table if that's useful as you decide whether/how to surface a programmatic primitive.

Happy to provide

  • Full reference docs: docs/khimaira-chat.md
  • Specific examples where this would have prevented rate-limit blowups
  • A separate write-up walking through the cost math

View original on GitHub ↗

This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗