/** * v2 observer dispatcher. * * Observers are the `tool_result` side of the steering engine: rules * decide "can this run" pre-execution; observers record "what happened" * post-execution. Typical use is to `appendEntry` into pi's session * JSONL so later predicates can gate on prior turn state (the * "description was read in a PRIOR turn" idiom from the ADR). * * This module merges user-declared observers (from * {@link SteeringConfig.observers}) with plugin-shipped ones (from * {@link ResolvedPluginState.observers}), then on every `tool_result`: * * 1. Applies the observer's `watch` filter (toolName + inputMatches * + exitCode). No `watch` means "fire on every tool_result". * 2. Calls `observer.onResult(event, observerCtx)`. Awaits if it * returns a promise. * 3. Catches thrown errors per-observer — one buggy observer does * NOT prevent the rest from running. * * Observer context (`ObserverContext`) is built fresh per event, using * `appendEntry` and `findEntries` closures shared with the evaluator. * `exec` is deliberately NOT exposed — observers are expected to be * lightweight state-recording hooks. Complex tool_result analysis that * needs to shell out belongs in a separate pi extension hook or in a * rule's `when.condition` pre-execution, not in an observer. * * Wiring (Phase 3c): the extension runtime subscribes to `tool_result` * and forwards the event + current `agentLoopIndex` into `dispatch`. */ import type { ExtensionContext, ToolResultEvent as PiToolResultEvent } from "@earendil-works/pi-coding-agent"; import { type EvaluatorHost } from "./evaluator-internals/context.ts"; import type { ResolvedPluginState } from "./plugin-merger.ts"; import type { Observer } from "./schema.ts"; export { matchesWatch } from "./internal/watch-matcher.ts"; /** * Runtime-facing dispatcher handle. Phase 3c holds an instance per * session and calls {@link dispatch} from the pi `tool_result` listener. */ export interface ObserverDispatcher { /** * Dispatch a single `tool_result` to every matching observer. * * Resolves when all observer handlers have settled. Handlers that * throw are caught + logged via `console.warn` and do not prevent * subsequent observers from running. */ dispatch(event: PiToolResultEvent, ctx: ExtensionContext, agentLoopIndex: number): Promise; } /** * Construct an {@link ObserverDispatcher}. * * Arguments: * - `resolved` — merged plugin state from {@link resolvePlugins}. * Source of plugin-shipped observers and the * registry used by the evaluator at the same * level. * - `userObservers` — the user's `config.observers` list (already * deduped at the loader level in Phase 2). User * observers fire BEFORE plugin observers on the * same event; within each group, registration * order decides. * - `host` — narrow surface exposing pi's `exec` + * `appendEntry`. Passed straight through to the * per-event observer context. */ export declare function buildObserverDispatcher(resolved: ResolvedPluginState, userObservers: readonly Observer[], host: EvaluatorHost): ObserverDispatcher; export type { EvaluatorHost } from "./evaluator-internals/context.ts"; //# sourceMappingURL=observer-dispatcher.d.ts.map