UNPKG

eve

Version:

Filesystem-first framework for durable backend AI agents that run anywhere.

128 lines (127 loc) 4.95 kB
/** * The subagent child-stream subsystem: for every `subagent.called` on the * parent stream, a parallel pump over the child session folds its events * into the renderer's nested subagent view. Extracted from the runner — * the subsystem touches nothing but its own run state, the client, and the * {@link SubagentView} seam. */ import type { Client } from "#client/index.js"; import { type ActionResultStreamEvent, type SubagentCalledStreamEvent } from "#protocol/message.js"; /** * The renderer's subagent surface. One cohesive capability: a renderer that * implements it renders whole sections — header, nested steps and tools, * ghost sweeps, completion — and a renderer without it simply has no * subagent view. Individually-optional methods would let a renderer * implement a type-legal subset that ghosts placeholders or duplicates * parent tool rows. */ export interface SubagentView { /** Opens a call's section the moment its dispatch is announced. */ begin(update: { callId: string; name: string; }): void; upsertStep(update: SubagentStepUpdate): void; upsertTool(update: SubagentToolUpdate): void; /** Drops a child tool row whose call never materialized. */ removeTool(update: { callId: string; childCallId: string; }): void; /** Marks a call complete so its section collapses on `└ Done…`. */ complete(update: { callId: string; }): void; /** Suppresses the parent-level tool row for a child-owned call id. */ markChildToolCallId(callId: string): void; } type SubagentChildStep = { reasoning: string; message: string; finalized: boolean; }; type SubagentToolStatus = "preparing" | "approval-requested" | "executing" | "done" | "failed" | "rejected"; type SubagentToolState = { toolName: string; input: unknown; status: SubagentToolStatus; output?: unknown; errorText?: string; }; export type SubagentRun = { name: string; /** * The run's one lifecycle authority. `settled` means the final assistant * message is in (the child's turn boundary, or the parent's * `subagent.completed` fallback); a late child event — a HITL-parked * turn resuming — explicitly reopens the run rather than mutating a * completed section by accident. */ status: "running" | "settled"; /** * One entry per logical "child message" — independent of the child's * `stepIndex` field, which the harness can reuse across multiple * assistant messages within a turn (e.g. a message before a tool call * and another message after the tool result both arrive under * `stepIndex: 0`). The key is a monotonic counter so each * `message.completed` opens a new box on the next inbound delta. */ steps: Map<number, SubagentChildStep>; /** * Section currently accepting reasoning/message deltas. `null` means * the next delta opens a new section. */ currentSectionKey: number | null; /** Monotonic counter for new section keys. */ nextSectionKey: number; tools: Map<string, SubagentToolState>; }; export type SubagentStepUpdate = { callId: string; subagentName: string; sectionKey: number; reasoning: string; message: string; finalized: boolean; }; export type SubagentToolUpdate = { callId: string; subagentName: string; childCallId: string; toolName: string; input: unknown; status: SubagentToolStatus; output?: unknown; errorText?: string; }; export interface SubagentPumpOptions { client?: Client; view?: SubagentView; formatActionResultError: (event: ActionResultStreamEvent) => string; } export declare class SubagentPump { #private; constructor(options: SubagentPumpOptions); /** * The moment a dispatch is known to be a subagent call, its section * header replaces the parent-level tool row (or its still-preparing * placeholder — subagent dispatches never upgrade one, since their * actions are not tool-call kind). Without this the placeholder is * swept at the step boundary and nothing shows until the child's first * content arrives. Idempotent for SSE-resume re-entries, which only * refresh the name. */ begin(called: SubagentCalledStreamEvent): void; /** * Parent reports subagent.completed. The child stream pump terminates * itself on the child's own turn boundary — the authoritative finish * signal, which already finalized the section — so this is a fallback * for runs whose boundary never reached us (a dropped child stream, a * HITL-parked turn resuming later). We do NOT abort the pump here, * because the child's `message.completed` event may still be in flight * (the parent and child streams are independent HTTP connections). */ settle(callId: string): void; abortAll(): void; } export {};