feat(coding-agent): add provider payload hook

This commit is contained in:
Mario Zechner
2026-03-07 14:23:25 +01:00
parent e3adaf1bd9
commit a3f05423d9
20 changed files with 157 additions and 32 deletions

View File

@@ -32,6 +32,8 @@ export type {
BashToolResultEvent,
BeforeAgentStartEvent,
BeforeAgentStartEventResult,
BeforeProviderRequestEvent,
BeforeProviderRequestEventResult,
// Context
CompactOptions,
// Events - Agent

View File

@@ -13,6 +13,7 @@ import type { SessionManager } from "../session-manager.js";
import type {
BeforeAgentStartEvent,
BeforeAgentStartEventResult,
BeforeProviderRequestEvent,
CompactOptions,
ContextEvent,
ContextEventResult,
@@ -105,6 +106,7 @@ type RunnerEmitEvent = Exclude<
| ToolResultEvent
| UserBashEvent
| ContextEvent
| BeforeProviderRequestEvent
| BeforeAgentStartEvent
| ResourcesDiscoverEvent
| InputEvent
@@ -710,6 +712,40 @@ export class ExtensionRunner {
return currentMessages;
}
async emitBeforeProviderRequest(payload: unknown): Promise<unknown> {
const ctx = this.createContext();
let currentPayload = payload;
for (const ext of this.extensions) {
const handlers = ext.handlers.get("before_provider_request");
if (!handlers || handlers.length === 0) continue;
for (const handler of handlers) {
try {
const event: BeforeProviderRequestEvent = {
type: "before_provider_request",
payload: currentPayload,
};
const handlerResult = await handler(event, ctx);
if (handlerResult !== undefined) {
currentPayload = handlerResult;
}
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
const stack = err instanceof Error ? err.stack : undefined;
this.emitError({
extensionPath: ext.path,
event: "before_provider_request",
error: message,
stack,
});
}
}
}
return currentPayload;
}
async emitBeforeAgentStart(
prompt: string,
images: ImageContent[] | undefined,

View File

@@ -493,6 +493,12 @@ export interface ContextEvent {
messages: AgentMessage[];
}
/** Fired before a provider request is sent. Can replace the payload. */
export interface BeforeProviderRequestEvent {
type: "before_provider_request";
payload: unknown;
}
/** Fired after user submits prompt but before agent loop. */
export interface BeforeAgentStartEvent {
type: "before_agent_start";
@@ -807,6 +813,7 @@ export type ExtensionEvent =
| ResourcesDiscoverEvent
| SessionEvent
| ContextEvent
| BeforeProviderRequestEvent
| BeforeAgentStartEvent
| AgentStartEvent
| AgentEndEvent
@@ -832,6 +839,8 @@ export interface ContextEventResult {
messages?: AgentMessage[];
}
export type BeforeProviderRequestEventResult = unknown;
export interface ToolCallEventResult {
block?: boolean;
reason?: string;
@@ -944,6 +953,10 @@ export interface ExtensionAPI {
on(event: "session_before_tree", handler: ExtensionHandler<SessionBeforeTreeEvent, SessionBeforeTreeResult>): void;
on(event: "session_tree", handler: ExtensionHandler<SessionTreeEvent>): void;
on(event: "context", handler: ExtensionHandler<ContextEvent, ContextEventResult>): void;
on(
event: "before_provider_request",
handler: ExtensionHandler<BeforeProviderRequestEvent, BeforeProviderRequestEventResult>,
): void;
on(event: "before_agent_start", handler: ExtensionHandler<BeforeAgentStartEvent, BeforeAgentStartEventResult>): void;
on(event: "agent_start", handler: ExtensionHandler<AgentStartEvent>): void;
on(event: "agent_end", handler: ExtensionHandler<AgentEndEvent>): void;

View File

@@ -292,6 +292,13 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
tools: [],
},
convertToLlm: convertToLlmWithBlockImages,
onPayload: async (payload, _model) => {
const runner = extensionRunnerRef.current;
if (!runner?.hasHandlers("before_provider_request")) {
return payload;
}
return runner.emitBeforeProviderRequest(payload);
},
sessionId: sessionManager.getSessionId(),
transformContext: async (messages) => {
const runner = extensionRunnerRef.current;

View File

@@ -56,6 +56,8 @@ export type {
AppAction,
BashToolCallEvent,
BeforeAgentStartEvent,
BeforeProviderRequestEvent,
BeforeProviderRequestEventResult,
CompactOptions,
ContextEvent,
ContextUsage,