experimental-a2 0.21.1 → 0.22.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/CHANGELOG.md +21 -0
- package/dist/{actor-HnGmrOZZ.d.ts → actor-CqibLgjQ.d.ts} +13 -5
- package/dist/actor-CqibLgjQ.d.ts.map +1 -0
- package/dist/actor-client.d.ts +1 -1
- package/dist/actor-react.d.ts +1 -1
- package/dist/actor-react.js +1 -1
- package/dist/actor-shared-DFET5q16.js.map +1 -1
- package/dist/actor.d.ts +1 -1
- package/dist/actor.js +3 -3
- package/dist/{ai-CvEnqVfo.d.ts → ai-DAu77CDr.d.ts} +2 -2
- package/dist/{ai-CvEnqVfo.d.ts.map → ai-DAu77CDr.d.ts.map} +1 -1
- package/dist/{ai-CzE672Q3.js → ai-EEfKyc-z.js} +2 -2
- package/dist/{ai-CzE672Q3.js.map → ai-EEfKyc-z.js.map} +1 -1
- package/dist/ai-client.d.ts +1 -1
- package/dist/ai-client.js +2 -2
- package/dist/ai-fx.d.ts +4 -2
- package/dist/ai-fx.d.ts.map +1 -1
- package/dist/ai-fx.js +211 -61
- package/dist/ai-fx.js.map +1 -1
- package/dist/{ai-harness-Bfk-Lycn.d.ts → ai-harness-DTjkAr2T.d.ts} +2 -2
- package/dist/{ai-harness-Bfk-Lycn.d.ts.map → ai-harness-DTjkAr2T.d.ts.map} +1 -1
- package/dist/ai-harness-dQeJaR1t.js.map +1 -1
- package/dist/ai-pi.d.ts +1 -1
- package/dist/ai-pi.js +1 -1
- package/dist/ai-react.d.ts +1 -1
- package/dist/ai-react.js +2 -2
- package/dist/ai-server.d.ts +3 -3
- package/dist/ai-server.d.ts.map +1 -1
- package/dist/ai-server.js +10 -4
- package/dist/ai-server.js.map +1 -1
- package/dist/ai.d.ts +1 -1
- package/dist/ai.js +1 -1
- package/dist/{client-9IeTsiAs.js → client-BQS4i_V9.js} +27 -4
- package/dist/client-BQS4i_V9.js.map +1 -0
- package/dist/client-CUMbS_38.d.ts.map +1 -1
- package/dist/client.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/{internal-Bc4PvfHk.js → internal-CvsrprnU.js} +32 -15
- package/dist/{internal-Bc4PvfHk.js.map → internal-CvsrprnU.js.map} +1 -1
- package/dist/react.js +1 -1
- package/dist/{runtime-consumer-BWmht60z.js → runtime-consumer-CIeL33wj.js} +2 -2
- package/dist/{runtime-consumer-BWmht60z.js.map → runtime-consumer-CIeL33wj.js.map} +1 -1
- package/dist/scheduler-qstash.d.ts +2 -2
- package/dist/scheduler-qstash.js +2 -2
- package/dist/scheduler-vercel.d.ts +2 -2
- package/dist/scheduler-vercel.js +2 -2
- package/dist/{server-DvIB8kj5.js → server-DtFrWxi3.js} +30 -8
- package/dist/server-DtFrWxi3.js.map +1 -0
- package/dist/{server-BshFZ0eL.d.ts → server-qgez0Xny.d.ts} +3 -3
- package/dist/{server-BshFZ0eL.d.ts.map → server-qgez0Xny.d.ts.map} +1 -1
- package/dist/server.d.ts +2 -2
- package/dist/server.js +1 -1
- package/dist/{session-clear-Bk_29eEw.d.ts → session-clear-D1PFe7xl.d.ts} +2 -2
- package/dist/session-clear-D1PFe7xl.d.ts.map +1 -0
- package/dist/{store-Z2qjw50n.d.ts → store-D0MhBvg6.d.ts} +15 -6
- package/dist/store-D0MhBvg6.d.ts.map +1 -0
- package/dist/store-N8PXxDAS.js.map +1 -1
- package/dist/store-memory.d.ts +1 -1
- package/dist/store-memory.d.ts.map +1 -1
- package/dist/store-memory.js +15 -17
- package/dist/store-memory.js.map +1 -1
- package/dist/{store-polling-DYNyxCev.js → store-polling-BZt2Rf3D.js} +2 -2
- package/dist/{store-polling-DYNyxCev.js.map → store-polling-BZt2Rf3D.js.map} +1 -1
- package/dist/store-postgres.d.ts +1 -1
- package/dist/store-postgres.d.ts.map +1 -1
- package/dist/store-postgres.js +31 -40
- package/dist/store-postgres.js.map +1 -1
- package/dist/store-presence-polling-JuAy2Ck2.js +111 -0
- package/dist/store-presence-polling-JuAy2Ck2.js.map +1 -0
- package/dist/store-redis-http.d.ts +1 -1
- package/dist/store-redis-http.js +3 -3
- package/dist/{store-redis-notify-IiVdlYHc.js → store-redis-notify-CsJw_hfD.js} +538 -397
- package/dist/store-redis-notify-CsJw_hfD.js.map +1 -0
- package/dist/store-redis.d.ts +1 -1
- package/dist/store-redis.d.ts.map +1 -1
- package/dist/store-redis.js +3 -4
- package/dist/store-redis.js.map +1 -1
- package/dist/store-sqlite.d.ts +1 -1
- package/dist/store-sqlite.d.ts.map +1 -1
- package/dist/store-sqlite.js +16 -18
- package/dist/store-sqlite.js.map +1 -1
- package/dist/stream-buffer-DrTyiIuo.js +57 -0
- package/dist/stream-buffer-DrTyiIuo.js.map +1 -0
- package/dist/{wire-CZGqNZRx.js → wire-CV-Z7wz-.js} +3 -3
- package/dist/{wire-CZGqNZRx.js.map → wire-CV-Z7wz-.js.map} +1 -1
- package/docs/actors/01-introduction.mdx +5 -2
- package/docs/agents/03-conversations.mdx +9 -8
- package/docs/agents/04-harnesses.mdx +7 -4
- package/docs/reference/01-core.mdx +3 -2
- package/docs/reference/02-server.mdx +5 -1
- package/docs/reference/04-ai.mdx +13 -4
- package/docs/reference/06-errors.mdx +1 -1
- package/docs/server/01-consumers.mdx +4 -1
- package/docs/server/06-guarantees.mdx +7 -4
- package/docs/stores/02-redis.mdx +16 -10
- package/docs/stores/03-redis-http.mdx +6 -4
- package/docs/stores/04-postgres.mdx +2 -2
- package/docs/upgrades/01-overview.mdx +1 -0
- package/docs/upgrades/78-0-22.mdx +115 -0
- package/examples/playground/app/a0/sandbox.ts +58 -13
- package/examples/playground/app/a2slack/db/a2.ts +7 -4
- package/examples/playground/app/a2slack/db/migrations/0004_a2_session_event_ids.sql +2 -0
- package/examples/playground/app/a2slack/db/migrations/meta/0004_snapshot.json +1165 -0
- package/examples/playground/app/a2slack/db/migrations/meta/_journal.json +8 -1
- package/examples/playground/app/chat/server.ts +2 -2
- package/examples/playground/lib/store.ts +1 -1
- package/examples/playground/package.json +1 -1
- package/package.json +1 -1
- package/sql/postgres/drizzle-schema.ts +7 -4
- package/sql/postgres/schema.sql +24 -3
- package/src/actor-shared.ts +10 -2
- package/src/ai-agent.ts +13 -0
- package/src/ai-fx.ts +321 -77
- package/src/ai-harness.ts +7 -0
- package/src/client.ts +19 -2
- package/src/deterministic-id.ts +1 -1
- package/src/internal.ts +42 -11
- package/src/presence-recovery.ts +18 -11
- package/src/presence-tombstones.ts +52 -0
- package/src/push-envelope.ts +2 -2
- package/src/server.ts +49 -15
- package/src/store-memory.ts +24 -32
- package/src/store-postgres.ts +43 -53
- package/src/store-presence-polling.ts +59 -34
- package/src/store-redis-core.ts +595 -439
- package/src/store-redis-notify.ts +74 -26
- package/src/store-redis.ts +2 -3
- package/src/store-sqlite.ts +34 -40
- package/src/store.ts +14 -5
- package/dist/actor-HnGmrOZZ.d.ts.map +0 -1
- package/dist/client-9IeTsiAs.js.map +0 -1
- package/dist/server-DvIB8kj5.js.map +0 -1
- package/dist/session-clear-Bk_29eEw.d.ts.map +0 -1
- package/dist/store-Z2qjw50n.d.ts.map +0 -1
- package/dist/store-presence-polling-CzQ0wdtM.js +0 -94
- package/dist/store-presence-polling-CzQ0wdtM.js.map +0 -1
- package/dist/store-redis-notify-IiVdlYHc.js.map +0 -1
- package/dist/stream-buffer-Cx7AAPGq.js +0 -23
- package/dist/stream-buffer-Cx7AAPGq.js.map +0 -1
package/dist/ai-fx.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-fx.js","names":[],"sources":["../src/ai-fx.ts"],"sourcesContent":["/**\n * experimental-a2/ai/fx — `fx()`: the FX harness for `agent({ harness })`.\n * Server-only; FX's kernel comes from the optional `libfx` peer dependency.\n */\n\n// oxlint-disable no-await-in-loop -- turns replay in order, each on the kernel the last one left.\n\nimport {\n asSchema,\n type FinishReason,\n type LanguageModelUsage,\n type ToolSet,\n type UIMessageChunk,\n} from 'ai'\nimport type { FxAgent, FxHostTool, FxPromptBlock, FxTurn } from 'libfx'\nimport {\n type Deferred,\n defineHarness,\n deferred,\n Divergence,\n gatewayCredential,\n type GatewayCredential,\n gatewaySessionAffinity,\n type Harness,\n type HarnessGeneration,\n harnessImages,\n type HarnessInput,\n type HarnessRead,\n type HarnessStep,\n type HarnessTurn,\n pending,\n readHarness,\n saveHarness,\n} from './ai-harness.ts'\nimport type { CompactionPartData, GenerationCompletedPayload } from './ai.ts'\nimport { schemaInput } from './ai-tool-input.ts'\nimport { boundedCache } from './bounded-cache.ts'\n\nexport type { Harness } from './ai-harness.ts'\n\n/**\n * The FX harness. Every model call FX makes is one durable step; A2 rebuilds\n * the kernel for each step from its latest checkpoint and the log since.\n */\nexport function fx(): Harness<'fx', never> {\n return defineHarness('fx', {\n tools: {},\n step: runStep,\n })\n}\n\nconst SNAPSHOT_VERSION = 1\nconst CATALOG_TTL_MS = 60 * 60 * 1000\n/** The Gateway model catalog libfx reads for each new kernel. */\nconst CATALOG_PATH = '/coding-agent/v1/models'\n\ntype StreamPart = { type: string } & Record<string, unknown>\n/** A recorded tool call, with the input the kernel reads from its text. */\ntype ToolCall = { toolCallId: string; toolName: string; input: unknown }\n\n/** A model response A2 recorded, in the Gateway's stream format. */\ntype RecordedCall = {\n kind: 'step' | 'compaction'\n sse: string\n toolCalls: ToolCall[]\n}\n\n/** One FX turn to replay: its prompt, recorded calls, and how it ended. */\ntype TurnPlan = {\n prompt: string | FxPromptBlock[]\n calls: RecordedCall[]\n /** Steering to queue while serving the call at that index. */\n steers: Map<number, string[]>\n end: 'stop' | 'interrupted' | 'live'\n}\n\ntype Completion = { finishReason?: FinishReason; usage?: LanguageModelUsage }\n\n/** The turn being replayed or run live, as the fetch and tool hooks see it. */\ntype TurnState = {\n plan: TurnPlan\n fxTurn: FxTurn\n next: number\n /** Tool calls of the last served step and the ones already answered. */\n served: ToolCall[]\n answered: Set<string>\n live: boolean\n liveStep: boolean\n failure?: Error\n /** Resolves when the live step is over: FX asked for its tools. */\n toolsRequested: Deferred\n parsed?: Promise<void>\n}\n\nfunction fail(state: TurnState, error: Error): never {\n state.failure ??= error\n state.fxTurn.cancel()\n throw state.failure\n}\n\nconst diverged = (message: string): Divergence => new Divergence('FX', message)\n\n// ---------------------------------------------------------------- step\n\nasync function runStep(step: HarnessStep): Promise<HarnessGeneration> {\n const { createFxAgent } = await loadLibfx()\n const credential = gatewayCredential('fx')\n const name = `${step.agentName}.a2.harness.fx`\n const { replayAll, ...first } = await readHarness({\n step,\n name,\n parse: savedCheckpoint,\n })\n let read: HarnessRead<Uint8Array> = first\n\n let completion: Completion = {}\n let stopped = false\n // Live responses belong to the step: the kernel aborts its fetch once it\n // has read a response's finish, while A2 may still be reading its copy.\n const released = new AbortController()\n const network = AbortSignal.any([step.signal, released.signal])\n let agent: FxAgent | undefined\n let turn: TurnState | undefined\n\n const run = async (\n controller: ReadableStreamDefaultController<UIMessageChunk>,\n turns: TurnPlan[],\n ): Promise<void> => {\n controller.enqueue({ type: 'start', messageId: step.responseMessageId })\n for (const plan of turns) {\n if (stopped) return\n const fxTurn = agent!.prompt(plan.prompt, { signal: step.signal })\n const state: TurnState = {\n plan,\n fxTurn,\n next: 0,\n served: [],\n answered: new Set(),\n live: false,\n liveStep: false,\n toolsRequested: deferred(),\n }\n turn = state\n const drained = (async () => {\n for await (const _ of fxTurn) {\n // Live output reaches A2 from the Gateway response itself.\n }\n })()\n // A failed turn surfaces through its result.\n drained.catch(() => {})\n if (plan.end !== 'live') {\n const result = await fxTurn.result.catch((error: unknown) => {\n throw state.failure ?? error\n })\n await drained\n if (state.failure) throw state.failure\n const expected = plan.end === 'stop' ? 'end_turn' : 'cancelled'\n if (result.stopReason !== expected)\n throw diverged(\n `a recorded turn ended with ${result.stopReason}, not ${expected}`,\n )\n continue\n }\n const outcome = await Promise.race([\n fxTurn.result.then((result) => ({ ended: true, result })),\n state.toolsRequested.promise.then(() => ({ ended: false as const })),\n ]).catch((error: unknown) => {\n throw state.failure ?? error\n })\n if (state.failure) throw state.failure\n await state.parsed\n if (outcome.ended) {\n await drained\n // Only a turn this attempt finished describes the log.\n if (outcome.result.stopReason === 'end_turn' && !step.signal.aborted)\n await saveCheckpoint()\n } else {\n // FX now waits on tools that A2's tool consumer run.\n fxTurn.cancel()\n }\n controller.enqueue({ type: 'finish-step' })\n controller.enqueue({\n type: 'finish',\n ...(completion.finishReason === undefined\n ? {}\n : { finishReason: completion.finishReason }),\n })\n }\n }\n\n const saveCheckpoint = async (): Promise<void> => {\n let checkpoint: Uint8Array\n try {\n checkpoint = await agent!.checkpoint()\n } catch {\n // Later steps replay from the previous snapshot.\n return\n }\n await saveHarness({\n step,\n name,\n state: {\n version: SNAPSHOT_VERSION,\n checkpoint: Buffer.from(checkpoint).toString('base64'),\n },\n })\n }\n\n const hostFetch = async (\n input: string | URL | Request,\n init: RequestInit = {},\n ): Promise<Response> => {\n const url = input instanceof Request ? input.url : String(input)\n const method = (init.method ?? 'GET').toUpperCase()\n if (method === 'GET' && new URL(url).pathname === CATALOG_PATH)\n return catalog(url, credential, withAuth(init.headers, credential))\n if (method === 'GET') return fetch(input, init)\n const state = turn!\n if (state.failure) throw state.failure\n const body = decodeBody(init.body)\n const compaction = isCompaction(body)\n const recorded = state.plan.calls[state.next]\n if (recorded !== undefined) {\n if ((recorded.kind === 'compaction') !== compaction)\n fail(state, diverged(`request ${state.next} is not a ${recorded.kind}`))\n const index = state.next++\n if (recorded.kind === 'step') {\n state.served = recorded.toolCalls\n state.answered = new Set()\n }\n const steers = state.plan.steers.get(index) ?? []\n return relay(recorded.sse, async () => {\n for (const text of steers) await state.fxTurn.steer(text)\n })\n }\n if (state.plan.end === 'stop')\n fail(state, diverged('FX continued a turn the log ended'))\n if (state.plan.end === 'interrupted') {\n state.fxTurn.cancel()\n throw new DOMException('The recorded turn was interrupted', 'AbortError')\n }\n if (state.liveStep && !compaction) {\n // One generation is one step; the next step is A2's next generation.\n state.toolsRequested.resolve()\n return pending(init.signal)\n }\n state.live = true\n return live(state, url, { ...init, body }, compaction)\n }\n\n const affinity = gatewaySessionAffinity({\n agentName: step.agentName,\n sessionId: step.sessionId,\n })\n let compactions = 0\n const live = async (\n state: TurnState,\n url: string,\n init: RequestInit & { body: string },\n compaction: boolean,\n ): Promise<Response> => {\n const headers = withAuth(init.headers, credential)\n // The kernel's own affinity is new with every rebuild; A2's lasts the session.\n headers.set('x-session-affinity', affinity)\n const response = await fetch(url, {\n ...init,\n body: withGatewayCaching(init.body),\n headers,\n signal: network,\n })\n // Abandoned before its headers came: the kernel has moved on without it.\n if (init.signal?.aborted) {\n void response.body?.cancel()\n throw init.signal.reason\n }\n if (!response.ok || response.body === null) return response\n const [fromNetwork, forLog] = response.body.tee()\n const forKernel = init.signal\n ? fromNetwork.pipeThrough(new TransformStream(), { signal: init.signal })\n : fromNetwork\n // Live responses reach the log in request order: a compaction's part\n // precedes the step that follows it.\n const previous = state.parsed\n if (compaction) {\n // Unique across the response's generations: the transcript keys data parts by ID.\n const id = `fx-compaction:${step.requestIndex}:${compactions++}`\n state.parsed = (async () => {\n const all = await readParts(forLog)\n await previous\n emit({\n type: 'data-a2-compaction',\n id,\n data: compactionData(all),\n })\n })()\n } else {\n state.liveStep = true\n state.parsed = (async () => {\n await previous\n emit({ type: 'start-step' })\n await streamStep({\n body: forLog,\n tools: step.tools,\n emit,\n finished: (finished) => {\n completion = finished\n },\n })\n })()\n }\n // A stopped step abandons its parse; its failure surfaces through the turn.\n state.parsed.catch(() => {})\n return new Response(forKernel, {\n status: response.status,\n headers: response.headers,\n })\n }\n\n const hostTools = await kernelTools(step.tools, (toolName, toolInput) => {\n const state = turn!\n if (state.failure) throw state.failure\n const call = state.served.find(\n (candidate) =>\n candidate.toolName === toolName &&\n !state.answered.has(candidate.toolCallId) &&\n sameJson(candidate.input, toolInput),\n )\n const result = call === undefined ? undefined : results.get(call.toolCallId)\n if (call !== undefined && result !== undefined) {\n state.answered.add(call.toolCallId)\n if (result.error !== undefined) throw new Error(result.error)\n if (result.denied === true) throw new Error('The tool call was denied.')\n return result.output ?? null\n }\n if (state.live) {\n state.toolsRequested.resolve()\n return pending(undefined)\n }\n if (state.plan.end === 'interrupted') {\n state.fxTurn.cancel()\n return pending(undefined)\n }\n return fail(state, diverged(`no recorded result for ${toolName}`))\n })\n\n const plans = (turns: HarnessTurn[]): Promise<TurnPlan[]> =>\n Promise.all(turns.map((recorded) => turnPlan(recorded, step.signal)))\n let results = read.results\n let controller: ReadableStreamDefaultController<UIMessageChunk> | undefined\n let closed = false\n const emit = (chunk: UIMessageChunk): void => {\n if (!closed) controller!.enqueue(chunk)\n }\n\n const create = (checkpoint: Uint8Array | undefined) =>\n createFxAgent({\n apiKey: credential.token,\n model: kernelModel(step),\n ...(step.instructions.length === 0\n ? {}\n : { instructions: step.instructions }),\n tools: hostTools,\n ...(checkpoint === undefined ? {} : { checkpoint }),\n fetch: hostFetch,\n })\n\n const stream = new ReadableStream<UIMessageChunk>({\n start(streamController) {\n controller = streamController\n const abort = () => {\n stopped = true\n turn?.fxTurn.cancel()\n }\n step.signal.addEventListener('abort', abort, { once: true })\n void (async () => {\n try {\n try {\n agent = await create(read.saved)\n } catch (error) {\n if (read.saved === undefined) throw error\n // An unusable checkpoint: replay the whole log instead.\n read = await replayAll()\n results = read.results\n agent = await create(undefined)\n }\n await run(streamController, await plans(read.turns))\n closed = true\n streamController.close()\n } catch (error) {\n closed = true\n streamController.error(\n step.signal.aborted ? step.signal.reason : error,\n )\n } finally {\n released.abort()\n step.signal.removeEventListener('abort', abort)\n await agent?.close().catch(() => {})\n }\n })()\n },\n cancel() {\n closed = true\n stopped = true\n released.abort()\n turn?.fxTurn.cancel()\n },\n })\n\n return { stream, completion: () => completion }\n}\n\n// ------------------------------------------------------------ the plan\n\nasync function turnPlan(\n turn: HarnessTurn,\n signal: AbortSignal,\n): Promise<TurnPlan> {\n const calls: RecordedCall[] = []\n const steers = new Map<number, string[]>()\n turn.generations.forEach((generation, index) => {\n calls.push(...recordedCalls(generation.chunks, generation.completed))\n const inputs = turn.steers.get(index)\n // A generation's step is its last call.\n if (inputs !== undefined)\n steers.set(calls.length - 1, inputs.map(steerText))\n })\n const images = await harnessImages({\n harness: 'fx()',\n input: turn.prompt,\n signal,\n })\n return {\n prompt:\n images.length === 0\n ? turn.prompt.text\n : [\n ...images.map((image): FxPromptBlock => ({\n type: 'image',\n data: image.data,\n mimeType: image.mediaType,\n })),\n ...(turn.prompt.text === ''\n ? []\n : [{ type: 'text' as const, text: turn.prompt.text }]),\n ],\n calls,\n steers,\n end: turn.end,\n }\n}\n\n/** libfx steers with text alone, so a steer's files fail the step. */\nfunction steerText(input: HarnessInput): string {\n if (input.files.length > 0)\n throw new Error(\n 'fx() steers with text only: send files in a message of their own',\n )\n return input.text\n}\n\n/** A completed generation's model calls: its compactions, then its step. */\nfunction recordedCalls(\n chunks: readonly UIMessageChunk[],\n completed: GenerationCompletedPayload,\n): RecordedCall[] {\n const calls: RecordedCall[] = []\n const parts: StreamPart[] = []\n const toolCalls: ToolCall[] = []\n // A streamed call's input is the provider's exact text; the kernel copies it.\n const inputText = new Map<string, string>()\n for (const chunk of chunks) {\n const metadata =\n 'providerMetadata' in chunk && chunk.providerMetadata !== undefined\n ? { providerMetadata: chunk.providerMetadata }\n : {}\n switch (chunk.type) {\n case 'data-a2-compaction':\n calls.push({\n kind: 'compaction',\n sse: compactionSse(chunk.data as CompactionPartData),\n toolCalls: [],\n })\n break\n case 'text-start':\n case 'text-end':\n case 'reasoning-start':\n case 'reasoning-end':\n parts.push({ type: chunk.type, id: chunk.id, ...metadata })\n break\n case 'text-delta':\n case 'reasoning-delta':\n parts.push({\n type: chunk.type,\n id: chunk.id,\n delta: chunk.delta,\n ...metadata,\n })\n break\n case 'tool-input-start':\n inputText.set(chunk.toolCallId, '')\n parts.push({\n type: 'tool-input-start',\n id: chunk.toolCallId,\n toolName: chunk.toolName,\n ...metadata,\n })\n break\n case 'tool-input-delta':\n inputText.set(\n chunk.toolCallId,\n (inputText.get(chunk.toolCallId) ?? '') + chunk.inputTextDelta,\n )\n parts.push({\n type: 'tool-input-delta',\n id: chunk.toolCallId,\n delta: chunk.inputTextDelta,\n })\n break\n case 'tool-input-error':\n case 'tool-input-available': {\n // A call recorded without its text: input that failed to parse is\n // that text.\n const text =\n inputText.get(chunk.toolCallId) ||\n (chunk.type === 'tool-input-error' && typeof chunk.input === 'string'\n ? chunk.input\n : JSON.stringify(chunk.input))\n if (inputText.has(chunk.toolCallId))\n parts.push({ type: 'tool-input-end', id: chunk.toolCallId })\n // The kernel calls back with this text's JSON, not the input the\n // tool's schema made of it. A call it answers itself, such as one\n // whose text is not JSON, is never asked for.\n const input = parseJson(text)\n if (input !== undefined)\n toolCalls.push({\n toolCallId: chunk.toolCallId,\n toolName: chunk.toolName,\n input,\n })\n parts.push({\n type: 'tool-call',\n toolCallId: chunk.toolCallId,\n toolName: chunk.toolName,\n input: text,\n ...metadata,\n })\n break\n }\n }\n }\n parts.push({\n type: 'finish',\n finishReason: { unified: completed.finishReason ?? 'other' },\n usage: streamUsage(completed.usage),\n })\n calls.push({ kind: 'step', sse: sse(parts), toolCalls })\n return calls\n}\n\nfunction compactionSse(data: CompactionPartData): string {\n return sse([\n { type: 'text-start', id: '0' },\n { type: 'text-delta', id: '0', delta: data.summary ?? '' },\n { type: 'text-end', id: '0' },\n {\n type: 'finish',\n finishReason: { unified: 'stop' },\n usage: streamUsage(data.usage),\n },\n ])\n}\n\n// ------------------------------------------------------- live responses\n\n/** Streams one live step response as UI chunks and reports its finish. */\nasync function streamStep({\n body,\n tools,\n emit,\n finished,\n}: {\n body: ReadableStream<Uint8Array>\n tools: ToolSet\n emit: (chunk: UIMessageChunk) => void\n finished: (completion: Completion) => void\n}): Promise<void> {\n // The tool calls whose input started streaming, and whether text arrived.\n const streamed = new Map<string, boolean>()\n for await (const part of sseParts(body)) {\n const metadata =\n part['providerMetadata'] === undefined\n ? {}\n : { providerMetadata: part['providerMetadata'] as never }\n switch (part.type) {\n case 'text-start':\n case 'text-end':\n case 'reasoning-start':\n case 'reasoning-end':\n emit({ type: part.type, id: part['id'] as string, ...metadata })\n break\n case 'text-delta':\n case 'reasoning-delta':\n emit({\n type: part.type,\n id: part['id'] as string,\n delta: part['delta'] as string,\n ...metadata,\n })\n break\n case 'tool-input-start':\n streamed.set(part['id'] as string, false)\n emit({\n type: 'tool-input-start',\n toolCallId: part['id'] as string,\n toolName: part['toolName'] as string,\n ...metadata,\n })\n break\n case 'tool-input-delta':\n if (part['delta'] !== '') streamed.set(part['id'] as string, true)\n emit({\n type: 'tool-input-delta',\n toolCallId: part['id'] as string,\n inputTextDelta: part['delta'] as string,\n })\n break\n case 'tool-call': {\n const toolCallId = part['toolCallId'] as string\n const toolName = part['toolName'] as string\n const text = part['input'] as string\n // Replay hands the kernel the text the log holds, so the log keeps\n // the provider's text when it streamed none.\n if (streamed.get(toolCallId) !== true && text !== '') {\n if (!streamed.has(toolCallId))\n emit({ type: 'tool-input-start', toolCallId, toolName })\n emit({ type: 'tool-input-delta', toolCallId, inputTextDelta: text })\n }\n const input = await parseToolInput({\n tools,\n toolName,\n text,\n })\n emit(\n input.success\n ? {\n type: 'tool-input-available',\n toolCallId,\n toolName,\n input: input.value,\n ...metadata,\n }\n : {\n type: 'tool-input-error',\n toolCallId,\n toolName,\n input: input.input,\n errorText: input.errorText,\n ...metadata,\n },\n )\n break\n }\n case 'finish': {\n const reason = part['finishReason'] as { unified?: FinishReason }\n finished({\n ...(reason?.unified === undefined\n ? {}\n : { finishReason: reason.unified }),\n usage: modelUsage(part['usage']),\n })\n break\n }\n case 'error':\n emit({ type: 'error', errorText: errorText(part['error']) })\n break\n }\n }\n}\n\ntype ToolInput =\n | { success: true; value: unknown }\n | { success: false; input: unknown; errorText: string }\n\n/**\n * A live call's input as the AI SDK step reads it: JSON, then the tool's\n * schema with its defaults. Input that fails either is an input error, which\n * A2 answers without running the tool.\n */\nasync function parseToolInput({\n tools,\n toolName,\n text,\n}: {\n tools: ToolSet\n toolName: string\n text: string\n}): Promise<ToolInput> {\n let value: unknown\n try {\n value = JSON.parse(text)\n } catch (error) {\n return { success: false, input: text, errorText: errorText(error) }\n }\n const input = await schemaInput({ tools, toolName, value })\n return input.success ? input : { ...input, input: value }\n}\n\nasync function readParts(\n body: ReadableStream<Uint8Array>,\n): Promise<StreamPart[]> {\n const all: StreamPart[] = []\n for await (const part of sseParts(body)) all.push(part)\n return all\n}\n\nfunction compactionData(all: readonly StreamPart[]): CompactionPartData {\n const finish = all.find((part) => part.type === 'finish')\n return {\n status: 'completed',\n summary: all\n .filter((part) => part.type === 'text-delta')\n .map((part) => part['delta'] as string)\n .join(''),\n ...(finish === undefined ? {} : { usage: modelUsage(finish['usage']) }),\n }\n}\n\nasync function* sseParts(\n body: ReadableStream<Uint8Array>,\n): AsyncGenerator<StreamPart> {\n const decoder = new TextDecoder()\n let pendingText = ''\n for await (const chunk of body) {\n pendingText += decoder.decode(chunk, { stream: true })\n let end: number\n while ((end = pendingText.indexOf('\\n\\n')) !== -1) {\n const frame = pendingText.slice(0, end)\n pendingText = pendingText.slice(end + 2)\n if (frame.startsWith('data: {')) yield JSON.parse(frame.slice(6))\n }\n }\n}\n\n/**\n * Hands a recorded response to the kernel frame by frame. Steering due after\n * it is queued before its finish part, while the kernel still waits on it.\n */\nfunction relay(text: string, beforeFinish: () => Promise<void>): Response {\n const encoder = new TextEncoder()\n const frames = text.split(/(?<=\\n\\n)/)\n let index = 0\n let steered = false\n return new Response(\n new ReadableStream({\n async pull(controller) {\n const frame = frames[index++]\n if (frame === undefined) {\n controller.close()\n return\n }\n if (!steered && frame.includes('\"type\":\"finish\"')) {\n steered = true\n await beforeFinish()\n }\n controller.enqueue(encoder.encode(frame))\n },\n }),\n { headers: { 'content-type': 'text/event-stream' } },\n )\n}\n\n// ----------------------------------------------------------- the kernel\n\nasync function loadLibfx(): Promise<typeof import('libfx')> {\n try {\n return await import('libfx')\n } catch (error) {\n throw new Error('fx() needs the libfx package: install libfx', {\n cause: error,\n })\n }\n}\n\n/**\n * A model request with Gateway's automatic caching, as the AI SDK harness\n * asks for it; options the request already has take precedence. Every other\n * byte stays as the kernel wrote it, a tool input's spacing included.\n */\nfunction withGatewayCaching(body: string): string {\n const request = JSON.parse(body) as {\n providerOptions?: Record<string, Record<string, unknown> | undefined>\n }\n const options = request.providerOptions\n if (options === undefined)\n return body.replace(\n /^\\{/,\n '{\"providerOptions\":{\"gateway\":{\"caching\":\"auto\"}},',\n )\n // The kernel writes its own options last; any other shape goes unchanged.\n const own = `\"providerOptions\":${JSON.stringify(options)}}`\n if (!body.endsWith(own)) return body\n const merged = {\n ...options,\n gateway: { caching: 'auto', ...options['gateway'] },\n }\n return `${body.slice(0, -own.length)}\"providerOptions\":${JSON.stringify(merged)}}`\n}\n\nfunction withAuth(\n headers: HeadersInit | undefined,\n credential: GatewayCredential,\n): Headers {\n const result = new Headers(headers)\n result.set('ai-gateway-auth-method', credential.method)\n return result\n}\n\nfunction kernelModel(\n step: HarnessStep,\n): string | { id: string; effort?: string; fast?: boolean } {\n const effort =\n step.reasoning === undefined || step.reasoning === 'provider-default'\n ? undefined\n : step.reasoning\n const fast = step.speed === 'fast'\n if (effort === undefined && !fast) return step.model\n return {\n id: step.model,\n ...(effort === undefined ? {} : { effort }),\n ...(fast ? { fast } : {}),\n }\n}\n\nasync function kernelTools(\n tools: ToolSet,\n execute: (toolName: string, input: unknown) => unknown,\n): Promise<FxHostTool[]> {\n return Promise.all(\n Object.entries(tools).map(async ([toolName, tool]) => ({\n name: toolName,\n description:\n typeof tool.description === 'string' ? tool.description : toolName,\n inputSchema: (await asSchema(tool.inputSchema).jsonSchema) as object,\n execute: (input: unknown) => execute(toolName, input),\n })),\n )\n}\n\n// Keyed by credential, and Vercel's OIDC tokens rotate, so it is bounded.\nconst catalogCache = boundedCache<\n string,\n { expires: number; body: Promise<string> }\n>({ maxEntries: 16, maxBytes: 16 })\n\n/**\n * Model catalog reads, shared for an hour by every step in the process that\n * uses the same credential; credentials can see different catalogs.\n */\nasync function catalog(\n url: string,\n credential: GatewayCredential,\n headers: Headers,\n): Promise<Response> {\n const key = `${url}\\n${credential.token}`\n const now = Date.now()\n let entry = catalogCache.get(key)\n if (entry === undefined || entry.expires <= now) {\n const body = fetch(url, { headers }).then(async (response) => {\n if (!response.ok)\n throw new Error(`catalog request failed: ${response.status}`)\n return response.text()\n })\n entry = { expires: now + CATALOG_TTL_MS, body }\n catalogCache.set(key, entry, 1)\n body.catch(() => catalogCache.delete(key))\n }\n return new Response(await entry.body, {\n headers: { 'content-type': 'application/json' },\n })\n}\n\n// ------------------------------------------------------------- helpers\n\nfunction savedCheckpoint(state: unknown): Uint8Array | undefined {\n if (typeof state !== 'object' || state === null) return undefined\n const { version, checkpoint } = state as Record<string, unknown>\n if (version !== SNAPSHOT_VERSION || typeof checkpoint !== 'string')\n return undefined\n return new Uint8Array(Buffer.from(checkpoint, 'base64'))\n}\n\nfunction isCompaction(body: string): boolean {\n const request = JSON.parse(body) as { toolChoice?: { type?: string } }\n return request.toolChoice?.type === 'none'\n}\n\nfunction decodeBody(body: BodyInit | null | undefined): string {\n if (typeof body === 'string') return body\n if (body instanceof Uint8Array || body instanceof ArrayBuffer)\n return new TextDecoder().decode(body)\n throw new TypeError('FX sent an unexpected request body')\n}\n\nfunction parseJson(text: string): unknown {\n try {\n return JSON.parse(text)\n } catch {\n return undefined\n }\n}\n\nfunction sameJson(a: unknown, b: unknown): boolean {\n return JSON.stringify(a) === JSON.stringify(b)\n}\n\nfunction sse(all: readonly object[]): string {\n return (\n all.map((part) => `data: ${JSON.stringify(part)}\\n\\n`).join('') +\n 'data: [DONE]\\n\\n'\n )\n}\n\nfunction errorText(error: unknown): string {\n if (typeof error === 'string') return error\n if (error instanceof Error) return error.message\n return JSON.stringify(error)\n}\n\ntype StreamUsage = {\n inputTokens?: {\n total?: number | undefined\n noCache?: number | undefined\n cacheRead?: number | undefined\n cacheWrite?: number | undefined\n }\n outputTokens?: {\n total?: number | undefined\n text?: number | undefined\n reasoning?: number | undefined\n }\n}\n\n/** A Gateway stream usage, as AI SDK normalizes it. */\nfunction modelUsage(value: unknown): LanguageModelUsage {\n const usage = (value ?? {}) as StreamUsage\n const input = usage.inputTokens ?? {}\n const output = usage.outputTokens ?? {}\n return {\n inputTokens: input.total,\n inputTokenDetails: {\n noCacheTokens: input.noCache,\n cacheReadTokens: input.cacheRead,\n cacheWriteTokens: input.cacheWrite,\n },\n outputTokens: output.total,\n outputTokenDetails: {\n textTokens: output.text,\n reasoningTokens: output.reasoning,\n },\n totalTokens:\n input.total === undefined && output.total === undefined\n ? undefined\n : (input.total ?? 0) + (output.total ?? 0),\n }\n}\n\n/** The inverse of `modelUsage`, for replay. */\nfunction streamUsage(usage: LanguageModelUsage | undefined): StreamUsage {\n return {\n inputTokens: {\n total: usage?.inputTokens,\n noCache: usage?.inputTokenDetails?.noCacheTokens,\n cacheRead: usage?.inputTokenDetails?.cacheReadTokens,\n cacheWrite: usage?.inputTokenDetails?.cacheWriteTokens,\n },\n outputTokens: {\n total: usage?.outputTokens,\n text: usage?.outputTokenDetails?.textTokens,\n reasoning: usage?.outputTokenDetails?.reasoningTokens,\n },\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ai-fx.js","names":[],"sources":["../src/ai-fx.ts"],"sourcesContent":["/**\n * experimental-a2/ai/fx — `fx()`: the FX harness for `agent({ harness })`.\n * Server-only; FX's kernel comes from the optional `libfx` peer dependency.\n */\n\n// oxlint-disable no-await-in-loop -- turns replay in order, each on the kernel the last one left.\n\nimport {\n asSchema,\n type FinishReason,\n type LanguageModelUsage,\n type ToolSet,\n type UIMessageChunk,\n} from 'ai'\nimport type { FxAgent, FxHostTool, FxPromptBlock, FxTurn } from 'libfx'\nimport {\n type Deferred,\n defineHarness,\n deferred,\n Divergence,\n gatewayCredential,\n type GatewayCredential,\n gatewaySessionAffinity,\n type Harness,\n type HarnessGeneration,\n harnessImages,\n type HarnessInput,\n type HarnessRead,\n type HarnessStep,\n type HarnessTurn,\n pending,\n readHarness,\n saveHarness,\n} from './ai-harness.ts'\nimport type { CompactionPartData, GenerationCompletedPayload } from './ai.ts'\nimport { schemaInput } from './ai-tool-input.ts'\nimport { HARNESS_PARKING } from './internal.ts'\nimport { boundedCache } from './bounded-cache.ts'\n\nexport type { Harness } from './ai-harness.ts'\n\n/**\n * The FX harness. Every model call FX makes is one durable step; A2 rebuilds\n * the kernel for each step from its latest checkpoint and the log since,\n * unless this process holds the kernel the step before left waiting on its\n * tool calls.\n */\nexport function fx(): Harness<'fx', never> {\n // Per agent, as its saved states are: sessions of two agents never share one.\n const lots = new WeakMap<object, ParkingLot>()\n const lotOf = (scope: object): ParkingLot => {\n let lot = lots.get(scope)\n if (lot === undefined) {\n lot = parkingLot()\n lots.set(scope, lot)\n }\n return lot\n }\n return defineHarness('fx', {\n tools: {},\n step: (step) => runStep(step, lotOf(step.scope)),\n release: ({ scope, sessionId }) => {\n const entry = lots.get(scope)?.take(sessionId)\n if (entry !== undefined) closeParked(entry)\n },\n })\n}\n\nconst SNAPSHOT_VERSION = 1\nconst CATALOG_TTL_MS = 60 * 60 * 1000\n/** The Gateway model catalog libfx reads for each new kernel. */\nconst CATALOG_PATH = '/coding-agent/v1/models'\n\ntype StreamPart = { type: string } & Record<string, unknown>\n/** A recorded tool call, with the input the kernel reads from its text. */\ntype ToolCall = { toolCallId: string; toolName: string; input: unknown }\n\n/** A model response A2 recorded, in the Gateway's stream format. */\ntype RecordedCall = {\n kind: 'step' | 'compaction'\n sse: string\n toolCalls: ToolCall[]\n}\n\n/** One FX turn to replay: its prompt, recorded calls, and how it ended. */\ntype TurnPlan = {\n prompt: string | FxPromptBlock[]\n calls: RecordedCall[]\n /** Steering to queue while serving the call at that index. */\n steers: Map<number, string[]>\n end: 'stop' | 'interrupted' | 'live'\n /** The attempt that made its last committed model call. */\n generationId?: string\n}\n\ntype Completion = { finishReason?: FinishReason; usage?: LanguageModelUsage }\n\n/** The turn being replayed or run live, as the fetch and tool hooks see it. */\ntype TurnState = {\n plan: TurnPlan\n fxTurn: FxTurn\n next: number\n /** Tool calls of the last served step and the ones already answered. */\n served: ToolCall[]\n answered: Set<string>\n live: boolean\n liveStep: boolean\n failure?: Error\n /** Resolves when the live step is over: FX asked for its tools. */\n toolsRequested: Deferred\n parsed?: Promise<void>\n}\n\n/** A model request as the kernel sends it to the Gateway. */\ntype GatewayRequest = {\n toolChoice?: { type?: string }\n providerOptions?: Record<string, Record<string, unknown> | undefined>\n}\n\nfunction fail(state: TurnState, error: Error): never {\n state.failure ??= error\n state.fxTurn.cancel()\n throw state.failure\n}\n\nconst diverged = (message: string): Divergence => new Divergence('FX', message)\n\n// ------------------------------------------------------------- kernels\n\n/** A tool call the kernel waits on while A2's tool consumer runs it. */\ntype Waiting = {\n toolName: string\n input: unknown\n resolve: (output: unknown) => void\n reject: (error: unknown) => void\n}\n\n/** What a kernel's fetch and tools call: the step driving it now. */\ntype KernelHooks = {\n fetch: (\n input: string | URL | Request,\n init?: RequestInit,\n ) => Promise<Response>\n tool: (toolName: string, input: unknown) => unknown\n}\n\ntype Kernel = { agent: FxAgent; hooks: KernelHooks; waiting: Waiting[] }\n\n/**\n * A kernel a step left waiting on its tool calls. The session's next step in\n * this process, in the same response or one that steered it, continues it\n * with their results instead of rebuilding one from the log.\n */\ntype Parked = {\n kernel: Kernel\n fxTurn: FxTurn\n drained: Promise<void>\n /** The attempt whose model call the kernel made. */\n generationId: string\n settings: string\n}\n\ntype ParkingLot = {\n /** Removes the session's parked kernel; the caller then owns it. */\n take(sessionId: string): Parked | undefined\n park(sessionId: string, entry: Parked): void\n}\n\nfunction closeParked(entry: Parked): void {\n entry.fxTurn.cancel()\n void entry.kernel.agent.close().catch(() => {})\n}\n\n/**\n * One agent's parked kernels: one per session, at most `maxKernels`. Each is\n * closed when its session has nothing left to run in this process (the\n * agent's release), when its turn ends, or when another takes its place.\n */\nfunction parkingLot(): ParkingLot {\n const held = new Map<string, Parked>()\n const take = (sessionId: string): Parked | undefined => {\n const entry = held.get(sessionId)\n held.delete(sessionId)\n return entry\n }\n return {\n take,\n park(sessionId, entry) {\n const replaced = take(sessionId)\n if (replaced !== undefined) closeParked(replaced)\n held.set(sessionId, entry)\n // A turn that ends while it waits, as a cancelled one does, is done.\n const ended = () => {\n if (held.get(sessionId) !== entry) return\n held.delete(sessionId)\n closeParked(entry)\n }\n entry.fxTurn.result.then(ended, ended)\n const oldest = held.keys().next().value\n if (held.size > HARNESS_PARKING.maxKernels && oldest !== undefined) {\n const evicted = take(oldest)\n if (evicted !== undefined) closeParked(evicted)\n }\n },\n }\n}\n\n// ---------------------------------------------------------------- step\n\nasync function runStep(\n step: HarnessStep,\n lot: ParkingLot,\n): Promise<HarnessGeneration> {\n const { createFxAgent } = await loadLibfx()\n const credential = gatewayCredential('fx')\n const name = `${step.agentName}.a2.harness.fx`\n const definitions = await toolDefinitions(step.tools)\n // What the kernel is made with: one made otherwise can't continue the step.\n const settings = JSON.stringify([\n kernelModel(step),\n step.instructions,\n definitions,\n ])\n const { replayAll, ...first } = await readHarness({\n step,\n name,\n parse: savedCheckpoint,\n })\n let read: HarnessRead<Uint8Array> = first\n\n let completion: Completion = {}\n let stopped = false\n // Live responses belong to the step: the kernel aborts its fetch once it\n // has read a response's finish, while A2 may still be reading its copy.\n const released = new AbortController()\n const network = AbortSignal.any([step.signal, released.signal])\n let kernel: Kernel | undefined\n /** Whether this step left its kernel for the next one. */\n let parked = false\n let turn: TurnState | undefined\n\n const run = async (\n controller: ReadableStreamDefaultController<UIMessageChunk>,\n turns: TurnPlan[],\n adopted: Parked | undefined,\n ): Promise<void> => {\n controller.enqueue({ type: 'start', messageId: step.responseMessageId })\n if (adopted !== undefined) {\n await resume(controller, turns.at(-1)!, adopted)\n return\n }\n for (const plan of turns) {\n if (stopped || step.signal.aborted) return\n // The step's abort cancels the turn; the turn outlives it when parked.\n const fxTurn = kernel!.agent.prompt(plan.prompt)\n const state: TurnState = {\n plan,\n fxTurn,\n next: 0,\n served: [],\n answered: new Set(),\n live: false,\n liveStep: false,\n toolsRequested: deferred(),\n }\n turn = state\n const drained = (async () => {\n for await (const _ of fxTurn) {\n // Live output reaches A2 from the Gateway response itself.\n }\n })()\n // A failed turn surfaces through its result.\n drained.catch(() => {})\n if (plan.end !== 'live') {\n const result = await fxTurn.result.catch((error: unknown) => {\n throw state.failure ?? error\n })\n await drained\n if (state.failure) throw state.failure\n const expected = plan.end === 'stop' ? 'end_turn' : 'cancelled'\n if (result.stopReason !== expected)\n throw diverged(\n `a recorded turn ended with ${result.stopReason}, not ${expected}`,\n )\n continue\n }\n await finishLive(controller, state, drained)\n }\n }\n\n /** Continues a parked kernel: the results it waits on are in the log. */\n const resume = async (\n controller: ReadableStreamDefaultController<UIMessageChunk>,\n plan: TurnPlan,\n adopted: Parked,\n ): Promise<void> => {\n const state: TurnState = {\n plan,\n fxTurn: adopted.fxTurn,\n next: plan.calls.length,\n served: plan.calls.at(-1)!.toolCalls,\n answered: new Set(),\n live: false,\n liveStep: false,\n toolsRequested: deferred(),\n }\n turn = state\n // Steering goes in before any tool result, so FX's next model call can't\n // start without it, as replay queues it before the recorded call ends.\n for (const text of plan.steers.get(plan.calls.length - 1) ?? [])\n await adopted.fxTurn.steer(text)\n for (const waiting of kernel!.waiting.splice(0)) {\n try {\n const answered = answer(state, waiting.toolName, waiting.input)\n if (answered === undefined)\n fail(state, diverged(`no recorded result for ${waiting.toolName}`))\n waiting.resolve(answered.output)\n } catch (error) {\n waiting.reject(error)\n }\n }\n await finishLive(controller, state, adopted.drained)\n }\n\n const finishLive = async (\n controller: ReadableStreamDefaultController<UIMessageChunk>,\n state: TurnState,\n drained: Promise<void>,\n ): Promise<void> => {\n const outcome = await Promise.race([\n state.fxTurn.result.then((result) => ({ ended: true, result })),\n state.toolsRequested.promise.then(() => ({ ended: false as const })),\n ]).catch((error: unknown) => {\n throw state.failure ?? error\n })\n if (state.failure) throw state.failure\n await state.parsed\n if (outcome.ended) {\n if (\n outcome.result.stopReason === 'cancelled' &&\n !stopped &&\n !step.signal.aborted\n )\n throw diverged('the turn was cancelled while this step ran it')\n await drained\n // Only a turn this attempt finished describes the log.\n if (outcome.result.stopReason === 'end_turn' && !step.signal.aborted)\n await saveCheckpoint()\n } else if (kernel!.waiting.length > 0 && !stopped && !closed) {\n // FX now waits on tools that A2's tool consumer runs; the next step in\n // this process can answer it with their results.\n lot.park(step.sessionId, {\n kernel: kernel!,\n fxTurn: state.fxTurn,\n drained,\n generationId: step.generationId,\n settings,\n })\n parked = true\n } else {\n state.fxTurn.cancel()\n }\n controller.enqueue({ type: 'finish-step' })\n controller.enqueue({\n type: 'finish',\n ...(completion.finishReason === undefined\n ? {}\n : { finishReason: completion.finishReason }),\n })\n }\n\n /**\n * Whether a parked kernel stands where this step's log does: made the same\n * way, its model call the last one the log's live turn committed, and a\n * recorded result for each tool call it waits on. A steered response\n * continues the same turn, so its steps qualify too.\n */\n const adoptable = (entry: Parked, turns: TurnPlan[]): boolean => {\n const plan = turns.at(-1)\n if (\n entry.settings !== settings ||\n plan?.end !== 'live' ||\n plan.generationId !== entry.generationId\n )\n return false\n const unanswered = [...(plan.calls.at(-1)?.toolCalls ?? [])]\n return entry.kernel.waiting.every((waiting) => {\n const at = unanswered.findIndex(\n (call) =>\n call.toolName === waiting.toolName &&\n sameJson(call.input, waiting.input) &&\n results.has(call.toolCallId),\n )\n if (at === -1) return false\n unanswered.splice(at, 1)\n return true\n })\n }\n\n const saveCheckpoint = async (): Promise<void> => {\n let checkpoint: Uint8Array\n try {\n checkpoint = await kernel!.agent.checkpoint()\n } catch {\n // Later steps replay from the previous snapshot.\n return\n }\n await saveHarness({\n step,\n name,\n state: {\n version: SNAPSHOT_VERSION,\n checkpoint: Buffer.from(checkpoint).toString('base64'),\n },\n })\n }\n\n const hostFetch = async (\n input: string | URL | Request,\n init: RequestInit = {},\n ): Promise<Response> => {\n const url = input instanceof Request ? input.url : String(input)\n const method = (init.method ?? 'GET').toUpperCase()\n if (method === 'GET' && new URL(url).pathname === CATALOG_PATH)\n return catalog(url, credential, withAuth(init.headers, credential))\n if (method === 'GET') return fetch(input, init)\n const state = turn!\n if (state.failure) throw state.failure\n const body = decodeBody(init.body)\n const request = JSON.parse(body) as GatewayRequest\n const compaction = request.toolChoice?.type === 'none'\n const recorded = state.plan.calls[state.next]\n if (recorded !== undefined) {\n if ((recorded.kind === 'compaction') !== compaction)\n fail(state, diverged(`request ${state.next} is not a ${recorded.kind}`))\n const index = state.next++\n if (recorded.kind === 'step') {\n state.served = recorded.toolCalls\n state.answered = new Set()\n }\n const steers = state.plan.steers.get(index) ?? []\n return relay(recorded.sse, async () => {\n for (const text of steers) await state.fxTurn.steer(text)\n })\n }\n if (state.plan.end === 'stop')\n fail(state, diverged('FX continued a turn the log ended'))\n if (state.plan.end === 'interrupted') {\n state.fxTurn.cancel()\n throw new DOMException('The recorded turn was interrupted', 'AbortError')\n }\n if (state.liveStep && !compaction) {\n // One generation is one step; the next step is A2's next generation.\n state.toolsRequested.resolve()\n return pending(init.signal)\n }\n state.live = true\n return live(\n state,\n url,\n { ...init, body: withGatewayCaching(body, request) },\n compaction,\n )\n }\n\n const affinity = gatewaySessionAffinity({\n agentName: step.agentName,\n sessionId: step.sessionId,\n })\n let compactions = 0\n const live = async (\n state: TurnState,\n url: string,\n init: RequestInit,\n compaction: boolean,\n ): Promise<Response> => {\n const headers = withAuth(init.headers, credential)\n // The kernel's own affinity is new with every rebuild; A2's lasts the session.\n headers.set('x-session-affinity', affinity)\n const response = await fetch(url, { ...init, headers, signal: network })\n // Abandoned before its headers came: the kernel has moved on without it.\n if (init.signal?.aborted) {\n void response.body?.cancel()\n throw init.signal.reason\n }\n if (!response.ok || response.body === null) return response\n const [fromNetwork, forLog] = response.body.tee()\n const forKernel = init.signal\n ? fromNetwork.pipeThrough(new TransformStream(), { signal: init.signal })\n : fromNetwork\n // Live responses reach the log in request order: a compaction's part\n // precedes the step that follows it.\n const previous = state.parsed\n if (compaction) {\n // Unique across the response's generations: the transcript keys data parts by ID.\n const id = `fx-compaction:${step.requestIndex}:${compactions++}`\n state.parsed = (async () => {\n const all = await readParts(forLog)\n await previous\n emit({\n type: 'data-a2-compaction',\n id,\n data: compactionData(all),\n })\n })()\n } else {\n state.liveStep = true\n state.parsed = (async () => {\n await previous\n emit({ type: 'start-step' })\n await streamStep({\n body: forLog,\n tools: step.tools,\n emit,\n finished: (finished) => {\n completion = finished\n },\n })\n })()\n }\n // A stopped step abandons its parse; its failure surfaces through the turn.\n state.parsed.catch(() => {})\n return new Response(forKernel, {\n status: response.status,\n headers: response.headers,\n })\n }\n\n /** The recorded result of a call from the step the kernel was served. */\n const answer = (\n state: TurnState,\n toolName: string,\n toolInput: unknown,\n ): { output: unknown } | undefined => {\n const call = state.served.find(\n (candidate) =>\n candidate.toolName === toolName &&\n !state.answered.has(candidate.toolCallId) &&\n sameJson(candidate.input, toolInput),\n )\n const result = call === undefined ? undefined : results.get(call.toolCallId)\n if (call === undefined || result === undefined) return undefined\n state.answered.add(call.toolCallId)\n if (result.error !== undefined) throw new Error(result.error)\n if (result.denied === true) throw new Error('The tool call was denied.')\n return { output: result.output ?? null }\n }\n\n const hostTool = (toolName: string, toolInput: unknown): unknown => {\n const state = turn!\n if (state.failure) throw state.failure\n const answered = answer(state, toolName, toolInput)\n if (answered !== undefined) return answered.output\n if (state.live) {\n state.toolsRequested.resolve()\n return new Promise((resolve, reject) => {\n kernel!.waiting.push({ toolName, input: toolInput, resolve, reject })\n })\n }\n if (state.plan.end === 'interrupted') {\n state.fxTurn.cancel()\n return pending(undefined)\n }\n return fail(state, diverged(`no recorded result for ${toolName}`))\n }\n\n const hooks: KernelHooks = { fetch: hostFetch, tool: hostTool }\n const planTurns = (turns: HarnessTurn[]): Promise<TurnPlan[]> =>\n Promise.all(turns.map((recorded) => turnPlan(recorded, step.signal)))\n let results = read.results\n let controller: ReadableStreamDefaultController<UIMessageChunk> | undefined\n let closed = false\n const emit = (chunk: UIMessageChunk): void => {\n if (!closed) controller!.enqueue(chunk)\n }\n\n const create = async (\n checkpoint: Uint8Array | undefined,\n ): Promise<Kernel> => {\n const created = { hooks, waiting: [] as Waiting[] }\n const agent = await createFxAgent({\n apiKey: credential.token,\n model: kernelModel(step),\n ...(step.instructions.length === 0\n ? {}\n : { instructions: step.instructions }),\n tools: definitions.map((definition): FxHostTool => ({\n ...definition,\n execute: (toolInput: unknown) =>\n created.hooks.tool(definition.name, toolInput),\n })),\n ...(checkpoint === undefined ? {} : { checkpoint }),\n fetch: (input, init) => created.hooks.fetch(input, init),\n })\n return Object.assign(created, { agent })\n }\n\n const stream = new ReadableStream<UIMessageChunk>({\n start(streamController) {\n controller = streamController\n const abort = () => {\n stopped = true\n turn?.fxTurn.cancel()\n }\n step.signal.addEventListener('abort', abort, { once: true })\n void (async () => {\n try {\n let turns = await planTurns(read.turns)\n // An aborted step leaves a parked kernel for a later attempt.\n const candidate = step.signal.aborted\n ? undefined\n : lot.take(step.sessionId)\n const adopted =\n candidate !== undefined && adoptable(candidate, turns)\n ? candidate\n : undefined\n if (candidate !== undefined && adopted === undefined)\n closeParked(candidate)\n if (adopted !== undefined) {\n kernel = adopted.kernel\n kernel.hooks = hooks\n } else {\n try {\n kernel = await create(read.saved)\n } catch (error) {\n if (read.saved === undefined) throw error\n // An unusable checkpoint: replay the whole log instead.\n read = await replayAll()\n results = read.results\n kernel = await create(undefined)\n turns = await planTurns(read.turns)\n }\n }\n await run(streamController, turns, adopted)\n closed = true\n streamController.close()\n } catch (error) {\n closed = true\n streamController.error(\n step.signal.aborted ? step.signal.reason : error,\n )\n } finally {\n released.abort()\n step.signal.removeEventListener('abort', abort)\n if (!parked) await kernel?.agent.close().catch(() => {})\n }\n })()\n },\n cancel() {\n closed = true\n stopped = true\n released.abort()\n turn?.fxTurn.cancel()\n },\n })\n\n return { stream, completion: () => completion }\n}\n\n// ------------------------------------------------------------ the plan\n\nasync function turnPlan(\n turn: HarnessTurn,\n signal: AbortSignal,\n): Promise<TurnPlan> {\n const calls: RecordedCall[] = []\n const steers = new Map<number, string[]>()\n turn.generations.forEach((generation, index) => {\n calls.push(...recordedCalls(generation.chunks, generation.completed))\n const inputs = turn.steers.get(index)\n // A generation's step is its last call.\n if (inputs !== undefined)\n steers.set(calls.length - 1, inputs.map(steerText))\n })\n const images = await harnessImages({\n harness: 'fx()',\n input: turn.prompt,\n signal,\n })\n const generationId = turn.generations.at(-1)?.completed.generationId\n return {\n ...(generationId === undefined ? {} : { generationId }),\n prompt:\n images.length === 0\n ? turn.prompt.text\n : [\n ...images.map((image): FxPromptBlock => ({\n type: 'image',\n data: image.data,\n mimeType: image.mediaType,\n })),\n ...(turn.prompt.text === ''\n ? []\n : [{ type: 'text' as const, text: turn.prompt.text }]),\n ],\n calls,\n steers,\n end: turn.end,\n }\n}\n\n/** libfx steers with text alone, so a steer's files fail the step. */\nfunction steerText(input: HarnessInput): string {\n if (input.files.length > 0)\n throw new Error(\n 'fx() steers with text only: send files in a message of their own',\n )\n return input.text\n}\n\n/** A completed generation's model calls: its compactions, then its step. */\nfunction recordedCalls(\n chunks: readonly UIMessageChunk[],\n completed: GenerationCompletedPayload,\n): RecordedCall[] {\n const calls: RecordedCall[] = []\n const parts: StreamPart[] = []\n const toolCalls: ToolCall[] = []\n // A streamed call's input is the provider's exact text; the kernel copies it.\n const inputText = new Map<string, string>()\n for (const chunk of chunks) {\n const metadata =\n 'providerMetadata' in chunk && chunk.providerMetadata !== undefined\n ? { providerMetadata: chunk.providerMetadata }\n : {}\n switch (chunk.type) {\n case 'data-a2-compaction':\n calls.push({\n kind: 'compaction',\n sse: compactionSse(chunk.data as CompactionPartData),\n toolCalls: [],\n })\n break\n case 'text-start':\n case 'text-end':\n case 'reasoning-start':\n case 'reasoning-end':\n parts.push({ type: chunk.type, id: chunk.id, ...metadata })\n break\n case 'text-delta':\n case 'reasoning-delta':\n parts.push({\n type: chunk.type,\n id: chunk.id,\n delta: chunk.delta,\n ...metadata,\n })\n break\n case 'tool-input-start':\n inputText.set(chunk.toolCallId, '')\n parts.push({\n type: 'tool-input-start',\n id: chunk.toolCallId,\n toolName: chunk.toolName,\n ...metadata,\n })\n break\n case 'tool-input-delta':\n inputText.set(\n chunk.toolCallId,\n (inputText.get(chunk.toolCallId) ?? '') + chunk.inputTextDelta,\n )\n parts.push({\n type: 'tool-input-delta',\n id: chunk.toolCallId,\n delta: chunk.inputTextDelta,\n })\n break\n case 'tool-input-error':\n case 'tool-input-available': {\n // A call recorded without its text: input that failed to parse is\n // that text.\n const text =\n inputText.get(chunk.toolCallId) ||\n (chunk.type === 'tool-input-error' && typeof chunk.input === 'string'\n ? chunk.input\n : JSON.stringify(chunk.input))\n if (inputText.has(chunk.toolCallId))\n parts.push({ type: 'tool-input-end', id: chunk.toolCallId })\n // The kernel calls back with this text's JSON, not the input the\n // tool's schema made of it. A call it answers itself, such as one\n // whose text is not JSON, is never asked for.\n const input = parseJson(text)\n if (input !== undefined)\n toolCalls.push({\n toolCallId: chunk.toolCallId,\n toolName: chunk.toolName,\n input,\n })\n parts.push({\n type: 'tool-call',\n toolCallId: chunk.toolCallId,\n toolName: chunk.toolName,\n input: text,\n ...metadata,\n })\n break\n }\n }\n }\n parts.push({\n type: 'finish',\n finishReason: { unified: completed.finishReason ?? 'other' },\n usage: streamUsage(completed.usage),\n })\n calls.push({ kind: 'step', sse: sse(parts), toolCalls })\n return calls\n}\n\nfunction compactionSse(data: CompactionPartData): string {\n return sse([\n { type: 'text-start', id: '0' },\n { type: 'text-delta', id: '0', delta: data.summary ?? '' },\n { type: 'text-end', id: '0' },\n {\n type: 'finish',\n finishReason: { unified: 'stop' },\n usage: streamUsage(data.usage),\n },\n ])\n}\n\n// ------------------------------------------------------- live responses\n\n/** Streams one live step response as UI chunks and reports its finish. */\nasync function streamStep({\n body,\n tools,\n emit,\n finished,\n}: {\n body: ReadableStream<Uint8Array>\n tools: ToolSet\n emit: (chunk: UIMessageChunk) => void\n finished: (completion: Completion) => void\n}): Promise<void> {\n // The tool calls whose input started streaming, and whether text arrived.\n const streamed = new Map<string, boolean>()\n for await (const part of sseParts(body)) {\n const metadata =\n part['providerMetadata'] === undefined\n ? {}\n : { providerMetadata: part['providerMetadata'] as never }\n switch (part.type) {\n case 'text-start':\n case 'text-end':\n case 'reasoning-start':\n case 'reasoning-end':\n emit({ type: part.type, id: part['id'] as string, ...metadata })\n break\n case 'text-delta':\n case 'reasoning-delta':\n emit({\n type: part.type,\n id: part['id'] as string,\n delta: part['delta'] as string,\n ...metadata,\n })\n break\n case 'tool-input-start':\n streamed.set(part['id'] as string, false)\n emit({\n type: 'tool-input-start',\n toolCallId: part['id'] as string,\n toolName: part['toolName'] as string,\n ...metadata,\n })\n break\n case 'tool-input-delta':\n if (part['delta'] !== '') streamed.set(part['id'] as string, true)\n emit({\n type: 'tool-input-delta',\n toolCallId: part['id'] as string,\n inputTextDelta: part['delta'] as string,\n })\n break\n case 'tool-call': {\n const toolCallId = part['toolCallId'] as string\n const toolName = part['toolName'] as string\n const text = part['input'] as string\n // Replay hands the kernel the text the log holds, so the log keeps\n // the provider's text when it streamed none.\n if (streamed.get(toolCallId) !== true && text !== '') {\n if (!streamed.has(toolCallId))\n emit({ type: 'tool-input-start', toolCallId, toolName })\n emit({ type: 'tool-input-delta', toolCallId, inputTextDelta: text })\n }\n const input = await parseToolInput({\n tools,\n toolName,\n text,\n })\n emit(\n input.success\n ? {\n type: 'tool-input-available',\n toolCallId,\n toolName,\n input: input.value,\n ...metadata,\n }\n : {\n type: 'tool-input-error',\n toolCallId,\n toolName,\n input: input.input,\n errorText: input.errorText,\n ...metadata,\n },\n )\n break\n }\n case 'finish': {\n const reason = part['finishReason'] as { unified?: FinishReason }\n finished({\n ...(reason?.unified === undefined\n ? {}\n : { finishReason: reason.unified }),\n usage: modelUsage(part['usage']),\n })\n break\n }\n case 'error':\n emit({ type: 'error', errorText: errorText(part['error']) })\n break\n }\n }\n}\n\ntype ToolInput =\n | { success: true; value: unknown }\n | { success: false; input: unknown; errorText: string }\n\n/**\n * A live call's input as the AI SDK step reads it: JSON, then the tool's\n * schema with its defaults. Input that fails either is an input error, which\n * A2 answers without running the tool.\n */\nasync function parseToolInput({\n tools,\n toolName,\n text,\n}: {\n tools: ToolSet\n toolName: string\n text: string\n}): Promise<ToolInput> {\n let value: unknown\n try {\n value = JSON.parse(text)\n } catch (error) {\n return { success: false, input: text, errorText: errorText(error) }\n }\n const input = await schemaInput({ tools, toolName, value })\n return input.success ? input : { ...input, input: value }\n}\n\nasync function readParts(\n body: ReadableStream<Uint8Array>,\n): Promise<StreamPart[]> {\n const all: StreamPart[] = []\n for await (const part of sseParts(body)) all.push(part)\n return all\n}\n\nfunction compactionData(all: readonly StreamPart[]): CompactionPartData {\n const finish = all.find((part) => part.type === 'finish')\n return {\n status: 'completed',\n summary: all\n .filter((part) => part.type === 'text-delta')\n .map((part) => part['delta'] as string)\n .join(''),\n ...(finish === undefined ? {} : { usage: modelUsage(finish['usage']) }),\n }\n}\n\nasync function* sseParts(\n body: ReadableStream<Uint8Array>,\n): AsyncGenerator<StreamPart> {\n const decoder = new TextDecoder()\n let pendingText = ''\n for await (const chunk of body) {\n pendingText += decoder.decode(chunk, { stream: true })\n let end: number\n while ((end = pendingText.indexOf('\\n\\n')) !== -1) {\n const frame = pendingText.slice(0, end)\n pendingText = pendingText.slice(end + 2)\n if (frame.startsWith('data: {')) yield JSON.parse(frame.slice(6))\n }\n }\n}\n\n/**\n * Hands a recorded response to the kernel frame by frame. Steering due after\n * it is queued before its finish part, while the kernel still waits on it.\n */\nfunction relay(text: string, beforeFinish: () => Promise<void>): Response {\n const encoder = new TextEncoder()\n const frames = text.split(/(?<=\\n\\n)/)\n let index = 0\n let steered = false\n return new Response(\n new ReadableStream({\n async pull(controller) {\n const frame = frames[index++]\n if (frame === undefined) {\n controller.close()\n return\n }\n if (!steered && frame.includes('\"type\":\"finish\"')) {\n steered = true\n await beforeFinish()\n }\n controller.enqueue(encoder.encode(frame))\n },\n }),\n { headers: { 'content-type': 'text/event-stream' } },\n )\n}\n\n// ----------------------------------------------------------- the kernel\n\nasync function loadLibfx(): Promise<typeof import('libfx')> {\n try {\n return await import('libfx')\n } catch (error) {\n throw new Error('fx() needs the libfx package: install libfx', {\n cause: error,\n })\n }\n}\n\n/**\n * A model request with Gateway's automatic caching, as the AI SDK harness\n * asks for it; options the request already has take precedence. Every other\n * byte stays as the kernel wrote it, a tool input's spacing included.\n */\nfunction withGatewayCaching(body: string, request: GatewayRequest): string {\n const options = request.providerOptions\n if (options === undefined)\n return body.replace(\n /^\\{/,\n '{\"providerOptions\":{\"gateway\":{\"caching\":\"auto\"}},',\n )\n // The kernel writes its own options last; any other shape goes unchanged.\n const own = `\"providerOptions\":${JSON.stringify(options)}}`\n if (!body.endsWith(own)) return body\n const merged = {\n ...options,\n gateway: { caching: 'auto', ...options['gateway'] },\n }\n return `${body.slice(0, -own.length)}\"providerOptions\":${JSON.stringify(merged)}}`\n}\n\nfunction withAuth(\n headers: HeadersInit | undefined,\n credential: GatewayCredential,\n): Headers {\n const result = new Headers(headers)\n result.set('ai-gateway-auth-method', credential.method)\n // A parked kernel keeps the credential it was made with; the step's is current.\n if (result.has('authorization'))\n result.set('authorization', `Bearer ${credential.token}`)\n return result\n}\n\nfunction kernelModel(\n step: HarnessStep,\n): string | { id: string; effort?: string; fast?: boolean } {\n const effort =\n step.reasoning === undefined || step.reasoning === 'provider-default'\n ? undefined\n : step.reasoning\n const fast = step.speed === 'fast'\n if (effort === undefined && !fast) return step.model\n return {\n id: step.model,\n ...(effort === undefined ? {} : { effort }),\n ...(fast ? { fast } : {}),\n }\n}\n\n/** The agent's tools as the kernel takes them, without their execution. */\nasync function toolDefinitions(\n tools: ToolSet,\n): Promise<{ name: string; description: string; inputSchema: object }[]> {\n return Promise.all(\n Object.entries(tools).map(async ([toolName, tool]) => ({\n name: toolName,\n description:\n typeof tool.description === 'string' ? tool.description : toolName,\n inputSchema: (await asSchema(tool.inputSchema).jsonSchema) as object,\n })),\n )\n}\n\n// Keyed by credential, and Vercel's OIDC tokens rotate, so it is bounded.\nconst catalogCache = boundedCache<\n string,\n { expires: number; body: Promise<string> }\n>({ maxEntries: 16, maxBytes: 16 })\n\n/**\n * Model catalog reads, shared for an hour by every step in the process that\n * uses the same credential; credentials can see different catalogs.\n */\nasync function catalog(\n url: string,\n credential: GatewayCredential,\n headers: Headers,\n): Promise<Response> {\n const key = `${url}\\n${credential.token}`\n const now = Date.now()\n let entry = catalogCache.get(key)\n if (entry === undefined || entry.expires <= now) {\n const body = fetch(url, { headers }).then(async (response) => {\n if (!response.ok)\n throw new Error(`catalog request failed: ${response.status}`)\n return response.text()\n })\n entry = { expires: now + CATALOG_TTL_MS, body }\n catalogCache.set(key, entry, 1)\n body.catch(() => catalogCache.delete(key))\n }\n return new Response(await entry.body, {\n headers: { 'content-type': 'application/json' },\n })\n}\n\n// ------------------------------------------------------------- helpers\n\nfunction savedCheckpoint(state: unknown): Uint8Array | undefined {\n if (typeof state !== 'object' || state === null) return undefined\n const { version, checkpoint } = state as Record<string, unknown>\n if (version !== SNAPSHOT_VERSION || typeof checkpoint !== 'string')\n return undefined\n return new Uint8Array(Buffer.from(checkpoint, 'base64'))\n}\n\nfunction decodeBody(body: BodyInit | null | undefined): string {\n if (typeof body === 'string') return body\n if (body instanceof Uint8Array || body instanceof ArrayBuffer)\n return new TextDecoder().decode(body)\n throw new TypeError('FX sent an unexpected request body')\n}\n\nfunction parseJson(text: string): unknown {\n try {\n return JSON.parse(text)\n } catch {\n return undefined\n }\n}\n\nfunction sameJson(a: unknown, b: unknown): boolean {\n return JSON.stringify(a) === JSON.stringify(b)\n}\n\nfunction sse(all: readonly object[]): string {\n return (\n all.map((part) => `data: ${JSON.stringify(part)}\\n\\n`).join('') +\n 'data: [DONE]\\n\\n'\n )\n}\n\nfunction errorText(error: unknown): string {\n if (typeof error === 'string') return error\n if (error instanceof Error) return error.message\n return JSON.stringify(error)\n}\n\ntype StreamUsage = {\n inputTokens?: {\n total?: number | undefined\n noCache?: number | undefined\n cacheRead?: number | undefined\n cacheWrite?: number | undefined\n }\n outputTokens?: {\n total?: number | undefined\n text?: number | undefined\n reasoning?: number | undefined\n }\n}\n\n/** A Gateway stream usage, as AI SDK normalizes it. */\nfunction modelUsage(value: unknown): LanguageModelUsage {\n const usage = (value ?? {}) as StreamUsage\n const input = usage.inputTokens ?? {}\n const output = usage.outputTokens ?? {}\n return {\n inputTokens: input.total,\n inputTokenDetails: {\n noCacheTokens: input.noCache,\n cacheReadTokens: input.cacheRead,\n cacheWriteTokens: input.cacheWrite,\n },\n outputTokens: output.total,\n outputTokenDetails: {\n textTokens: output.text,\n reasoningTokens: output.reasoning,\n },\n totalTokens:\n input.total === undefined && output.total === undefined\n ? undefined\n : (input.total ?? 0) + (output.total ?? 0),\n }\n}\n\n/** The inverse of `modelUsage`, for replay. */\nfunction streamUsage(usage: LanguageModelUsage | undefined): StreamUsage {\n return {\n inputTokens: {\n total: usage?.inputTokens,\n noCache: usage?.inputTokenDetails?.noCacheTokens,\n cacheRead: usage?.inputTokenDetails?.cacheReadTokens,\n cacheWrite: usage?.inputTokenDetails?.cacheWriteTokens,\n },\n outputTokens: {\n total: usage?.outputTokens,\n text: usage?.outputTokenDetails?.textTokens,\n reasoning: usage?.outputTokenDetails?.reasoningTokens,\n },\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA+CA,SAAgB,KAA2B;CAEzC,MAAM,uBAAO,IAAI,QAA4B;CAC7C,MAAM,SAAS,UAA8B;EAC3C,IAAI,MAAM,KAAK,IAAI,KAAK;EACxB,IAAI,QAAQ,KAAA,GAAW;GACrB,MAAM,WAAW;GACjB,KAAK,IAAI,OAAO,GAAG;EACrB;EACA,OAAO;CACT;CACA,OAAO,cAAc,MAAM;EACzB,OAAO,CAAC;EACR,OAAO,SAAS,QAAQ,MAAM,MAAM,KAAK,KAAK,CAAC;EAC/C,UAAU,EAAE,OAAO,gBAAgB;GACjC,MAAM,QAAQ,KAAK,IAAI,KAAK,CAAC,EAAE,KAAK,SAAS;GAC7C,IAAI,UAAU,KAAA,GAAW,YAAY,KAAK;EAC5C;CACF,CAAC;AACH;AAEA,MAAM,mBAAmB;AACzB,MAAM,iBAAiB;;AAEvB,MAAM,eAAe;AAgDrB,SAAS,KAAK,OAAkB,OAAqB;CACnD,MAAM,YAAY;CAClB,MAAM,OAAO,OAAO;CACpB,MAAM,MAAM;AACd;AAEA,MAAM,YAAY,YAAgC,IAAI,WAAW,MAAM,OAAO;AA2C9E,SAAS,YAAY,OAAqB;CACxC,MAAM,OAAO,OAAO;CACpB,MAAW,OAAO,MAAM,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;AAChD;;;;;;AAOA,SAAS,aAAyB;CAChC,MAAM,uBAAO,IAAI,IAAoB;CACrC,MAAM,QAAQ,cAA0C;EACtD,MAAM,QAAQ,KAAK,IAAI,SAAS;EAChC,KAAK,OAAO,SAAS;EACrB,OAAO;CACT;CACA,OAAO;EACL;EACA,KAAK,WAAW,OAAO;GACrB,MAAM,WAAW,KAAK,SAAS;GAC/B,IAAI,aAAa,KAAA,GAAW,YAAY,QAAQ;GAChD,KAAK,IAAI,WAAW,KAAK;GAEzB,MAAM,cAAc;IAClB,IAAI,KAAK,IAAI,SAAS,MAAM,OAAO;IACnC,KAAK,OAAO,SAAS;IACrB,YAAY,KAAK;GACnB;GACA,MAAM,OAAO,OAAO,KAAK,OAAO,KAAK;GACrC,MAAM,SAAS,KAAK,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC;GAClC,IAAI,KAAK,OAAO,gBAAgB,cAAc,WAAW,KAAA,GAAW;IAClE,MAAM,UAAU,KAAK,MAAM;IAC3B,IAAI,YAAY,KAAA,GAAW,YAAY,OAAO;GAChD;EACF;CACF;AACF;AAIA,eAAe,QACb,MACA,KAC4B;CAC5B,MAAM,EAAE,kBAAkB,MAAM,UAAU;CAC1C,MAAM,aAAa,kBAAkB,IAAI;CACzC,MAAM,OAAO,GAAG,KAAK,UAAU;CAC/B,MAAM,cAAc,MAAM,gBAAgB,KAAK,KAAK;CAEpD,MAAM,WAAW,KAAK,UAAU;EAC9B,YAAY,IAAI;EAChB,KAAK;EACL;CACF,CAAC;CACD,MAAM,EAAE,WAAW,GAAG,UAAU,MAAM,YAAY;EAChD;EACA;EACA,OAAO;CACT,CAAC;CACD,IAAI,OAAgC;CAEpC,IAAI,aAAyB,CAAC;CAC9B,IAAI,UAAU;CAGd,MAAM,WAAW,IAAI,gBAAgB;CACrC,MAAM,UAAU,YAAY,IAAI,CAAC,KAAK,QAAQ,SAAS,MAAM,CAAC;CAC9D,IAAI;;CAEJ,IAAI,SAAS;CACb,IAAI;CAEJ,MAAM,MAAM,OACV,YACA,OACA,YACkB;EAClB,WAAW,QAAQ;GAAE,MAAM;GAAS,WAAW,KAAK;EAAkB,CAAC;EACvE,IAAI,YAAY,KAAA,GAAW;GACzB,MAAM,OAAO,YAAY,MAAM,GAAG,EAAE,GAAI,OAAO;GAC/C;EACF;EACA,KAAK,MAAM,QAAQ,OAAO;GACxB,IAAI,WAAW,KAAK,OAAO,SAAS;GAEpC,MAAM,SAAS,OAAQ,MAAM,OAAO,KAAK,MAAM;GAC/C,MAAM,QAAmB;IACvB;IACA;IACA,MAAM;IACN,QAAQ,CAAC;IACT,0BAAU,IAAI,IAAI;IAClB,MAAM;IACN,UAAU;IACV,gBAAgB,SAAS;GAC3B;GACA,OAAO;GACP,MAAM,WAAW,YAAY;IAC3B,WAAW,MAAM,KAAK;GAGxB,EAAA,CAAG;GAEH,QAAQ,YAAY,CAAC,CAAC;GACtB,IAAI,KAAK,QAAQ,QAAQ;IACvB,MAAM,SAAS,MAAM,OAAO,OAAO,OAAO,UAAmB;KAC3D,MAAM,MAAM,WAAW;IACzB,CAAC;IACD,MAAM;IACN,IAAI,MAAM,SAAS,MAAM,MAAM;IAC/B,MAAM,WAAW,KAAK,QAAQ,SAAS,aAAa;IACpD,IAAI,OAAO,eAAe,UACxB,MAAM,SACJ,8BAA8B,OAAO,WAAW,QAAQ,UAC1D;IACF;GACF;GACA,MAAM,WAAW,YAAY,OAAO,OAAO;EAC7C;CACF;;CAGA,MAAM,SAAS,OACb,YACA,MACA,YACkB;EAClB,MAAM,QAAmB;GACvB;GACA,QAAQ,QAAQ;GAChB,MAAM,KAAK,MAAM;GACjB,QAAQ,KAAK,MAAM,GAAG,EAAE,CAAC,CAAE;GAC3B,0BAAU,IAAI,IAAI;GAClB,MAAM;GACN,UAAU;GACV,gBAAgB,SAAS;EAC3B;EACA,OAAO;EAGP,KAAK,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,MAAM,SAAS,CAAC,KAAK,CAAC,GAC5D,MAAM,QAAQ,OAAO,MAAM,IAAI;EACjC,KAAK,MAAM,WAAW,OAAQ,QAAQ,OAAO,CAAC,GAC5C,IAAI;GACF,MAAM,WAAW,OAAO,OAAO,QAAQ,UAAU,QAAQ,KAAK;GAC9D,IAAI,aAAa,KAAA,GACf,KAAK,OAAO,SAAS,0BAA0B,QAAQ,UAAU,CAAC;GACpE,QAAQ,QAAQ,SAAS,MAAM;EACjC,SAAS,OAAO;GACd,QAAQ,OAAO,KAAK;EACtB;EAEF,MAAM,WAAW,YAAY,OAAO,QAAQ,OAAO;CACrD;CAEA,MAAM,aAAa,OACjB,YACA,OACA,YACkB;EAClB,MAAM,UAAU,MAAM,QAAQ,KAAK,CACjC,MAAM,OAAO,OAAO,MAAM,YAAY;GAAE,OAAO;GAAM;EAAO,EAAE,GAC9D,MAAM,eAAe,QAAQ,YAAY,EAAE,OAAO,MAAe,EAAE,CACrE,CAAC,CAAC,CAAC,OAAO,UAAmB;GAC3B,MAAM,MAAM,WAAW;EACzB,CAAC;EACD,IAAI,MAAM,SAAS,MAAM,MAAM;EAC/B,MAAM,MAAM;EACZ,IAAI,QAAQ,OAAO;GACjB,IACE,QAAQ,OAAO,eAAe,eAC9B,CAAC,WACD,CAAC,KAAK,OAAO,SAEb,MAAM,SAAS,+CAA+C;GAChE,MAAM;GAEN,IAAI,QAAQ,OAAO,eAAe,cAAc,CAAC,KAAK,OAAO,SAC3D,MAAM,eAAe;EACzB,OAAO,IAAI,OAAQ,QAAQ,SAAS,KAAK,CAAC,WAAW,CAAC,QAAQ;GAG5D,IAAI,KAAK,KAAK,WAAW;IACf;IACR,QAAQ,MAAM;IACd;IACA,cAAc,KAAK;IACnB;GACF,CAAC;GACD,SAAS;EACX,OACE,MAAM,OAAO,OAAO;EAEtB,WAAW,QAAQ,EAAE,MAAM,cAAc,CAAC;EAC1C,WAAW,QAAQ;GACjB,MAAM;GACN,GAAI,WAAW,iBAAiB,KAAA,IAC5B,CAAC,IACD,EAAE,cAAc,WAAW,aAAa;EAC9C,CAAC;CACH;;;;;;;CAQA,MAAM,aAAa,OAAe,UAA+B;EAC/D,MAAM,OAAO,MAAM,GAAG,EAAE;EACxB,IACE,MAAM,aAAa,YACnB,MAAM,QAAQ,UACd,KAAK,iBAAiB,MAAM,cAE5B,OAAO;EACT,MAAM,aAAa,CAAC,GAAI,KAAK,MAAM,GAAG,EAAE,CAAC,EAAE,aAAa,CAAC,CAAE;EAC3D,OAAO,MAAM,OAAO,QAAQ,OAAO,YAAY;GAC7C,MAAM,KAAK,WAAW,WACnB,SACC,KAAK,aAAa,QAAQ,YAC1B,SAAS,KAAK,OAAO,QAAQ,KAAK,KAClC,QAAQ,IAAI,KAAK,UAAU,CAC/B;GACA,IAAI,OAAO,IAAI,OAAO;GACtB,WAAW,OAAO,IAAI,CAAC;GACvB,OAAO;EACT,CAAC;CACH;CAEA,MAAM,iBAAiB,YAA2B;EAChD,IAAI;EACJ,IAAI;GACF,aAAa,MAAM,OAAQ,MAAM,WAAW;EAC9C,QAAQ;GAEN;EACF;EACA,MAAM,YAAY;GAChB;GACA;GACA,OAAO;IACL,SAAS;IACT,YAAY,OAAO,KAAK,UAAU,CAAC,CAAC,SAAS,QAAQ;GACvD;EACF,CAAC;CACH;CAEA,MAAM,YAAY,OAChB,OACA,OAAoB,CAAC,MACC;EACtB,MAAM,MAAM,iBAAiB,UAAU,MAAM,MAAM,OAAO,KAAK;EAC/D,MAAM,UAAU,KAAK,UAAU,MAAA,CAAO,YAAY;EAClD,IAAI,WAAW,SAAS,IAAI,IAAI,GAAG,CAAC,CAAC,aAAa,cAChD,OAAO,QAAQ,KAAK,YAAY,SAAS,KAAK,SAAS,UAAU,CAAC;EACpE,IAAI,WAAW,OAAO,OAAO,MAAM,OAAO,IAAI;EAC9C,MAAM,QAAQ;EACd,IAAI,MAAM,SAAS,MAAM,MAAM;EAC/B,MAAM,OAAO,WAAW,KAAK,IAAI;EACjC,MAAM,UAAU,KAAK,MAAM,IAAI;EAC/B,MAAM,aAAa,QAAQ,YAAY,SAAS;EAChD,MAAM,WAAW,MAAM,KAAK,MAAM,MAAM;EACxC,IAAI,aAAa,KAAA,GAAW;GAC1B,IAAK,SAAS,SAAS,iBAAkB,YACvC,KAAK,OAAO,SAAS,WAAW,MAAM,KAAK,YAAY,SAAS,MAAM,CAAC;GACzE,MAAM,QAAQ,MAAM;GACpB,IAAI,SAAS,SAAS,QAAQ;IAC5B,MAAM,SAAS,SAAS;IACxB,MAAM,2BAAW,IAAI,IAAI;GAC3B;GACA,MAAM,SAAS,MAAM,KAAK,OAAO,IAAI,KAAK,KAAK,CAAC;GAChD,OAAO,MAAM,SAAS,KAAK,YAAY;IACrC,KAAK,MAAM,QAAQ,QAAQ,MAAM,MAAM,OAAO,MAAM,IAAI;GAC1D,CAAC;EACH;EACA,IAAI,MAAM,KAAK,QAAQ,QACrB,KAAK,OAAO,SAAS,mCAAmC,CAAC;EAC3D,IAAI,MAAM,KAAK,QAAQ,eAAe;GACpC,MAAM,OAAO,OAAO;GACpB,MAAM,IAAI,aAAa,qCAAqC,YAAY;EAC1E;EACA,IAAI,MAAM,YAAY,CAAC,YAAY;GAEjC,MAAM,eAAe,QAAQ;GAC7B,OAAO,QAAQ,KAAK,MAAM;EAC5B;EACA,MAAM,OAAO;EACb,OAAO,KACL,OACA,KACA;GAAE,GAAG;GAAM,MAAM,mBAAmB,MAAM,OAAO;EAAE,GACnD,UACF;CACF;CAEA,MAAM,WAAW,uBAAuB;EACtC,WAAW,KAAK;EAChB,WAAW,KAAK;CAClB,CAAC;CACD,IAAI,cAAc;CAClB,MAAM,OAAO,OACX,OACA,KACA,MACA,eACsB;EACtB,MAAM,UAAU,SAAS,KAAK,SAAS,UAAU;EAEjD,QAAQ,IAAI,sBAAsB,QAAQ;EAC1C,MAAM,WAAW,MAAM,MAAM,KAAK;GAAE,GAAG;GAAM;GAAS,QAAQ;EAAQ,CAAC;EAEvE,IAAI,KAAK,QAAQ,SAAS;GACxB,SAAc,MAAM,OAAO;GAC3B,MAAM,KAAK,OAAO;EACpB;EACA,IAAI,CAAC,SAAS,MAAM,SAAS,SAAS,MAAM,OAAO;EACnD,MAAM,CAAC,aAAa,UAAU,SAAS,KAAK,IAAI;EAChD,MAAM,YAAY,KAAK,SACnB,YAAY,YAAY,IAAI,gBAAgB,GAAG,EAAE,QAAQ,KAAK,OAAO,CAAC,IACtE;EAGJ,MAAM,WAAW,MAAM;EACvB,IAAI,YAAY;GAEd,MAAM,KAAK,iBAAiB,KAAK,aAAa,GAAG;GACjD,MAAM,UAAU,YAAY;IAC1B,MAAM,MAAM,MAAM,UAAU,MAAM;IAClC,MAAM;IACN,KAAK;KACH,MAAM;KACN;KACA,MAAM,eAAe,GAAG;IAC1B,CAAC;GACH,EAAA,CAAG;EACL,OAAO;GACL,MAAM,WAAW;GACjB,MAAM,UAAU,YAAY;IAC1B,MAAM;IACN,KAAK,EAAE,MAAM,aAAa,CAAC;IAC3B,MAAM,WAAW;KACf,MAAM;KACN,OAAO,KAAK;KACZ;KACA,WAAW,aAAa;MACtB,aAAa;KACf;IACF,CAAC;GACH,EAAA,CAAG;EACL;EAEA,MAAM,OAAO,YAAY,CAAC,CAAC;EAC3B,OAAO,IAAI,SAAS,WAAW;GAC7B,QAAQ,SAAS;GACjB,SAAS,SAAS;EACpB,CAAC;CACH;;CAGA,MAAM,UACJ,OACA,UACA,cACoC;EACpC,MAAM,OAAO,MAAM,OAAO,MACvB,cACC,UAAU,aAAa,YACvB,CAAC,MAAM,SAAS,IAAI,UAAU,UAAU,KACxC,SAAS,UAAU,OAAO,SAAS,CACvC;EACA,MAAM,SAAS,SAAS,KAAA,IAAY,KAAA,IAAY,QAAQ,IAAI,KAAK,UAAU;EAC3E,IAAI,SAAS,KAAA,KAAa,WAAW,KAAA,GAAW,OAAO,KAAA;EACvD,MAAM,SAAS,IAAI,KAAK,UAAU;EAClC,IAAI,OAAO,UAAU,KAAA,GAAW,MAAM,IAAI,MAAM,OAAO,KAAK;EAC5D,IAAI,OAAO,WAAW,MAAM,MAAM,IAAI,MAAM,2BAA2B;EACvE,OAAO,EAAE,QAAQ,OAAO,UAAU,KAAK;CACzC;CAEA,MAAM,YAAY,UAAkB,cAAgC;EAClE,MAAM,QAAQ;EACd,IAAI,MAAM,SAAS,MAAM,MAAM;EAC/B,MAAM,WAAW,OAAO,OAAO,UAAU,SAAS;EAClD,IAAI,aAAa,KAAA,GAAW,OAAO,SAAS;EAC5C,IAAI,MAAM,MAAM;GACd,MAAM,eAAe,QAAQ;GAC7B,OAAO,IAAI,SAAS,SAAS,WAAW;IACtC,OAAQ,QAAQ,KAAK;KAAE;KAAU,OAAO;KAAW;KAAS;IAAO,CAAC;GACtE,CAAC;EACH;EACA,IAAI,MAAM,KAAK,QAAQ,eAAe;GACpC,MAAM,OAAO,OAAO;GACpB,OAAO,QAAQ,KAAA,CAAS;EAC1B;EACA,OAAO,KAAK,OAAO,SAAS,0BAA0B,UAAU,CAAC;CACnE;CAEA,MAAM,QAAqB;EAAE,OAAO;EAAW,MAAM;CAAS;CAC9D,MAAM,aAAa,UACjB,QAAQ,IAAI,MAAM,KAAK,aAAa,SAAS,UAAU,KAAK,MAAM,CAAC,CAAC;CACtE,IAAI,UAAU,KAAK;CACnB,IAAI;CACJ,IAAI,SAAS;CACb,MAAM,QAAQ,UAAgC;EAC5C,IAAI,CAAC,QAAQ,WAAY,QAAQ,KAAK;CACxC;CAEA,MAAM,SAAS,OACb,eACoB;EACpB,MAAM,UAAU;GAAE;GAAO,SAAS,CAAC;EAAe;EAClD,MAAM,QAAQ,MAAM,cAAc;GAChC,QAAQ,WAAW;GACnB,OAAO,YAAY,IAAI;GACvB,GAAI,KAAK,aAAa,WAAW,IAC7B,CAAC,IACD,EAAE,cAAc,KAAK,aAAa;GACtC,OAAO,YAAY,KAAK,gBAA4B;IAClD,GAAG;IACH,UAAU,cACR,QAAQ,MAAM,KAAK,WAAW,MAAM,SAAS;GACjD,EAAE;GACF,GAAI,eAAe,KAAA,IAAY,CAAC,IAAI,EAAE,WAAW;GACjD,QAAQ,OAAO,SAAS,QAAQ,MAAM,MAAM,OAAO,IAAI;EACzD,CAAC;EACD,OAAO,OAAO,OAAO,SAAS,EAAE,MAAM,CAAC;CACzC;CA6DA,OAAO;EAAE,QAAA,IA3DU,eAA+B;GAChD,MAAM,kBAAkB;IACtB,aAAa;IACb,MAAM,cAAc;KAClB,UAAU;KACV,MAAM,OAAO,OAAO;IACtB;IACA,KAAK,OAAO,iBAAiB,SAAS,OAAO,EAAE,MAAM,KAAK,CAAC;IAC3D,CAAM,YAAY;KAChB,IAAI;MACF,IAAI,QAAQ,MAAM,UAAU,KAAK,KAAK;MAEtC,MAAM,YAAY,KAAK,OAAO,UAC1B,KAAA,IACA,IAAI,KAAK,KAAK,SAAS;MAC3B,MAAM,UACJ,cAAc,KAAA,KAAa,UAAU,WAAW,KAAK,IACjD,YACA,KAAA;MACN,IAAI,cAAc,KAAA,KAAa,YAAY,KAAA,GACzC,YAAY,SAAS;MACvB,IAAI,YAAY,KAAA,GAAW;OACzB,SAAS,QAAQ;OACjB,OAAO,QAAQ;MACjB,OACE,IAAI;OACF,SAAS,MAAM,OAAO,KAAK,KAAK;MAClC,SAAS,OAAO;OACd,IAAI,KAAK,UAAU,KAAA,GAAW,MAAM;OAEpC,OAAO,MAAM,UAAU;OACvB,UAAU,KAAK;OACf,SAAS,MAAM,OAAO,KAAA,CAAS;OAC/B,QAAQ,MAAM,UAAU,KAAK,KAAK;MACpC;MAEF,MAAM,IAAI,kBAAkB,OAAO,OAAO;MAC1C,SAAS;MACT,iBAAiB,MAAM;KACzB,SAAS,OAAO;MACd,SAAS;MACT,iBAAiB,MACf,KAAK,OAAO,UAAU,KAAK,OAAO,SAAS,KAC7C;KACF,UAAU;MACR,SAAS,MAAM;MACf,KAAK,OAAO,oBAAoB,SAAS,KAAK;MAC9C,IAAI,CAAC,QAAQ,MAAM,QAAQ,MAAM,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;KACzD;IACF,EAAA,CAAG;GACL;GACA,SAAS;IACP,SAAS;IACT,UAAU;IACV,SAAS,MAAM;IACf,MAAM,OAAO,OAAO;GACtB;EACF,CAEc;EAAG,kBAAkB;CAAW;AAChD;AAIA,eAAe,SACb,MACA,QACmB;CACnB,MAAM,QAAwB,CAAC;CAC/B,MAAM,yBAAS,IAAI,IAAsB;CACzC,KAAK,YAAY,SAAS,YAAY,UAAU;EAC9C,MAAM,KAAK,GAAG,cAAc,WAAW,QAAQ,WAAW,SAAS,CAAC;EACpE,MAAM,SAAS,KAAK,OAAO,IAAI,KAAK;EAEpC,IAAI,WAAW,KAAA,GACb,OAAO,IAAI,MAAM,SAAS,GAAG,OAAO,IAAI,SAAS,CAAC;CACtD,CAAC;CACD,MAAM,SAAS,MAAM,cAAc;EACjC,SAAS;EACT,OAAO,KAAK;EACZ;CACF,CAAC;CACD,MAAM,eAAe,KAAK,YAAY,GAAG,EAAE,CAAC,EAAE,UAAU;CACxD,OAAO;EACL,GAAI,iBAAiB,KAAA,IAAY,CAAC,IAAI,EAAE,aAAa;EACrD,QACE,OAAO,WAAW,IACd,KAAK,OAAO,OACZ,CACE,GAAG,OAAO,KAAK,WAA0B;GACvC,MAAM;GACN,MAAM,MAAM;GACZ,UAAU,MAAM;EAClB,EAAE,GACF,GAAI,KAAK,OAAO,SAAS,KACrB,CAAC,IACD,CAAC;GAAE,MAAM;GAAiB,MAAM,KAAK,OAAO;EAAK,CAAC,CACxD;EACN;EACA;EACA,KAAK,KAAK;CACZ;AACF;;AAGA,SAAS,UAAU,OAA6B;CAC9C,IAAI,MAAM,MAAM,SAAS,GACvB,MAAM,IAAI,MACR,kEACF;CACF,OAAO,MAAM;AACf;;AAGA,SAAS,cACP,QACA,WACgB;CAChB,MAAM,QAAwB,CAAC;CAC/B,MAAM,QAAsB,CAAC;CAC7B,MAAM,YAAwB,CAAC;CAE/B,MAAM,4BAAY,IAAI,IAAoB;CAC1C,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,WACJ,sBAAsB,SAAS,MAAM,qBAAqB,KAAA,IACtD,EAAE,kBAAkB,MAAM,iBAAiB,IAC3C,CAAC;EACP,QAAQ,MAAM,MAAd;GACE,KAAK;IACH,MAAM,KAAK;KACT,MAAM;KACN,KAAK,cAAc,MAAM,IAA0B;KACnD,WAAW,CAAC;IACd,CAAC;IACD;GACF,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK;IACH,MAAM,KAAK;KAAE,MAAM,MAAM;KAAM,IAAI,MAAM;KAAI,GAAG;IAAS,CAAC;IAC1D;GACF,KAAK;GACL,KAAK;IACH,MAAM,KAAK;KACT,MAAM,MAAM;KACZ,IAAI,MAAM;KACV,OAAO,MAAM;KACb,GAAG;IACL,CAAC;IACD;GACF,KAAK;IACH,UAAU,IAAI,MAAM,YAAY,EAAE;IAClC,MAAM,KAAK;KACT,MAAM;KACN,IAAI,MAAM;KACV,UAAU,MAAM;KAChB,GAAG;IACL,CAAC;IACD;GACF,KAAK;IACH,UAAU,IACR,MAAM,aACL,UAAU,IAAI,MAAM,UAAU,KAAK,MAAM,MAAM,cAClD;IACA,MAAM,KAAK;KACT,MAAM;KACN,IAAI,MAAM;KACV,OAAO,MAAM;IACf,CAAC;IACD;GACF,KAAK;GACL,KAAK,wBAAwB;IAG3B,MAAM,OACJ,UAAU,IAAI,MAAM,UAAU,MAC7B,MAAM,SAAS,sBAAsB,OAAO,MAAM,UAAU,WACzD,MAAM,QACN,KAAK,UAAU,MAAM,KAAK;IAChC,IAAI,UAAU,IAAI,MAAM,UAAU,GAChC,MAAM,KAAK;KAAE,MAAM;KAAkB,IAAI,MAAM;IAAW,CAAC;IAI7D,MAAM,QAAQ,UAAU,IAAI;IAC5B,IAAI,UAAU,KAAA,GACZ,UAAU,KAAK;KACb,YAAY,MAAM;KAClB,UAAU,MAAM;KAChB;IACF,CAAC;IACH,MAAM,KAAK;KACT,MAAM;KACN,YAAY,MAAM;KAClB,UAAU,MAAM;KAChB,OAAO;KACP,GAAG;IACL,CAAC;IACD;GACF;EACF;CACF;CACA,MAAM,KAAK;EACT,MAAM;EACN,cAAc,EAAE,SAAS,UAAU,gBAAgB,QAAQ;EAC3D,OAAO,YAAY,UAAU,KAAK;CACpC,CAAC;CACD,MAAM,KAAK;EAAE,MAAM;EAAQ,KAAK,IAAI,KAAK;EAAG;CAAU,CAAC;CACvD,OAAO;AACT;AAEA,SAAS,cAAc,MAAkC;CACvD,OAAO,IAAI;EACT;GAAE,MAAM;GAAc,IAAI;EAAI;EAC9B;GAAE,MAAM;GAAc,IAAI;GAAK,OAAO,KAAK,WAAW;EAAG;EACzD;GAAE,MAAM;GAAY,IAAI;EAAI;EAC5B;GACE,MAAM;GACN,cAAc,EAAE,SAAS,OAAO;GAChC,OAAO,YAAY,KAAK,KAAK;EAC/B;CACF,CAAC;AACH;;AAKA,eAAe,WAAW,EACxB,MACA,OACA,MACA,YAMgB;CAEhB,MAAM,2BAAW,IAAI,IAAqB;CAC1C,WAAW,MAAM,QAAQ,SAAS,IAAI,GAAG;EACvC,MAAM,WACJ,KAAK,wBAAwB,KAAA,IACzB,CAAC,IACD,EAAE,kBAAkB,KAAK,oBAA6B;EAC5D,QAAQ,KAAK,MAAb;GACE,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK;IACH,KAAK;KAAE,MAAM,KAAK;KAAM,IAAI,KAAK;KAAiB,GAAG;IAAS,CAAC;IAC/D;GACF,KAAK;GACL,KAAK;IACH,KAAK;KACH,MAAM,KAAK;KACX,IAAI,KAAK;KACT,OAAO,KAAK;KACZ,GAAG;IACL,CAAC;IACD;GACF,KAAK;IACH,SAAS,IAAI,KAAK,OAAiB,KAAK;IACxC,KAAK;KACH,MAAM;KACN,YAAY,KAAK;KACjB,UAAU,KAAK;KACf,GAAG;IACL,CAAC;IACD;GACF,KAAK;IACH,IAAI,KAAK,aAAa,IAAI,SAAS,IAAI,KAAK,OAAiB,IAAI;IACjE,KAAK;KACH,MAAM;KACN,YAAY,KAAK;KACjB,gBAAgB,KAAK;IACvB,CAAC;IACD;GACF,KAAK,aAAa;IAChB,MAAM,aAAa,KAAK;IACxB,MAAM,WAAW,KAAK;IACtB,MAAM,OAAO,KAAK;IAGlB,IAAI,SAAS,IAAI,UAAU,MAAM,QAAQ,SAAS,IAAI;KACpD,IAAI,CAAC,SAAS,IAAI,UAAU,GAC1B,KAAK;MAAE,MAAM;MAAoB;MAAY;KAAS,CAAC;KACzD,KAAK;MAAE,MAAM;MAAoB;MAAY,gBAAgB;KAAK,CAAC;IACrE;IACA,MAAM,QAAQ,MAAM,eAAe;KACjC;KACA;KACA;IACF,CAAC;IACD,KACE,MAAM,UACF;KACE,MAAM;KACN;KACA;KACA,OAAO,MAAM;KACb,GAAG;IACL,IACA;KACE,MAAM;KACN;KACA;KACA,OAAO,MAAM;KACb,WAAW,MAAM;KACjB,GAAG;IACL,CACN;IACA;GACF;GACA,KAAK,UAAU;IACb,MAAM,SAAS,KAAK;IACpB,SAAS;KACP,GAAI,QAAQ,YAAY,KAAA,IACpB,CAAC,IACD,EAAE,cAAc,OAAO,QAAQ;KACnC,OAAO,WAAW,KAAK,QAAQ;IACjC,CAAC;IACD;GACF;GACA,KAAK,SACH,KAAK;IAAE,MAAM;IAAS,WAAW,UAAU,KAAK,QAAQ;GAAE,CAAC;EAE/D;CACF;AACF;;;;;;AAWA,eAAe,eAAe,EAC5B,OACA,UACA,QAKqB;CACrB,IAAI;CACJ,IAAI;EACF,QAAQ,KAAK,MAAM,IAAI;CACzB,SAAS,OAAO;EACd,OAAO;GAAE,SAAS;GAAO,OAAO;GAAM,WAAW,UAAU,KAAK;EAAE;CACpE;CACA,MAAM,QAAQ,MAAM,YAAY;EAAE;EAAO;EAAU;CAAM,CAAC;CAC1D,OAAO,MAAM,UAAU,QAAQ;EAAE,GAAG;EAAO,OAAO;CAAM;AAC1D;AAEA,eAAe,UACb,MACuB;CACvB,MAAM,MAAoB,CAAC;CAC3B,WAAW,MAAM,QAAQ,SAAS,IAAI,GAAG,IAAI,KAAK,IAAI;CACtD,OAAO;AACT;AAEA,SAAS,eAAe,KAAgD;CACtE,MAAM,SAAS,IAAI,MAAM,SAAS,KAAK,SAAS,QAAQ;CACxD,OAAO;EACL,QAAQ;EACR,SAAS,IACN,QAAQ,SAAS,KAAK,SAAS,YAAY,CAAC,CAC5C,KAAK,SAAS,KAAK,QAAkB,CAAC,CACtC,KAAK,EAAE;EACV,GAAI,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO,WAAW,OAAO,QAAQ,EAAE;CACvE;AACF;AAEA,gBAAgB,SACd,MAC4B;CAC5B,MAAM,UAAU,IAAI,YAAY;CAChC,IAAI,cAAc;CAClB,WAAW,MAAM,SAAS,MAAM;EAC9B,eAAe,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;EACrD,IAAI;EACJ,QAAQ,MAAM,YAAY,QAAQ,MAAM,OAAO,IAAI;GACjD,MAAM,QAAQ,YAAY,MAAM,GAAG,GAAG;GACtC,cAAc,YAAY,MAAM,MAAM,CAAC;GACvC,IAAI,MAAM,WAAW,SAAS,GAAG,MAAM,KAAK,MAAM,MAAM,MAAM,CAAC,CAAC;EAClE;CACF;AACF;;;;;AAMA,SAAS,MAAM,MAAc,cAA6C;CACxE,MAAM,UAAU,IAAI,YAAY;CAChC,MAAM,SAAS,KAAK,MAAM,WAAW;CACrC,IAAI,QAAQ;CACZ,IAAI,UAAU;CACd,OAAO,IAAI,SACT,IAAI,eAAe,EACjB,MAAM,KAAK,YAAY;EACrB,MAAM,QAAQ,OAAO;EACrB,IAAI,UAAU,KAAA,GAAW;GACvB,WAAW,MAAM;GACjB;EACF;EACA,IAAI,CAAC,WAAW,MAAM,SAAS,qBAAiB,GAAG;GACjD,UAAU;GACV,MAAM,aAAa;EACrB;EACA,WAAW,QAAQ,QAAQ,OAAO,KAAK,CAAC;CAC1C,EACF,CAAC,GACD,EAAE,SAAS,EAAE,gBAAgB,oBAAoB,EAAE,CACrD;AACF;AAIA,eAAe,YAA6C;CAC1D,IAAI;EACF,OAAO,MAAM,OAAO;CACtB,SAAS,OAAO;EACd,MAAM,IAAI,MAAM,+CAA+C,EAC7D,OAAO,MACT,CAAC;CACH;AACF;;;;;;AAOA,SAAS,mBAAmB,MAAc,SAAiC;CACzE,MAAM,UAAU,QAAQ;CACxB,IAAI,YAAY,KAAA,GACd,OAAO,KAAK,QACV,OACA,4DACF;CAEF,MAAM,MAAM,qBAAqB,KAAK,UAAU,OAAO,EAAE;CACzD,IAAI,CAAC,KAAK,SAAS,GAAG,GAAG,OAAO;CAChC,MAAM,SAAS;EACb,GAAG;EACH,SAAS;GAAE,SAAS;GAAQ,GAAG,QAAQ;EAAW;CACpD;CACA,OAAO,GAAG,KAAK,MAAM,GAAG,CAAC,IAAI,MAAM,EAAE,oBAAoB,KAAK,UAAU,MAAM,EAAE;AAClF;AAEA,SAAS,SACP,SACA,YACS;CACT,MAAM,SAAS,IAAI,QAAQ,OAAO;CAClC,OAAO,IAAI,0BAA0B,WAAW,MAAM;CAEtD,IAAI,OAAO,IAAI,eAAe,GAC5B,OAAO,IAAI,iBAAiB,UAAU,WAAW,OAAO;CAC1D,OAAO;AACT;AAEA,SAAS,YACP,MAC0D;CAC1D,MAAM,SACJ,KAAK,cAAc,KAAA,KAAa,KAAK,cAAc,qBAC/C,KAAA,IACA,KAAK;CACX,MAAM,OAAO,KAAK,UAAU;CAC5B,IAAI,WAAW,KAAA,KAAa,CAAC,MAAM,OAAO,KAAK;CAC/C,OAAO;EACL,IAAI,KAAK;EACT,GAAI,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO;EACzC,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;CACzB;AACF;;AAGA,eAAe,gBACb,OACuE;CACvE,OAAO,QAAQ,IACb,OAAO,QAAQ,KAAK,CAAC,CAAC,IAAI,OAAO,CAAC,UAAU,WAAW;EACrD,MAAM;EACN,aACE,OAAO,KAAK,gBAAgB,WAAW,KAAK,cAAc;EAC5D,aAAc,MAAM,SAAS,KAAK,WAAW,CAAC,CAAC;CACjD,EAAE,CACJ;AACF;AAGA,MAAM,eAAe,aAGnB;CAAE,YAAY;CAAI,UAAU;AAAG,CAAC;;;;;AAMlC,eAAe,QACb,KACA,YACA,SACmB;CACnB,MAAM,MAAM,GAAG,IAAI,IAAI,WAAW;CAClC,MAAM,MAAM,KAAK,IAAI;CACrB,IAAI,QAAQ,aAAa,IAAI,GAAG;CAChC,IAAI,UAAU,KAAA,KAAa,MAAM,WAAW,KAAK;EAC/C,MAAM,OAAO,MAAM,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,OAAO,aAAa;GAC5D,IAAI,CAAC,SAAS,IACZ,MAAM,IAAI,MAAM,2BAA2B,SAAS,QAAQ;GAC9D,OAAO,SAAS,KAAK;EACvB,CAAC;EACD,QAAQ;GAAE,SAAS,MAAM;GAAgB;EAAK;EAC9C,aAAa,IAAI,KAAK,OAAO,CAAC;EAC9B,KAAK,YAAY,aAAa,OAAO,GAAG,CAAC;CAC3C;CACA,OAAO,IAAI,SAAS,MAAM,MAAM,MAAM,EACpC,SAAS,EAAE,gBAAgB,mBAAmB,EAChD,CAAC;AACH;AAIA,SAAS,gBAAgB,OAAwC;CAC/D,IAAI,OAAO,UAAU,YAAY,UAAU,MAAM,OAAO,KAAA;CACxD,MAAM,EAAE,SAAS,eAAe;CAChC,IAAI,YAAY,oBAAoB,OAAO,eAAe,UACxD,OAAO,KAAA;CACT,OAAO,IAAI,WAAW,OAAO,KAAK,YAAY,QAAQ,CAAC;AACzD;AAEA,SAAS,WAAW,MAA2C;CAC7D,IAAI,OAAO,SAAS,UAAU,OAAO;CACrC,IAAI,gBAAgB,cAAc,gBAAgB,aAChD,OAAO,IAAI,YAAY,CAAC,CAAC,OAAO,IAAI;CACtC,MAAM,IAAI,UAAU,oCAAoC;AAC1D;AAEA,SAAS,UAAU,MAAuB;CACxC,IAAI;EACF,OAAO,KAAK,MAAM,IAAI;CACxB,QAAQ;EACN;CACF;AACF;AAEA,SAAS,SAAS,GAAY,GAAqB;CACjD,OAAO,KAAK,UAAU,CAAC,MAAM,KAAK,UAAU,CAAC;AAC/C;AAEA,SAAS,IAAI,KAAgC;CAC3C,OACE,IAAI,KAAK,SAAS,SAAS,KAAK,UAAU,IAAI,EAAE,KAAK,CAAC,CAAC,KAAK,EAAE,IAC9D;AAEJ;AAEA,SAAS,UAAU,OAAwB;CACzC,IAAI,OAAO,UAAU,UAAU,OAAO;CACtC,IAAI,iBAAiB,OAAO,OAAO,MAAM;CACzC,OAAO,KAAK,UAAU,KAAK;AAC7B;;AAiBA,SAAS,WAAW,OAAoC;CACtD,MAAM,QAAS,SAAS,CAAC;CACzB,MAAM,QAAQ,MAAM,eAAe,CAAC;CACpC,MAAM,SAAS,MAAM,gBAAgB,CAAC;CACtC,OAAO;EACL,aAAa,MAAM;EACnB,mBAAmB;GACjB,eAAe,MAAM;GACrB,iBAAiB,MAAM;GACvB,kBAAkB,MAAM;EAC1B;EACA,cAAc,OAAO;EACrB,oBAAoB;GAClB,YAAY,OAAO;GACnB,iBAAiB,OAAO;EAC1B;EACA,aACE,MAAM,UAAU,KAAA,KAAa,OAAO,UAAU,KAAA,IAC1C,KAAA,KACC,MAAM,SAAS,MAAM,OAAO,SAAS;CAC9C;AACF;;AAGA,SAAS,YAAY,OAAoD;CACvE,OAAO;EACL,aAAa;GACX,OAAO,OAAO;GACd,SAAS,OAAO,mBAAmB;GACnC,WAAW,OAAO,mBAAmB;GACrC,YAAY,OAAO,mBAAmB;EACxC;EACA,cAAc;GACZ,OAAO,OAAO;GACd,MAAM,OAAO,oBAAoB;GACjC,WAAW,OAAO,oBAAoB;EACxC;CACF;AACF"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import "./ai-
|
|
1
|
+
import "./ai-DAu77CDr.js";
|
|
2
2
|
import { Experimental_SandboxSession, ToolSet, UIMessage, UIMessageChunk } from "ai";
|
|
3
3
|
//#region src/ai-harness.d.ts
|
|
4
4
|
declare const harnessBrand: unique symbol;
|
|
@@ -23,4 +23,4 @@ type HarnessSandboxContext = {
|
|
|
23
23
|
type HarnessSandbox = (context: HarnessSandboxContext) => Experimental_SandboxSession | Promise<Experimental_SandboxSession>;
|
|
24
24
|
//#endregion
|
|
25
25
|
export { HarnessSandbox as n, HarnessSandboxContext as r, Harness as t };
|
|
26
|
-
//# sourceMappingURL=ai-harness-
|
|
26
|
+
//# sourceMappingURL=ai-harness-DTjkAr2T.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-harness-
|
|
1
|
+
{"version":3,"file":"ai-harness-DTjkAr2T.d.ts","names":[],"sources":["../src/ai-harness.ts"],"mappings":";;;cAqBc;;;;;KAMF,QACV,wCACA;WAES,MAAM;YACL;aAA0B,OAAO;;;;KAIjC;EACV;aAAoB;;EACpB,QAAQ;;;KAIE,kBACV,SAAS,0BACN,8BAA8B,QAAQ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-harness-dQeJaR1t.js","names":[],"sources":["../src/ai-harness.ts"],"sourcesContent":["import {\n createDownload,\n type Experimental_SandboxSession,\n type FileUIPart,\n type FinishReason,\n type LanguageModelUsage,\n type ToolSet,\n type UIMessage,\n type UIMessageChunk,\n} from 'ai'\nimport type {\n GenerationCompletedPayload,\n MessageCreatedPayload,\n ReasoningLevel,\n ToolResultPayload,\n} from './ai.ts'\nimport { createHash } from 'node:crypto'\nimport { STEERED_REASON } from './ai-control.ts'\nimport { boundedCache, type BoundedCache } from './bounded-cache.ts'\nimport type { ContractEvent, EventDefs } from './contract.ts'\n\ndeclare const harnessBrand: unique symbol\n\n/**\n * A coding agent's own loop for `agent({ harness })`, from `fx()` or `pi()`.\n * `Tools` names the tools the harness brings, which `toolRecovery` may name.\n */\nexport type Harness<\n Type extends 'fx' | 'pi' = 'fx' | 'pi',\n Tools extends string = string,\n> = {\n readonly type: Type\n readonly [harnessBrand]: { readonly tools: Tools }\n}\n\n/** What a harness's sandbox resolver receives for one step or tool execution. */\nexport type HarnessSandboxContext = {\n session: { readonly id: string }\n signal: AbortSignal\n}\n\n/** Resolves the AI SDK sandbox session for an agent session, the same one every time. */\nexport type HarnessSandbox = (\n context: HarnessSandboxContext,\n) => Experimental_SandboxSession | Promise<Experimental_SandboxSession>\n\n/** What a harness step streams, as the default generator does. */\nexport type HarnessGeneration = {\n stream: ReadableStream<UIMessageChunk>\n completion: () => { finishReason?: FinishReason; usage?: LanguageModelUsage }\n}\n\n/** One stored snapshot and the log after it, read in one store operation. */\nexport type HarnessLogRead = {\n snapshot: { index: number; state: unknown } | null\n events: ContractEvent<EventDefs>[]\n}\n\n/** The session log as a harness step reads it, plus the state saved in it. */\nexport type HarnessLog = {\n /** The latest saved state under `name` and the log after it; `fresh` reads it all. */\n read(options: {\n name: string\n throughIndex: number\n fresh?: boolean\n }): Promise<HarnessLogRead>\n write(options: { name: string; index: number; state: unknown }): Promise<void>\n}\n\n/** Everything one durable generation hands its harness. */\nexport type HarnessStep = {\n agentName: string\n sessionId: string\n requestIndex: number\n /** This attempt at the generation. */\n generationId: string\n responseMessageId: string\n /** The log index this step's context was read through. */\n throughIndex: number\n /** The transcript through that index. */\n messages: readonly UIMessage[]\n model: string\n reasoning?: ReasoningLevel\n speed?: 'fast' | 'standard'\n instructions: string[]\n tools: ToolSet\n log: HarnessLog\n signal: AbortSignal\n}\n\n/** The internal side of a harness value. */\nexport type HarnessDriver = {\n /** The session's sandbox; every local tool execution receives it. */\n readonly sandbox?: HarnessSandbox\n /** Tools the harness brings; A2 executes them like the agent's own. */\n readonly tools: ToolSet\n step(step: HarnessStep): Promise<HarnessGeneration>\n}\n\n// A global symbol, so harness values work across separately bundled copies.\nconst DRIVER = Symbol.for('experimental-a2.ai.harness')\n\nexport function defineHarness<\n Type extends 'fx' | 'pi',\n Tools extends string = never,\n>(type: Type, driver: HarnessDriver): Harness<Type, Tools> {\n const harness = { type }\n Object.defineProperty(harness, DRIVER, { value: driver })\n return Object.freeze(harness) as unknown as Harness<Type, Tools>\n}\n\nexport function harnessDriver(harness: unknown): HarnessDriver {\n const driver =\n typeof harness === 'object' && harness !== null\n ? (harness as { [DRIVER]?: HarnessDriver })[DRIVER]\n : undefined\n if (driver === undefined)\n throw new TypeError(\n \"harness must be { type: 'ai-sdk' }, or come from fx() or pi()\",\n )\n return driver\n}\n\n// ------------------------------------------------------- saved state\n\n/**\n * What an agent's steps saved in this process at the end of a response, per\n * session and harness. A cache: a cold process rebuilds from the whole log.\n */\nexport type HarnessStates = BoundedCache<\n string,\n { index: number; state: unknown }\n>\n\nexport const harnessStates = (): HarnessStates =>\n boundedCache({ maxEntries: 1_000, maxBytes: 64 * 1024 * 1024 })\n\nconst savedKey = (sessionId: string, name: string): string =>\n `${sessionId}\\u0000${name}`\n\n// Binary state counts its bytes; everything else its JSON text.\nconst sizeOf = (value: unknown): number => {\n let binary = 0\n const text = JSON.stringify(value, (_key, item: unknown) => {\n if (!(item instanceof Uint8Array)) return item\n binary += item.byteLength\n return null\n })\n return binary + (text?.length ?? 0)\n}\n\n/**\n * The session log as a harness step reads it: the state this process saved\n * at the end of the last response, when it has one, and the log after it.\n * Saved states are copied in and out, so a step cannot change one in place.\n */\nexport function harnessLog(options: {\n states: HarnessStates\n session: {\n readonly id: string\n history(bounds: {\n gte?: number\n lte?: number\n }): Promise<ContractEvent<EventDefs>[]>\n }\n}): HarnessLog {\n const { states, session } = options\n return {\n read: async ({ name, throughIndex, fresh }) => {\n const entry = fresh ? undefined : states.get(savedKey(session.id, name))\n const snapshot =\n entry === undefined || entry.index > throughIndex\n ? null\n : { index: entry.index, state: structuredClone(entry.state) }\n const events = await session.history(\n snapshot === null\n ? { lte: throughIndex }\n : { gte: snapshot.index + 1, lte: throughIndex },\n )\n return { snapshot, events }\n },\n write: async ({ name, index, state }) => {\n const copy = structuredClone(state)\n states.set(\n savedKey(session.id, name),\n { index, state: copy },\n sizeOf(copy),\n )\n },\n }\n}\n\nconst agentStates = new WeakMap<object, HarnessStates>()\n\n/** Registers an agent server's saved harness states, for `savedHarnessState`. */\nexport function registerHarnessStates(\n server: object,\n states: HarnessStates,\n): void {\n agentStates.set(server, states)\n}\n\n/** What an agent's process saved for one session's harness; tests read it. */\nexport function savedHarnessState(\n server: object,\n options: { sessionId: string; name: string },\n): { index: number; state: unknown } | undefined {\n return agentStates.get(server)?.get(savedKey(options.sessionId, options.name))\n}\n\n/** A harness's state at the end of a response, from the attempt that ran it. */\ntype SavedEnvelope = {\n generationId: string\n responseMessageId: string\n state: unknown\n}\n\nexport type HarnessRead<S> = {\n /** The state saved at the end of the last response, when it is usable. */\n saved: S | undefined\n turns: HarnessTurn[]\n results: Map<string, ToolResultPayload>\n}\n\n/**\n * Reads a harness's saved state and the log since, as turns to replay. A\n * snapshot counts only when the attempt that wrote it committed: a stopped,\n * superseded, or still-running attempt can write one the log never kept.\n * `replayAll` reads the whole log instead, for a state that fails to restore.\n */\nexport async function readHarness<S>({\n step,\n name,\n parse,\n}: {\n step: HarnessStep\n name: string\n parse: (state: unknown) => S | undefined\n}): Promise<HarnessRead<S> & { replayAll: () => Promise<HarnessRead<S>> }> {\n const replayAll = async (): Promise<HarnessRead<S>> => {\n const read = await step.log.read({\n name,\n throughIndex: step.throughIndex,\n fresh: true,\n })\n return { saved: undefined, ...harnessTurns(step, read.events, undefined) }\n }\n const read = await step.log.read({ name, throughIndex: step.throughIndex })\n if (read.snapshot === null)\n return {\n saved: undefined,\n ...harnessTurns(step, read.events, undefined),\n replayAll,\n }\n const envelope = savedEnvelope(read.snapshot.state)\n const saved = envelope === undefined ? undefined : parse(envelope.state)\n const committed =\n envelope !== undefined &&\n read.events.some(\n (event) =>\n event.type === 'ai.generation.completed' &&\n (event.payload as { generationId?: unknown }).generationId ===\n envelope.generationId,\n )\n // A snapshot of this very response belongs to an earlier attempt at it.\n if (\n saved === undefined ||\n !committed ||\n envelope.responseMessageId === step.responseMessageId\n )\n return { ...(await replayAll()), replayAll }\n return {\n saved,\n ...harnessTurns(step, read.events, envelope.responseMessageId),\n replayAll,\n }\n}\n\n/** Saves a harness's state at the end of a response; losing it costs a longer replay. */\nexport async function saveHarness({\n step,\n name,\n state,\n}: {\n step: HarnessStep\n name: string\n state: unknown\n}): Promise<void> {\n const envelope: SavedEnvelope = {\n generationId: step.generationId,\n responseMessageId: step.responseMessageId,\n state,\n }\n try {\n await step.log.write({ name, index: step.requestIndex, state: envelope })\n } catch {\n // The snapshot is a cache.\n }\n}\n\nfunction savedEnvelope(value: unknown): SavedEnvelope | undefined {\n if (typeof value !== 'object' || value === null) return undefined\n const { generationId, responseMessageId, state } = value as Record<\n string,\n unknown\n >\n return typeof generationId === 'string' &&\n typeof responseMessageId === 'string'\n ? { generationId, responseMessageId, state }\n : undefined\n}\n\n// ------------------------------------------------- the log as harness turns\n\n/** A completed generation as the log recorded it. */\nexport type RecordedGeneration = {\n chunks: UIMessageChunk[]\n completed: GenerationCompletedPayload\n}\n\n/** What user messages hand a harness: their text, and the files they attach. */\nexport type HarnessInput = { text: string; files: FileUIPart[] }\n\n/** One harness turn to rebuild: its prompt, completed generations, and end. */\nexport type HarnessTurn = {\n /** With any steering that entered before the turn's first completed generation. */\n prompt: HarnessInput\n generations: RecordedGeneration[]\n /** Steering that entered after the generation at that index. */\n steers: Map<number, HarnessInput[]>\n end: 'stop' | 'interrupted' | 'live'\n}\n\n/**\n * Groups the log since a harness snapshot into harness turns. A response\n * steered after a step continues its turn, with the next response's prompt as\n * steering. `covered` is the response the snapshot already holds.\n */\nexport function harnessTurns(\n step: HarnessStep,\n events: readonly ContractEvent<EventDefs>[],\n covered: string | undefined,\n): { turns: HarnessTurn[]; results: Map<string, ToolResultPayload> } {\n type Generation = {\n id: string\n chunks: UIMessageChunk[]\n completed?: GenerationCompletedPayload\n }\n type Response = {\n id: string\n prompt: HarnessInput[]\n generations: Generation[]\n end?: 'stop' | 'steered' | 'interrupted'\n }\n // Prompts take their content from the transcript: a harness agent never\n // runs A2's compaction, so the step's messages hold every input admitted by\n // its read, before the snapshot or after it. A session another loop\n // compacted can't move to a harness.\n const userInput = new Map<string, HarnessInput>(\n step.messages\n .filter((message) => message.role === 'user')\n .map((message) => [message.id, inputOf(message)]),\n )\n let passive: string[] = []\n const responses = new Map<string, Response>()\n const byStart = new Map<number, Generation>()\n const byId = new Map<string, Generation>()\n const results = new Map<string, ToolResultPayload>()\n\n for (const event of events) {\n const payload = event.payload as Record<string, unknown>\n switch (event.type) {\n case 'ai.message.created': {\n const admitted = event.payload as MessageCreatedPayload\n if (admitted.role === 'user' && admitted.generate === false)\n passive.push(admitted.messageId)\n break\n }\n case 'ai.generation.requested': {\n const id = payload['responseMessageId'] as string\n if (id === covered || responses.has(id)) break\n const messageId = payload['messageId'] as string\n const ids = [...passive.filter((p) => p !== messageId), messageId]\n passive = []\n responses.set(id, {\n id,\n prompt: ids.flatMap((p) => userInput.get(p) ?? []),\n generations: [],\n })\n break\n }\n case 'ai.generation.started': {\n const response = responses.get(payload['responseMessageId'] as string)\n if (response === undefined) break\n const generation: Generation = {\n id: payload['generationId'] as string,\n chunks: [],\n }\n response.generations.push(generation)\n byStart.set(event.index, generation)\n byId.set(generation.id, generation)\n break\n }\n case 'ai.generation.progress':\n byStart\n .get(payload['startIndex'] as number)\n ?.chunks.push(...(payload['chunks'] as UIMessageChunk[]))\n break\n case 'ai.generation.completed': {\n const generation = byId.get(payload['generationId'] as string)\n if (generation)\n generation.completed = event.payload as GenerationCompletedPayload\n break\n }\n case 'ai.tool.result':\n if (payload['preliminary'] !== true)\n results.set(\n payload['toolCallId'] as string,\n event.payload as ToolResultPayload,\n )\n break\n case 'ai.message.completed': {\n const response = responses.get(payload['messageId'] as string)\n if (response) response.end = 'stop'\n break\n }\n case 'ai.message.interrupted': {\n const response = responses.get(payload['messageId'] as string)\n if (response)\n response.end =\n payload['reason'] === STEERED_REASON ? 'steered' : 'interrupted'\n break\n }\n }\n }\n\n const turns: HarnessTurn[] = []\n let steered = false\n for (const response of responses.values()) {\n const input: HarnessInput = {\n text: response.prompt.map((prompt) => prompt.text).join('\\n\\n'),\n files: response.prompt.flatMap((prompt) => prompt.files),\n }\n let turn = turns.at(-1)\n if (steered && turn?.generations.length === 0) {\n // Nothing answered the prompt yet, so the steering joins it, as messages\n // queued before a response join its prompt.\n turn.prompt = {\n text: `${turn.prompt.text}\\n\\n${input.text}`,\n files: [...turn.prompt.files, ...input.files],\n }\n } else if (steered && turn !== undefined) {\n const at = turn.generations.length - 1\n turn.steers.set(at, [...(turn.steers.get(at) ?? []), input])\n } else {\n turn = { prompt: input, generations: [], steers: new Map(), end: 'live' }\n turns.push(turn)\n }\n for (const generation of response.generations)\n if (generation.completed !== undefined)\n turn.generations.push({\n chunks: generation.chunks,\n completed: generation.completed,\n })\n turn.end =\n response.id === step.responseMessageId\n ? 'live'\n : response.end === 'stop'\n ? 'stop'\n : 'interrupted'\n steered = response.end === 'steered'\n }\n return { turns, results }\n}\n\nfunction inputOf(message: UIMessage): HarnessInput {\n return {\n text: message.parts\n .flatMap((part) => (part.type === 'text' ? [part.text] : []))\n .join('\\n'),\n files: message.parts.filter(\n (part): part is FileUIPart => part.type === 'file',\n ),\n }\n}\n\n// ------------------------------------------------------------- attachments\n\n/** An attached image as an engine takes it: its bytes in base64. */\nexport type HarnessImage = { mediaType: string; data: string }\n\n// What one message's files hold in a step's memory, at most.\nconst MAX_ATTACHMENT_BYTES = 32 * 1024 * 1024\n\ntype Attachment = { data: string; bytes: number }\n\n// Every step of a response replays its turn, and a URL's file doesn't change,\n// so the process keeps what it fetched.\nconst attachments = boundedCache<string, Attachment>({\n maxEntries: 256,\n maxBytes: 64 * 1024 * 1024,\n})\n\n/**\n * The images an input attaches, as its engine takes them: engines take\n * bytes, not URLs. A file it can't attach fails the step, naming the file,\n * and is never left out: the failure may pass, and the step is retried like\n * any other.\n */\nexport async function harnessImages({\n harness,\n input,\n signal,\n}: {\n /** As the error names it: `fx()` or `pi()`. */\n harness: string\n input: HarnessInput\n signal: AbortSignal\n}): Promise<HarnessImage[]> {\n const images: HarnessImage[] = []\n let budget = MAX_ATTACHMENT_BYTES\n // One at a time, so the budget bounds what the step holds.\n for (const file of input.files) {\n try {\n if (!file.mediaType.startsWith('image/'))\n throw new Error(`it takes images only, not ${file.mediaType}`)\n const attached = await attachment({ url: file.url, budget, signal })\n budget -= attached.bytes\n images.push({\n mediaType: file.mediaType,\n data: attached.data,\n })\n } catch (error) {\n if (signal.aborted) throw error\n throw new Error(\n `${harness} couldn't attach ${file.filename ?? 'a file'}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n { cause: error },\n )\n }\n }\n return images\n}\n\nasync function attachment({\n url,\n budget,\n signal,\n}: {\n url: string\n budget: number\n signal: AbortSignal\n}): Promise<Attachment> {\n const tooLarge = () =>\n new Error(\n `a message's files may hold ${MAX_ATTACHMENT_BYTES / 1024 / 1024} MiB in a step`,\n )\n const cached = attachments.get(url)\n if (cached !== undefined) {\n if (cached.bytes > budget) throw tooLarge()\n return cached\n }\n const { protocol } = new URL(url)\n if (protocol !== 'https:' && protocol !== 'data:')\n throw new Error(`attachments must be https: or data: URLs, not ${protocol}`)\n // AI SDK's download checks an https: URL's address and its redirects,\n // which a message's sender chooses; a data: URL holds its bytes.\n const bytes =\n protocol === 'data:'\n ? dataBytes(url)\n : (\n await createDownload({ maxBytes: budget })({\n url: new URL(url),\n abortSignal: signal,\n })\n ).data\n if (bytes.byteLength > budget) throw tooLarge()\n const attached: Attachment = {\n data: Buffer.from(bytes).toString('base64'),\n bytes: bytes.byteLength,\n }\n if (protocol === 'https:')\n attachments.set(url, attached, attached.data.length)\n return attached\n}\n\nfunction dataBytes(url: string): Uint8Array {\n const comma = url.indexOf(',')\n if (comma === -1) throw new Error('its data: URL has no data')\n const body = url.slice(comma + 1)\n return url.slice(0, comma).endsWith(';base64')\n ? Buffer.from(body, 'base64')\n : Buffer.from(decodeURIComponent(body))\n}\n\n// ------------------------------------------------------------- Gateway\n\nexport type GatewayCredential = { token: string; method: 'api-key' | 'oidc' }\n\n/** Read per step: on Vercel the OIDC token rides on each function request. */\nexport function gatewayCredential(harness: string): GatewayCredential {\n const key = process.env['AI_GATEWAY_API_KEY']\n if (key) return { token: key, method: 'api-key' }\n const oidc = requestOidcToken() ?? process.env['VERCEL_OIDC_TOKEN']\n if (oidc) return { token: oidc, method: 'oidc' }\n throw new Error(`${harness}() needs AI_GATEWAY_API_KEY or VERCEL_OIDC_TOKEN`)\n}\n\n/**\n * The `x-session-affinity` an agent session's Gateway calls carry: stable for\n * the session, so supporting providers can reuse their cache, and free of its\n * raw ID.\n */\nexport function gatewaySessionAffinity({\n agentName,\n sessionId,\n}: {\n agentName: string\n sessionId: string\n}): string {\n return createHash('sha256')\n .update(JSON.stringify([agentName, sessionId]))\n .digest('hex')\n}\n\n// Where Vercel Functions keep the current request, as @vercel/oidc reads it.\nconst REQUEST_CONTEXT = Symbol.for('@vercel/request-context')\n\nfunction requestOidcToken(): string | undefined {\n const context = (\n globalThis as {\n [REQUEST_CONTEXT]?: { get?: () => { headers?: Record<string, unknown> } }\n }\n )[REQUEST_CONTEXT]\n const token = context?.get?.()?.headers?.['x-vercel-oidc-token']\n return typeof token === 'string' && token !== '' ? token : undefined\n}\n\n// ------------------------------------------------------------- helpers\n\nexport class Divergence extends Error {\n constructor(harness: string, message: string) {\n super(`${harness} replay diverged from the log: ${message}`)\n }\n}\n\nexport type Deferred = { promise: Promise<void>; resolve: () => void }\n\nexport function deferred(): Deferred {\n let resolve!: () => void\n const promise = new Promise<void>((done) => {\n resolve = done\n })\n return { promise, resolve }\n}\n\n/** A result that never settles; the harness is stopped instead. */\nexport function pending(\n signal: AbortSignal | null | undefined,\n): Promise<never> {\n return new Promise((_, reject) => {\n if (signal?.aborted) reject(signal.reason)\n signal?.addEventListener('abort', () => reject(signal.reason), {\n once: true,\n })\n })\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ai-harness-dQeJaR1t.js","names":[],"sources":["../src/ai-harness.ts"],"sourcesContent":["import {\n createDownload,\n type Experimental_SandboxSession,\n type FileUIPart,\n type FinishReason,\n type LanguageModelUsage,\n type ToolSet,\n type UIMessage,\n type UIMessageChunk,\n} from 'ai'\nimport type {\n GenerationCompletedPayload,\n MessageCreatedPayload,\n ReasoningLevel,\n ToolResultPayload,\n} from './ai.ts'\nimport { createHash } from 'node:crypto'\nimport { STEERED_REASON } from './ai-control.ts'\nimport { boundedCache, type BoundedCache } from './bounded-cache.ts'\nimport type { ContractEvent, EventDefs } from './contract.ts'\n\ndeclare const harnessBrand: unique symbol\n\n/**\n * A coding agent's own loop for `agent({ harness })`, from `fx()` or `pi()`.\n * `Tools` names the tools the harness brings, which `toolRecovery` may name.\n */\nexport type Harness<\n Type extends 'fx' | 'pi' = 'fx' | 'pi',\n Tools extends string = string,\n> = {\n readonly type: Type\n readonly [harnessBrand]: { readonly tools: Tools }\n}\n\n/** What a harness's sandbox resolver receives for one step or tool execution. */\nexport type HarnessSandboxContext = {\n session: { readonly id: string }\n signal: AbortSignal\n}\n\n/** Resolves the AI SDK sandbox session for an agent session, the same one every time. */\nexport type HarnessSandbox = (\n context: HarnessSandboxContext,\n) => Experimental_SandboxSession | Promise<Experimental_SandboxSession>\n\n/** What a harness step streams, as the default generator does. */\nexport type HarnessGeneration = {\n stream: ReadableStream<UIMessageChunk>\n completion: () => { finishReason?: FinishReason; usage?: LanguageModelUsage }\n}\n\n/** One stored snapshot and the log after it, read in one store operation. */\nexport type HarnessLogRead = {\n snapshot: { index: number; state: unknown } | null\n events: ContractEvent<EventDefs>[]\n}\n\n/** The session log as a harness step reads it, plus the state saved in it. */\nexport type HarnessLog = {\n /** The latest saved state under `name` and the log after it; `fresh` reads it all. */\n read(options: {\n name: string\n throughIndex: number\n fresh?: boolean\n }): Promise<HarnessLogRead>\n write(options: { name: string; index: number; state: unknown }): Promise<void>\n}\n\n/** Everything one durable generation hands its harness. */\nexport type HarnessStep = {\n /** The agent's own identity: what a harness keeps in memory is per scope. */\n scope: object\n agentName: string\n sessionId: string\n requestIndex: number\n /** This attempt at the generation. */\n generationId: string\n responseMessageId: string\n /** The log index this step's context was read through. */\n throughIndex: number\n /** The transcript through that index. */\n messages: readonly UIMessage[]\n model: string\n reasoning?: ReasoningLevel\n speed?: 'fast' | 'standard'\n instructions: string[]\n tools: ToolSet\n log: HarnessLog\n signal: AbortSignal\n}\n\n/** The internal side of a harness value. */\nexport type HarnessDriver = {\n /** The session's sandbox; every local tool execution receives it. */\n readonly sandbox?: HarnessSandbox\n /** Tools the harness brings; A2 executes them like the agent's own. */\n readonly tools: ToolSet\n step(step: HarnessStep): Promise<HarnessGeneration>\n /**\n * The session has nothing left to run in this process. Lets go of anything\n * kept in memory for its next step; it holds nothing the log needs.\n */\n release?(session: { scope: object; sessionId: string }): void\n}\n\n// A global symbol, so harness values work across separately bundled copies.\nconst DRIVER = Symbol.for('experimental-a2.ai.harness')\n\nexport function defineHarness<\n Type extends 'fx' | 'pi',\n Tools extends string = never,\n>(type: Type, driver: HarnessDriver): Harness<Type, Tools> {\n const harness = { type }\n Object.defineProperty(harness, DRIVER, { value: driver })\n return Object.freeze(harness) as unknown as Harness<Type, Tools>\n}\n\nexport function harnessDriver(harness: unknown): HarnessDriver {\n const driver =\n typeof harness === 'object' && harness !== null\n ? (harness as { [DRIVER]?: HarnessDriver })[DRIVER]\n : undefined\n if (driver === undefined)\n throw new TypeError(\n \"harness must be { type: 'ai-sdk' }, or come from fx() or pi()\",\n )\n return driver\n}\n\n// ------------------------------------------------------- saved state\n\n/**\n * What an agent's steps saved in this process at the end of a response, per\n * session and harness. A cache: a cold process rebuilds from the whole log.\n */\nexport type HarnessStates = BoundedCache<\n string,\n { index: number; state: unknown }\n>\n\nexport const harnessStates = (): HarnessStates =>\n boundedCache({ maxEntries: 1_000, maxBytes: 64 * 1024 * 1024 })\n\nconst savedKey = (sessionId: string, name: string): string =>\n `${sessionId}\\u0000${name}`\n\n// Binary state counts its bytes; everything else its JSON text.\nconst sizeOf = (value: unknown): number => {\n let binary = 0\n const text = JSON.stringify(value, (_key, item: unknown) => {\n if (!(item instanceof Uint8Array)) return item\n binary += item.byteLength\n return null\n })\n return binary + (text?.length ?? 0)\n}\n\n/**\n * The session log as a harness step reads it: the state this process saved\n * at the end of the last response, when it has one, and the log after it.\n * Saved states are copied in and out, so a step cannot change one in place.\n */\nexport function harnessLog(options: {\n states: HarnessStates\n session: {\n readonly id: string\n history(bounds: {\n gte?: number\n lte?: number\n }): Promise<ContractEvent<EventDefs>[]>\n }\n}): HarnessLog {\n const { states, session } = options\n return {\n read: async ({ name, throughIndex, fresh }) => {\n const entry = fresh ? undefined : states.get(savedKey(session.id, name))\n const snapshot =\n entry === undefined || entry.index > throughIndex\n ? null\n : { index: entry.index, state: structuredClone(entry.state) }\n const events = await session.history(\n snapshot === null\n ? { lte: throughIndex }\n : { gte: snapshot.index + 1, lte: throughIndex },\n )\n return { snapshot, events }\n },\n write: async ({ name, index, state }) => {\n const copy = structuredClone(state)\n states.set(\n savedKey(session.id, name),\n { index, state: copy },\n sizeOf(copy),\n )\n },\n }\n}\n\nconst agentStates = new WeakMap<object, HarnessStates>()\n\n/** Registers an agent server's saved harness states, for `savedHarnessState`. */\nexport function registerHarnessStates(\n server: object,\n states: HarnessStates,\n): void {\n agentStates.set(server, states)\n}\n\n/** What an agent's process saved for one session's harness; tests read it. */\nexport function savedHarnessState(\n server: object,\n options: { sessionId: string; name: string },\n): { index: number; state: unknown } | undefined {\n return agentStates.get(server)?.get(savedKey(options.sessionId, options.name))\n}\n\n/** A harness's state at the end of a response, from the attempt that ran it. */\ntype SavedEnvelope = {\n generationId: string\n responseMessageId: string\n state: unknown\n}\n\nexport type HarnessRead<S> = {\n /** The state saved at the end of the last response, when it is usable. */\n saved: S | undefined\n turns: HarnessTurn[]\n results: Map<string, ToolResultPayload>\n}\n\n/**\n * Reads a harness's saved state and the log since, as turns to replay. A\n * snapshot counts only when the attempt that wrote it committed: a stopped,\n * superseded, or still-running attempt can write one the log never kept.\n * `replayAll` reads the whole log instead, for a state that fails to restore.\n */\nexport async function readHarness<S>({\n step,\n name,\n parse,\n}: {\n step: HarnessStep\n name: string\n parse: (state: unknown) => S | undefined\n}): Promise<HarnessRead<S> & { replayAll: () => Promise<HarnessRead<S>> }> {\n const replayAll = async (): Promise<HarnessRead<S>> => {\n const read = await step.log.read({\n name,\n throughIndex: step.throughIndex,\n fresh: true,\n })\n return { saved: undefined, ...harnessTurns(step, read.events, undefined) }\n }\n const read = await step.log.read({ name, throughIndex: step.throughIndex })\n if (read.snapshot === null)\n return {\n saved: undefined,\n ...harnessTurns(step, read.events, undefined),\n replayAll,\n }\n const envelope = savedEnvelope(read.snapshot.state)\n const saved = envelope === undefined ? undefined : parse(envelope.state)\n const committed =\n envelope !== undefined &&\n read.events.some(\n (event) =>\n event.type === 'ai.generation.completed' &&\n (event.payload as { generationId?: unknown }).generationId ===\n envelope.generationId,\n )\n // A snapshot of this very response belongs to an earlier attempt at it.\n if (\n saved === undefined ||\n !committed ||\n envelope.responseMessageId === step.responseMessageId\n )\n return { ...(await replayAll()), replayAll }\n return {\n saved,\n ...harnessTurns(step, read.events, envelope.responseMessageId),\n replayAll,\n }\n}\n\n/** Saves a harness's state at the end of a response; losing it costs a longer replay. */\nexport async function saveHarness({\n step,\n name,\n state,\n}: {\n step: HarnessStep\n name: string\n state: unknown\n}): Promise<void> {\n const envelope: SavedEnvelope = {\n generationId: step.generationId,\n responseMessageId: step.responseMessageId,\n state,\n }\n try {\n await step.log.write({ name, index: step.requestIndex, state: envelope })\n } catch {\n // The snapshot is a cache.\n }\n}\n\nfunction savedEnvelope(value: unknown): SavedEnvelope | undefined {\n if (typeof value !== 'object' || value === null) return undefined\n const { generationId, responseMessageId, state } = value as Record<\n string,\n unknown\n >\n return typeof generationId === 'string' &&\n typeof responseMessageId === 'string'\n ? { generationId, responseMessageId, state }\n : undefined\n}\n\n// ------------------------------------------------- the log as harness turns\n\n/** A completed generation as the log recorded it. */\nexport type RecordedGeneration = {\n chunks: UIMessageChunk[]\n completed: GenerationCompletedPayload\n}\n\n/** What user messages hand a harness: their text, and the files they attach. */\nexport type HarnessInput = { text: string; files: FileUIPart[] }\n\n/** One harness turn to rebuild: its prompt, completed generations, and end. */\nexport type HarnessTurn = {\n /** With any steering that entered before the turn's first completed generation. */\n prompt: HarnessInput\n generations: RecordedGeneration[]\n /** Steering that entered after the generation at that index. */\n steers: Map<number, HarnessInput[]>\n end: 'stop' | 'interrupted' | 'live'\n}\n\n/**\n * Groups the log since a harness snapshot into harness turns. A response\n * steered after a step continues its turn, with the next response's prompt as\n * steering. `covered` is the response the snapshot already holds.\n */\nexport function harnessTurns(\n step: HarnessStep,\n events: readonly ContractEvent<EventDefs>[],\n covered: string | undefined,\n): { turns: HarnessTurn[]; results: Map<string, ToolResultPayload> } {\n type Generation = {\n id: string\n chunks: UIMessageChunk[]\n completed?: GenerationCompletedPayload\n }\n type Response = {\n id: string\n prompt: HarnessInput[]\n generations: Generation[]\n end?: 'stop' | 'steered' | 'interrupted'\n }\n // Prompts take their content from the transcript: a harness agent never\n // runs A2's compaction, so the step's messages hold every input admitted by\n // its read, before the snapshot or after it. A session another loop\n // compacted can't move to a harness.\n const userInput = new Map<string, HarnessInput>(\n step.messages\n .filter((message) => message.role === 'user')\n .map((message) => [message.id, inputOf(message)]),\n )\n let passive: string[] = []\n const responses = new Map<string, Response>()\n const byStart = new Map<number, Generation>()\n const byId = new Map<string, Generation>()\n const results = new Map<string, ToolResultPayload>()\n\n for (const event of events) {\n const payload = event.payload as Record<string, unknown>\n switch (event.type) {\n case 'ai.message.created': {\n const admitted = event.payload as MessageCreatedPayload\n if (admitted.role === 'user' && admitted.generate === false)\n passive.push(admitted.messageId)\n break\n }\n case 'ai.generation.requested': {\n const id = payload['responseMessageId'] as string\n if (id === covered || responses.has(id)) break\n const messageId = payload['messageId'] as string\n const ids = [...passive.filter((p) => p !== messageId), messageId]\n passive = []\n responses.set(id, {\n id,\n prompt: ids.flatMap((p) => userInput.get(p) ?? []),\n generations: [],\n })\n break\n }\n case 'ai.generation.started': {\n const response = responses.get(payload['responseMessageId'] as string)\n if (response === undefined) break\n const generation: Generation = {\n id: payload['generationId'] as string,\n chunks: [],\n }\n response.generations.push(generation)\n byStart.set(event.index, generation)\n byId.set(generation.id, generation)\n break\n }\n case 'ai.generation.progress':\n byStart\n .get(payload['startIndex'] as number)\n ?.chunks.push(...(payload['chunks'] as UIMessageChunk[]))\n break\n case 'ai.generation.completed': {\n const generation = byId.get(payload['generationId'] as string)\n if (generation)\n generation.completed = event.payload as GenerationCompletedPayload\n break\n }\n case 'ai.tool.result':\n if (payload['preliminary'] !== true)\n results.set(\n payload['toolCallId'] as string,\n event.payload as ToolResultPayload,\n )\n break\n case 'ai.message.completed': {\n const response = responses.get(payload['messageId'] as string)\n if (response) response.end = 'stop'\n break\n }\n case 'ai.message.interrupted': {\n const response = responses.get(payload['messageId'] as string)\n if (response)\n response.end =\n payload['reason'] === STEERED_REASON ? 'steered' : 'interrupted'\n break\n }\n }\n }\n\n const turns: HarnessTurn[] = []\n let steered = false\n for (const response of responses.values()) {\n const input: HarnessInput = {\n text: response.prompt.map((prompt) => prompt.text).join('\\n\\n'),\n files: response.prompt.flatMap((prompt) => prompt.files),\n }\n let turn = turns.at(-1)\n if (steered && turn?.generations.length === 0) {\n // Nothing answered the prompt yet, so the steering joins it, as messages\n // queued before a response join its prompt.\n turn.prompt = {\n text: `${turn.prompt.text}\\n\\n${input.text}`,\n files: [...turn.prompt.files, ...input.files],\n }\n } else if (steered && turn !== undefined) {\n const at = turn.generations.length - 1\n turn.steers.set(at, [...(turn.steers.get(at) ?? []), input])\n } else {\n turn = { prompt: input, generations: [], steers: new Map(), end: 'live' }\n turns.push(turn)\n }\n for (const generation of response.generations)\n if (generation.completed !== undefined)\n turn.generations.push({\n chunks: generation.chunks,\n completed: generation.completed,\n })\n turn.end =\n response.id === step.responseMessageId\n ? 'live'\n : response.end === 'stop'\n ? 'stop'\n : 'interrupted'\n steered = response.end === 'steered'\n }\n return { turns, results }\n}\n\nfunction inputOf(message: UIMessage): HarnessInput {\n return {\n text: message.parts\n .flatMap((part) => (part.type === 'text' ? [part.text] : []))\n .join('\\n'),\n files: message.parts.filter(\n (part): part is FileUIPart => part.type === 'file',\n ),\n }\n}\n\n// ------------------------------------------------------------- attachments\n\n/** An attached image as an engine takes it: its bytes in base64. */\nexport type HarnessImage = { mediaType: string; data: string }\n\n// What one message's files hold in a step's memory, at most.\nconst MAX_ATTACHMENT_BYTES = 32 * 1024 * 1024\n\ntype Attachment = { data: string; bytes: number }\n\n// Every step of a response replays its turn, and a URL's file doesn't change,\n// so the process keeps what it fetched.\nconst attachments = boundedCache<string, Attachment>({\n maxEntries: 256,\n maxBytes: 64 * 1024 * 1024,\n})\n\n/**\n * The images an input attaches, as its engine takes them: engines take\n * bytes, not URLs. A file it can't attach fails the step, naming the file,\n * and is never left out: the failure may pass, and the step is retried like\n * any other.\n */\nexport async function harnessImages({\n harness,\n input,\n signal,\n}: {\n /** As the error names it: `fx()` or `pi()`. */\n harness: string\n input: HarnessInput\n signal: AbortSignal\n}): Promise<HarnessImage[]> {\n const images: HarnessImage[] = []\n let budget = MAX_ATTACHMENT_BYTES\n // One at a time, so the budget bounds what the step holds.\n for (const file of input.files) {\n try {\n if (!file.mediaType.startsWith('image/'))\n throw new Error(`it takes images only, not ${file.mediaType}`)\n const attached = await attachment({ url: file.url, budget, signal })\n budget -= attached.bytes\n images.push({\n mediaType: file.mediaType,\n data: attached.data,\n })\n } catch (error) {\n if (signal.aborted) throw error\n throw new Error(\n `${harness} couldn't attach ${file.filename ?? 'a file'}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n { cause: error },\n )\n }\n }\n return images\n}\n\nasync function attachment({\n url,\n budget,\n signal,\n}: {\n url: string\n budget: number\n signal: AbortSignal\n}): Promise<Attachment> {\n const tooLarge = () =>\n new Error(\n `a message's files may hold ${MAX_ATTACHMENT_BYTES / 1024 / 1024} MiB in a step`,\n )\n const cached = attachments.get(url)\n if (cached !== undefined) {\n if (cached.bytes > budget) throw tooLarge()\n return cached\n }\n const { protocol } = new URL(url)\n if (protocol !== 'https:' && protocol !== 'data:')\n throw new Error(`attachments must be https: or data: URLs, not ${protocol}`)\n // AI SDK's download checks an https: URL's address and its redirects,\n // which a message's sender chooses; a data: URL holds its bytes.\n const bytes =\n protocol === 'data:'\n ? dataBytes(url)\n : (\n await createDownload({ maxBytes: budget })({\n url: new URL(url),\n abortSignal: signal,\n })\n ).data\n if (bytes.byteLength > budget) throw tooLarge()\n const attached: Attachment = {\n data: Buffer.from(bytes).toString('base64'),\n bytes: bytes.byteLength,\n }\n if (protocol === 'https:')\n attachments.set(url, attached, attached.data.length)\n return attached\n}\n\nfunction dataBytes(url: string): Uint8Array {\n const comma = url.indexOf(',')\n if (comma === -1) throw new Error('its data: URL has no data')\n const body = url.slice(comma + 1)\n return url.slice(0, comma).endsWith(';base64')\n ? Buffer.from(body, 'base64')\n : Buffer.from(decodeURIComponent(body))\n}\n\n// ------------------------------------------------------------- Gateway\n\nexport type GatewayCredential = { token: string; method: 'api-key' | 'oidc' }\n\n/** Read per step: on Vercel the OIDC token rides on each function request. */\nexport function gatewayCredential(harness: string): GatewayCredential {\n const key = process.env['AI_GATEWAY_API_KEY']\n if (key) return { token: key, method: 'api-key' }\n const oidc = requestOidcToken() ?? process.env['VERCEL_OIDC_TOKEN']\n if (oidc) return { token: oidc, method: 'oidc' }\n throw new Error(`${harness}() needs AI_GATEWAY_API_KEY or VERCEL_OIDC_TOKEN`)\n}\n\n/**\n * The `x-session-affinity` an agent session's Gateway calls carry: stable for\n * the session, so supporting providers can reuse their cache, and free of its\n * raw ID.\n */\nexport function gatewaySessionAffinity({\n agentName,\n sessionId,\n}: {\n agentName: string\n sessionId: string\n}): string {\n return createHash('sha256')\n .update(JSON.stringify([agentName, sessionId]))\n .digest('hex')\n}\n\n// Where Vercel Functions keep the current request, as @vercel/oidc reads it.\nconst REQUEST_CONTEXT = Symbol.for('@vercel/request-context')\n\nfunction requestOidcToken(): string | undefined {\n const context = (\n globalThis as {\n [REQUEST_CONTEXT]?: { get?: () => { headers?: Record<string, unknown> } }\n }\n )[REQUEST_CONTEXT]\n const token = context?.get?.()?.headers?.['x-vercel-oidc-token']\n return typeof token === 'string' && token !== '' ? token : undefined\n}\n\n// ------------------------------------------------------------- helpers\n\nexport class Divergence extends Error {\n constructor(harness: string, message: string) {\n super(`${harness} replay diverged from the log: ${message}`)\n }\n}\n\nexport type Deferred = { promise: Promise<void>; resolve: () => void }\n\nexport function deferred(): Deferred {\n let resolve!: () => void\n const promise = new Promise<void>((done) => {\n resolve = done\n })\n return { promise, resolve }\n}\n\n/** A result that never settles; the harness is stopped instead. */\nexport function pending(\n signal: AbortSignal | null | undefined,\n): Promise<never> {\n return new Promise((_, reject) => {\n if (signal?.aborted) reject(signal.reason)\n signal?.addEventListener('abort', () => reject(signal.reason), {\n once: true,\n })\n })\n}\n"],"mappings":"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"}
|
package/dist/ai-pi.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as HarnessSandbox, r as HarnessSandboxContext, t as Harness } from "./ai-harness-
|
|
1
|
+
import { n as HarnessSandbox, r as HarnessSandboxContext, t as Harness } from "./ai-harness-DTjkAr2T.js";
|
|
2
2
|
//#region src/ai-pi.d.ts
|
|
3
3
|
/** What `pi()` takes. */
|
|
4
4
|
type PiOptions = {
|
package/dist/ai-pi.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { d as ambientToolScopeStorage } from "./internal-CvsrprnU.js";
|
|
2
2
|
import { t as boundedCache } from "./bounded-cache-CPHWF-8h.js";
|
|
3
3
|
import { d as readHarness, i as gatewayCredential, n as deferred, p as saveHarness, r as defineHarness, s as harnessImages, t as Divergence, u as pending } from "./ai-harness-dQeJaR1t.js";
|
|
4
4
|
import { t as schemaInput } from "./ai-tool-input-DmSqMUQi.js";
|
package/dist/ai-react.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { s as Contract, t as Reducer, u as EventDefs } from "./reducer-CYPHsSmd.js";
|
|
2
2
|
import { t as InitialSnapshot } from "./initial-snapshot-D3Lve43o.js";
|
|
3
3
|
import { t as A2Client, u as PushResult } from "./client-CUMbS_38.js";
|
|
4
|
-
import { B as PendingApproval, V as PendingInput, d as AgentStatus, i as AIState, t as AIEventDefs } from "./ai-
|
|
4
|
+
import { B as PendingApproval, V as PendingInput, d as AgentStatus, i as AIState, t as AIEventDefs } from "./ai-DAu77CDr.js";
|
|
5
5
|
import { AgentClient, AgentMessage, AnyAgent } from "./ai-client.js";
|
|
6
6
|
import { c as UseSessionResult } from "./react-BoHvC3v9.js";
|
|
7
7
|
import { JSONValue, UIMessage } from "ai";
|
package/dist/ai-react.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { t as createClient } from "./client-
|
|
2
|
+
import { t as createClient } from "./client-BQS4i_V9.js";
|
|
3
3
|
import { useSession } from "./react.js";
|
|
4
|
-
import { s as inputs, x as isCompactionPart } from "./ai-
|
|
4
|
+
import { s as inputs, x as isCompactionPart } from "./ai-EEfKyc-z.js";
|
|
5
5
|
import { createAgentClient } from "./ai-client.js";
|
|
6
6
|
//#region src/ai-agent-actions.ts
|
|
7
7
|
const visible = (part) => part.type === "text" ? part.text.length > 0 : part.type !== "reasoning" && part.type !== "step-start" && !isCompactionPart(part);
|
package/dist/ai-server.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { c as ContractEvent, s as Contract, t as Reducer, u as EventDefs, v as StandardSchemaV1 } from "./reducer-CYPHsSmd.js";
|
|
2
|
-
import { i as Consumer, n as A2Server, o as ConsumerContext, v as ServerOptions } from "./server-
|
|
3
|
-
import { H as ReasoningLevel, a as AI_STATE_REDUCER, f as AgentToolContext, i as AIState, p as AgentToolEventDefs, r as AIMessageOf, t as AIEventDefs } from "./ai-
|
|
4
|
-
import { t as Harness } from "./ai-harness-
|
|
2
|
+
import { i as Consumer, n as A2Server, o as ConsumerContext, v as ServerOptions } from "./server-qgez0Xny.js";
|
|
3
|
+
import { H as ReasoningLevel, a as AI_STATE_REDUCER, f as AgentToolContext, i as AIState, p as AgentToolEventDefs, r as AIMessageOf, t as AIEventDefs } from "./ai-DAu77CDr.js";
|
|
4
|
+
import { t as Harness } from "./ai-harness-DTjkAr2T.js";
|
|
5
5
|
import { Instructions, ModelMessage, TextStreamPart, ToolSet, ToolSet as ToolSet$1, UIMessage, UIMessageChunk, streamText } from "ai";
|
|
6
6
|
//#region src/ai-sdk-step.d.ts
|
|
7
7
|
type StreamTextOptions<T extends ToolSet$1> = Parameters<typeof streamText<T>>[0];
|
package/dist/ai-server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-server.d.ts","names":[],"sources":["../src/ai-sdk-step.ts","../src/ai-agent.ts"],"mappings":";;;;;;KAiBK,kBAAkB,UAAU,aAAW,kBAAkB,WAAW;KAKpE,iBAAiB,UAAU,sBAE5B,KACE,QAAQ,kBAAkB;KAI3B;KAmBA;KASA,iBAAiB,UAAU,yBACpB,WAAW,IAAI,KAAK,EAAE;KAG7B,aAAa,UAAU,WAAS,gBAAgB,kBAAkB,MACrE,YAAY,kBAAkB,GAAG,cAAa,aAAa,qBACvD;KAGD,qBAAqB,UAAU,aAAW,KAC7C,aAAa;WAGJ,OAAO,iBAAiB;;KAG9B,yBAAyB,UAAU,aAAW,KACjD,aAAa;WAGJ,OAAO,iBAAiB;;KAG9B,mBAAmB,UAAU,OAAO,iBAAiB;KAErD,yBAAyB,UAAU,aAAW,YACjD,kBAAkB;KAGf,yBAAyB,UAAU,cACtC,SAAS,KAAK,WAAW,yBAAyB;EAChD,OAAO,iBAAiB;MAEvB,WAAW,yBAAyB;KAEpC,2BAA2B,UAAU,aAAW,YACnD,kBAAkB;KAGf,oBAAoB,UAAU,aAAW,QAC5C,2BAA2B,QACvB;KAGD,oBAAoB,UAAU,cACjC,SAAS,KAAK,WAAW,oBAAoB;EAC3C,OAAO,iBAAiB;MAEvB,WAAW,oBAAoB;;KAGxB,kBAAkB,UAAU,aAAW,QACjD,KACE,kBAAkB,IAClB,8BAA8B;EAGhC,UAAU,iBAAiB;EAC3B,UAAU,mBAAmB,qBAAqB;EAClD,uBAAuB,mBAAmB,qBAAqB;EAC/D,cAAc,mBAAmB,yBAAyB;EAC1D,2BAA2B,mBAAmB,yBAAyB;EACvE,iBAAiB,yBAAyB;EAC1C,8BAA8B,yBAAyB;EACvD,yBACE,oBAAoB,KAAK,MAAM,oBAAoB;;;;
|
|
1
|
+
{"version":3,"file":"ai-server.d.ts","names":[],"sources":["../src/ai-sdk-step.ts","../src/ai-agent.ts"],"mappings":"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"}
|
package/dist/ai-server.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { i as A2Error } from "./session-clear-wHd5GCpZ.js";
|
|
2
2
|
import { a as reducer, s as validateSync } from "./reducer-CjmvZSAb.js";
|
|
3
|
-
import {
|
|
3
|
+
import { _ as installAmbientToolScopeStorage, g as failedPreviousAttempt, h as drainSettledHooks, m as consumeSchedulerSendFailure } from "./internal-CvsrprnU.js";
|
|
4
4
|
import { t as boundedCache } from "./bounded-cache-CPHWF-8h.js";
|
|
5
|
-
import { a as CONSOLE_TELEMETRY, n as createServer, o as setServerFetchHooks } from "./server-
|
|
5
|
+
import { a as CONSOLE_TELEMETRY, n as createServer, o as setServerFetchHooks } from "./server-DtFrWxi3.js";
|
|
6
6
|
import { a as unexplainedAdmission, i as steersOn, n as initialControlState, r as steeringEnd, t as STEERED_REASON } from "./ai-control-CvLbW10B.js";
|
|
7
|
-
import { A as interruptUIMessage, C as reduceToolActivity, D as flattenProgressBatches, E as upsertResponse, M as assertMessageBase, N as restoreMessageBase, O as progressBatches, S as projectedMessage, T as upsertMessage, _ as applyControlChanges, b as findMessage, d as appSlice, f as isAgentContract, g as isUIMessage, h as aiEventId, j as pauseUIMessage, k as progressOwnedBy, m as AI_EVENT_ID_PREFIX, n as agentContract, o as events, p as withAIStorage, u as appReducerOf, v as controlCommit, w as retainMessageBase, y as advanceProjection } from "./ai-
|
|
7
|
+
import { A as interruptUIMessage, C as reduceToolActivity, D as flattenProgressBatches, E as upsertResponse, M as assertMessageBase, N as restoreMessageBase, O as progressBatches, S as projectedMessage, T as upsertMessage, _ as applyControlChanges, b as findMessage, d as appSlice, f as isAgentContract, g as isUIMessage, h as aiEventId, j as pauseUIMessage, k as progressOwnedBy, m as AI_EVENT_ID_PREFIX, n as agentContract, o as events, p as withAIStorage, u as appReducerOf, v as controlCommit, w as retainMessageBase, y as advanceProjection } from "./ai-EEfKyc-z.js";
|
|
8
8
|
import { a as gatewaySessionAffinity, c as harnessLog, f as registerHarnessStates, l as harnessStates, o as harnessDriver } from "./ai-harness-dQeJaR1t.js";
|
|
9
|
-
import { t as runtimeConsumer } from "./runtime-consumer-
|
|
9
|
+
import { t as runtimeConsumer } from "./runtime-consumer-CIeL33wj.js";
|
|
10
10
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
11
11
|
import { asSchema, convertToModelMessages, isToolUIPart, stepCountIs, streamText, toUIMessageStream } from "ai";
|
|
12
12
|
import { Buffer } from "node:buffer";
|
|
@@ -2627,6 +2627,7 @@ function createConsumers(options) {
|
|
|
2627
2627
|
};
|
|
2628
2628
|
const updates = consumeGeneration({
|
|
2629
2629
|
source: harness !== void 0 ? await harness.step({
|
|
2630
|
+
scope: states,
|
|
2630
2631
|
agentName: options.contract.name,
|
|
2631
2632
|
sessionId: ctx.event.sessionId,
|
|
2632
2633
|
requestIndex: ctx.event.index,
|
|
@@ -2882,6 +2883,11 @@ function agent(options) {
|
|
|
2882
2883
|
consumers: [...builtIns, ...serverOptions.consumers ?? []]
|
|
2883
2884
|
});
|
|
2884
2885
|
registerHarnessStates(server, states);
|
|
2886
|
+
const release = (harness === void 0 || harness.type === "ai-sdk" ? void 0 : harnessDriver(harness))?.release;
|
|
2887
|
+
if (release !== void 0) drainSettledHooks.set(server, (sessionId) => release({
|
|
2888
|
+
scope: states,
|
|
2889
|
+
sessionId
|
|
2890
|
+
}));
|
|
2885
2891
|
const applicationEvents = "contract" in rest;
|
|
2886
2892
|
setServerFetchHooks(server, { validateIngress: (context) => validateAgentIngress({
|
|
2887
2893
|
context,
|