← Back to PRs

#22624: feat(plugins): add before_context_send hook and model routing via before_prompt_build

by davidrudduck open 2026-02-21 12:16 View on GitHub →
agents size: L trusted-contributor
## Summary - Adds `before_context_send` plugin hook — sync, per-LLM-call, modifying. Plugins can transform `AgentMessage[]` with model awareness (modelId, provider, contextWindowTokens) on every LLM call via a new pi-agent-core bridge extension. - Enriches `before_prompt_build` with model info (`modelId`, `provider`, `contextWindowTokens`) and adds `modelOverride`/`providerOverride` to its result type, enabling dynamic model routing based on conversation context. - New `context-hooks` pi-extension bridges pi-agent-core's sync `context` event to the plugin hook system, following the same pattern as `context-pruning`. Standalone from PR #14544 (Windelly's `before_context_send`). If both land, they coexist via independent registries. ### Example plugin pattern enabled by this PR ``` session_start -> load config (model tiers, token budgets, decay rules) tool_result_persist -> note large results, kick off background summarization before_prompt_build -> classify complexity -> return modelOverride -> store pre-computed summaries in plugin state before_context_send -> strip thinking blocks from old messages -> replace tool results with pre-computed summaries -> enforce token budget based on contextWindowTokens llm_output -> accumulate usage stats, adjust decay aggressiveness ``` ### Files changed (9) | File | Change | |---|---| | `src/plugins/types.ts` | New hook name, event/result types, enriched prompt build types | | `src/plugins/hooks.ts` | `runBeforeContextSend()` sync chain-pass, `mergeBeforePromptBuild` with model override | | `src/plugin-sdk/index.ts` | SDK exports for plugin authors | | `src/agents/pi-extensions/context-hooks/runtime.ts` | **NEW** — WeakMap registry for bridge runtime | | `src/agents/pi-extensions/context-hooks/extension.ts` | **NEW** — Bridge: pi-agent-core context event → plugin hooks | | `src/agents/pi-extensions/context-hooks.ts` | **NEW** — Barrel re-export | | `src/agents/pi-embedded-runner/extensions.ts` | `buildContextHooksExtension()`, pass `hookCtx` | | `src/agents/pi-embedded-runner/run/attempt.ts` | Early hookCtx, model info in prompt build, model routing via `streamFn` wrap | | `src/agents/pi-extensions/context-hooks.test.ts` | **NEW** — 6 tests | ## Test plan - [x] `npx tsc --noEmit` — no new type errors - [x] `npx oxlint` — 0 warnings, 0 errors on all changed files - [x] `npx vitest run src/agents/pi-extensions/context-hooks.test.ts` — 6/6 pass - [x] `npx vitest run src/plugins/` — 117/117 pass (existing hooks tests unaffected) - [ ] Manual: create a minimal test plugin registering `before_context_send` — verify it fires on every LLM call - [ ] Manual: create a test plugin returning `modelOverride` from `before_prompt_build` — verify routed model is used <!-- greptile_comment --> <h3>Greptile Summary</h3> This PR adds two complementary plugin hook features: 1. **`before_context_send` hook**: A new synchronous plugin hook that fires on every LLM call, allowing plugins to transform the `AgentMessage[]` array before it's sent to the model. Receives model metadata (`modelId`, `provider`, `contextWindowTokens`) to enable token-aware filtering and summarization. 2. **Enhanced `before_prompt_build` hook**: Enriched with model info (`modelId`, `provider`, `contextWindowTokens`) and adds `modelOverride`/`providerOverride` to the result type, enabling dynamic model routing based on conversation context. The implementation follows existing patterns from `context-pruning` and uses a WeakMap-based registry to bridge pi-agent-core's synchronous context event to the plugin system. Model routing is handled by wrapping the agent's `streamFn`, and the context-hooks runtime is updated when routing occurs so `before_context_send` sees the correct model. All changes are well-tested (6 new tests pass) and backward-compatible. The PR description clearly documents the intended plugin pattern and states it's standalone from PR #14544. <h3>Confidence Score: 4/5</h3> - This PR is safe to merge with minor considerations - The implementation is solid with good test coverage and follows existing patterns. Score reflects well-structured code with proper error handling and guard clauses. The synchronous hook design is appropriate and properly documented. Minor consideration: dynamic imports in hot path (model routing) may have performance implications, but this only occurs when plugins actively route models. - Pay close attention to `src/agents/pi-embedded-runner/run/attempt.ts` for the model routing logic and runtime updates <sub>Last reviewed commit: 97b1448</sub> <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs