refactor(plugins): split before-agent hooks by model and prompt phases

This commit is contained in:
Peter Steinberger
2026-02-17 03:28:10 +01:00
parent a75e95be02
commit 0c1c34c950
8 changed files with 389 additions and 225 deletions

View File

@@ -13,6 +13,10 @@ import type {
PluginHookAgentEndEvent,
PluginHookBeforeAgentStartEvent,
PluginHookBeforeAgentStartResult,
PluginHookBeforeModelResolveEvent,
PluginHookBeforeModelResolveResult,
PluginHookBeforePromptBuildEvent,
PluginHookBeforePromptBuildResult,
PluginHookBeforeCompactionEvent,
PluginHookLlmInputEvent,
PluginHookLlmOutputEvent,
@@ -45,6 +49,10 @@ export type {
PluginHookAgentContext,
PluginHookBeforeAgentStartEvent,
PluginHookBeforeAgentStartResult,
PluginHookBeforeModelResolveEvent,
PluginHookBeforeModelResolveResult,
PluginHookBeforePromptBuildEvent,
PluginHookBeforePromptBuildResult,
PluginHookLlmInputEvent,
PluginHookLlmOutputEvent,
PluginHookAgentEndEvent,
@@ -104,6 +112,26 @@ export function createHookRunner(registry: PluginRegistry, options: HookRunnerOp
const logger = options.logger;
const catchErrors = options.catchErrors ?? true;
const mergeBeforeModelResolve = (
acc: PluginHookBeforeModelResolveResult | undefined,
next: PluginHookBeforeModelResolveResult,
): PluginHookBeforeModelResolveResult => ({
// Keep the first defined override so higher-priority hooks win.
modelOverride: acc?.modelOverride ?? next.modelOverride,
providerOverride: acc?.providerOverride ?? next.providerOverride,
});
const mergeBeforePromptBuild = (
acc: PluginHookBeforePromptBuildResult | undefined,
next: PluginHookBeforePromptBuildResult,
): PluginHookBeforePromptBuildResult => ({
systemPrompt: next.systemPrompt ?? acc?.systemPrompt,
prependContext:
acc?.prependContext && next.prependContext
? `${acc.prependContext}\n\n${next.prependContext}`
: (next.prependContext ?? acc?.prependContext),
});
/**
* Run a hook that doesn't return a value (fire-and-forget style).
* All handlers are executed in parallel for performance.
@@ -185,10 +213,41 @@ export function createHookRunner(registry: PluginRegistry, options: HookRunnerOp
// Agent Hooks
// =========================================================================
/**
* Run before_model_resolve hook.
* Allows plugins to override provider/model before model resolution.
*/
async function runBeforeModelResolve(
event: PluginHookBeforeModelResolveEvent,
ctx: PluginHookAgentContext,
): Promise<PluginHookBeforeModelResolveResult | undefined> {
return runModifyingHook<"before_model_resolve", PluginHookBeforeModelResolveResult>(
"before_model_resolve",
event,
ctx,
mergeBeforeModelResolve,
);
}
/**
* Run before_prompt_build hook.
* Allows plugins to inject context and system prompt before prompt submission.
*/
async function runBeforePromptBuild(
event: PluginHookBeforePromptBuildEvent,
ctx: PluginHookAgentContext,
): Promise<PluginHookBeforePromptBuildResult | undefined> {
return runModifyingHook<"before_prompt_build", PluginHookBeforePromptBuildResult>(
"before_prompt_build",
event,
ctx,
mergeBeforePromptBuild,
);
}
/**
* Run before_agent_start hook.
* Allows plugins to inject context into the system prompt.
* Runs sequentially, merging systemPrompt and prependContext from all handlers.
* Legacy compatibility hook that combines model resolve + prompt build phases.
*/
async function runBeforeAgentStart(
event: PluginHookBeforeAgentStartEvent,
@@ -199,14 +258,8 @@ export function createHookRunner(registry: PluginRegistry, options: HookRunnerOp
event,
ctx,
(acc, next) => ({
systemPrompt: next.systemPrompt ?? acc?.systemPrompt,
prependContext:
acc?.prependContext && next.prependContext
? `${acc.prependContext}\n\n${next.prependContext}`
: (next.prependContext ?? acc?.prependContext),
// Keep the first defined override so higher-priority hooks win.
modelOverride: acc?.modelOverride ?? next.modelOverride,
providerOverride: acc?.providerOverride ?? next.providerOverride,
...mergeBeforePromptBuild(acc, next),
...mergeBeforeModelResolve(acc, next),
}),
);
}
@@ -563,6 +616,8 @@ export function createHookRunner(registry: PluginRegistry, options: HookRunnerOp
return {
// Agent hooks
runBeforeModelResolve,
runBeforePromptBuild,
runBeforeAgentStart,
runLlmInput,
runLlmOutput,