← Back to PRs

#17614: feat: allow before_agent_start hook to override model selection

by plc open 2026-02-16 00:34 View on GitHub →
agents stale size: S
## Summary Add an optional `model` field to `PluginHookBeforeAgentStartResult` so plugins can override the model for an agent run. This builds on #14873 (which exposes model/tools/sender context to the hook) by letting plugins write back a model override. This enables plugin-based model routers. ## Motivation Plugins like cost-optimising model routers need to select models per-request based on prompt complexity, session context, and cost. Currently the only way to do this is with an external HTTP proxy (e.g. ClawRoute), which has no access to OpenClaw session context. With this change, a plugin can: ```ts api.on('before_agent_start', (event, ctx) => { const tier = classifyComplexity(event.prompt, ctx); return { model: tierToModel[tier] }; // e.g. 'anthropic/claude-haiku-3-5' }); ``` ## Use cases - Cost-optimising model routers: route simple messages to cheap models, complex ones to frontier - A/B testing: randomly assign models to measure quality - Rate-limit-aware routing: switch providers when one is throttled - Per-user model assignment: different users get different tiers ## Changes 3 files, +46/-2 lines: 1. `src/plugins/types.ts`: Add `model?: string` to `PluginHookBeforeAgentStartResult` with JSDoc 2. `src/plugins/hooks.ts`: Add `model` to the merge function in `runBeforeAgentStart` (last plugin wins) 3. `src/agents/pi-embedded-runner/run.ts`: Run the hook early in `runEmbeddedPiAgent` (before auth/context-window resolution) to extract the model override. The hook continues to run later in `attempt.ts` for `systemPrompt` and `prependContext`. ## Design decisions User overrides take precedence: If `authProfileIdSource === 'user'` (e.g. `/model` command), the hook model is skipped entirely. Fail-open: If the hook throws or returns an unresolvable model, the original model is used with a warning log. Early execution: The hook runs before `resolveModel()` so auth profiles, context window checks, and failover all work naturally with the overridden provider/model. Aliases supported: Uses existing `resolveModelRefFromString`, so model aliases work. Backward-compatible: `model` is optional; existing plugins are unaffected. ## Testing notes - Plugin returns `{ model: 'anthropic/claude-haiku-3-5' }` → model changes, auth resolves for new provider - Plugin returns `{ model: 'nonexistent/model' }` → `resolveModel` fails, original model used - Plugin returns `{}` or `undefined` → no change (existing behaviour) - User has `/model opus` override → hook is skipped entirely - Multiple plugins return model → last by priority wins (consistent with `systemPrompt` merge) <!-- greptile_comment --> <h3>Greptile Summary</h3> Adds plugin-based model override capability via `before_agent_start` hook. The hook runs early in the agent lifecycle (before auth resolution) to allow plugins to select models based on prompt complexity, session context, and cost optimization. **Key changes:** - Added optional `model` field to `PluginHookBeforeAgentStartResult` in `src/plugins/types.ts:338` - Updated hook merge logic to honor last plugin's model selection in `src/plugins/hooks.ts:203` - Implemented early hook execution in `src/agents/pi-embedded-runner/run.ts:222-252` with proper fallback handling **Strengths:** - Proper null checking for `resolveModelRefFromString` result (addresses previous review feedback) - Falls back to original model if hook-provided model fails to resolve - Error handling with try-catch and informative logging - Backward compatible (optional field) **Known limitation:** - User model selection via `/model` command is not detected (only `authProfileIdSource` is checked). This is documented in the code comments at `src/agents/pi-embedded-runner/run.ts:216-217` as a future enhancement. Plugins could potentially override user's `/model` selections. <h3>Confidence Score: 4/5</h3> - This PR is safe to merge with minor limitations documented in code - The implementation is well-designed with proper error handling, null safety, and fallback logic. Previous review feedback on null checking has been addressed. The main limitation (not detecting `/model` command overrides) is documented and acknowledged as a future enhancement. The code follows existing patterns and is backward compatible. - No files require special attention - all changes are straightforward and well-implemented <sub>Last reviewed commit: 25cf41f</sub> <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs