@aparte/runner-ag-ui 0.19.2 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -97,28 +97,17 @@ function interruptKind(interrupt) {
97
97
  }
98
98
  function readUsage(value) {
99
99
  const entries = (Array.isArray(value) ? value : value && typeof value === "object" ? [value] : []).filter((u) => Boolean(u) && typeof u === "object");
100
- if (entries.length === 0) return void 0;
101
- const sum = (key) => entries.reduce((n, u) => n + (typeof u[key] === "number" ? u[key] : 0), 0);
102
- const cached = sum("cachedInputTokens");
100
+ const last = entries.at(-1);
101
+ if (!last) return void 0;
102
+ const num = (key) => typeof last[key] === "number" ? last[key] : 0;
103
+ const cached = num("cachedInputTokens");
103
104
  return {
104
- inputTokens: sum("inputTokens"),
105
- outputTokens: sum("outputTokens"),
106
- totalTokens: sum("totalTokens") || void 0,
105
+ inputTokens: num("inputTokens"),
106
+ outputTokens: num("outputTokens"),
107
+ totalTokens: num("totalTokens") || void 0,
107
108
  ...cached ? { cacheReadTokens: cached } : {}
108
109
  };
109
110
  }
110
- function addUsage(total, step) {
111
- if (!step) return total;
112
- if (!total) return step;
113
- const cache = (total.cacheReadTokens ?? 0) + (step.cacheReadTokens ?? 0);
114
- const sumTotal = (total.totalTokens ?? 0) + (step.totalTokens ?? 0);
115
- return {
116
- inputTokens: total.inputTokens + step.inputTokens,
117
- outputTokens: total.outputTokens + step.outputTokens,
118
- ...sumTotal ? { totalTokens: sumTotal } : {},
119
- ...cache ? { cacheReadTokens: cache } : {}
120
- };
121
- }
122
111
  function parseArgs(args) {
123
112
  if (!args.trim()) return {};
124
113
  try {
@@ -132,8 +121,19 @@ function resultText(content) {
132
121
  if (content === void 0 || content === null) return "";
133
122
  return JSON.stringify(content);
134
123
  }
124
+ function rejectionText(decision) {
125
+ return decision.reason?.trim() || (decision.instruction ? `The user rejected this tool call and said: ${decision.instruction}` : "Tool execution was rejected by the user.");
126
+ }
135
127
  function resumeItem(interrupt, outcome) {
136
128
  if (!outcome) return { interruptId: interrupt.id, status: "cancelled" };
129
+ if (outcome.kind === "decision" && interrupt.kind === "external" && !outcome.approved) {
130
+ return {
131
+ interruptId: interrupt.id,
132
+ status: "resolved",
133
+ payload: { error: rejectionText(outcome) },
134
+ metadata: { tanstack: { state: "output-error" } }
135
+ };
136
+ }
137
137
  if (outcome.kind === "decision") {
138
138
  return { interruptId: interrupt.id, status: "resolved", payload: { approved: outcome.approved } };
139
139
  }
@@ -163,12 +163,13 @@ function createAgUiRunner(options) {
163
163
  const interrupted = new Set((ticket?.interrupts ?? []).map((i) => i.toolCallId));
164
164
  const messages = toAgUiMessages(request.messages);
165
165
  for (const [toolCallId, outcome] of Object.entries(outcomes)) {
166
- if (interrupted.has(toolCallId) || outcome.kind === "decision") continue;
166
+ if (interrupted.has(toolCallId)) continue;
167
+ if (outcome.kind === "decision" && outcome.approved) continue;
167
168
  messages.push({
168
169
  id: `aparte-result-${toolCallId}`,
169
170
  role: "tool",
170
171
  toolCallId,
171
- content: outcome.kind === "failed" ? `Error: ${outcome.error}` : outcome.content
172
+ content: outcome.kind === "failed" ? `Error: ${outcome.error}` : outcome.kind === "decision" ? rejectionText(outcome) : outcome.content
172
173
  });
173
174
  }
174
175
  const resume = ticket?.interrupts.length ? ticket.interrupts.map((interrupt) => resumeItem(interrupt, outcomes[interrupt.toolCallId])) : void 0;
@@ -268,8 +269,7 @@ function createAgUiRunner(options) {
268
269
  if (outcome.approved) {
269
270
  emit({ type: "tool-approved", toolCallId });
270
271
  } else {
271
- const reason = outcome.reason?.trim() || (outcome.instruction ? `The user rejected this tool call and said: ${outcome.instruction}` : "Tool execution was rejected by the user.");
272
- emit({ type: "tool-rejected", toolCallId, reason });
272
+ emit({ type: "tool-rejected", toolCallId, reason: rejectionText(outcome) });
273
273
  rejected.add(toolCallId);
274
274
  }
275
275
  }
@@ -361,7 +361,7 @@ function createAgUiRunner(options) {
361
361
  emit({ type: "text-flush" });
362
362
  textOpen = false;
363
363
  }
364
- usage = addUsage(usage, readUsage(event["usage"]));
364
+ usage = readUsage(event["usage"]) ?? usage;
365
365
  stepEnded = true;
366
366
  endedOnFinish = true;
367
367
  finished = {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/messages.ts","../src/wire.ts","../src/runner.ts"],"sourcesContent":["import { contentToText } from '@aparte/core';\nimport type { AparteChatMessage, AparteTool } from '@aparte/core';\nimport type { AgUiMessage, AgUiRunAgentInput } from './wire.js';\n\n/**\n * aparté's history as `RunAgentInput.messages`. Every message gets an id (the protocol\n * requires one) that is stable across the runs of one turn — its position — so a resume\n * replays the same history the paused run saw. A call is `{ id, type: 'function',\n * function: { name, arguments } }` with the arguments as a JSON string, and a result is\n * a `tool` message naming its call. Content goes as text: an image part has no AG-UI\n * shape every agent reads, so it is left out rather than guessed.\n */\nexport function toAgUiMessages(messages: readonly AparteChatMessage[]): AgUiMessage[] {\n const out: AgUiMessage[] = [];\n messages.forEach((m, index) => {\n const id = `aparte-${index}`;\n const content = contentToText(m.content);\n if (m.role === 'system' || m.role === 'user') {\n out.push({ id, role: m.role, content });\n } else if (m.role === 'assistant') {\n out.push({\n id,\n role: 'assistant',\n ...(content ? { content } : {}),\n ...(m.toolCalls?.length\n ? {\n toolCalls: m.toolCalls.map((call) => ({\n id: call.id,\n type: 'function' as const,\n function: { name: call.name, arguments: JSON.stringify(call.input ?? {}) },\n })),\n }\n : {}),\n });\n } else if (m.role === 'tool' && m.toolCallId) {\n out.push({ id, role: 'tool', content, toolCallId: m.toolCallId });\n }\n });\n return out;\n}\n\n/** The page's tools as `RunAgentInput.tools`: the ones the agent may ask the browser to run. */\nexport function toAgUiTools(tools: readonly AparteTool[] | undefined): AgUiRunAgentInput['tools'] {\n return (tools ?? []).map((tool) => ({ name: tool.name, description: tool.description, parameters: tool.inputSchema }));\n}\n","/**\n * The AG-UI wire, as much of it as aparté reads — typed here rather than taken from\n * `@ag-ui/core`, whose client pulls rxjs, zod and protobuf into a bundle that only\n * needs to read JSON lines. Field names follow the protocol (docs.ag-ui.com, 1.0).\n */\n\n/** An interrupt an agent ends its run on (`RUN_FINISHED.outcome`). */\nexport interface AgUiInterrupt {\n id: string;\n /** `tool_call`, `confirmation`, `input_required`, or a producer's own string. */\n reason: string;\n message?: string;\n toolCallId?: string;\n responseSchema?: unknown;\n metadata?: Record<string, unknown>;\n}\n\n/** The events the runner maps; any other `type` is read and ignored. */\nexport type AgUiEvent =\n | { type: 'RUN_STARTED'; threadId?: string; runId?: string }\n | {\n type: 'RUN_FINISHED';\n threadId?: string;\n runId?: string;\n outcome?: { type: 'success' } | { type: 'interrupt'; interrupts: AgUiInterrupt[] };\n /** TanStack AI reports usage here, as an array. */\n usage?: unknown;\n }\n | { type: 'RUN_ERROR'; message?: string; code?: string }\n | { type: 'TEXT_MESSAGE_START'; messageId?: string; role?: string }\n | { type: 'TEXT_MESSAGE_CONTENT'; messageId?: string; delta?: string }\n | { type: 'TEXT_MESSAGE_END'; messageId?: string }\n | { type: 'TEXT_MESSAGE_CHUNK'; messageId?: string; role?: string; delta?: string }\n | { type: 'REASONING_MESSAGE_CONTENT'; messageId?: string; delta?: string }\n | { type: 'REASONING_MESSAGE_CHUNK'; messageId?: string; delta?: string }\n | { type: 'TOOL_CALL_START'; toolCallId: string; toolCallName: string; parentMessageId?: string }\n | { type: 'TOOL_CALL_ARGS'; toolCallId: string; delta?: string }\n | { type: 'TOOL_CALL_END'; toolCallId: string }\n | { type: 'TOOL_CALL_CHUNK'; toolCallId?: string; toolCallName?: string; delta?: string }\n | { type: 'TOOL_CALL_RESULT'; toolCallId: string; content?: unknown; metadata?: Record<string, unknown> };\n\n/** One message of `RunAgentInput.messages`. */\nexport type AgUiMessage =\n | { id: string; role: 'system'; content: string }\n | { id: string; role: 'user'; content: string }\n | {\n id: string;\n role: 'assistant';\n content?: string;\n toolCalls?: { id: string; type: 'function'; function: { name: string; arguments: string } }[];\n }\n | { id: string; role: 'tool'; content: string; toolCallId: string };\n\n/** One entry of `RunAgentInput.resume`: the answer to one interrupt. */\nexport interface AgUiResumeItem {\n interruptId: string;\n status: 'resolved' | 'cancelled';\n payload?: unknown;\n metadata?: Record<string, unknown>;\n}\n\n/** The request body an AG-UI agent endpoint takes. */\nexport interface AgUiRunAgentInput {\n threadId: string;\n runId: string;\n parentRunId?: string;\n state: Record<string, unknown>;\n messages: AgUiMessage[];\n tools: { name: string; description: string; parameters: unknown }[];\n context: { description: string; value: string }[];\n forwardedProps: Record<string, unknown>;\n resume?: AgUiResumeItem[];\n}\n\n/**\n * The events of an AG-UI response, in order. Server-Sent Events by default — each\n * `data:` block is one JSON event, comments and `event:` lines are skipped, and a\n * `[DONE]` sentinel ends nothing on its own. An NDJSON body (one event per line, the\n * other encoding TanStack AI serves) is read the same way. A block that is not JSON\n * is skipped: a proxy's keep-alive must not end the run.\n */\nexport async function* readAgUiEvents(\n body: ReadableStream<Uint8Array>,\n contentType: string | null,\n): AsyncGenerator<{ type: string } & Record<string, unknown>> {\n const ndjson = Boolean(contentType && /ndjson|jsonl/i.test(contentType));\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n let data: string[] = [];\n const parse = (text: string): ({ type: string } & Record<string, unknown>) | undefined => {\n const trimmed = text.trim();\n if (!trimmed || trimmed === '[DONE]') return undefined;\n try {\n const value = JSON.parse(trimmed) as unknown;\n return value && typeof value === 'object' && typeof (value as { type?: unknown }).type === 'string'\n ? value as { type: string } & Record<string, unknown>\n : undefined;\n } catch {\n return undefined;\n }\n };\n const takeBlock = () => {\n const event = parse(data.join('\\n'));\n data = [];\n return event;\n };\n const line = (raw: string) => {\n const text = raw.endsWith('\\r') ? raw.slice(0, -1) : raw;\n if (ndjson) return parse(text);\n if (text === '') return takeBlock();\n if (text.startsWith('data:')) data.push(text.slice(5).replace(/^ /, ''));\n // `event:`, `id:`, `retry:` and `:` comments: AG-UI carries the type in the JSON.\n return undefined;\n };\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n let newline: number;\n while ((newline = buffer.indexOf('\\n')) !== -1) {\n const event = line(buffer.slice(0, newline));\n buffer = buffer.slice(newline + 1);\n if (event) yield event;\n }\n }\n buffer += decoder.decode();\n if (buffer) {\n const event = line(buffer);\n if (event) yield event;\n }\n if (!ndjson) {\n const event = takeBlock();\n if (event) yield event;\n }\n } finally {\n reader.releaseLock();\n }\n}\n","import { AparteError, AparteErrorCode, uuid } from '@aparte/core';\nimport type {\n AparteChatRequest,\n AparteStreamRunEvent,\n AparteStreamRunner,\n AparteStreamRunOptions,\n} from '@aparte/core';\nimport { toAgUiMessages, toAgUiTools } from './messages.js';\nimport { readAgUiEvents } from './wire.js';\nimport type { AgUiInterrupt, AgUiMessage, AgUiResumeItem, AgUiRunAgentInput } from './wire.js';\n\ntype Suspended = Extract<AparteStreamRunEvent, { type: 'run-suspended' }>;\ntype PendingCall = Suspended['pending'][number];\ntype Usage = NonNullable<Extract<AparteStreamRunEvent, { type: 'run-done' }>['usage']>;\ntype Outcome = NonNullable<NonNullable<AparteStreamRunOptions['deferral']>['outcomes']>[string];\n\nexport interface AgUiRunnerOptions {\n /** The agent's AG-UI endpoint, e.g. Agno's `/agui`, or a TanStack AI chat route. */\n endpoint: string;\n /** Sent with every request; a function is called per request (a fresh token each time). */\n headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);\n /** @default the global `fetch` */\n fetch?: typeof fetch;\n /** For a cross-origin endpoint that authenticates by cookie. */\n credentials?: RequestCredentials;\n /**\n * The AG-UI thread a turn runs in. Give the conversation's id when the agent keeps\n * state per thread (Agno's session); a function receives the run's options. A paused\n * run is always resumed in its own thread, whatever this returns.\n * @default a new thread per turn — the agent gets the whole history every time\n */\n threadId?: string | ((opts: AparteStreamRunOptions) => string);\n /** `RunAgentInput.forwardedProps`: settings your agent reads from the request. */\n forwardedProps?: Record<string, unknown>;\n /** `RunAgentInput.context`: extra context entries for the agent. */\n context?: { description: string; value: string }[];\n}\n\n/**\n * What a paused run hands the page and gets back with the answers — `deferral.token`.\n * Opaque to the client; the runner reads it to resume in the same thread, after the\n * run it continues, with the interrupts it must answer.\n */\ninterface Ticket {\n v: 1;\n threadId: string;\n runId: string;\n interrupts: { id: string; toolCallId: string; kind: 'approval' | 'external' }[];\n}\n\nfunction readTicket(token: string | undefined): Ticket | undefined {\n if (!token) return undefined;\n try {\n const ticket = JSON.parse(token) as Ticket;\n return ticket && ticket.v === 1 && typeof ticket.threadId === 'string' && typeof ticket.runId === 'string'\n && Array.isArray(ticket.interrupts) ? ticket : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** An interrupt's reason, as what the page must do about its call. */\nfunction interruptKind(interrupt: AgUiInterrupt): 'approval' | 'external' | undefined {\n if (interrupt.reason === 'tool_call' || interrupt.reason === 'confirmation') return 'approval';\n if (/client_tool/.test(interrupt.reason)) return 'external';\n return undefined;\n}\n\n/** Usage as AG-UI agents report it — an object, or TanStack AI's array of steps — summed. */\nfunction readUsage(value: unknown): Usage | undefined {\n const entries = (Array.isArray(value) ? value : value && typeof value === 'object' ? [value] : [])\n .filter((u): u is Record<string, unknown> => Boolean(u) && typeof u === 'object');\n if (entries.length === 0) return undefined;\n const sum = (key: string) => entries.reduce((n, u) => n + (typeof u[key] === 'number' ? u[key] as number : 0), 0);\n const cached = sum('cachedInputTokens');\n return {\n inputTokens: sum('inputTokens'),\n outputTokens: sum('outputTokens'),\n totalTokens: sum('totalTokens') || undefined,\n ...(cached ? { cacheReadTokens: cached } : {}),\n } as Usage;\n}\n\n/** Two usage reports as one: a TanStack AI run reports one per model step. */\nfunction addUsage(total: Usage | undefined, step: Usage | undefined): Usage | undefined {\n if (!step) return total;\n if (!total) return step;\n const cache = (total.cacheReadTokens ?? 0) + (step.cacheReadTokens ?? 0);\n const sumTotal = (total.totalTokens ?? 0) + (step.totalTokens ?? 0);\n return {\n inputTokens: total.inputTokens + step.inputTokens,\n outputTokens: total.outputTokens + step.outputTokens,\n ...(sumTotal ? { totalTokens: sumTotal } : {}),\n ...(cache ? { cacheReadTokens: cache } : {}),\n } as Usage;\n}\n\n/** A call's streamed arguments as its input: `{}` for none, the raw text when not JSON. */\nfunction parseArgs(args: string): unknown {\n if (!args.trim()) return {};\n try { return JSON.parse(args); } catch { return { arguments: args }; }\n}\n\n/** A tool result as the row shows it: text as is, anything else as JSON. */\nfunction resultText(content: unknown): string {\n if (typeof content === 'string') return content;\n if (content === undefined || content === null) return '';\n return JSON.stringify(content);\n}\n\n/** The answer a resumed interrupt carries, in the shape AG-UI (and TanStack AI) reads. */\nfunction resumeItem(interrupt: Ticket['interrupts'][number], outcome: Outcome | undefined): AgUiResumeItem {\n if (!outcome) return { interruptId: interrupt.id, status: 'cancelled' };\n if (outcome.kind === 'decision') {\n return { interruptId: interrupt.id, status: 'resolved', payload: { approved: outcome.approved } };\n }\n if (outcome.kind === 'failed') {\n return {\n interruptId: interrupt.id,\n status: 'resolved',\n payload: { error: outcome.error },\n metadata: { tanstack: { state: 'output-error' } },\n };\n }\n return {\n interruptId: interrupt.id,\n status: 'resolved',\n payload: outcome.structuredContent !== undefined ? outcome.structuredContent : outcome.content,\n };\n}\n\n/**\n * An `AparteStreamRunner` for an agent served over AG-UI — Agno, LangGraph, Mastra,\n * CrewAI, Pydantic AI, CopilotKit's runtime, TanStack AI. The agent runs on its server,\n * with its own tools; this runner turns its event stream into the rows, text and\n * approvals `AparteClient` draws.\n *\n * ```ts\n * const client = new AparteClient({ streamRunner: createAgUiRunner({ endpoint: '/agui' }) });\n * ```\n *\n * Tools the PAGE registers go to the agent as AG-UI frontend tools. When the agent calls\n * one — or pauses on a call that needs a person (an AG-UI interrupt) — the run ends on\n * `run-suspended`; the client runs the tool or asks through its approval panel, then\n * calls this runner again with the answers, which go back as `tool` messages or as one\n * `resume` batch for the interrupts.\n */\nexport function createAgUiRunner(options: AgUiRunnerOptions): AparteStreamRunner {\n return async (opts) => {\n const doFetch = options.fetch ?? fetch;\n const headers = typeof options.headers === 'function' ? await options.headers() : options.headers ?? {};\n const request = opts.baseRequest as AparteChatRequest;\n const ticket = readTicket(opts.deferral?.token);\n const outcomes = opts.deferral?.outcomes ?? {};\n const threadId = ticket?.threadId\n ?? (typeof options.threadId === 'function' ? options.threadId(opts) : options.threadId)\n ?? `aparte-${uuid()}`;\n const runId = `aparte-run-${uuid()}`;\n const interrupted = new Set((ticket?.interrupts ?? []).map((i) => i.toolCallId));\n\n // Answers to calls the agent left to the browser without an interrupt go back as\n // `tool` messages after the history; answers to interrupts go in one `resume`\n // batch, since the protocol refuses a partial one.\n const messages: AgUiMessage[] = toAgUiMessages(request.messages);\n for (const [toolCallId, outcome] of Object.entries(outcomes)) {\n if (interrupted.has(toolCallId) || outcome.kind === 'decision') continue;\n messages.push({\n id: `aparte-result-${toolCallId}`,\n role: 'tool',\n toolCallId,\n content: outcome.kind === 'failed' ? `Error: ${outcome.error}` : outcome.content,\n });\n }\n const resume = ticket?.interrupts.length\n ? ticket.interrupts.map((interrupt) => resumeItem(interrupt, outcomes[interrupt.toolCallId]))\n : undefined;\n\n const body: AgUiRunAgentInput = {\n threadId,\n runId,\n ...(ticket ? { parentRunId: ticket.runId } : {}),\n state: {},\n messages,\n tools: toAgUiTools(request.tools),\n context: options.context ?? [],\n forwardedProps: options.forwardedProps ?? {},\n ...(resume ? { resume } : {}),\n };\n const response = await doFetch(options.endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'text/event-stream', ...headers },\n body: JSON.stringify(body),\n signal: opts.signal,\n ...(options.credentials ? { credentials: options.credentials } : {}),\n });\n if (!response.ok || !response.body) {\n const text = await response.text().catch(() => '');\n throw new AparteError(\n text.trim().slice(0, 300) || `The agent endpoint answered HTTP ${response.status}.`,\n AparteError.codeForStatus(response.status) ?? AparteErrorCode.PROVIDER_ERROR,\n undefined, undefined, response.status,\n );\n }\n\n // Calls the history already declares: a resumed run answers them, it does not\n // start them again (no second row).\n const history = new Map(request.messages.flatMap((m) => (m.toolCalls ?? []).map((c) => [c.id, { name: c.name, input: c.input ?? {} }] as const)));\n const known = new Set(history.keys());\n const browserTools = new Set((request.tools ?? []).map((t) => t.name));\n const calls = new Map<string, { name: string; args: string }>();\n const started = new Map<string, { name: string; input: unknown }>();\n const answered = new Set<string>();\n const rejected = new Set<string>();\n const settledByPage = new Set<string>();\n let textOpen = false;\n let usage: Usage | undefined;\n let finished: { runId: string; outcome?: { type?: string; interrupts?: AgUiInterrupt[] } } | undefined;\n const emit = opts.emitter;\n\n const startTool = (toolCallId: string) => {\n const call = calls.get(toolCallId);\n if (!call || started.has(toolCallId) || known.has(toolCallId)) return;\n const input = parseArgs(call.args);\n started.set(toolCallId, { name: call.name, input });\n if (textOpen) { emit({ type: 'text-flush' }); textOpen = false; }\n emit({ type: 'tool-start', toolCallId, name: call.name, input });\n stepEnded = true;\n };\n\n // A model step ends on a tool call, or on a RUN_FINISHED (TanStack AI closes each\n // step with one). What the next step draws, its reasoning or its text, opens a new\n // turn, the way the in-page loop opens one per model call. Drawn into the old turn,\n // a second step's reasoning landed in the first step's thinking block, above the\n // tool row.\n let stepEnded = false;\n const nextStep = () => {\n if (!stepEnded) return;\n stepEnded = false;\n if (textOpen) { emit({ type: 'text-flush' }); textOpen = false; }\n emit({ type: 'turn-start' });\n };\n\n /** The call an interrupt is about: started in this run, else in the history, else as the interrupt describes it. */\n const callOf = (interrupt: AgUiInterrupt & { toolCallId: string }): { name: string; input: unknown } => {\n const here = started.get(interrupt.toolCallId) ?? history.get(interrupt.toolCallId);\n if (here) return here;\n const streamed = calls.get(interrupt.toolCallId);\n if (streamed) return { name: streamed.name, input: parseArgs(streamed.args) };\n // TanStack AI describes the call on the interrupt itself.\n const meta = interrupt.metadata as { toolName?: unknown; input?: unknown } | undefined;\n return { name: typeof meta?.toolName === 'string' ? meta.toolName : '', input: meta?.input ?? {} };\n };\n\n // A stream that closes between two steps is cut, not finished: TanStack AI sends a\n // RUN_FINISHED per step, so having seen one says nothing about the last.\n let endedOnFinish = false;\n\n // The run opens on its FIRST event, whatever it is: a resumed TanStack AI run sends\n // the approved call's TOOL_CALL_RESULT before RUN_STARTED, and settling that row\n // after its result put it back to pending. The answers this run carries settle\n // their rows before anything else.\n let begun = false;\n const begin = () => {\n if (begun) return;\n begun = true;\n emit({ type: 'run-start' });\n emit({ type: 'turn-start' });\n for (const [toolCallId, outcome] of Object.entries(outcomes)) {\n // A tool the PAGE ran: its row settles here, with the result the page holds —\n // a plain AG-UI agent never echoes a frontend tool's result, so nothing\n // else would, and an echo that does come is not drawn twice.\n if (outcome.kind === 'result') {\n emit({\n type: 'tool-resolved',\n toolCallId,\n result: outcome.content,\n ...(outcome.structuredContent !== undefined ? { structuredResult: outcome.structuredContent } : {}),\n });\n settledByPage.add(toolCallId);\n continue;\n }\n if (outcome.kind === 'failed') {\n emit({ type: 'tool-failed', toolCallId, error: outcome.error });\n settledByPage.add(toolCallId);\n continue;\n }\n if (outcome.approved) {\n emit({ type: 'tool-approved', toolCallId });\n } else {\n const reason = outcome.reason?.trim()\n || (outcome.instruction ? `The user rejected this tool call and said: ${outcome.instruction}` : 'Tool execution was rejected by the user.');\n emit({ type: 'tool-rejected', toolCallId, reason });\n rejected.add(toolCallId);\n }\n }\n };\n\n for await (const event of readAgUiEvents(response.body, response.headers.get('content-type'))) {\n begin();\n if (/^(TEXT_MESSAGE|REASONING|TOOL_CALL)_/.test(event.type)) endedOnFinish = false;\n switch (event.type) {\n case 'RUN_STARTED':\n break;\n case 'TEXT_MESSAGE_START':\n case 'REASONING_START':\n case 'REASONING_MESSAGE_START':\n nextStep();\n break;\n case 'TEXT_MESSAGE_CONTENT':\n case 'TEXT_MESSAGE_CHUNK': {\n const delta = typeof event['delta'] === 'string' ? event['delta'] : '';\n if (delta) { nextStep(); emit({ type: 'text-delta', delta }); textOpen = true; }\n break;\n }\n case 'TEXT_MESSAGE_END': {\n if (textOpen) { emit({ type: 'text-flush' }); textOpen = false; }\n break;\n }\n case 'REASONING_MESSAGE_CONTENT':\n case 'REASONING_MESSAGE_CHUNK': {\n const delta = typeof event['delta'] === 'string' ? event['delta'] : '';\n if (delta) { nextStep(); emit({ type: 'thinking-delta', delta }); }\n break;\n }\n case 'TOOL_CALL_START': {\n const id = String(event['toolCallId']);\n calls.set(id, { name: String(event['toolCallName'] ?? ''), args: '' });\n break;\n }\n case 'TOOL_CALL_ARGS': {\n const call = calls.get(String(event['toolCallId']));\n if (call && typeof event['delta'] === 'string') call.args += event['delta'];\n break;\n }\n case 'TOOL_CALL_CHUNK': {\n const id = typeof event['toolCallId'] === 'string' ? event['toolCallId'] : [...calls.keys()].at(-1);\n if (!id) break;\n const call = calls.get(id) ?? { name: '', args: '' };\n if (typeof event['toolCallName'] === 'string' && event['toolCallName']) call.name = event['toolCallName'];\n if (typeof event['delta'] === 'string') call.args += event['delta'];\n calls.set(id, call);\n break;\n }\n case 'TOOL_CALL_END': {\n startTool(String(event['toolCallId']));\n break;\n }\n case 'TOOL_CALL_RESULT': {\n const toolCallId = String(event['toolCallId']);\n startTool(toolCallId);\n if (rejected.has(toolCallId) || settledByPage.has(toolCallId)) break; // already settled\n answered.add(toolCallId);\n const content = resultText(event['content']);\n const meta = event['metadata'] as { tanstack?: { state?: unknown } } | undefined;\n if (meta?.tanstack?.state === 'output-error') {\n emit({ type: 'tool-failed', toolCallId, error: content });\n } else {\n emit({ type: 'tool-resolved', toolCallId, result: content });\n }\n break;\n }\n case 'RUN_ERROR': {\n throw new AparteError(\n typeof event['message'] === 'string' && event['message'] ? event['message'] : 'The agent run failed.',\n AparteErrorCode.PROVIDER_ERROR,\n typeof event['code'] === 'string' ? { code: event['code'] } : undefined,\n );\n }\n case 'RUN_FINISHED': {\n // Not the end of the stream: TanStack AI closes each model step with a\n // RUN_FINISHED of its own (`finishReason: tool_calls`), then streams the\n // tool results and the next step. The LAST one says how the run ended.\n for (const id of calls.keys()) startTool(id);\n if (textOpen) { emit({ type: 'text-flush' }); textOpen = false; }\n usage = addUsage(usage, readUsage(event['usage']));\n stepEnded = true;\n endedOnFinish = true;\n finished = {\n runId: typeof event['runId'] === 'string' ? event['runId'] : runId,\n outcome: event['outcome'] as { type?: string; interrupts?: AgUiInterrupt[] } | undefined,\n };\n break;\n }\n default:\n // STEP_*, STATE_*, MESSAGES_SNAPSHOT, ACTIVITY_*, RAW, CUSTOM, SUBAGENT_*:\n // agent state aparté does not draw.\n break;\n }\n }\n if (opts.signal.aborted) return usage;\n if (!finished) {\n throw new AparteError('The agent stream ended without RUN_FINISHED.', AparteErrorCode.PROVIDER_ERROR);\n }\n if (!endedOnFinish) {\n throw new AparteError('The agent stream was cut: it ended in the middle of a step, after a RUN_FINISHED.', AparteErrorCode.PROVIDER_ERROR);\n }\n if (textOpen) emit({ type: 'text-flush' });\n\n const pending: PendingCall[] = [];\n const interrupts: Ticket['interrupts'] = [];\n if (finished.outcome?.type === 'interrupt') {\n for (const interrupt of finished.outcome.interrupts ?? []) {\n const kind = interruptKind(interrupt);\n if (!kind || !interrupt.toolCallId) {\n throw new AparteError(\n `The agent paused for something aparté cannot answer yet (interrupt reason \"${interrupt.reason}\").`,\n AparteErrorCode.PROVIDER_ERROR,\n );\n }\n // A resumed run can pause again on a call an earlier run started (TanStack AI\n // asks to approve a client tool, then to run it): its name comes from there.\n const call = callOf(interrupt as AgUiInterrupt & { toolCallId: string });\n if (kind === 'approval') {\n emit({ type: 'tool-awaiting-approval', toolCallId: interrupt.toolCallId, name: call.name, input: call.input });\n }\n interrupts.push({ id: interrupt.id, toolCallId: interrupt.toolCallId, kind });\n pending.push({ toolCallId: interrupt.toolCallId, name: call.name, input: call.input, reason: kind });\n }\n } else {\n // Plain AG-UI: the calls left to the browser are the ones the agent names\n // (`pendingToolCallIds`, AG-UI 1.0), else the page's own tools nobody answered.\n // Any other call without a result has nobody left to answer it: its row stops\n // waiting rather than spinning after the run. A cancelled run leaves nothing.\n const outcome = finished.outcome as { type?: string; pendingToolCallIds?: unknown } | undefined;\n const named = Array.isArray(outcome?.pendingToolCallIds)\n ? new Set(outcome.pendingToolCallIds.filter((id): id is string => typeof id === 'string'))\n : undefined;\n for (const [toolCallId, call] of started) {\n if (answered.has(toolCallId)) continue;\n const forPage = outcome?.type !== 'cancelled' && (named ? named.has(toolCallId) : browserTools.has(call.name));\n if (forPage) pending.push({ toolCallId, name: call.name, input: call.input, reason: 'external' });\n else emit({ type: 'tool-aborted', toolCallId });\n }\n }\n if (pending.length > 0) {\n const next: Ticket = { v: 1, threadId, runId: finished.runId, interrupts };\n emit({ type: 'run-suspended', pending, ...(usage ? { usage } : {}), ticket: JSON.stringify(next) } as Suspended);\n } else {\n emit({ type: 'run-done', ...(usage ? { usage } : {}) });\n }\n return usage;\n };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/messages.ts","../src/wire.ts","../src/runner.ts"],"sourcesContent":["import { contentToText } from '@aparte/core';\nimport type { AparteChatMessage, AparteTool } from '@aparte/core';\nimport type { AgUiMessage, AgUiRunAgentInput } from './wire.js';\n\n/**\n * aparté's history as `RunAgentInput.messages`. Every message gets an id (the protocol\n * requires one) that is stable across the runs of one turn — its position — so a resume\n * replays the same history the paused run saw. A call is `{ id, type: 'function',\n * function: { name, arguments } }` with the arguments as a JSON string, and a result is\n * a `tool` message naming its call. Content goes as text: an image part has no AG-UI\n * shape every agent reads, so it is left out rather than guessed.\n */\nexport function toAgUiMessages(messages: readonly AparteChatMessage[]): AgUiMessage[] {\n const out: AgUiMessage[] = [];\n messages.forEach((m, index) => {\n const id = `aparte-${index}`;\n const content = contentToText(m.content);\n if (m.role === 'system' || m.role === 'user') {\n out.push({ id, role: m.role, content });\n } else if (m.role === 'assistant') {\n out.push({\n id,\n role: 'assistant',\n ...(content ? { content } : {}),\n ...(m.toolCalls?.length\n ? {\n toolCalls: m.toolCalls.map((call) => ({\n id: call.id,\n type: 'function' as const,\n function: { name: call.name, arguments: JSON.stringify(call.input ?? {}) },\n })),\n }\n : {}),\n });\n } else if (m.role === 'tool' && m.toolCallId) {\n out.push({ id, role: 'tool', content, toolCallId: m.toolCallId });\n }\n });\n return out;\n}\n\n/** The page's tools as `RunAgentInput.tools`: the ones the agent may ask the browser to run. */\nexport function toAgUiTools(tools: readonly AparteTool[] | undefined): AgUiRunAgentInput['tools'] {\n return (tools ?? []).map((tool) => ({ name: tool.name, description: tool.description, parameters: tool.inputSchema }));\n}\n","/**\n * The AG-UI wire, as much of it as aparté reads — typed here rather than taken from\n * `@ag-ui/core`, whose client pulls rxjs, zod and protobuf into a bundle that only\n * needs to read JSON lines. Field names follow the protocol (docs.ag-ui.com, 1.0).\n */\n\n/** An interrupt an agent ends its run on (`RUN_FINISHED.outcome`). */\nexport interface AgUiInterrupt {\n id: string;\n /** `tool_call`, `confirmation`, `input_required`, or a producer's own string. */\n reason: string;\n message?: string;\n toolCallId?: string;\n responseSchema?: unknown;\n metadata?: Record<string, unknown>;\n}\n\n/** The events the runner maps; any other `type` is read and ignored. */\nexport type AgUiEvent =\n | { type: 'RUN_STARTED'; threadId?: string; runId?: string }\n | {\n type: 'RUN_FINISHED';\n threadId?: string;\n runId?: string;\n outcome?: { type: 'success' } | { type: 'interrupt'; interrupts: AgUiInterrupt[] };\n /** TanStack AI reports usage here, as an array. */\n usage?: unknown;\n }\n | { type: 'RUN_ERROR'; message?: string; code?: string }\n | { type: 'TEXT_MESSAGE_START'; messageId?: string; role?: string }\n | { type: 'TEXT_MESSAGE_CONTENT'; messageId?: string; delta?: string }\n | { type: 'TEXT_MESSAGE_END'; messageId?: string }\n | { type: 'TEXT_MESSAGE_CHUNK'; messageId?: string; role?: string; delta?: string }\n | { type: 'REASONING_MESSAGE_CONTENT'; messageId?: string; delta?: string }\n | { type: 'REASONING_MESSAGE_CHUNK'; messageId?: string; delta?: string }\n | { type: 'TOOL_CALL_START'; toolCallId: string; toolCallName: string; parentMessageId?: string }\n | { type: 'TOOL_CALL_ARGS'; toolCallId: string; delta?: string }\n | { type: 'TOOL_CALL_END'; toolCallId: string }\n | { type: 'TOOL_CALL_CHUNK'; toolCallId?: string; toolCallName?: string; delta?: string }\n | { type: 'TOOL_CALL_RESULT'; toolCallId: string; content?: unknown; metadata?: Record<string, unknown> };\n\n/** One message of `RunAgentInput.messages`. */\nexport type AgUiMessage =\n | { id: string; role: 'system'; content: string }\n | { id: string; role: 'user'; content: string }\n | {\n id: string;\n role: 'assistant';\n content?: string;\n toolCalls?: { id: string; type: 'function'; function: { name: string; arguments: string } }[];\n }\n | { id: string; role: 'tool'; content: string; toolCallId: string };\n\n/** One entry of `RunAgentInput.resume`: the answer to one interrupt. */\nexport interface AgUiResumeItem {\n interruptId: string;\n status: 'resolved' | 'cancelled';\n payload?: unknown;\n metadata?: Record<string, unknown>;\n}\n\n/** The request body an AG-UI agent endpoint takes. */\nexport interface AgUiRunAgentInput {\n threadId: string;\n runId: string;\n parentRunId?: string;\n state: Record<string, unknown>;\n messages: AgUiMessage[];\n tools: { name: string; description: string; parameters: unknown }[];\n context: { description: string; value: string }[];\n forwardedProps: Record<string, unknown>;\n resume?: AgUiResumeItem[];\n}\n\n/**\n * The events of an AG-UI response, in order. Server-Sent Events by default — each\n * `data:` block is one JSON event, comments and `event:` lines are skipped, and a\n * `[DONE]` sentinel ends nothing on its own. An NDJSON body (one event per line, the\n * other encoding TanStack AI serves) is read the same way. A block that is not JSON\n * is skipped: a proxy's keep-alive must not end the run.\n */\nexport async function* readAgUiEvents(\n body: ReadableStream<Uint8Array>,\n contentType: string | null,\n): AsyncGenerator<{ type: string } & Record<string, unknown>> {\n const ndjson = Boolean(contentType && /ndjson|jsonl/i.test(contentType));\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n let data: string[] = [];\n const parse = (text: string): ({ type: string } & Record<string, unknown>) | undefined => {\n const trimmed = text.trim();\n if (!trimmed || trimmed === '[DONE]') return undefined;\n try {\n const value = JSON.parse(trimmed) as unknown;\n return value && typeof value === 'object' && typeof (value as { type?: unknown }).type === 'string'\n ? value as { type: string } & Record<string, unknown>\n : undefined;\n } catch {\n return undefined;\n }\n };\n const takeBlock = () => {\n const event = parse(data.join('\\n'));\n data = [];\n return event;\n };\n const line = (raw: string) => {\n const text = raw.endsWith('\\r') ? raw.slice(0, -1) : raw;\n if (ndjson) return parse(text);\n if (text === '') return takeBlock();\n if (text.startsWith('data:')) data.push(text.slice(5).replace(/^ /, ''));\n // `event:`, `id:`, `retry:` and `:` comments: AG-UI carries the type in the JSON.\n return undefined;\n };\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n let newline: number;\n while ((newline = buffer.indexOf('\\n')) !== -1) {\n const event = line(buffer.slice(0, newline));\n buffer = buffer.slice(newline + 1);\n if (event) yield event;\n }\n }\n buffer += decoder.decode();\n if (buffer) {\n const event = line(buffer);\n if (event) yield event;\n }\n if (!ndjson) {\n const event = takeBlock();\n if (event) yield event;\n }\n } finally {\n reader.releaseLock();\n }\n}\n","import { AparteError, AparteErrorCode, uuid } from '@aparte/core';\nimport type {\n AparteChatRequest,\n AparteStreamRunEvent,\n AparteStreamRunner,\n AparteStreamRunOptions,\n} from '@aparte/core';\nimport { toAgUiMessages, toAgUiTools } from './messages.js';\nimport { readAgUiEvents } from './wire.js';\nimport type { AgUiInterrupt, AgUiMessage, AgUiResumeItem, AgUiRunAgentInput } from './wire.js';\n\ntype Suspended = Extract<AparteStreamRunEvent, { type: 'run-suspended' }>;\ntype PendingCall = Suspended['pending'][number];\ntype Usage = NonNullable<Extract<AparteStreamRunEvent, { type: 'run-done' }>['usage']>;\ntype Outcome = NonNullable<NonNullable<AparteStreamRunOptions['deferral']>['outcomes']>[string];\n\nexport interface AgUiRunnerOptions {\n /** The agent's AG-UI endpoint, e.g. Agno's `/agui`, or a TanStack AI chat route. */\n endpoint: string;\n /** Sent with every request; a function is called per request (a fresh token each time). */\n headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);\n /** @default the global `fetch` */\n fetch?: typeof fetch;\n /** For a cross-origin endpoint that authenticates by cookie. */\n credentials?: RequestCredentials;\n /**\n * The AG-UI thread a turn runs in. Give the conversation's id when the agent keeps\n * state per thread (Agno's session); a function receives the run's options. A paused\n * run is always resumed in its own thread, whatever this returns.\n * @default a new thread per turn — the agent gets the whole history every time\n */\n threadId?: string | ((opts: AparteStreamRunOptions) => string);\n /** `RunAgentInput.forwardedProps`: settings your agent reads from the request. */\n forwardedProps?: Record<string, unknown>;\n /** `RunAgentInput.context`: extra context entries for the agent. */\n context?: { description: string; value: string }[];\n}\n\n/**\n * What a paused run hands the page and gets back with the answers — `deferral.token`.\n * Opaque to the client; the runner reads it to resume in the same thread, after the\n * run it continues, with the interrupts it must answer.\n */\ninterface Ticket {\n v: 1;\n threadId: string;\n runId: string;\n interrupts: { id: string; toolCallId: string; kind: 'approval' | 'external' }[];\n}\n\nfunction readTicket(token: string | undefined): Ticket | undefined {\n if (!token) return undefined;\n try {\n const ticket = JSON.parse(token) as Ticket;\n return ticket && ticket.v === 1 && typeof ticket.threadId === 'string' && typeof ticket.runId === 'string'\n && Array.isArray(ticket.interrupts) ? ticket : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** An interrupt's reason, as what the page must do about its call. */\nfunction interruptKind(interrupt: AgUiInterrupt): 'approval' | 'external' | undefined {\n if (interrupt.reason === 'tool_call' || interrupt.reason === 'confirmation') return 'approval';\n if (/client_tool/.test(interrupt.reason)) return 'external';\n return undefined;\n}\n\n/**\n * Usage as AG-UI agents report it — an object, or TanStack AI's array of model steps — as\n * the LAST step's report. That is what the page's own loop reports for a run of several\n * turns, and what the context gauge reads: the size of the conversation the model last saw.\n * Summed, a three-step run read as three times the context it used.\n */\nfunction readUsage(value: unknown): Usage | undefined {\n const entries = (Array.isArray(value) ? value : value && typeof value === 'object' ? [value] : [])\n .filter((u): u is Record<string, unknown> => Boolean(u) && typeof u === 'object');\n const last = entries.at(-1);\n if (!last) return undefined;\n const num = (key: string): number => (typeof last[key] === 'number' ? last[key] as number : 0);\n const cached = num('cachedInputTokens');\n return {\n inputTokens: num('inputTokens'),\n outputTokens: num('outputTokens'),\n totalTokens: num('totalTokens') || undefined,\n ...(cached ? { cacheReadTokens: cached } : {}),\n } as Usage;\n}\n\n/** A call's streamed arguments as its input: `{}` for none, the raw text when not JSON. */\nfunction parseArgs(args: string): unknown {\n if (!args.trim()) return {};\n try { return JSON.parse(args); } catch { return { arguments: args }; }\n}\n\n/** A tool result as the row shows it: text as is, anything else as JSON. */\nfunction resultText(content: unknown): string {\n if (typeof content === 'string') return content;\n if (content === undefined || content === null) return '';\n return JSON.stringify(content);\n}\n\n/** What the model reads about a refusal: the person's words, else core's fixed sentence. */\nfunction rejectionText(decision: { reason?: string; instruction?: string }): string {\n return decision.reason?.trim()\n || (decision.instruction ? `The user rejected this tool call and said: ${decision.instruction}` : 'Tool execution was rejected by the user.');\n}\n\n/** The answer a resumed interrupt carries, in the shape AG-UI (and TanStack AI) reads. */\nfunction resumeItem(interrupt: Ticket['interrupts'][number], outcome: Outcome | undefined): AgUiResumeItem {\n if (!outcome) return { interruptId: interrupt.id, status: 'cancelled' };\n if (outcome.kind === 'decision' && interrupt.kind === 'external' && !outcome.approved) {\n // A client tool the page refused to run (its `needsApproval`, its policy). An\n // execution interrupt answered `{ approved: false }` reads as a successful OUTPUT —\n // TanStack AI's `clientToolResult` takes any resolved payload without the error\n // state as `output-available` — so the model would read the decision as the tool's\n // result and lose the reason. It goes back as the tool's error, with the sentence.\n return {\n interruptId: interrupt.id,\n status: 'resolved',\n payload: { error: rejectionText(outcome) },\n metadata: { tanstack: { state: 'output-error' } },\n };\n }\n if (outcome.kind === 'decision') {\n return { interruptId: interrupt.id, status: 'resolved', payload: { approved: outcome.approved } };\n }\n if (outcome.kind === 'failed') {\n return {\n interruptId: interrupt.id,\n status: 'resolved',\n payload: { error: outcome.error },\n metadata: { tanstack: { state: 'output-error' } },\n };\n }\n return {\n interruptId: interrupt.id,\n status: 'resolved',\n payload: outcome.structuredContent !== undefined ? outcome.structuredContent : outcome.content,\n };\n}\n\n/**\n * An `AparteStreamRunner` for an agent served over AG-UI — Agno, LangGraph, Mastra,\n * CrewAI, Pydantic AI, CopilotKit's runtime, TanStack AI. The agent runs on its server,\n * with its own tools; this runner turns its event stream into the rows, text and\n * approvals `AparteClient` draws.\n *\n * ```ts\n * const client = new AparteClient({ streamRunner: createAgUiRunner({ endpoint: '/agui' }) });\n * ```\n *\n * Tools the PAGE registers go to the agent as AG-UI frontend tools. When the agent calls\n * one — or pauses on a call that needs a person (an AG-UI interrupt) — the run ends on\n * `run-suspended`; the client runs the tool or asks through its approval panel, then\n * calls this runner again with the answers, which go back as `tool` messages or as one\n * `resume` batch for the interrupts.\n */\nexport function createAgUiRunner(options: AgUiRunnerOptions): AparteStreamRunner {\n return async (opts) => {\n const doFetch = options.fetch ?? fetch;\n const headers = typeof options.headers === 'function' ? await options.headers() : options.headers ?? {};\n const request = opts.baseRequest as AparteChatRequest;\n const ticket = readTicket(opts.deferral?.token);\n const outcomes = opts.deferral?.outcomes ?? {};\n const threadId = ticket?.threadId\n ?? (typeof options.threadId === 'function' ? options.threadId(opts) : options.threadId)\n ?? `aparte-${uuid()}`;\n const runId = `aparte-run-${uuid()}`;\n const interrupted = new Set((ticket?.interrupts ?? []).map((i) => i.toolCallId));\n\n // Answers to calls the agent left to the browser without an interrupt go back as\n // `tool` messages after the history; answers to interrupts go in one `resume`\n // batch, since the protocol refuses a partial one.\n const messages: AgUiMessage[] = toAgUiMessages(request.messages);\n for (const [toolCallId, outcome] of Object.entries(outcomes)) {\n if (interrupted.has(toolCallId)) continue;\n // A page tool the page refused (its `needsApproval`, its policy): the refusal is\n // the call's result, the sentence the model reads. An approval needs no message.\n if (outcome.kind === 'decision' && outcome.approved) continue;\n messages.push({\n id: `aparte-result-${toolCallId}`,\n role: 'tool',\n toolCallId,\n content: outcome.kind === 'failed' ? `Error: ${outcome.error}`\n : outcome.kind === 'decision' ? rejectionText(outcome)\n : outcome.content,\n });\n }\n const resume = ticket?.interrupts.length\n ? ticket.interrupts.map((interrupt) => resumeItem(interrupt, outcomes[interrupt.toolCallId]))\n : undefined;\n\n const body: AgUiRunAgentInput = {\n threadId,\n runId,\n ...(ticket ? { parentRunId: ticket.runId } : {}),\n state: {},\n messages,\n tools: toAgUiTools(request.tools),\n context: options.context ?? [],\n forwardedProps: options.forwardedProps ?? {},\n ...(resume ? { resume } : {}),\n };\n const response = await doFetch(options.endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Accept: 'text/event-stream', ...headers },\n body: JSON.stringify(body),\n signal: opts.signal,\n ...(options.credentials ? { credentials: options.credentials } : {}),\n });\n if (!response.ok || !response.body) {\n const text = await response.text().catch(() => '');\n throw new AparteError(\n text.trim().slice(0, 300) || `The agent endpoint answered HTTP ${response.status}.`,\n AparteError.codeForStatus(response.status) ?? AparteErrorCode.PROVIDER_ERROR,\n undefined, undefined, response.status,\n );\n }\n\n // Calls the history already declares: a resumed run answers them, it does not\n // start them again (no second row).\n const history = new Map(request.messages.flatMap((m) => (m.toolCalls ?? []).map((c) => [c.id, { name: c.name, input: c.input ?? {} }] as const)));\n const known = new Set(history.keys());\n const browserTools = new Set((request.tools ?? []).map((t) => t.name));\n const calls = new Map<string, { name: string; args: string }>();\n const started = new Map<string, { name: string; input: unknown }>();\n const answered = new Set<string>();\n const rejected = new Set<string>();\n const settledByPage = new Set<string>();\n let textOpen = false;\n let usage: Usage | undefined;\n let finished: { runId: string; outcome?: { type?: string; interrupts?: AgUiInterrupt[] } } | undefined;\n const emit = opts.emitter;\n\n const startTool = (toolCallId: string) => {\n const call = calls.get(toolCallId);\n if (!call || started.has(toolCallId) || known.has(toolCallId)) return;\n const input = parseArgs(call.args);\n started.set(toolCallId, { name: call.name, input });\n if (textOpen) { emit({ type: 'text-flush' }); textOpen = false; }\n emit({ type: 'tool-start', toolCallId, name: call.name, input });\n stepEnded = true;\n };\n\n // A model step ends on a tool call, or on a RUN_FINISHED (TanStack AI closes each\n // step with one). What the next step draws, its reasoning or its text, opens a new\n // turn, the way the in-page loop opens one per model call. Drawn into the old turn,\n // a second step's reasoning landed in the first step's thinking block, above the\n // tool row.\n let stepEnded = false;\n const nextStep = () => {\n if (!stepEnded) return;\n stepEnded = false;\n if (textOpen) { emit({ type: 'text-flush' }); textOpen = false; }\n emit({ type: 'turn-start' });\n };\n\n /** The call an interrupt is about: started in this run, else in the history, else as the interrupt describes it. */\n const callOf = (interrupt: AgUiInterrupt & { toolCallId: string }): { name: string; input: unknown } => {\n const here = started.get(interrupt.toolCallId) ?? history.get(interrupt.toolCallId);\n if (here) return here;\n const streamed = calls.get(interrupt.toolCallId);\n if (streamed) return { name: streamed.name, input: parseArgs(streamed.args) };\n // TanStack AI describes the call on the interrupt itself.\n const meta = interrupt.metadata as { toolName?: unknown; input?: unknown } | undefined;\n return { name: typeof meta?.toolName === 'string' ? meta.toolName : '', input: meta?.input ?? {} };\n };\n\n // A stream that closes between two steps is cut, not finished: TanStack AI sends a\n // RUN_FINISHED per step, so having seen one says nothing about the last.\n let endedOnFinish = false;\n\n // The run opens on its FIRST event, whatever it is: a resumed TanStack AI run sends\n // the approved call's TOOL_CALL_RESULT before RUN_STARTED, and settling that row\n // after its result put it back to pending. The answers this run carries settle\n // their rows before anything else.\n let begun = false;\n const begin = () => {\n if (begun) return;\n begun = true;\n emit({ type: 'run-start' });\n emit({ type: 'turn-start' });\n for (const [toolCallId, outcome] of Object.entries(outcomes)) {\n // A tool the PAGE ran: its row settles here, with the result the page holds —\n // a plain AG-UI agent never echoes a frontend tool's result, so nothing\n // else would, and an echo that does come is not drawn twice.\n if (outcome.kind === 'result') {\n emit({\n type: 'tool-resolved',\n toolCallId,\n result: outcome.content,\n ...(outcome.structuredContent !== undefined ? { structuredResult: outcome.structuredContent } : {}),\n });\n settledByPage.add(toolCallId);\n continue;\n }\n if (outcome.kind === 'failed') {\n emit({ type: 'tool-failed', toolCallId, error: outcome.error });\n settledByPage.add(toolCallId);\n continue;\n }\n if (outcome.approved) {\n emit({ type: 'tool-approved', toolCallId });\n } else {\n emit({ type: 'tool-rejected', toolCallId, reason: rejectionText(outcome) });\n rejected.add(toolCallId);\n }\n }\n };\n\n for await (const event of readAgUiEvents(response.body, response.headers.get('content-type'))) {\n begin();\n if (/^(TEXT_MESSAGE|REASONING|TOOL_CALL)_/.test(event.type)) endedOnFinish = false;\n switch (event.type) {\n case 'RUN_STARTED':\n break;\n case 'TEXT_MESSAGE_START':\n case 'REASONING_START':\n case 'REASONING_MESSAGE_START':\n nextStep();\n break;\n case 'TEXT_MESSAGE_CONTENT':\n case 'TEXT_MESSAGE_CHUNK': {\n const delta = typeof event['delta'] === 'string' ? event['delta'] : '';\n if (delta) { nextStep(); emit({ type: 'text-delta', delta }); textOpen = true; }\n break;\n }\n case 'TEXT_MESSAGE_END': {\n if (textOpen) { emit({ type: 'text-flush' }); textOpen = false; }\n break;\n }\n case 'REASONING_MESSAGE_CONTENT':\n case 'REASONING_MESSAGE_CHUNK': {\n const delta = typeof event['delta'] === 'string' ? event['delta'] : '';\n if (delta) { nextStep(); emit({ type: 'thinking-delta', delta }); }\n break;\n }\n case 'TOOL_CALL_START': {\n const id = String(event['toolCallId']);\n calls.set(id, { name: String(event['toolCallName'] ?? ''), args: '' });\n break;\n }\n case 'TOOL_CALL_ARGS': {\n const call = calls.get(String(event['toolCallId']));\n if (call && typeof event['delta'] === 'string') call.args += event['delta'];\n break;\n }\n case 'TOOL_CALL_CHUNK': {\n const id = typeof event['toolCallId'] === 'string' ? event['toolCallId'] : [...calls.keys()].at(-1);\n if (!id) break;\n const call = calls.get(id) ?? { name: '', args: '' };\n if (typeof event['toolCallName'] === 'string' && event['toolCallName']) call.name = event['toolCallName'];\n if (typeof event['delta'] === 'string') call.args += event['delta'];\n calls.set(id, call);\n break;\n }\n case 'TOOL_CALL_END': {\n startTool(String(event['toolCallId']));\n break;\n }\n case 'TOOL_CALL_RESULT': {\n const toolCallId = String(event['toolCallId']);\n startTool(toolCallId);\n if (rejected.has(toolCallId) || settledByPage.has(toolCallId)) break; // already settled\n answered.add(toolCallId);\n const content = resultText(event['content']);\n const meta = event['metadata'] as { tanstack?: { state?: unknown } } | undefined;\n if (meta?.tanstack?.state === 'output-error') {\n emit({ type: 'tool-failed', toolCallId, error: content });\n } else {\n emit({ type: 'tool-resolved', toolCallId, result: content });\n }\n break;\n }\n case 'RUN_ERROR': {\n throw new AparteError(\n typeof event['message'] === 'string' && event['message'] ? event['message'] : 'The agent run failed.',\n AparteErrorCode.PROVIDER_ERROR,\n typeof event['code'] === 'string' ? { code: event['code'] } : undefined,\n );\n }\n case 'RUN_FINISHED': {\n // Not the end of the stream: TanStack AI closes each model step with a\n // RUN_FINISHED of its own (`finishReason: tool_calls`), then streams the\n // tool results and the next step. The LAST one says how the run ended.\n for (const id of calls.keys()) startTool(id);\n if (textOpen) { emit({ type: 'text-flush' }); textOpen = false; }\n usage = readUsage(event['usage']) ?? usage; // the last step's report wins\n stepEnded = true;\n endedOnFinish = true;\n finished = {\n runId: typeof event['runId'] === 'string' ? event['runId'] : runId,\n outcome: event['outcome'] as { type?: string; interrupts?: AgUiInterrupt[] } | undefined,\n };\n break;\n }\n default:\n // STEP_*, STATE_*, MESSAGES_SNAPSHOT, ACTIVITY_*, RAW, CUSTOM, SUBAGENT_*:\n // agent state aparté does not draw.\n break;\n }\n }\n if (opts.signal.aborted) return usage;\n if (!finished) {\n throw new AparteError('The agent stream ended without RUN_FINISHED.', AparteErrorCode.PROVIDER_ERROR);\n }\n if (!endedOnFinish) {\n throw new AparteError('The agent stream was cut: it ended in the middle of a step, after a RUN_FINISHED.', AparteErrorCode.PROVIDER_ERROR);\n }\n if (textOpen) emit({ type: 'text-flush' });\n\n const pending: PendingCall[] = [];\n const interrupts: Ticket['interrupts'] = [];\n if (finished.outcome?.type === 'interrupt') {\n for (const interrupt of finished.outcome.interrupts ?? []) {\n const kind = interruptKind(interrupt);\n if (!kind || !interrupt.toolCallId) {\n throw new AparteError(\n `The agent paused for something aparté cannot answer yet (interrupt reason \"${interrupt.reason}\").`,\n AparteErrorCode.PROVIDER_ERROR,\n );\n }\n // A resumed run can pause again on a call an earlier run started (TanStack AI\n // asks to approve a client tool, then to run it): its name comes from there.\n const call = callOf(interrupt as AgUiInterrupt & { toolCallId: string });\n if (kind === 'approval') {\n emit({ type: 'tool-awaiting-approval', toolCallId: interrupt.toolCallId, name: call.name, input: call.input });\n }\n interrupts.push({ id: interrupt.id, toolCallId: interrupt.toolCallId, kind });\n pending.push({ toolCallId: interrupt.toolCallId, name: call.name, input: call.input, reason: kind });\n }\n } else {\n // Plain AG-UI: the calls left to the browser are the ones the agent names\n // (`pendingToolCallIds`, AG-UI 1.0), else the page's own tools nobody answered.\n // Any other call without a result has nobody left to answer it: its row stops\n // waiting rather than spinning after the run. A cancelled run leaves nothing.\n const outcome = finished.outcome as { type?: string; pendingToolCallIds?: unknown } | undefined;\n const named = Array.isArray(outcome?.pendingToolCallIds)\n ? new Set(outcome.pendingToolCallIds.filter((id): id is string => typeof id === 'string'))\n : undefined;\n for (const [toolCallId, call] of started) {\n if (answered.has(toolCallId)) continue;\n const forPage = outcome?.type !== 'cancelled' && (named ? named.has(toolCallId) : browserTools.has(call.name));\n if (forPage) pending.push({ toolCallId, name: call.name, input: call.input, reason: 'external' });\n else emit({ type: 'tool-aborted', toolCallId });\n }\n }\n if (pending.length > 0) {\n const next: Ticket = { v: 1, threadId, runId: finished.runId, interrupts };\n emit({ type: 'run-suspended', pending, ...(usage ? { usage } : {}), ticket: JSON.stringify(next) } as Suspended);\n } else {\n emit({ type: 'run-done', ...(usage ? { usage } : {}) });\n }\n return usage;\n };\n}\n"],"names":[],"mappings":";AAYO,SAAS,eAAe,UAAuD;AAClF,QAAM,MAAqB,CAAA;AAC3B,WAAS,QAAQ,CAAC,GAAG,UAAU;AAC3B,UAAM,KAAK,UAAU,KAAK;AAC1B,UAAM,UAAU,cAAc,EAAE,OAAO;AACvC,QAAI,EAAE,SAAS,YAAY,EAAE,SAAS,QAAQ;AAC1C,UAAI,KAAK,EAAE,IAAI,MAAM,EAAE,MAAM,SAAS;AAAA,IAC1C,WAAW,EAAE,SAAS,aAAa;AAC/B,UAAI,KAAK;AAAA,QACL;AAAA,QACA,MAAM;AAAA,QACN,GAAI,UAAU,EAAE,QAAA,IAAY,CAAA;AAAA,QAC5B,GAAI,EAAE,WAAW,SACX;AAAA,UACE,WAAW,EAAE,UAAU,IAAI,CAAC,UAAU;AAAA,YAClC,IAAI,KAAK;AAAA,YACT,MAAM;AAAA,YACN,UAAU,EAAE,MAAM,KAAK,MAAM,WAAW,KAAK,UAAU,KAAK,SAAS,EAAE,EAAA;AAAA,UAAE,EAC3E;AAAA,QAAA,IAEJ,CAAA;AAAA,MAAC,CACV;AAAA,IACL,WAAW,EAAE,SAAS,UAAU,EAAE,YAAY;AAC1C,UAAI,KAAK,EAAE,IAAI,MAAM,QAAQ,SAAS,YAAY,EAAE,YAAY;AAAA,IACpE;AAAA,EACJ,CAAC;AACD,SAAO;AACX;AAGO,SAAS,YAAY,OAAsE;AAC9F,UAAQ,SAAS,CAAA,GAAI,IAAI,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,aAAa,KAAK,aAAa,YAAY,KAAK,cAAc;AACzH;ACqCA,gBAAuB,eACnB,MACA,aAC0D;AAC1D,QAAM,SAAS,QAAQ,eAAe,gBAAgB,KAAK,WAAW,CAAC;AACvE,QAAM,SAAS,KAAK,UAAA;AACpB,QAAM,UAAU,IAAI,YAAA;AACpB,MAAI,SAAS;AACb,MAAI,OAAiB,CAAA;AACrB,QAAM,QAAQ,CAAC,SAA2E;AACtF,UAAM,UAAU,KAAK,KAAA;AACrB,QAAI,CAAC,WAAW,YAAY,SAAU,QAAO;AAC7C,QAAI;AACA,YAAM,QAAQ,KAAK,MAAM,OAAO;AAChC,aAAO,SAAS,OAAO,UAAU,YAAY,OAAQ,MAA6B,SAAS,WACrF,QACA;AAAA,IACV,QAAQ;AACJ,aAAO;AAAA,IACX;AAAA,EACJ;AACA,QAAM,YAAY,MAAM;AACpB,UAAM,QAAQ,MAAM,KAAK,KAAK,IAAI,CAAC;AACnC,WAAO,CAAA;AACP,WAAO;AAAA,EACX;AACA,QAAM,OAAO,CAAC,QAAgB;AAC1B,UAAM,OAAO,IAAI,SAAS,IAAI,IAAI,IAAI,MAAM,GAAG,EAAE,IAAI;AACrD,QAAI,OAAQ,QAAO,MAAM,IAAI;AAC7B,QAAI,SAAS,GAAI,QAAO,UAAA;AACxB,QAAI,KAAK,WAAW,OAAO,EAAG,MAAK,KAAK,KAAK,MAAM,CAAC,EAAE,QAAQ,MAAM,EAAE,CAAC;AAEvE,WAAO;AAAA,EACX;AACA,MAAI;AACA,eAAS;AACL,YAAM,EAAE,MAAM,MAAA,IAAU,MAAM,OAAO,KAAA;AACrC,UAAI,KAAM;AACV,gBAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,MAAM;AAChD,UAAI;AACJ,cAAQ,UAAU,OAAO,QAAQ,IAAI,OAAO,IAAI;AAC5C,cAAM,QAAQ,KAAK,OAAO,MAAM,GAAG,OAAO,CAAC;AAC3C,iBAAS,OAAO,MAAM,UAAU,CAAC;AACjC,YAAI,MAAO,OAAM;AAAA,MACrB;AAAA,IACJ;AACA,cAAU,QAAQ,OAAA;AAClB,QAAI,QAAQ;AACR,YAAM,QAAQ,KAAK,MAAM;AACzB,UAAI,MAAO,OAAM;AAAA,IACrB;AACA,QAAI,CAAC,QAAQ;AACT,YAAM,QAAQ,UAAA;AACd,UAAI,MAAO,OAAM;AAAA,IACrB;AAAA,EACJ,UAAA;AACI,WAAO,YAAA;AAAA,EACX;AACJ;ACzFA,SAAS,WAAW,OAA+C;AAC/D,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI;AACA,UAAM,SAAS,KAAK,MAAM,KAAK;AAC/B,WAAO,UAAU,OAAO,MAAM,KAAK,OAAO,OAAO,aAAa,YAAY,OAAO,OAAO,UAAU,YAC3F,MAAM,QAAQ,OAAO,UAAU,IAAI,SAAS;AAAA,EACvD,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAGA,SAAS,cAAc,WAA+D;AAClF,MAAI,UAAU,WAAW,eAAe,UAAU,WAAW,eAAgB,QAAO;AACpF,MAAI,cAAc,KAAK,UAAU,MAAM,EAAG,QAAO;AACjD,SAAO;AACX;AAQA,SAAS,UAAU,OAAmC;AAClD,QAAM,WAAW,MAAM,QAAQ,KAAK,IAAI,QAAQ,SAAS,OAAO,UAAU,WAAW,CAAC,KAAK,IAAI,CAAA,GAC1F,OAAO,CAAC,MAAoC,QAAQ,CAAC,KAAK,OAAO,MAAM,QAAQ;AACpF,QAAM,OAAO,QAAQ,GAAG,EAAE;AAC1B,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,MAAM,CAAC,QAAyB,OAAO,KAAK,GAAG,MAAM,WAAW,KAAK,GAAG,IAAc;AAC5F,QAAM,SAAS,IAAI,mBAAmB;AACtC,SAAO;AAAA,IACH,aAAa,IAAI,aAAa;AAAA,IAC9B,cAAc,IAAI,cAAc;AAAA,IAChC,aAAa,IAAI,aAAa,KAAK;AAAA,IACnC,GAAI,SAAS,EAAE,iBAAiB,WAAW,CAAA;AAAA,EAAC;AAEpD;AAGA,SAAS,UAAU,MAAuB;AACtC,MAAI,CAAC,KAAK,KAAA,UAAe,CAAA;AACzB,MAAI;AAAE,WAAO,KAAK,MAAM,IAAI;AAAA,EAAG,QAAQ;AAAE,WAAO,EAAE,WAAW,KAAA;AAAA,EAAQ;AACzE;AAGA,SAAS,WAAW,SAA0B;AAC1C,MAAI,OAAO,YAAY,SAAU,QAAO;AACxC,MAAI,YAAY,UAAa,YAAY,KAAM,QAAO;AACtD,SAAO,KAAK,UAAU,OAAO;AACjC;AAGA,SAAS,cAAc,UAA6D;AAChF,SAAO,SAAS,QAAQ,KAAA,MAChB,SAAS,cAAc,8CAA8C,SAAS,WAAW,KAAK;AAC1G;AAGA,SAAS,WAAW,WAAyC,SAA8C;AACvG,MAAI,CAAC,QAAS,QAAO,EAAE,aAAa,UAAU,IAAI,QAAQ,YAAA;AAC1D,MAAI,QAAQ,SAAS,cAAc,UAAU,SAAS,cAAc,CAAC,QAAQ,UAAU;AAMnF,WAAO;AAAA,MACH,aAAa,UAAU;AAAA,MACvB,QAAQ;AAAA,MACR,SAAS,EAAE,OAAO,cAAc,OAAO,EAAA;AAAA,MACvC,UAAU,EAAE,UAAU,EAAE,OAAO,iBAAe;AAAA,IAAE;AAAA,EAExD;AACA,MAAI,QAAQ,SAAS,YAAY;AAC7B,WAAO,EAAE,aAAa,UAAU,IAAI,QAAQ,YAAY,SAAS,EAAE,UAAU,QAAQ,SAAA,EAAS;AAAA,EAClG;AACA,MAAI,QAAQ,SAAS,UAAU;AAC3B,WAAO;AAAA,MACH,aAAa,UAAU;AAAA,MACvB,QAAQ;AAAA,MACR,SAAS,EAAE,OAAO,QAAQ,MAAA;AAAA,MAC1B,UAAU,EAAE,UAAU,EAAE,OAAO,iBAAe;AAAA,IAAE;AAAA,EAExD;AACA,SAAO;AAAA,IACH,aAAa,UAAU;AAAA,IACvB,QAAQ;AAAA,IACR,SAAS,QAAQ,sBAAsB,SAAY,QAAQ,oBAAoB,QAAQ;AAAA,EAAA;AAE/F;AAkBO,SAAS,iBAAiB,SAAgD;AAC7E,SAAO,OAAO,SAAS;AACnB,UAAM,UAAU,QAAQ,SAAS;AACjC,UAAM,UAAU,OAAO,QAAQ,YAAY,aAAa,MAAM,QAAQ,QAAA,IAAY,QAAQ,WAAW,CAAA;AACrG,UAAM,UAAU,KAAK;AACrB,UAAM,SAAS,WAAW,KAAK,UAAU,KAAK;AAC9C,UAAM,WAAW,KAAK,UAAU,YAAY,CAAA;AAC5C,UAAM,WAAW,QAAQ,aACjB,OAAO,QAAQ,aAAa,aAAa,QAAQ,SAAS,IAAI,IAAI,QAAQ,aAC3E,UAAU,MAAM;AACvB,UAAM,QAAQ,cAAc,KAAA,CAAM;AAClC,UAAM,cAAc,IAAI,KAAK,QAAQ,cAAc,IAAI,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC;AAK/E,UAAM,WAA0B,eAAe,QAAQ,QAAQ;AAC/D,eAAW,CAAC,YAAY,OAAO,KAAK,OAAO,QAAQ,QAAQ,GAAG;AAC1D,UAAI,YAAY,IAAI,UAAU,EAAG;AAGjC,UAAI,QAAQ,SAAS,cAAc,QAAQ,SAAU;AACrD,eAAS,KAAK;AAAA,QACV,IAAI,iBAAiB,UAAU;AAAA,QAC/B,MAAM;AAAA,QACN;AAAA,QACA,SAAS,QAAQ,SAAS,WAAW,UAAU,QAAQ,KAAK,KACtD,QAAQ,SAAS,aAAa,cAAc,OAAO,IACnD,QAAQ;AAAA,MAAA,CACjB;AAAA,IACL;AACA,UAAM,SAAS,QAAQ,WAAW,SAC5B,OAAO,WAAW,IAAI,CAAC,cAAc,WAAW,WAAW,SAAS,UAAU,UAAU,CAAC,CAAC,IAC1F;AAEN,UAAM,OAA0B;AAAA,MAC5B;AAAA,MACA;AAAA,MACA,GAAI,SAAS,EAAE,aAAa,OAAO,MAAA,IAAU,CAAA;AAAA,MAC7C,OAAO,CAAA;AAAA,MACP;AAAA,MACA,OAAO,YAAY,QAAQ,KAAK;AAAA,MAChC,SAAS,QAAQ,WAAW,CAAA;AAAA,MAC5B,gBAAgB,QAAQ,kBAAkB,CAAA;AAAA,MAC1C,GAAI,SAAS,EAAE,WAAW,CAAA;AAAA,IAAC;AAE/B,UAAM,WAAW,MAAM,QAAQ,QAAQ,UAAU;AAAA,MAC7C,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,oBAAoB,QAAQ,qBAAqB,GAAG,QAAA;AAAA,MAC/E,MAAM,KAAK,UAAU,IAAI;AAAA,MACzB,QAAQ,KAAK;AAAA,MACb,GAAI,QAAQ,cAAc,EAAE,aAAa,QAAQ,YAAA,IAAgB,CAAA;AAAA,IAAC,CACrE;AACD,QAAI,CAAC,SAAS,MAAM,CAAC,SAAS,MAAM;AAChC,YAAM,OAAO,MAAM,SAAS,OAAO,MAAM,MAAM,EAAE;AACjD,YAAM,IAAI;AAAA,QACN,KAAK,OAAO,MAAM,GAAG,GAAG,KAAK,oCAAoC,SAAS,MAAM;AAAA,QAChF,YAAY,cAAc,SAAS,MAAM,KAAK,gBAAgB;AAAA,QAC9D;AAAA,QAAW;AAAA,QAAW,SAAS;AAAA,MAAA;AAAA,IAEvC;AAIA,UAAM,UAAU,IAAI,IAAI,QAAQ,SAAS,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAA,GAAI,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,OAAO,EAAE,SAAS,GAAC,CAAG,CAAU,CAAC,CAAC;AAChJ,UAAM,QAAQ,IAAI,IAAI,QAAQ,MAAM;AACpC,UAAM,eAAe,IAAI,KAAK,QAAQ,SAAS,IAAI,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;AACrE,UAAM,4BAAY,IAAA;AAClB,UAAM,8BAAc,IAAA;AACpB,UAAM,+BAAe,IAAA;AACrB,UAAM,+BAAe,IAAA;AACrB,UAAM,oCAAoB,IAAA;AAC1B,QAAI,WAAW;AACf,QAAI;AACJ,QAAI;AACJ,UAAM,OAAO,KAAK;AAElB,UAAM,YAAY,CAAC,eAAuB;AACtC,YAAM,OAAO,MAAM,IAAI,UAAU;AACjC,UAAI,CAAC,QAAQ,QAAQ,IAAI,UAAU,KAAK,MAAM,IAAI,UAAU,EAAG;AAC/D,YAAM,QAAQ,UAAU,KAAK,IAAI;AACjC,cAAQ,IAAI,YAAY,EAAE,MAAM,KAAK,MAAM,OAAO;AAClD,UAAI,UAAU;AAAE,aAAK,EAAE,MAAM,cAAc;AAAG,mBAAW;AAAA,MAAO;AAChE,WAAK,EAAE,MAAM,cAAc,YAAY,MAAM,KAAK,MAAM,OAAO;AAC/D,kBAAY;AAAA,IAChB;AAOA,QAAI,YAAY;AAChB,UAAM,WAAW,MAAM;AACnB,UAAI,CAAC,UAAW;AAChB,kBAAY;AACZ,UAAI,UAAU;AAAE,aAAK,EAAE,MAAM,cAAc;AAAG,mBAAW;AAAA,MAAO;AAChE,WAAK,EAAE,MAAM,cAAc;AAAA,IAC/B;AAGA,UAAM,SAAS,CAAC,cAAwF;AACpG,YAAM,OAAO,QAAQ,IAAI,UAAU,UAAU,KAAK,QAAQ,IAAI,UAAU,UAAU;AAClF,UAAI,KAAM,QAAO;AACjB,YAAM,WAAW,MAAM,IAAI,UAAU,UAAU;AAC/C,UAAI,SAAU,QAAO,EAAE,MAAM,SAAS,MAAM,OAAO,UAAU,SAAS,IAAI,EAAA;AAE1E,YAAM,OAAO,UAAU;AACvB,aAAO,EAAE,MAAM,OAAO,MAAM,aAAa,WAAW,KAAK,WAAW,IAAI,OAAO,MAAM,SAAS,CAAA,EAAC;AAAA,IACnG;AAIA,QAAI,gBAAgB;AAMpB,QAAI,QAAQ;AACZ,UAAM,QAAQ,MAAM;AAChB,UAAI,MAAO;AACX,cAAQ;AACR,WAAK,EAAE,MAAM,aAAa;AAC1B,WAAK,EAAE,MAAM,cAAc;AAC3B,iBAAW,CAAC,YAAY,OAAO,KAAK,OAAO,QAAQ,QAAQ,GAAG;AAI1D,YAAI,QAAQ,SAAS,UAAU;AAC3B,eAAK;AAAA,YACD,MAAM;AAAA,YACN;AAAA,YACA,QAAQ,QAAQ;AAAA,YAChB,GAAI,QAAQ,sBAAsB,SAAY,EAAE,kBAAkB,QAAQ,sBAAsB,CAAA;AAAA,UAAC,CACpG;AACD,wBAAc,IAAI,UAAU;AAC5B;AAAA,QACJ;AACA,YAAI,QAAQ,SAAS,UAAU;AAC3B,eAAK,EAAE,MAAM,eAAe,YAAY,OAAO,QAAQ,OAAO;AAC9D,wBAAc,IAAI,UAAU;AAC5B;AAAA,QACJ;AACA,YAAI,QAAQ,UAAU;AAClB,eAAK,EAAE,MAAM,iBAAiB,WAAA,CAAY;AAAA,QAC9C,OAAO;AACH,eAAK,EAAE,MAAM,iBAAiB,YAAY,QAAQ,cAAc,OAAO,GAAG;AAC1E,mBAAS,IAAI,UAAU;AAAA,QAC3B;AAAA,MACJ;AAAA,IACJ;AAEA,qBAAiB,SAAS,eAAe,SAAS,MAAM,SAAS,QAAQ,IAAI,cAAc,CAAC,GAAG;AAC3F,YAAA;AACA,UAAI,uCAAuC,KAAK,MAAM,IAAI,EAAG,iBAAgB;AAC7E,cAAQ,MAAM,MAAA;AAAA,QACV,KAAK;AACD;AAAA,QACJ,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AACD,mBAAA;AACA;AAAA,QACJ,KAAK;AAAA,QACL,KAAK,sBAAsB;AACvB,gBAAM,QAAQ,OAAO,MAAM,OAAO,MAAM,WAAW,MAAM,OAAO,IAAI;AACpE,cAAI,OAAO;AAAE,qBAAA;AAAY,iBAAK,EAAE,MAAM,cAAc,MAAA,CAAO;AAAG,uBAAW;AAAA,UAAM;AAC/E;AAAA,QACJ;AAAA,QACA,KAAK,oBAAoB;AACrB,cAAI,UAAU;AAAE,iBAAK,EAAE,MAAM,cAAc;AAAG,uBAAW;AAAA,UAAO;AAChE;AAAA,QACJ;AAAA,QACA,KAAK;AAAA,QACL,KAAK,2BAA2B;AAC5B,gBAAM,QAAQ,OAAO,MAAM,OAAO,MAAM,WAAW,MAAM,OAAO,IAAI;AACpE,cAAI,OAAO;AAAE,qBAAA;AAAY,iBAAK,EAAE,MAAM,kBAAkB,MAAA,CAAO;AAAA,UAAG;AAClE;AAAA,QACJ;AAAA,QACA,KAAK,mBAAmB;AACpB,gBAAM,KAAK,OAAO,MAAM,YAAY,CAAC;AACrC,gBAAM,IAAI,IAAI,EAAE,MAAM,OAAO,MAAM,cAAc,KAAK,EAAE,GAAG,MAAM,GAAA,CAAI;AACrE;AAAA,QACJ;AAAA,QACA,KAAK,kBAAkB;AACnB,gBAAM,OAAO,MAAM,IAAI,OAAO,MAAM,YAAY,CAAC,CAAC;AAClD,cAAI,QAAQ,OAAO,MAAM,OAAO,MAAM,SAAU,MAAK,QAAQ,MAAM,OAAO;AAC1E;AAAA,QACJ;AAAA,QACA,KAAK,mBAAmB;AACpB,gBAAM,KAAK,OAAO,MAAM,YAAY,MAAM,WAAW,MAAM,YAAY,IAAI,CAAC,GAAG,MAAM,KAAA,CAAM,EAAE,GAAG,EAAE;AAClG,cAAI,CAAC,GAAI;AACT,gBAAM,OAAO,MAAM,IAAI,EAAE,KAAK,EAAE,MAAM,IAAI,MAAM,GAAA;AAChD,cAAI,OAAO,MAAM,cAAc,MAAM,YAAY,MAAM,cAAc,EAAG,MAAK,OAAO,MAAM,cAAc;AACxG,cAAI,OAAO,MAAM,OAAO,MAAM,SAAU,MAAK,QAAQ,MAAM,OAAO;AAClE,gBAAM,IAAI,IAAI,IAAI;AAClB;AAAA,QACJ;AAAA,QACA,KAAK,iBAAiB;AAClB,oBAAU,OAAO,MAAM,YAAY,CAAC,CAAC;AACrC;AAAA,QACJ;AAAA,QACA,KAAK,oBAAoB;AACrB,gBAAM,aAAa,OAAO,MAAM,YAAY,CAAC;AAC7C,oBAAU,UAAU;AACpB,cAAI,SAAS,IAAI,UAAU,KAAK,cAAc,IAAI,UAAU,EAAG;AAC/D,mBAAS,IAAI,UAAU;AACvB,gBAAM,UAAU,WAAW,MAAM,SAAS,CAAC;AAC3C,gBAAM,OAAO,MAAM,UAAU;AAC7B,cAAI,MAAM,UAAU,UAAU,gBAAgB;AAC1C,iBAAK,EAAE,MAAM,eAAe,YAAY,OAAO,SAAS;AAAA,UAC5D,OAAO;AACH,iBAAK,EAAE,MAAM,iBAAiB,YAAY,QAAQ,SAAS;AAAA,UAC/D;AACA;AAAA,QACJ;AAAA,QACA,KAAK,aAAa;AACd,gBAAM,IAAI;AAAA,YACN,OAAO,MAAM,SAAS,MAAM,YAAY,MAAM,SAAS,IAAI,MAAM,SAAS,IAAI;AAAA,YAC9E,gBAAgB;AAAA,YAChB,OAAO,MAAM,MAAM,MAAM,WAAW,EAAE,MAAM,MAAM,MAAM,MAAM;AAAA,UAAA;AAAA,QAEtE;AAAA,QACA,KAAK,gBAAgB;AAIjB,qBAAW,MAAM,MAAM,KAAA,aAAkB,EAAE;AAC3C,cAAI,UAAU;AAAE,iBAAK,EAAE,MAAM,cAAc;AAAG,uBAAW;AAAA,UAAO;AAChE,kBAAQ,UAAU,MAAM,OAAO,CAAC,KAAK;AACrC,sBAAY;AACZ,0BAAgB;AAChB,qBAAW;AAAA,YACP,OAAO,OAAO,MAAM,OAAO,MAAM,WAAW,MAAM,OAAO,IAAI;AAAA,YAC7D,SAAS,MAAM,SAAS;AAAA,UAAA;AAE5B;AAAA,QACJ;AAAA,MAII;AAAA,IAEZ;AACA,QAAI,KAAK,OAAO,QAAS,QAAO;AAChC,QAAI,CAAC,UAAU;AACX,YAAM,IAAI,YAAY,gDAAgD,gBAAgB,cAAc;AAAA,IACxG;AACA,QAAI,CAAC,eAAe;AAChB,YAAM,IAAI,YAAY,qFAAqF,gBAAgB,cAAc;AAAA,IAC7I;AACA,QAAI,SAAU,MAAK,EAAE,MAAM,cAAc;AAEzC,UAAM,UAAyB,CAAA;AAC/B,UAAM,aAAmC,CAAA;AACzC,QAAI,SAAS,SAAS,SAAS,aAAa;AACxC,iBAAW,aAAa,SAAS,QAAQ,cAAc,CAAA,GAAI;AACvD,cAAM,OAAO,cAAc,SAAS;AACpC,YAAI,CAAC,QAAQ,CAAC,UAAU,YAAY;AAChC,gBAAM,IAAI;AAAA,YACN,8EAA8E,UAAU,MAAM;AAAA,YAC9F,gBAAgB;AAAA,UAAA;AAAA,QAExB;AAGA,cAAM,OAAO,OAAO,SAAmD;AACvE,YAAI,SAAS,YAAY;AACrB,eAAK,EAAE,MAAM,0BAA0B,YAAY,UAAU,YAAY,MAAM,KAAK,MAAM,OAAO,KAAK,MAAA,CAAO;AAAA,QACjH;AACA,mBAAW,KAAK,EAAE,IAAI,UAAU,IAAI,YAAY,UAAU,YAAY,MAAM;AAC5E,gBAAQ,KAAK,EAAE,YAAY,UAAU,YAAY,MAAM,KAAK,MAAM,OAAO,KAAK,OAAO,QAAQ,MAAM;AAAA,MACvG;AAAA,IACJ,OAAO;AAKH,YAAM,UAAU,SAAS;AACzB,YAAM,QAAQ,MAAM,QAAQ,SAAS,kBAAkB,IACjD,IAAI,IAAI,QAAQ,mBAAmB,OAAO,CAAC,OAAqB,OAAO,OAAO,QAAQ,CAAC,IACvF;AACN,iBAAW,CAAC,YAAY,IAAI,KAAK,SAAS;AACtC,YAAI,SAAS,IAAI,UAAU,EAAG;AAC9B,cAAM,UAAU,SAAS,SAAS,gBAAgB,QAAQ,MAAM,IAAI,UAAU,IAAI,aAAa,IAAI,KAAK,IAAI;AAC5G,YAAI,QAAS,SAAQ,KAAK,EAAE,YAAY,MAAM,KAAK,MAAM,OAAO,KAAK,OAAO,QAAQ,YAAY;AAAA,YAC3F,MAAK,EAAE,MAAM,gBAAgB,YAAY;AAAA,MAClD;AAAA,IACJ;AACA,QAAI,QAAQ,SAAS,GAAG;AACpB,YAAM,OAAe,EAAE,GAAG,GAAG,UAAU,OAAO,SAAS,OAAO,WAAA;AAC9D,WAAK,EAAE,MAAM,iBAAiB,SAAS,GAAI,QAAQ,EAAE,MAAA,IAAU,CAAA,GAAK,QAAQ,KAAK,UAAU,IAAI,GAAgB;AAAA,IACnH,OAAO;AACH,WAAK,EAAE,MAAM,YAAY,GAAI,QAAQ,EAAE,MAAA,IAAU,CAAA,GAAK;AAAA,IAC1D;AACA,WAAO;AAAA,EACX;AACJ;"}
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAGR,kBAAkB,EAClB,sBAAsB,EACzB,MAAM,cAAc,CAAC;AAUtB,MAAM,WAAW,iBAAiB;IAC9B,oFAAoF;IACpF,QAAQ,EAAE,MAAM,CAAC;IACjB,2FAA2F;IAC3F,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IACpG,kCAAkC;IAClC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,gEAAgE;IAChE,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,sBAAsB,KAAK,MAAM,CAAC,CAAC;IAC/D,kFAAkF;IAClF,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,oEAAoE;IACpE,OAAO,CAAC,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACtD;AA+FD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,kBAAkB,CAuS/E"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAGR,kBAAkB,EAClB,sBAAsB,EACzB,MAAM,cAAc,CAAC;AAUtB,MAAM,WAAW,iBAAiB;IAC9B,oFAAoF;IACpF,QAAQ,EAAE,MAAM,CAAC;IACjB,2FAA2F;IAC3F,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IACpG,kCAAkC;IAClC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,gEAAgE;IAChE,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,sBAAsB,KAAK,MAAM,CAAC,CAAC;IAC/D,kFAAkF;IAClF,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,oEAAoE;IACpE,OAAO,CAAC,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACtD;AA0GD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,kBAAkB,CA0S/E"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aparte/runner-ag-ui",
3
- "version": "0.19.2",
3
+ "version": "0.20.0",
4
4
  "homepage": "https://apartejs.dev/providers/agent/ag-ui/",
5
5
  "description": "Render an AG-UI agent in aparté — Agno, LangGraph, Mastra, CrewAI, Pydantic AI, TanStack AI: the agent runs on its server, aparté draws the chat, its tool calls and its approvals.",
6
6
  "type": "module",
@@ -25,7 +25,7 @@
25
25
  "node": ">=18"
26
26
  },
27
27
  "peerDependencies": {
28
- "@aparte/core": ">=0.19.2 <1.0.0"
28
+ "@aparte/core": ">=0.20.0 <1.0.0"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@ag-ui/core": "^1.0.0",
@@ -33,7 +33,7 @@
33
33
  "@types/node": "^22.0.0",
34
34
  "typescript": "^5.4.0",
35
35
  "vite": "^6.0.0",
36
- "@aparte/core": "0.19.2"
36
+ "@aparte/core": "0.20.0"
37
37
  },
38
38
  "keywords": [
39
39
  "ai",
@@ -57,7 +57,7 @@
57
57
  },
58
58
  "scripts": {
59
59
  "dev": "vite",
60
- "build": "vite build && tsc -b --emitDeclarationOnly --force",
60
+ "build": "vite build && tsc -b --emitDeclarationOnly --force && node ../../../../scripts/stamp-dist.mjs",
61
61
  "preview": "vite preview",
62
62
  "test": "vitest",
63
63
  "test:run": "vitest run",