/** * Named-event SSE protocol for streaming an agent loop to a browser. * * `@gemstack/ai-sdk` already ships the Vercel AI SDK data-stream protocol * ({@link toVercelDataStream}) - the numeric-prefix wire (`0:` / `9:` / `a:` * ...). This is the alternative for apps that want a plain * `text/event-stream` with self-describing event names that mirror the agent * loop's own lifecycle (`text`, `tool_call`, `tool_update`, `tool_result`, * `pending_client_tools`, `tool_approval_required`, `handoff`, `complete`, * `error`). * * Both ends live here so the wire vocabulary can never drift: * * - Server: {@link toAgentSseStream} / {@link toAgentSseResponse} project an * `agent.stream()` result onto the named events and frame them as SSE. * - Browser: {@link readAgentStream} decodes the same events back into an * {@link AgentStreamTurn} and fires per-event callbacks for UI side effects. * {@link applyAgentSseEvent} is exposed so the per-event reducer can be * unit-tested against a synthetic turn. * * Runtime-agnostic: uses only web globals (`ReadableStream`, `Response`, * `TextEncoder` / `TextDecoder`, `crypto.randomUUID`), no `node:` imports, so * the module is safe in the main entry and runs server-side (Node / edge) and * client-side alike. * * This ships the framework-generic core. App-specific events (conversation * ids, billing, sub-run fan-out bookkeeping, server-authoritative history * sync) are not part of it - emit and decode those alongside this protocol on * your own channel. */ import type { AgentStreamResponse, AiMessage, FinishReason, TokenUsage, ToolCall } from './types.js'; /** The named SSE events this protocol emits, in agent-loop order. */ export type AgentSseEventName = 'text' | 'tool_call' | 'tool_update' | 'tool_result' | 'pending_client_tools' | 'tool_approval_required' | 'handoff' | 'complete' | 'error'; /** What the loop parked on when it paused, surfaced on the `complete` event. */ export type AgentAwaiting = 'client_tools' | 'approval'; export interface AgentSseTextPayload { text: string; } export interface AgentSseToolCallPayload { id?: string; tool: string; input?: Record; } export interface AgentSseToolUpdatePayload { id?: string; tool?: string; update: unknown; } export interface AgentSseToolResultPayload { id?: string; toolCallId?: string; tool?: string; /** String passthrough when the tool returned a string, else JSON-encoded. */ content: string; } export interface AgentSsePendingClientToolsPayload { toolCalls: ToolCall[]; } export interface AgentSseApprovalPayload { toolCall: ToolCall; isClientTool: boolean; } export interface AgentSseHandoffPayload { from: string; to: string; message?: string; } export interface AgentSseCompletePayload { done: true; finishReason?: FinishReason; awaiting?: AgentAwaiting; /** Number of model steps the run took (`response.steps.length`). */ steps?: number; usage?: TokenUsage; } export interface AgentSseErrorPayload { message: string; } /** * Project an `agent.stream()` result onto the named-event SSE wire as a * `ReadableStream`. * * Iterates the chunk stream, emits one named event per loop chunk, then * awaits the `response` promise and emits a terminal `complete` event * carrying `done`, `finishReason`, the `awaiting` pause (if any), step count, * and usage. If iteration or the response throws, an `error` event is emitted * and the stream closes cleanly so the browser reader's `onError` fires. */ export declare function toAgentSseStream(streaming: AgentStreamResponse): ReadableStream; /** * Wrap {@link toAgentSseStream} in a `Response` with the standard * `text/event-stream` headers (no caching, no proxy buffering). Return it * directly from a route handler. */ export declare function toAgentSseResponse(streaming: AgentStreamResponse, init?: ResponseInit): Response; /** * The accumulated state of one streamed agent turn, built up event by event * by {@link readAgentStream}. Mirrors {@link AgentResponse} fields the browser * needs to render the turn and build the next continuation request. */ export interface AgentStreamTurn { /** Concatenated `text` event deltas. */ assistantText: string; /** Tool calls stamped by `tool_call` events (for the next continuation). */ assistantToolCalls: ToolCall[]; /** Server-side `role:'tool'` result messages from `tool_result` events. */ serverToolResults: AiMessage[]; /** Client tool calls from a `pending_client_tools` event to run locally. */ pendingClientTools: ToolCall[]; /** Approval pause from a `tool_approval_required` event. */ pendingApproval: AgentSseApprovalPayload | null; /** Chain of agent class names traversed via `handoff` events. */ handoffPath: string[]; /** `true` once a `complete` event with `done: true` arrived. */ done: boolean; /** What the run paused on, from the `complete` event. */ awaiting: AgentAwaiting | undefined; } /** A fresh, empty {@link AgentStreamTurn}. */ export declare function newAgentStreamTurn(): AgentStreamTurn; /** Per-event callbacks fired by {@link readAgentStream} for UI side effects. */ export interface AgentStreamCallbacks { onText?: (text: string) => void; onToolCall?: (call: AgentSseToolCallPayload) => void; onToolUpdate?: (update: AgentSseToolUpdatePayload) => void; onToolResult?: (result: AgentSseToolResultPayload) => void; onPendingClientTools?: (toolCalls: ToolCall[]) => void; onToolApprovalRequired?: (approval: AgentSseApprovalPayload) => void; onHandoff?: (handoff: AgentSseHandoffPayload) => void; onComplete?: (data: AgentSseCompletePayload) => void; onError?: (error: AgentSseErrorPayload) => void; /** Fired for any SSE event outside the standard protocol vocabulary. Use this * to observe app-specific events (e.g. a server-issued `run_started` carrying * a `runId`) without consuming the response body twice. */ onAppEvent?: (event: string, data: unknown) => void; } /** * Read a named-event agent-SSE response body, applying each event to a fresh * {@link AgentStreamTurn} and firing the matching callback. Resolves with the * accumulated turn once the stream closes. * * The caller owns the `fetch` and the `!resp.ok` branch (so a rich error body * can be read for non-2xx responses); pass an already-OK response. A missing * body resolves to an empty turn. Malformed event JSON is skipped. */ export declare function readAgentStream(resp: Response, callbacks?: AgentStreamCallbacks): Promise; /** * Apply a single parsed SSE event to the turn state and fire the matching * callback. Mutates `turn`; otherwise pure. Exposed for unit-testing the * reducer against a synthetic turn without a live stream. */ export declare function applyAgentSseEvent(event: string, data: unknown, turn: AgentStreamTurn, callbacks?: AgentStreamCallbacks): void; //# sourceMappingURL=agent-sse.d.ts.map