/** * src/extension/events.ts — B7: lifecycle event bus on `pi.events`. * * Publishes the subagents run lifecycle so OTHER Pi extensions (pi-mesh, * Mission Control, dashboards…) can react to our runs without polling: * * subagents:created — a run entered the monitor (queued -> dispatch) * subagents:started — the run transitioned to running * subagents:completed — terminal status complete * subagents:failed — terminal status failed / preflight_failed * subagents:aborted — terminal status aborted * subagents:steered — a mid-run steering message was accepted * subagents:escalated — B5: the child escalated to the master (ask_master) * * HOOK (zero engine changes): the DispatchEngine already emits a hash-only * ledger stream (`onLedger` engine option) at every lifecycle transition it * owns — `start`/`continue_start` (dispatch, background executor), `end` * (settle: complete/failed/aborted) and `preflight_failed`. This bus maps that * stream onto the event channels above. Out-of-band transitions that bypass * the ledger (steer via `engine.steer`, abort via `abortDelegationRun` in * FleetView/RPC stop) are covered by `steerRunWithEvents` / `emitAborted`. * * PAYLOAD POSTURE (I1, hash-only): payloads carry runId, agent, status, * durationMs, tokens/cost when available, taskHash/outputHash — NEVER a raw * body (no output, stderr, task text, gate errors, or error messages). Every * payload carries `bodyStored:false`. * * Zero @earendil-works/* imports (I9); degrades to a no-op without `pi.events`. */ import type { DelegationRunView } from "../engine/runs.js"; import type { EventBusApi } from "./pi-types.js"; /** Payload schema id (mirror of the hash-only ledger posture). */ export declare const SUBAGENTS_EVENT_SCHEMA = "zob.subagents-event.v1"; /** Lifecycle event channel names published on `pi.events`. */ export type SubagentsLifecycleEvent = "created" | "started" | "completed" | "failed" | "aborted" | "steered" | "escalated"; export declare const SUBAGENTS_EVENT_CHANNEL: Record; /** Channel for one lifecycle event (helper for consumers). */ export declare function subagentsEventChannel(event: SubagentsLifecycleEvent): string; /** Hash-only, body-free lifecycle payload (NEVER carries a raw body). */ export interface SubagentsLifecyclePayload { schema: string; event: SubagentsLifecycleEvent; runId: string; agent: string; status: string; mode?: string; source?: string; model?: string; background?: boolean; continuedFromRunId?: string; turnCount?: number; durationMs?: number; /** input+output token sum, only when usage is already available. */ tokens?: number; cost?: number; taskHash?: string; outputHash?: string; /** B5: sha-256 of the consumed ask_master message (hash-only, never a body). */ escalationHash?: string; failureKind?: string; bodyStored: false; } /** Minimal emit sink (the `pi.events` bus or a test double). */ export interface EventSink { emit(channel: string, payload?: unknown): void; } /** Build the hash-only payload from a raw engine ledger entry. */ export declare function lifecyclePayloadFromLedgerEntry(event: SubagentsLifecycleEvent, entry: Record): SubagentsLifecyclePayload | undefined; /** Build the hash-only payload from a monitor run view. */ export declare function lifecyclePayloadFromRun(event: SubagentsLifecycleEvent, run: DelegationRunView, nowMs?: number): SubagentsLifecyclePayload; /** * The lifecycle event bus. Feed it the engine's `onLedger` stream * (`handleLedgerEntry`); call `steerRunWithEvents`/`emitAborted` for the * out-of-band transitions. No-op (never throws) when no sink is available. */ export declare class SubagentsEventBus { private readonly sink; constructor(events?: EventBusApi | EventSink); /** Map one raw engine ledger entry onto lifecycle events (never throws). */ handleLedgerEntry(entry: Record): void; /** Emit `subagents:aborted` for an out-of-band abort (FleetView/RPC stop). */ emitAborted(run: DelegationRunView, nowMs?: number): void; /** Emit `subagents:steered` after a successful engine steer. */ emitSteered(run: DelegationRunView, nowMs?: number): void; private emitPayload; } /** * Steer a run through the engine AND emit `subagents:steered` on success. * Extension-side wrapper so steering stays observable on the event bus * without touching the engine. Returns the engine outcome unchanged. */ export declare function steerRunWithEvents(engine: { steer(runId: string, message: string): { ok: boolean; }; }, monitor: { runs: DelegationRunView[]; }, runId: string, message: string, bus: SubagentsEventBus, nowMs?: number): { ok: boolean; };