@tanstack/ai
Version:
Type-safe TypeScript AI SDK for streaming chat, tool calling, agents, structured outputs, and multimodal generation.
125 lines (124 loc) • 5.87 kB
TypeScript
import { EventType, Interrupt, ModelMessage, RunAgentResumeItem, StreamChunk, TokenUsage, Tool, UIMessage } from '../../../types.js';
import { SpecTokenUsage } from '../../../utilities/ag-ui-usage.js';
import { DefinedAgent, SubagentRunContext } from './define-agent.js';
import { ChatMiddleware } from '../middleware/types.js';
import { SubagentTurn } from './turn.js';
export declare const SUBAGENT_STARTED = EventType.SUBAGENT_STARTED;
export declare const SUBAGENT_FINISHED = EventType.SUBAGENT_FINISHED;
export declare const SUBAGENT_ERROR = EventType.SUBAGENT_ERROR;
export type SubagentOrder = 'parallel' | 'sequence';
export interface SubagentRouterPlan {
names: ReadonlyArray<string>;
/** Overrides `subagents.order` for this turn. */
order?: SubagentOrder;
}
export interface SubagentStep {
names: ReadonlyArray<string>;
/** Overrides `subagents.order` for this step. */
order?: SubagentOrder;
}
export interface SubagentStepsPlan {
steps: ReadonlyArray<SubagentStep>;
}
export type SubagentRouterPick = 'main' | string | ReadonlyArray<string> | SubagentRouterPlan | SubagentStepsPlan;
export interface SubagentsBag<TAgents extends ReadonlyArray<DefinedAgent> = ReadonlyArray<DefinedAgent>> {
agents: TAgents;
router?: (ctx: {
messages: SubagentRunContext['messages'];
agents: NoInfer<TAgents>;
abortSignal?: AbortSignal;
}) => SubagentRouterPick | Promise<SubagentRouterPick>;
strategy?: 'exclusive' | 'handoff';
/**
* How a router list runs. `parallel` starts every name together.
* `sequence` runs each name after the previous one finishes, and passes
* that child's text to the next child.
*/
order?: 'parallel' | 'sequence';
sandbox?: 'own' | 'inherit';
}
/** What the children of one parent run left behind for the parent terminal. */
export interface SubagentSink {
interrupts: Array<Interrupt>;
/** One AG-UI entry per child model call. */
usage: Array<SpecTokenUsage>;
/** Summed full usage of the children, including cost. */
total?: TokenUsage;
}
export declare function createSubagentSink(): SubagentSink;
/** One child to start, or a suspended child to continue. */
export interface SpawnEntry {
name: string;
resume?: {
subagentRunId: string;
/** The child's own messages from the interrupted run. */
messages: Array<UIMessage | ModelMessage>;
entries: Array<RunAgentResumeItem>;
/** Text the child wrote before it stopped. */
text: string;
};
}
interface SpawnContext {
messages: SubagentRunContext['messages'];
abortSignal?: AbortSignal;
threadId: string;
/** The parent chat run. */
parentRunId: string;
/** The interrupted parent run, on a resume. */
interruptedRunId?: string;
}
export declare function createSubagentId(): string;
/**
* Bind child interrupts to the parent run. The client resumes the parent run,
* so each binding must name that run. The resumed child then validates with
* the parent's interrupted run id.
*/
export declare function rebindInterrupts(interrupts: ReadonlyArray<Interrupt>, runId: string): Array<Interrupt>;
export declare function normalizeRouterPick(pick: SubagentRouterPick, agents: ReadonlyArray<DefinedAgent>): {
steps: ReadonlyArray<SubagentStep>;
};
/** Add a finished child run's usage to the sink. */
export declare function collectUsage(sink: SubagentSink, finished?: StreamChunk): void;
/** A parent run's last chunk: it completed, or it failed. */
type ParentTerminal = Extract<StreamChunk, {
type: 'RUN_FINISHED' | 'RUN_ERROR';
}>;
/**
* Put the children's usage on a parent terminal. `usage[]` keeps one entry per
* model call. `metadata.tanstack.usage` holds the summed cost and the other
* TanStack fields, so `fromSpecTokenUsage` reads the full total. Empties the
* sink, so the next parent terminal does not count it again.
*
* `RUN_ERROR` is accepted too: a turn that failed still spent whatever its
* children spent. Such a chunk carries no usage of its own, so `runUsage` and
* `fullUsage` return empty for it and the children's total stands alone.
*/
export declare function withChildUsage(chunk: ParentTerminal, sink: SubagentSink): ParentTerminal;
export declare function spawnAgentStream(agent: DefinedAgent, ctx: SubagentRunContext, sink?: SubagentSink, parentToolCallId?: string): AsyncIterable<StreamChunk>;
export declare function spawnNamedAgents(entries: ReadonlyArray<SpawnEntry>, bag: SubagentsBag, ctx: SpawnContext, sink?: SubagentSink): AsyncGenerator<import('../../..').AGUIEvent, void, any>;
/**
* Text of the named direct children, in `names` order. Text from nested
* children stays out: their chunks carry their own id.
*/
export declare function collectNamedText(chunks: Array<StreamChunk>, names: ReadonlyArray<string>): string;
/**
* Record the parent messages when the model calls a subagent tool, so the
* child reads the conversation as it is at that call.
*/
export declare function subagentCallMessages(names: ReadonlySet<string>): {
middleware: ChatMiddleware<unknown, never>;
messagesFor: (toolCallId: string | undefined) => ModelMessage<string | import('../../..').ContentPart<unknown, unknown, unknown, unknown, unknown>[] | null>[] | undefined;
};
export declare function createSyntheticSubagentTools(bag: SubagentsBag, parent: {
/** Messages the parent run started with. Used when no call was recorded. */
messages: SubagentRunContext['messages'];
/** The parent messages at a tool call. See subagentCallMessages. */
messagesFor?: (toolCallId: string | undefined) => SubagentRunContext['messages'] | undefined;
threadId: string;
runId: string;
interruptedRunId?: string;
abortSignal?: AbortSignal;
turn?: SubagentTurn;
sink: SubagentSink;
}): Array<Tool>;
export {};