Feature request: programmatic /effort and /model switching for multi-agent orchestration
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)
/modeland/effortslash commands — user-typed only~/.claude/settings.jsoneffortLevel+alwaysThinkingEnabled— read at session start onlyAgenttool'smodelparameter — 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:
- Hook output schema field — e.g.,
{"sessionUpdate": {"model": "sonnet", "effort": "medium"}}from a UserPromptSubmit or SessionStart hook, applied between user prompt and assistant turn.
- 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.
claude/channelmessage kind — e.g., a notification withkind=session-controlthat runs the switch on the receiving session. Composes naturally with our existing channel-block-based orchestration.
- 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
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗