#14647: feat(plugins): allow before_agent_start hook to override model (#14585)
agents
stale
size: S
trusted-contributor
Cluster:
Plugin and Hook Enhancements
## Summary
Fixes #14585
## Problem
The `before_agent_start` plugin hook can inject `systemPrompt` and `prependContext` into agent turns, but cannot override the model. This prevents plugins from implementing intent-based model routing — e.g., routing simple questions to a cheap model and complex tasks to a powerful one.
**Before:** A plugin returning `{ model: "openai/gpt-4.1" }` from `before_agent_start` is silently ignored. The configured default model is always used.
## Fix
1. **Plugin types** (`src/plugins/types.ts`): Add optional `model?: string` field to `PluginHookBeforeAgentStartResult`.
2. **Hook merge logic** (`src/plugins/hooks.ts`): Add `model` to the merge function — last plugin wins (same semantics as `systemPrompt`).
3. **Run entry point** (`src/agents/pi-embedded-runner/run.ts`): Call `before_agent_start` hook **before** `resolveModel()`. If the hook returns a `model` string (format: `provider/modelId`), override the `provider` and `modelId` variables so the correct model is resolved and used for session creation.
**After:** A plugin can return `{ model: "openai/gpt-4.1" }` and the agent turn will use that model instead of the configured default.
### Design decisions
- **Hook runs twice per turn**: once in `run.ts` (model phase, no messages) and once in `attempt.ts` (prompt phase, with messages). This preserves the existing `systemPrompt`/`prependContext` behavior which needs access to session messages, while allowing model override before session creation.
- **Model format**: `provider/modelId` string, consistent with the existing model reference format used throughout the codebase (e.g., `anthropic/claude-sonnet-4`, `openrouter/anthropic/claude-opus-4`).
- **Graceful degradation**: If the hook fails or returns an invalid model string (no `/`), the original configured model is used.
### Example plugin usage
```typescript
export default {
hooks: {
before_agent_start: async (event, ctx) => {
if (event.prompt.length < 100) {
return { model: "openai/gpt-4.1-mini" };
}
return { model: "anthropic/claude-sonnet-4" };
},
},
};
```
## Testing
- ✅ 5 tests pass (`pnpm vitest run src/plugins/hooks.test.ts`) — model merge logic: single hook, last-wins, preserve-on-undefined, merge-with-other-fields, empty-registry
- ✅ Lint passes (`pnpm lint`)
<!-- greptile_comment -->
<h2>Greptile Overview</h2>
<h3>Greptile Summary</h3>
This PR extends the `before_agent_start` plugin hook to support per-turn model routing by adding an optional `model` field to the hook result, merging it with “last-wins” semantics in the hook runner, and invoking the hook earlier in `pi-embedded-runner/run.ts` so model selection occurs before session creation.
Key flow change: `runEmbeddedPiAgent` now runs `before_agent_start` (model phase) to potentially override `provider/modelId`, then later `run/attempt.ts` still runs `before_agent_start` (prompt phase) to apply `prependContext`/`systemPrompt` against the live session messages.
<h3>Confidence Score: 4/5</h3>
- This PR is largely safe to merge, with one edge case that can break model override behavior.
- Core type and merge changes are straightforward and tested; the only verified issue is that an invalid-but-plausible hook return like `"provider/"` will override to an empty modelId and then hard-fail instead of degrading to defaults.
- src/agents/pi-embedded-runner/run.ts
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#17614: feat: allow before_agent_start hook to override model selection
by plc · 2026-02-16
92.1%
#11155: feat(hooks): before_agent_start model/provider override (run-scoped...
by alanranger · 2026-02-07
88.2%
#22624: feat(plugins): add before_context_send hook and model routing via b...
by davidrudduck · 2026-02-21
86.6%
#8022: feat: implement before_model_select plugin hook
by dead-pool-aka-wilson · 2026-02-03
86.2%
#23559: feat(plugins): add before_context_send hook and model routing via b...
by davidrudduck · 2026-02-22
85.5%
#14873: [Feature]: Extend before_agent_start hook context with Model, Tools...
by akv2011 · 2026-02-12
84.6%
#8178: feat: allow before_agent_start to override the prompt
by richardfogaca · 2026-02-03
81.0%
#13376: fix: pass model directly to agent for sub-agent runs
by jrbobbyhansen-pixel · 2026-02-10
80.9%
#11732: feat(plugins): add injectMessages to before_agent_start hook
by antra-tess · 2026-02-08
79.7%
#5945: fix: use configured model for slug generator (AI-assisted)
by HEDELKA · 2026-02-01
79.5%