/** * public-api.ts — Single entry point for the orchestrator's typed public API. * * Re-exports all RPC, hook, and event types that peer extensions and tests * need to consume, and provides a Symbol-based discovery mechanism so that * consumers don't need direct module references. * * Usage from a peer extension: * * ```ts * import { getSubagentsApi, type TypedHookPayload } from "@onlinechefgroep/pi-agent-orchestrator/public-api"; * * const api = getSubagentsApi(); * if (!api) throw new Error("pi-agent-orchestrator extension is not loaded"); * * // Typed RPC * const { id } = await api.rpc.spawn({ type: "Explore", prompt: "scan deps" }); * * // Typed lifecycle subscription — handler gets the exact data shape * // the extension emits for "subagent:start", not a `Record`. * api.hooks.on("subagent:start", async (payload) => { * // ^? TypedHookPayload<"subagent:start"> → { event, agentId, data: AgentStartData, ... } * console.log(payload.data.type, payload.data.description); * return "allow"; * }); * ``` * * **Symbol registry contract:** the orchestrator publishes its API on * `globalThis[Symbol.for("pi-subagents:api")]` and its raw `HookRegistry` on * `globalThis[Symbol.for("pi-subagents:hooks")]`. The latter name is * documented in `docs/api-reference.md` but was previously not actually * implemented — this module closes that drift. */ import { type EventBus, type SubagentsRpcClient } from "./cross-extension-rpc.js"; import { type HookEvent, HookRegistry, type HookResponse } from "./hooks.js"; /** Symbol under which the orchestrator publishes its typed public API. */ export declare const SUBAGENTS_API_SYMBOL: unique symbol; /** Symbol under which the orchestrator publishes its raw `HookRegistry`. */ export declare const SUBAGENTS_HOOKS_SYMBOL: unique symbol; /** Symbol under which the orchestrator publishes its read-only `SubagentManagerHandle`. */ export declare const SUBAGENTS_MANAGER_SYMBOL: unique symbol; export interface AgentStartData { type: string; description: string; parentId?: string; } export interface AgentEndData { status: "completed" | "aborted" | "error" | "stopped" | "steered"; result?: string; error?: string; durationMs?: number; tokensIn?: number; tokensOut?: number; turns?: number; /** Final assistant text available to quality-gate hooks. */ responseText?: string; /** 1-based end-hook attempt (initial completion = 1). */ attempt?: number; /** Total allowed attempts = 1 + maxEndHookRevisions. */ maxAttempts?: number; } export interface AgentErrorData { error: string; stack?: string; } export interface AgentSpawnData { type: string; description: string; parentId?: string; } export interface AgentSteerData { instruction: string; } export interface ToolCallData { toolName: string; input: unknown; } export interface ToolResultData { toolName: string; result: unknown; error?: string; } export interface CompactionStartData { messageCount: number; } export interface CompactionEndData { before: number; after: number; } export interface TurnStartData { turnNumber: number; } export interface TurnEndData { turnNumber: number; } export interface SwarmJoinData { swarmId: string; agentId: string; } export interface SwarmLeaveData { swarmId: string; agentId: string; reason?: string; } export interface ValidationStartData { criteria: readonly string[]; } export interface ValidationEndData { passed: boolean; summary: string; } export declare const TYPED_HOOK_PAYLOAD_MAP: { "subagent:start": AgentStartData; "subagent:end": AgentEndData; "subagent:error": AgentErrorData; "subagent:spawn": AgentSpawnData; "subagent:steer": AgentSteerData; "tool:call": ToolCallData; "tool:result": ToolResultData; "compaction:start": CompactionStartData; "compaction:end": CompactionEndData; "turn:start": TurnStartData; "turn:end": TurnEndData; "swarm:join": SwarmJoinData; "swarm:leave": SwarmLeaveData; "validation:start": ValidationStartData; "validation:end": ValidationEndData; }; export type TypedHookPayloadMap = typeof TYPED_HOOK_PAYLOAD_MAP; /** Discriminated hook payload — `data` is shaped by `event`. */ export type TypedHookPayload = { event: E; agentId: string; data: TypedHookPayloadMap[E]; timestamp?: number; }; /** Handler shape for the typed `on(event, handler)` helper. */ export type TypedHookHandler = (payload: TypedHookPayload) => Promise | HookResponse | undefined; /** Typed lifecycle subscription — discriminated by event name. */ export interface TypedEventSubscription { /** Subscribe to a single typed event. Returns an unsubscribe function. */ on(event: E, handler: TypedHookHandler): () => void; /** Subscribe to every lifecycle event (typed as the union). Returns an unsubscribe function. */ onAll(handler: (payload: TypedHookPayload) => Promise | HookResponse | undefined): () => void; /** Underlying registry — read-only mirror for advanced consumers. */ readonly registry: HookRegistry; } /** Top-level typed public API exposed to peer extensions. */ export interface SubagentsPublicApi { /** Protocol version the consumer is talking to. */ readonly protocolVersion: number; /** Typed RPC client — `ping` / `spawn` / `stop` / `sessionUsage`. */ readonly rpc: SubagentsRpcClient; /** Typed lifecycle subscription. */ readonly hooks: TypedEventSubscription; /** Read-only manager handle — `waitForAll` / `hasRunning` / `getRecord` / `listAgentIds`. */ readonly manager: SubagentManagerHandle; } /** * Maximum length of the `description` field exposed on the sanitized manager * record. Hardcoded to keep the security contract (don't leak full prompts * / long context to peer extensions) obvious; bump in a follow-up if a * legitimate consumer needs more. */ export declare const SUBAGENT_MANAGER_MAX_DESCRIPTION_CHARS = 200; /** * Sanitized, non-sensitive shape of an agent record that the manager handle * exposes to peer extensions. Sensitive fields (full description, prompt, * result, error, internal flags) are intentionally omitted; consumers that * need the full record should use the `Agent` tool or RPC instead. */ export interface SubagentManagerRecord { id: string; type: string; status: string; /** Truncated to `SUBAGENT_MANAGER_MAX_DESCRIPTION_CHARS` by the handle builder. */ description?: string; } /** * Read-only handle over the orchestrator's `AgentManager`, exposed to peer * extensions so they can observe running agents and read safe metadata * without gaining the ability to spawn, stop, or mutate them. */ export interface SubagentManagerHandle { /** Wait until every currently-running agent has reached a terminal state. */ waitForAll(): Promise; /** Whether any agent is currently running (not yet terminal). */ hasRunning(): boolean; /** Look up an agent by id. Returns a sanitized record or `undefined`. */ getRecord(id: string): SubagentManagerRecord | undefined; /** List the ids of all known agents of a given type. */ listAgentIds(type: string): string[]; } /** Structural shape of the manager required by `registerSubagentsApi`. */ export interface SubagentManagerLike { waitForAll(): Promise; hasRunning(): boolean; getRecord(id: string): { id: string; type: string; status: string; description?: string; } | undefined; listAgents(): ReadonlyArray<{ id: string; type: string; }>; } /** * Publish the orchestrator's typed API on `globalThis[Symbol.for("pi-subagents:api")]`, * its raw `HookRegistry` on `globalThis[Symbol.for("pi-subagents:hooks")]`, and * its read-only `SubagentManagerHandle` on `globalThis[Symbol.for("pi-subagents:manager")]`. * * Called once by the extension on load. **Idempotent** — last registration wins, * matching the pattern documented for `registerRpcHandlers` in * `src/cross-extension-rpc.ts`. */ export declare function registerSubagentsApi(events: EventBus, hooks: HookRegistry, manager: SubagentManagerLike, options?: { extensionId?: string; timeoutMs?: number; }): SubagentsPublicApi; /** Forget the published API, hook registry, and manager handle. Test-only / opt-in cleanup. */ export declare function clearSubagentsApi(): void; /** * Discover the orchestrator's typed API on globalThis. * Returns `undefined` if the extension is not loaded. */ export declare function getSubagentsApi(): SubagentsPublicApi | undefined; /** Discover the orchestrator's raw `HookRegistry`. Returns `undefined` if not loaded. */ export declare function getSubagentsHooks(): HookRegistry | undefined; /** Discover the orchestrator's read-only `SubagentManagerHandle`. Returns `undefined` if not loaded. */ export declare function getSubagentsManager(): SubagentManagerHandle | undefined; export { type AuthContext, createSubagentsRpcClient, type EventBus, type PingRpcReply, PROTOCOL_VERSION, type RateLimitConfig, type RpcReply, type SessionCapable, type SessionUsageRpcReply, type SpawnCapable, type SpawnRpcRequest, type StopRpcRequest, type SubagentsRpcClient, type SwarmCapable, } from "./cross-extension-rpc.js"; export { composeHandlers, type HookEvent, type HookHandler, type HookPayload, HookRegistry, type HookResponse, isBlockResponse, type NormalizedHookDecision, normalizeHookResponse, } from "./hooks.js";