@r3b1s/pi-subagents-deterministic
Deterministic subagent routing via Pi's tool_call hook — resolves model and thinking from model-routing.yml and injects them into pi-subagents' subagent tool calls.
Package details
Install @r3b1s/pi-subagents-deterministic from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@r3b1s/pi-subagents-deterministic- Package
@r3b1s/pi-subagents-deterministic- Version
0.3.0- Published
- Jun 12, 2026
- Downloads
- not available
- Author
- r3b1s
- License
- MIT
- Types
- extension
- Size
- 30.7 KB
- Dependencies
- 1 dependency · 1 peer
Pi manifest JSON
{
"extensions": [
"./src/index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
@r3b1s/pi-subagents-deterministic
Deterministic subagent routing for Pi — resolves model and thinking level from model-routing.yml and injects them into pi-subagents' subagent tool calls via Pi's tool_call hook.
Overview
This Pi extension registers one LLM-callable tool:
subagent_manual(escape hatch) — accepts explicitmodelandthinkingparameters, bypassingmodel-routing.yml. Use when you need a specific model or thinking level.
The subagent tool is not registered by PSD. Instead, PSD hooks Pi's tool_call event for toolName === "subagent" and injects deterministic model and thinking values into event.input before pi-subagents' native subagent tool executes. This means:
- No tool-registration conflicts with pi-subagents (first-writer-wins is irrelevant).
- Every
subagentcall is automatically routed according tomodel-routing.yml— the LLM cannot override the model or thinking level through thesubagenttool. - The
subagent_manualescape hatch remains available for explicit override.
PSD also exports setSpawner() and setResultProvider() for API stability with downstream consumers such as pi-tmux-sessionizer.
Architecture
PSD intercepts subagent tool calls using Pi's tool_call event hook:
- When the LLM calls
subagent, Pi fires atool_callevent before the registered tool executes. - PSD's hook handler catches events where
event.toolName === "subagent". - The handler reads
agent/model-routing.ymlfrom the Pi config directory (~/.pi/or$PI_CODING_AGENT_DIR). - It resolves the first model and the thinking level for the requested
subagent_typefrom the YAML (case-insensitive role matching). - It mutates
event.input.modelandevent.input.thinkingwith the resolved values — always overwriting any values the LLM may have set. - The call then proceeds to pi-subagents' native
subagenttool, which acceptsmodelandthinkingas optional parameters and forwards them toSubagentsService.spawn().
The hook blocks the call with a clear error reason if:
- The YAML file is missing or unparseable.
- The requested agent type has no matching role in the config.
- The matching role has an empty models list.
Installation
pi install npm:@r3b1s/pi-subagents-deterministic
PSD can be loaded before or after @gotgenes/pi-subagents — the hook fires regardless of load order.
Configuration
Place your routing configuration at ~/.pi/agent/model-routing.yml (or $PI_CODING_AGENT_DIR/agent/model-routing.yml):
roles:
Explore:
thinking: low
models:
- cheap-model
- fallback-cheap
implementer:
thinking: high
models:
- primary-model
- fallback-model
reviewer:
thinking: high
models:
- gpt-5.5
- opus:
thinking: xhigh
- deepseek-v4-pro
general-purpose:
models:
- cheap-model
Note: The hook always overwrites
modelandthinkingon thesubagenttool call with the resolved config values. Even if the LLM provides these parameters in its call, the routing config is authoritative.
Role Mapping
YAML role keys are matched case-insensitively against the subagent_type value passed by the LLM. Any agent type name works as a role key — there is no hardcoded translation table. For example, subagent_type: "Explore" matches the Explore, explore, or EXPLORE role key.
Common agent types used by the LLM:
subagent_type |
Purpose |
|---|---|
Explore |
Code search / file exploration |
websearch |
Web research / information gathering |
Plan |
Architecture / implementation planning |
implementer |
Implementation from plan |
reviewer |
Code review / quality checks |
retro |
Project retrospective / learning |
reflect |
Session reflection / summarization |
general-purpose |
General-purpose complex tasks |
Unknown agent types return an explicit error — there is no silent fallback.
Usage
Deterministic (preferred)
The LLM calls subagent (pi-subagents' native tool) with only task-related parameters:
{
"subagent_type": "Explore",
"prompt": "Search for unused imports in src/",
"description": "Check unused imports"
}
Model and thinking level are resolved from model-routing.yml automatically by the hook — no explicit model or thinking parameters needed.
Manual override
When the LLM needs to bypass the routing config, it calls subagent_manual:
{
"subagent_type": "reviewer",
"prompt": "Review the implementation plan",
"description": "Review plan",
"model": "opus",
"thinking": "xhigh"
}
subagent_manual is the only tool PSD registers. It accepts explicit model and thinking parameters and does not go through the hook.
Behavior with pi-tmux-sessionizer
When both PSD and pi-tmux-sessionizer (PTS) are installed, PTS calls setSpawner() to inject a tmux-based spawner. However, the deterministic subagent path does not use PSD's spawner — the hook only mutates event.input and lets pi-subagents' tool perform the actual spawn via SubagentsService.spawn(). As a result:
setSpawneris a no-op for deterministicsubagentcalls. The hook does not call spawners; it only injects routing values.subagent_manualstill routes through PTS's spawner when one is set, preserving tmux observability for manual override calls.- Users who want tmux observability for deterministic routing should use
subagent_manualwith the desired model and thinking level explicitly specified.
Behavior with get_subagent_result
PSD no longer registers a get_subagent_result tool. pi-subagents' native get_subagent_result tool handles result retrieval for all subagent and subagent_manual calls.
- Default behavior: Non-blocking — the tool returns immediately with
{ done: false }if the agent is still running. - Opt-in blocking: Pass
wait: trueto block until the agent completes and return the result.
This is a change from previous PSD versions, which always returned immediately without a wait option. The default non-blocking behavior is preserved; the LLM can opt in to blocking when needed.
Dependencies
@gotgenes/pi-subagents(peer, >=15.0.0) — SubagentsService for all subagent lifecycle managementjs-yaml— YAML config parsing at tool-call time
Development
# TypeScript check
pnpm run check
# Tests
pnpm run test
# Lint
pnpm run lint
# Format
pnpm run format