6.6 KiB
Delegation Enforcer
Automatic model parameter injection for Task/Agent calls
Problem
Claude Code does NOT automatically apply model parameters from agent definitions. When you invoke the Task tool (or Agent tool), you must manually specify the model parameter every time, even though each agent has a default model defined in its configuration.
This leads to:
- Verbose delegation code
- Forgotten model parameters defaulting to parent model
- Inconsistent model usage across codebase
Solution
The Delegation Enforcer is middleware that automatically injects the model parameter based on agent definitions when not explicitly specified.
How It Works
1. Pre-Tool-Use Hook
The enforcer runs as a pre-tool-use hook that intercepts Task and Agent tool calls:
// Before enforcement
Task(
subagent_type="oh-my-claudecode:executor",
prompt="Implement feature X"
)
// After enforcement (automatic)
Task(
subagent_type="oh-my-claudecode:executor",
model="sonnet", // ← Automatically injected
prompt="Implement feature X"
)
2. Agent Definition Lookup
Each agent has a default model in its definition:
export const executorAgent: AgentConfig = {
name: 'executor',
description: '...',
prompt: '...',
tools: [...],
model: 'sonnet' // ← Default model
};
The enforcer reads this definition and injects the model when not specified.
3. Explicit Models Preserved
If you explicitly specify a model, it's always preserved:
// Explicit model is never overridden
Task(
subagent_type="oh-my-claudecode:executor",
model="haiku", // ← Explicitly using haiku instead of default sonnet
prompt="Quick lookup"
)
API
Core Functions
enforceModel(agentInput: AgentInput): EnforcementResult
Enforces model parameter for a single agent delegation call.
import { enforceModel } from 'oh-my-claudecode';
const input = {
description: 'Implement feature',
prompt: 'Add validation',
subagent_type: 'executor'
};
const result = enforceModel(input);
console.log(result.modifiedInput.model); // 'sonnet'
console.log(result.injected); // true
getModelForAgent(agentType: string): ModelType
Get the default model for an agent type.
import { getModelForAgent } from 'oh-my-claudecode';
getModelForAgent('executor'); // 'sonnet'
getModelForAgent('executor-low'); // 'haiku'
getModelForAgent('executor-high'); // 'opus'
isAgentCall(toolName: string, toolInput: unknown): boolean
Check if a tool invocation is an agent delegation call.
import { isAgentCall } from 'oh-my-claudecode';
isAgentCall('Task', { subagent_type: 'executor', ... }); // true
isAgentCall('Bash', { command: 'ls' }); // false
Hook Integration
The enforcer automatically integrates with the pre-tool-use hook:
import { processHook } from 'oh-my-claudecode';
const hookInput = {
toolName: 'Task',
toolInput: {
description: 'Test',
prompt: 'Test',
subagent_type: 'executor'
}
};
const result = await processHook('pre-tool-use', hookInput);
console.log(result.modifiedInput.model); // 'sonnet'
Agent Model Mapping
| Agent Type | Default Model | Use Case |
|---|---|---|
architect |
opus | Complex analysis, debugging |
architect-medium |
sonnet | Standard analysis |
architect-low |
haiku | Quick questions |
executor |
sonnet | Standard implementation |
executor-high |
opus | Complex refactoring |
executor-low |
haiku | Simple changes |
explore |
haiku | Fast code search |
designer |
sonnet | UI implementation |
designer-high |
opus | Complex UI architecture |
designer-low |
haiku | Simple styling |
document-specialist |
sonnet | Documentation lookup |
writer |
haiku | Documentation writing |
vision |
sonnet | Image analysis |
planner |
opus | Strategic planning |
critic |
opus | Plan review |
analyst |
opus | Pre-planning analysis |
qa-tester |
sonnet | CLI testing |
scientist |
sonnet | Data analysis |
scientist-high |
opus | Complex research |
Debug Mode
Enable debug logging to see when models are auto-injected:
export OMC_DEBUG=true
When enabled, you'll see warnings like:
[OMC] Auto-injecting model: sonnet for executor
Important: Warnings are ONLY shown when OMC_DEBUG=true. Without this flag, enforcement happens silently.
Usage Examples
Before (Manual)
// Every delegation needs explicit model
Task(
subagent_type="oh-my-claudecode:executor",
model="sonnet",
prompt="Implement X"
)
Task(
subagent_type="oh-my-claudecode:executor-low",
model="haiku",
prompt="Quick lookup"
)
After (Automatic)
// Model automatically injected from definition
Task(
subagent_type="oh-my-claudecode:executor",
prompt="Implement X"
)
Task(
subagent_type="oh-my-claudecode:executor-low",
prompt="Quick lookup"
)
Override When Needed
// Use haiku for a simple executor task
Task(
subagent_type="oh-my-claudecode:executor",
model="haiku", // Override default sonnet
prompt="Find definition of X"
)
Implementation Details
Hook Integration
The enforcer runs in the pre-tool-use hook:
- Hook receives tool invocation
- Checks if tool is
TaskorAgent - Checks if
modelparameter is missing - Looks up agent definition
- Injects default model
- Returns modified input
Error Handling
- Unknown agent types throw errors
- Agents without default models throw errors
- Invalid input structures are passed through unchanged
- Non-agent tools are ignored
Performance
- O(1) lookup: Direct hash map lookup for agent definitions
- No async operations: Synchronous enforcement
- Minimal overhead: Only applies to Task/Agent calls
Testing
Run tests:
npm test -- delegation-enforcer
Run demo:
npx tsx examples/delegation-enforcer-demo.ts
Benefits
- Cleaner Code: No need to manually specify model every time
- Consistency: Always uses correct model tier for each agent
- Safety: Explicit models always preserved
- Transparency: Debug mode shows when models are injected
- Zero Config: Works automatically with existing agent definitions
Migration
No migration needed! The enforcer is backward compatible:
- Existing code with explicit models continues working
- New code can omit model parameter
- No breaking changes
Related
- Agent Definitions - Complete agent reference
- Features Reference - Model routing and delegation categories