/** * Graph Execution Engine v2 — DispatchManager Bridge * * Version: 2.0 * Date: 2026-07-24 * * A read-only seam over {@link DispatchManager}. The graph engine uses this * bridge as its *only* touchpoint into the dispatch subsystem. It wraps the * public methods (`launch`, `onTaskTerminated`, `removeTaskTerminatedListener`, * `getTask`, `getTasksByParent`, `cancelTask`, `getSessionUsage`) with proper * TS types so the engine never reaches into `DispatchManager` internals * directly. * * Invariant: this module is an **import-only consumer** of the dispatch * subsystem's *public* API. It imports only the `DispatchManager` class type * and dispatch value types (`DispatchInput`, `DispatchTask`) — no private * members of `src/dispatch/core/manager.ts`. * * A node in the graph is an `{agent, prompt}` tuple. "Executing" a node means * dispatching work to that node's bound agent via {@link executeNode}. * * Design reference: `.rolebox/design/engine-state-machine.md` §3. */ import type { DispatchManager } from "../../dispatch/core/manager.ts"; import type { DispatchInput, DispatchTask } from "../../dispatch/types.ts"; import type { UsageRecord } from "../../dispatch/budget/budget-tracker.ts"; import type { NodeRuntimeState } from "../../types.engine-v2.ts"; /** * Parent-context shape required by `DispatchManager.launch`/`executeSync`. * Mirrors the inline parameter type at `src/dispatch/core/manager.ts:209`. */ export interface DispatchParentContext { /** * Graph-scoped request/budget key. For a graph-level dispatch this is the * graph ID (see {@link graphParentContext}); the dispatch subsystem treats * the parent session ID as the **request** scope, seeding `requestUsage` * and `getBudgetTracker().getRequestUsage(...)` keyed off it. This is a * budget/request scope key — NOT necessarily a live platform session handle. */ sessionID: string; /** * REAL live parent session that owns this dispatch — the platform session * handle required by adapters (e.g. dsh) that must post notifications or * resume a run against a live parent. Distinct from {@link sessionID}, which * is the graph-scoped budget/request key. Omitted by * {@link graphParentContext} when the caller supplies no live parent, so * opencode/Pi contexts stay byte-identical to before this field existed. */ parentSessionId?: string; /** Agent ID of the parent. */ agent: string; /** Working directory of the parent. */ directory: string; /** * Graph-scope marker. Set by {@link graphParentContext}; carried onto the * dispatched {@link DispatchTask} by task-launcher. While set, the dispatch * layer suppresses its parent notifications — graph-node completion is * reported EXCLUSIVELY by the graph notifier * (`src/graph/engine/graph-notify.ts`). Real-session parents omit this field * and keep dispatch-layer notifications exactly as before. */ graphScoped?: boolean; } /** Options for building the parent context of a graph-level dispatch. */ export interface GraphParentOptions { /** Graph ID — becomes the request scope for request-level budget tracking. */ graphId: string; /** Acting agent for the graph executor (defaults to {@link DEFAULT_GRAPH_AGENT}). */ agent?: string; /** Working directory for dispatched graph nodes. */ directory: string; /** * REAL live parent session that owns the graph run. Copied onto the returned * context's {@link DispatchParentContext.parentSessionId} only when supplied. * Platform adapters that require a live parent handle (dsh) read it in place * of the graph-scoped {@link DispatchParentContext.sessionID}. Omit it for * opencode/Pi, whose contexts are unchanged. */ parentSessionId?: string; } /** Callback signature for task termination (matches `DispatchManager.onTaskTerminated`). */ export type TaskTerminatedCallback = (taskId: string, status: string) => void; /** Fallback acting-agent identity when a graph supplies no explicit agent. */ export declare const DEFAULT_GRAPH_AGENT = "emperor--jinyiwei"; /** * Build the parent context for a graph-level dispatch. * * `graphId` is deliberately placed in `sessionID` because the dispatch * subsystem treats the parent session ID as the **request** scope: it seeds * `requestUsage` and `getBudgetTracker().getRequestUsage(...)` keyed off it. * Scoping requests to the graph therefore makes request-level budget checks * per-graph (see `budget-bridge.ts`). * * When `opts.parentSessionId` is supplied it is copied onto * {@link DispatchParentContext.parentSessionId} — the REAL live parent session * that platform adapters requiring a live handle (dsh) use instead of the * graph-scoped `sessionID`. The key is omitted entirely when absent so * opencode/Pi contexts are byte-identical to the pre-field shape. */ export declare function graphParentContext(opts: GraphParentOptions): DispatchParentContext; /** * Read-only wrapper over {@link DispatchManager}'s public surface. * * The instance is injected (dependency injection) — this matches the existing * dispatch-tool pattern (`src/dispatch/tools.ts`) rather than a module-level * singleton. The graph engine constructs one `DispatchBridge` per graph * execution from the active manager. */ export declare class DispatchBridge { private readonly manager; constructor(manager: DispatchManager); /** Dispatch a task asynchronously (returns immediately with the task handle). */ launch(input: DispatchInput, parentContext: DispatchParentContext): Promise; /** Register a one-time listener fired when a task enters a terminal state. */ onTaskTerminated(taskId: string, callback: TaskTerminatedCallback): TaskTerminatedCallback; /** * Remove a previously-registered task-terminated listener (monitor M4). * * Delegates to `DispatchManager.removeTaskTerminatedListener` * (`src/dispatch/core/manager.ts`). Consumed by the engine's subscription * accessor (`AdvanceEngine.getTerminationSubscriptions`) so a teardown path * (S7 dispose) can unregister every listener this engine wired — closing the * leak previously left by fire-once `onTaskTerminated` subscriptions. */ removeTaskTerminatedListener(taskId: string, callback: TaskTerminatedCallback): void; /** * Look up a dispatched task's current status (for recovery reconciliation * and to read `task.error` on an errored task). Returns `undefined` for an * unknown task id. */ getTask(taskId: string): DispatchTask | undefined; /** * List every task launched under a given parent session id (the graph id * seeds the dispatch parent session via {@link graphParentContext}). Used by * recovery to sweep the crash-window orphan: a node persisted `running` * before `executeNode` resolved its task handle carries no `dispatchTaskId`, * so the live session is matched by parent session + node-scoped description * instead (`engine-recovery.ts::reconcileEngine`). */ getTasksByParent(parentSessionId: string): DispatchTask[]; /** Cancel a running task. Returns `true` if the cancellation was issued. */ cancelTask(taskId: string): Promise; /** * Cumulative token/cost usage for a single dispatched session (keyed by the * dispatch session ID). Delegates to the budget tracker's per-session ledger. * * This is the per-node usage surface: a node's `dispatchSessionId` identifies * exactly one dispatched session, so the engine reads this at task termination * to populate `node.tokensConsumed` and feed the graph-level budget counters * (`engine-recovery.ts::captureNodeUsage`). * Returns a zeroed `UsageRecord` when the tracker has no record for the * session (e.g. usage was never sampled or the session was reset). */ getSessionUsage(sessionId: string): UsageRecord; /** * Execute a graph node by dispatching to its bound agent. * * "Executing a node" === dispatching work to `node.agent` with `node.prompt`. * Runs in the background (async) and returns the task handle so the caller * can register an `onTaskTerminated` listener and await completion. * * @param node The node's runtime state (source of `agent` + `prompt`). * @param parentContext Parent context; use {@link graphParentContext} to scope * request-level budget to the owning graph. * @param description Optional human-readable task description. */ executeNode(node: NodeRuntimeState, parentContext: DispatchParentContext, description?: string): Promise; } //# sourceMappingURL=dispatch-bridge.d.ts.map