experimental-a2 0.17.0 → 0.18.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.
Files changed (211) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +1 -1
  3. package/dist/{actor-DBbe5xeE.d.ts → actor-Bjce9_5U.d.ts} +5 -5
  4. package/dist/{actor-DBbe5xeE.d.ts.map → actor-Bjce9_5U.d.ts.map} +1 -1
  5. package/dist/actor-client.d.ts +1 -1
  6. package/dist/actor-react.d.ts +3 -3
  7. package/dist/actor-react.js +1 -1
  8. package/dist/actor.d.ts +1 -1
  9. package/dist/actor.js +2 -2
  10. package/dist/{ai-UrWfKeuv.js → ai-BpOGm-zB.js} +2 -2
  11. package/dist/{ai-UrWfKeuv.js.map → ai-BpOGm-zB.js.map} +1 -1
  12. package/dist/{ai-vpGtcaCB.d.ts → ai-DrUaMyRt.d.ts} +3 -3
  13. package/dist/{ai-vpGtcaCB.d.ts.map → ai-DrUaMyRt.d.ts.map} +1 -1
  14. package/dist/ai-client.d.ts +2 -2
  15. package/dist/ai-client.js +2 -2
  16. package/dist/ai-fx.browser.d.ts +1 -0
  17. package/dist/ai-fx.browser.js +6 -0
  18. package/dist/ai-fx.browser.js.map +1 -0
  19. package/dist/ai-fx.d.ts +10 -0
  20. package/dist/ai-fx.d.ts.map +1 -0
  21. package/dist/ai-fx.js +634 -0
  22. package/dist/ai-fx.js.map +1 -0
  23. package/dist/ai-harness-BuLh_EYi.js +273 -0
  24. package/dist/ai-harness-BuLh_EYi.js.map +1 -0
  25. package/dist/ai-harness-DvsPeJeB.d.ts +26 -0
  26. package/dist/ai-harness-DvsPeJeB.d.ts.map +1 -0
  27. package/dist/ai-pi.browser.d.ts +1 -0
  28. package/dist/ai-pi.browser.js +6 -0
  29. package/dist/ai-pi.browser.js.map +1 -0
  30. package/dist/ai-pi.d.ts +17 -0
  31. package/dist/ai-pi.d.ts.map +1 -0
  32. package/dist/ai-pi.js +904 -0
  33. package/dist/ai-pi.js.map +1 -0
  34. package/dist/ai-react.d.ts +5 -5
  35. package/dist/ai-react.js +2 -2
  36. package/dist/ai-server.d.ts +47 -25
  37. package/dist/ai-server.d.ts.map +1 -1
  38. package/dist/ai-server.js +106 -37
  39. package/dist/ai-server.js.map +1 -1
  40. package/dist/ai.d.ts +1 -1
  41. package/dist/ai.js +1 -1
  42. package/dist/bounded-cache-CPHWF-8h.js +41 -0
  43. package/dist/bounded-cache-CPHWF-8h.js.map +1 -0
  44. package/dist/{client-COaiaDub.js → client-BReKqwZC.js} +3 -3
  45. package/dist/{client-COaiaDub.js.map → client-BReKqwZC.js.map} +1 -1
  46. package/dist/{client-qRnRUzBS.d.ts → client-h_DKFAjj.d.ts} +3 -3
  47. package/dist/{client-qRnRUzBS.d.ts.map → client-h_DKFAjj.d.ts.map} +1 -1
  48. package/dist/client.d.ts +3 -3
  49. package/dist/client.js +1 -1
  50. package/dist/index.d.ts +5 -5
  51. package/dist/{initial-snapshot-BrLso5x_.d.ts → initial-snapshot-Bx-c0pDP.d.ts} +2 -2
  52. package/dist/{initial-snapshot-BrLso5x_.d.ts.map → initial-snapshot-Bx-c0pDP.d.ts.map} +1 -1
  53. package/dist/{internal-CbVvEwad.js → internal-B2T3U4Rq.js} +8 -2
  54. package/dist/internal-B2T3U4Rq.js.map +1 -0
  55. package/dist/otel.d.ts +1 -1
  56. package/dist/{react-BbJ-g6Uu.d.ts → react-BBXgHWFc.d.ts} +4 -4
  57. package/dist/{react-BbJ-g6Uu.d.ts.map → react-BBXgHWFc.d.ts.map} +1 -1
  58. package/dist/react.d.ts +1 -1
  59. package/dist/react.js +1 -1
  60. package/dist/reducer-BAXc8JyF.js.map +1 -1
  61. package/dist/{reducer-BYu6DT7h.d.ts → reducer-CqA59Q6U.d.ts} +2 -2
  62. package/dist/{reducer-BYu6DT7h.d.ts.map → reducer-CqA59Q6U.d.ts.map} +1 -1
  63. package/dist/scheduler-qstash.d.ts +2 -2
  64. package/dist/scheduler-qstash.js +2 -2
  65. package/dist/scheduler-vercel.d.ts +2 -2
  66. package/dist/scheduler-vercel.js +2 -2
  67. package/dist/{server-Rj64fNvX.js → server-Dw_k_7kO.js} +93 -60
  68. package/dist/server-Dw_k_7kO.js.map +1 -0
  69. package/dist/{server-CGM8WzRG.d.ts → server-En7cbi3v.d.ts} +36 -7
  70. package/dist/server-En7cbi3v.d.ts.map +1 -0
  71. package/dist/server.d.ts +4 -4
  72. package/dist/server.js +1 -1
  73. package/dist/{session-clear-Dshpuhff.d.ts → session-clear-Bo-XTFEy.d.ts} +2 -2
  74. package/dist/session-clear-Bo-XTFEy.d.ts.map +1 -0
  75. package/dist/{store-Gpkq7WBo.d.ts → store-Djdv0bnZ.d.ts} +31 -2
  76. package/dist/store-Djdv0bnZ.d.ts.map +1 -0
  77. package/dist/store-N8PXxDAS.js.map +1 -1
  78. package/dist/store-memory.d.ts +1 -1
  79. package/dist/store-memory.js +32 -6
  80. package/dist/store-memory.js.map +1 -1
  81. package/dist/{store-polling-4_y8lETm.js → store-polling-B5FcwJfo.js} +2 -2
  82. package/dist/{store-polling-4_y8lETm.js.map → store-polling-B5FcwJfo.js.map} +1 -1
  83. package/dist/store-postgres.d.ts +1 -1
  84. package/dist/store-postgres.js +54 -3
  85. package/dist/store-postgres.js.map +1 -1
  86. package/dist/{store-presence-polling-Q1C-k2dd.js → store-presence-polling-BAwS8RWn.js} +3 -3
  87. package/dist/{store-presence-polling-Q1C-k2dd.js.map → store-presence-polling-BAwS8RWn.js.map} +1 -1
  88. package/dist/store-redis-http.d.ts +1 -1
  89. package/dist/store-redis-http.js +3 -3
  90. package/dist/{store-redis-notify-tqWNnMRu.js → store-redis-notify-BRITti_o.js} +187 -68
  91. package/dist/store-redis-notify-BRITti_o.js.map +1 -0
  92. package/dist/store-redis.d.ts +1 -1
  93. package/dist/store-redis.js +1 -1
  94. package/dist/store-sqlite.d.ts +1 -1
  95. package/dist/store-sqlite.js +32 -2
  96. package/dist/store-sqlite.js.map +1 -1
  97. package/dist/{telemetry-cZH-cibj.d.ts → telemetry-Cyfqo6Qt.d.ts} +2 -2
  98. package/dist/telemetry-Cyfqo6Qt.d.ts.map +1 -0
  99. package/dist/{wire-DjfHOvdW.js → wire-rSUglYo8.js} +2 -2
  100. package/dist/{wire-DjfHOvdW.js.map → wire-rSUglYo8.js.map} +1 -1
  101. package/docs/01-quickstart.mdx +49 -83
  102. package/docs/{guides/07-examples.mdx → 02-examples.mdx} +10 -20
  103. package/docs/actors/01-introduction.mdx +48 -86
  104. package/docs/actors/02-concurrency.mdx +43 -70
  105. package/docs/actors/03-timers.mdx +28 -42
  106. package/docs/actors/04-routes.mdx +110 -150
  107. package/docs/actors/meta.ts +1 -1
  108. package/docs/agents/01-introduction.mdx +257 -0
  109. package/docs/agents/02-tools.mdx +247 -0
  110. package/docs/agents/03-conversations.mdx +538 -0
  111. package/docs/agents/04-harnesses.mdx +136 -0
  112. package/docs/agents/meta.ts +1 -0
  113. package/docs/client/01-react.mdx +294 -0
  114. package/docs/client/02-transports.mdx +86 -0
  115. package/docs/client/03-presence.mdx +224 -0
  116. package/docs/client/meta.ts +1 -0
  117. package/docs/contract/01-events.mdx +57 -0
  118. package/docs/contract/02-state.mdx +142 -0
  119. package/docs/contract/03-sessions.mdx +175 -0
  120. package/docs/index.mdx +108 -144
  121. package/docs/reference/01-core.mdx +139 -0
  122. package/docs/reference/02-server.mdx +855 -0
  123. package/docs/reference/03-client.mdx +301 -0
  124. package/docs/reference/04-ai.mdx +722 -0
  125. package/docs/reference/05-adapters.mdx +351 -0
  126. package/docs/reference/06-errors.mdx +138 -0
  127. package/docs/reference/meta.ts +1 -1
  128. package/docs/server/01-handlers.mdx +200 -0
  129. package/docs/server/02-timers.mdx +130 -0
  130. package/docs/server/03-cancellation.mdx +141 -0
  131. package/docs/server/04-routes.mdx +243 -0
  132. package/docs/server/05-production.mdx +322 -0
  133. package/docs/server/06-guarantees.mdx +123 -0
  134. package/examples/playground/DEPLOY.md +1 -1
  135. package/examples/playground/app/a0/[appId]/page.tsx +11 -6
  136. package/examples/playground/app/a0/app-list.tsx +9 -2
  137. package/examples/playground/app/a0/app-view.tsx +9 -1
  138. package/examples/playground/app/a0/apps/[appId]/route.ts +10 -5
  139. package/examples/playground/app/a0/apps.test.ts +12 -0
  140. package/examples/playground/app/a0/apps.ts +22 -7
  141. package/examples/playground/app/a0/chat.tsx +47 -1
  142. package/examples/playground/app/a0/fx/events/route.ts +9 -0
  143. package/examples/playground/app/a0/fx/scheduler/route.ts +7 -0
  144. package/examples/playground/app/a0/fx/short/events/route.ts +11 -0
  145. package/examples/playground/app/a0/model.test.ts +18 -1
  146. package/examples/playground/app/a0/model.ts +47 -10
  147. package/examples/playground/app/a0/no-photon.cjs +4 -0
  148. package/examples/playground/app/a0/no-quickjs.cjs +3 -0
  149. package/examples/playground/app/a0/pi/events/route.ts +9 -0
  150. package/examples/playground/app/a0/pi/scheduler/route.ts +7 -0
  151. package/examples/playground/app/a0/pi/short/events/route.ts +11 -0
  152. package/examples/playground/app/a0/sandbox-log.ts +10 -4
  153. package/examples/playground/app/a0/sandbox-session.ts +152 -0
  154. package/examples/playground/app/a0/scheduler/route.ts +2 -2
  155. package/examples/playground/app/a0/server.ts +100 -45
  156. package/examples/playground/app/a0/session.ts +17 -5
  157. package/examples/playground/app/a0/shell.tsx +11 -3
  158. package/examples/playground/app/agent/ingress.test.ts +6 -3
  159. package/examples/playground/app/agent/server.ts +9 -6
  160. package/examples/playground/app/agent/telemetry.test.ts +71 -50
  161. package/examples/playground/app/canvas/[canvasId]/canvas-client.tsx +1 -1
  162. package/examples/playground/app/chat/server.test.ts +30 -26
  163. package/examples/playground/app/chat/server.ts +46 -46
  164. package/examples/playground/next.config.ts +17 -5
  165. package/examples/playground/package.json +4 -1
  166. package/examples/playground/vercel.json +18 -0
  167. package/package.json +27 -3
  168. package/src/ai-agent.ts +261 -87
  169. package/src/ai-fx.browser.ts +5 -0
  170. package/src/ai-fx.ts +833 -0
  171. package/src/ai-harness.ts +518 -0
  172. package/src/ai-model-metadata.ts +11 -6
  173. package/src/ai-pi.browser.ts +5 -0
  174. package/src/ai-pi.ts +1263 -0
  175. package/src/ai-sdk-step.ts +2 -2
  176. package/src/ai-server.ts +3 -1
  177. package/src/internal.ts +7 -0
  178. package/src/libfx.d.ts +34 -0
  179. package/src/reducer.ts +1 -1
  180. package/src/server.ts +177 -19
  181. package/src/store-memory.ts +42 -7
  182. package/src/store-postgres.ts +80 -0
  183. package/src/store-redis-core.ts +210 -79
  184. package/src/store-redis-notify.ts +2 -0
  185. package/src/store-sqlite.ts +60 -0
  186. package/src/store.ts +33 -0
  187. package/src/telemetry.ts +1 -0
  188. package/dist/internal-CbVvEwad.js.map +0 -1
  189. package/dist/server-CGM8WzRG.d.ts.map +0 -1
  190. package/dist/server-Rj64fNvX.js.map +0 -1
  191. package/dist/session-clear-Dshpuhff.d.ts.map +0 -1
  192. package/dist/store-Gpkq7WBo.d.ts.map +0 -1
  193. package/dist/store-redis-notify-tqWNnMRu.js.map +0 -1
  194. package/dist/telemetry-cZH-cibj.d.ts.map +0 -1
  195. package/docs/concepts/01-contracts.mdx +0 -237
  196. package/docs/concepts/02-handlers.mdx +0 -284
  197. package/docs/concepts/03-durability.mdx +0 -314
  198. package/docs/concepts/04-state.mdx +0 -209
  199. package/docs/guides/01-timers.mdx +0 -193
  200. package/docs/guides/02-cancellation.mdx +0 -161
  201. package/docs/guides/03-react.mdx +0 -324
  202. package/docs/guides/04-local-first.mdx +0 -47
  203. package/docs/guides/05-production.mdx +0 -534
  204. package/docs/guides/06-ai-agents.mdx +0 -1408
  205. package/docs/guides/08-application-data.mdx +0 -113
  206. package/docs/guides/09-presence.mdx +0 -262
  207. package/docs/guides/10-transports.mdx +0 -221
  208. package/docs/reference/01-api.mdx +0 -2358
  209. package/docs/reference/02-errors.mdx +0 -150
  210. /package/docs/{concepts → contract}/meta.ts +0 -0
  211. /package/docs/{guides → server}/meta.ts +0 -0
package/dist/ai-fx.js ADDED
@@ -0,0 +1,634 @@
1
+ import { t as boundedCache } from "./bounded-cache-CPHWF-8h.js";
2
+ import { c as pending, d as saveHarness, i as gatewayCredential, l as readHarness, n as deferred, r as defineHarness, t as Divergence } from "./ai-harness-BuLh_EYi.js";
3
+ import { asSchema } from "ai";
4
+ //#region src/ai-fx.ts
5
+ /**
6
+ * experimental-a2/ai/fx — `fx()`: the FX harness for `agent({ harness })`.
7
+ * Server-only; FX's kernel comes from the optional `libfx` peer dependency.
8
+ */
9
+ /**
10
+ * The FX harness. Every model call FX makes is one durable step; A2 rebuilds
11
+ * the kernel for each step from its latest checkpoint and the log since.
12
+ */
13
+ function fx() {
14
+ return defineHarness("fx", {
15
+ tools: {},
16
+ step: runStep
17
+ });
18
+ }
19
+ const SNAPSHOT_VERSION = 1;
20
+ const CATALOG_TTL_MS = 36e5;
21
+ function fail(state, error) {
22
+ state.failure ??= error;
23
+ state.fxTurn.cancel();
24
+ throw state.failure;
25
+ }
26
+ const diverged = (message) => new Divergence("FX", message);
27
+ async function runStep(step) {
28
+ const { createFxAgent } = await loadLibfx();
29
+ const credential = gatewayCredential("fx");
30
+ const name = `${step.agentName}.a2.harness.fx`;
31
+ const { replayAll, ...first } = await readHarness({
32
+ step,
33
+ name,
34
+ parse: savedCheckpoint
35
+ });
36
+ let read = first;
37
+ let completion = {};
38
+ let stopped = false;
39
+ let agent;
40
+ let turn;
41
+ const run = async (controller, turns) => {
42
+ controller.enqueue({
43
+ type: "start",
44
+ messageId: step.responseMessageId
45
+ });
46
+ for (const plan of turns) {
47
+ if (stopped) return;
48
+ const fxTurn = agent.prompt(plan.prompt, { signal: step.signal });
49
+ const state = {
50
+ plan,
51
+ fxTurn,
52
+ next: 0,
53
+ served: [],
54
+ answered: /* @__PURE__ */ new Set(),
55
+ live: false,
56
+ liveStep: false,
57
+ toolsRequested: deferred()
58
+ };
59
+ turn = state;
60
+ const drained = (async () => {
61
+ for await (const _ of fxTurn);
62
+ })();
63
+ drained.catch(() => {});
64
+ if (plan.end !== "live") {
65
+ const result = await fxTurn.result.catch((error) => {
66
+ throw state.failure ?? error;
67
+ });
68
+ await drained;
69
+ if (state.failure) throw state.failure;
70
+ const expected = plan.end === "stop" ? "end_turn" : "cancelled";
71
+ if (result.stopReason !== expected) throw diverged(`a recorded turn ended with ${result.stopReason}, not ${expected}`);
72
+ continue;
73
+ }
74
+ const outcome = await Promise.race([fxTurn.result.then((result) => ({
75
+ ended: true,
76
+ result
77
+ })), state.toolsRequested.promise.then(() => ({ ended: false }))]).catch((error) => {
78
+ throw state.failure ?? error;
79
+ });
80
+ if (state.failure) throw state.failure;
81
+ await state.parsed;
82
+ if (outcome.ended) {
83
+ await drained;
84
+ if (outcome.result.stopReason === "end_turn" && !step.signal.aborted) await saveCheckpoint();
85
+ } else fxTurn.cancel();
86
+ controller.enqueue({ type: "finish-step" });
87
+ controller.enqueue({
88
+ type: "finish",
89
+ ...completion.finishReason === void 0 ? {} : { finishReason: completion.finishReason }
90
+ });
91
+ }
92
+ };
93
+ const saveCheckpoint = async () => {
94
+ let checkpoint;
95
+ try {
96
+ checkpoint = await agent.checkpoint();
97
+ } catch {
98
+ return;
99
+ }
100
+ await saveHarness({
101
+ step,
102
+ name,
103
+ state: {
104
+ version: SNAPSHOT_VERSION,
105
+ checkpoint: Buffer.from(checkpoint).toString("base64")
106
+ }
107
+ });
108
+ };
109
+ const hostFetch = async (input, init = {}) => {
110
+ const url = input instanceof Request ? input.url : String(input);
111
+ if ((init.method ?? "GET").toUpperCase() === "GET") return catalog(url, credential, withAuth(init.headers, credential));
112
+ const state = turn;
113
+ if (state.failure) throw state.failure;
114
+ const compaction = isCompaction(decodeBody(init.body));
115
+ const recorded = state.plan.calls[state.next];
116
+ if (recorded !== void 0) {
117
+ if (recorded.kind === "compaction" !== compaction) fail(state, diverged(`request ${state.next} is not a ${recorded.kind}`));
118
+ const index = state.next++;
119
+ if (recorded.kind === "step") {
120
+ state.served = recorded.toolCalls;
121
+ state.answered = /* @__PURE__ */ new Set();
122
+ }
123
+ const steers = state.plan.steers.get(index) ?? [];
124
+ return relay(recorded.sse, async () => {
125
+ for (const text of steers) await state.fxTurn.steer(text);
126
+ });
127
+ }
128
+ if (state.plan.end === "stop") fail(state, diverged("FX continued a turn the log ended"));
129
+ if (state.plan.end === "interrupted") {
130
+ state.fxTurn.cancel();
131
+ throw new DOMException("The recorded turn was interrupted", "AbortError");
132
+ }
133
+ if (state.liveStep && !compaction) {
134
+ state.toolsRequested.resolve();
135
+ return pending(init.signal);
136
+ }
137
+ state.live = true;
138
+ return live(state, url, init, compaction);
139
+ };
140
+ let compactions = 0;
141
+ const live = async (state, url, init, compaction) => {
142
+ const response = await fetch(url, {
143
+ ...init,
144
+ headers: withAuth(init.headers, credential)
145
+ });
146
+ if (!response.ok || response.body === null) return response;
147
+ const [forKernel, forLog] = response.body.tee();
148
+ const previous = state.parsed;
149
+ if (compaction) {
150
+ const id = `fx-compaction:${step.requestIndex}:${compactions++}`;
151
+ state.parsed = (async () => {
152
+ const all = await readParts(forLog);
153
+ await previous;
154
+ emit({
155
+ type: "data-a2-compaction",
156
+ id,
157
+ data: compactionData(all)
158
+ });
159
+ })();
160
+ } else {
161
+ state.liveStep = true;
162
+ state.parsed = (async () => {
163
+ await previous;
164
+ emit({ type: "start-step" });
165
+ await streamStep(forLog, emit, (finished) => {
166
+ completion = finished;
167
+ });
168
+ })();
169
+ }
170
+ state.parsed.catch(() => {});
171
+ return new Response(forKernel, {
172
+ status: response.status,
173
+ headers: response.headers
174
+ });
175
+ };
176
+ const hostTools = await kernelTools(step.tools, (toolName, toolInput) => {
177
+ const state = turn;
178
+ if (state.failure) throw state.failure;
179
+ const call = state.served.find((candidate) => candidate.toolName === toolName && !state.answered.has(candidate.toolCallId) && sameJson(candidate.input, toolInput));
180
+ const result = call === void 0 ? void 0 : results.get(call.toolCallId);
181
+ if (call !== void 0 && result !== void 0) {
182
+ state.answered.add(call.toolCallId);
183
+ if (result.error !== void 0) throw new Error(result.error);
184
+ if (result.denied === true) throw new Error("The tool call was denied.");
185
+ return result.output ?? null;
186
+ }
187
+ if (state.live) {
188
+ state.toolsRequested.resolve();
189
+ return pending(void 0);
190
+ }
191
+ if (state.plan.end === "interrupted") {
192
+ state.fxTurn.cancel();
193
+ return pending(void 0);
194
+ }
195
+ return fail(state, diverged(`no recorded result for ${toolName}`));
196
+ });
197
+ let turns = read.turns.map(turnPlan);
198
+ let results = read.results;
199
+ let controller;
200
+ let closed = false;
201
+ const emit = (chunk) => {
202
+ if (!closed) controller.enqueue(chunk);
203
+ };
204
+ const create = (checkpoint) => createFxAgent({
205
+ apiKey: credential.token,
206
+ model: kernelModel(step),
207
+ ...step.instructions.length === 0 ? {} : { instructions: step.instructions },
208
+ tools: hostTools,
209
+ ...checkpoint === void 0 ? {} : { checkpoint },
210
+ fetch: hostFetch
211
+ });
212
+ return {
213
+ stream: new ReadableStream({
214
+ start(streamController) {
215
+ controller = streamController;
216
+ const abort = () => {
217
+ stopped = true;
218
+ turn?.fxTurn.cancel();
219
+ };
220
+ step.signal.addEventListener("abort", abort, { once: true });
221
+ (async () => {
222
+ try {
223
+ try {
224
+ agent = await create(read.saved);
225
+ } catch (error) {
226
+ if (read.saved === void 0) throw error;
227
+ read = await replayAll();
228
+ turns = read.turns.map(turnPlan);
229
+ results = read.results;
230
+ agent = await create(void 0);
231
+ }
232
+ await run(streamController, turns);
233
+ closed = true;
234
+ streamController.close();
235
+ } catch (error) {
236
+ closed = true;
237
+ streamController.error(step.signal.aborted ? step.signal.reason : error);
238
+ } finally {
239
+ step.signal.removeEventListener("abort", abort);
240
+ await agent?.close().catch(() => {});
241
+ }
242
+ })();
243
+ },
244
+ cancel() {
245
+ closed = true;
246
+ stopped = true;
247
+ turn?.fxTurn.cancel();
248
+ }
249
+ }),
250
+ completion: () => completion
251
+ };
252
+ }
253
+ function turnPlan(turn) {
254
+ const calls = [];
255
+ const steers = /* @__PURE__ */ new Map();
256
+ const steer = (at, texts) => {
257
+ if (texts !== void 0) steers.set(Math.max(0, at), texts);
258
+ };
259
+ steer(0, turn.generations.length === 0 ? turn.steers.get(0) : void 0);
260
+ turn.generations.forEach((generation, index) => {
261
+ calls.push(...recordedCalls(generation.chunks, generation.completed));
262
+ steer(calls.length - 1, turn.steers.get(index));
263
+ });
264
+ return {
265
+ prompt: turn.prompt,
266
+ calls,
267
+ steers,
268
+ end: turn.end
269
+ };
270
+ }
271
+ /** A completed generation's model calls: its compactions, then its step. */
272
+ function recordedCalls(chunks, completed) {
273
+ const calls = [];
274
+ const parts = [];
275
+ const toolCalls = [];
276
+ const inputText = /* @__PURE__ */ new Map();
277
+ for (const chunk of chunks) {
278
+ const metadata = "providerMetadata" in chunk && chunk.providerMetadata !== void 0 ? { providerMetadata: chunk.providerMetadata } : {};
279
+ switch (chunk.type) {
280
+ case "data-a2-compaction":
281
+ calls.push({
282
+ kind: "compaction",
283
+ sse: compactionSse(chunk.data),
284
+ toolCalls: []
285
+ });
286
+ break;
287
+ case "text-start":
288
+ case "text-end":
289
+ case "reasoning-start":
290
+ case "reasoning-end":
291
+ parts.push({
292
+ type: chunk.type,
293
+ id: chunk.id,
294
+ ...metadata
295
+ });
296
+ break;
297
+ case "text-delta":
298
+ case "reasoning-delta":
299
+ parts.push({
300
+ type: chunk.type,
301
+ id: chunk.id,
302
+ delta: chunk.delta,
303
+ ...metadata
304
+ });
305
+ break;
306
+ case "tool-input-start":
307
+ inputText.set(chunk.toolCallId, "");
308
+ parts.push({
309
+ type: "tool-input-start",
310
+ id: chunk.toolCallId,
311
+ toolName: chunk.toolName,
312
+ ...metadata
313
+ });
314
+ break;
315
+ case "tool-input-delta":
316
+ inputText.set(chunk.toolCallId, (inputText.get(chunk.toolCallId) ?? "") + chunk.inputTextDelta);
317
+ parts.push({
318
+ type: "tool-input-delta",
319
+ id: chunk.toolCallId,
320
+ delta: chunk.inputTextDelta
321
+ });
322
+ break;
323
+ case "tool-input-error":
324
+ if (inputText.has(chunk.toolCallId)) parts.push({
325
+ type: "tool-input-end",
326
+ id: chunk.toolCallId
327
+ });
328
+ parts.push({
329
+ type: "tool-call",
330
+ toolCallId: chunk.toolCallId,
331
+ toolName: chunk.toolName,
332
+ input: inputText.get(chunk.toolCallId) || (typeof chunk.input === "string" ? chunk.input : JSON.stringify(chunk.input))
333
+ });
334
+ break;
335
+ case "tool-input-available":
336
+ if (inputText.has(chunk.toolCallId)) parts.push({
337
+ type: "tool-input-end",
338
+ id: chunk.toolCallId
339
+ });
340
+ toolCalls.push({
341
+ toolCallId: chunk.toolCallId,
342
+ toolName: chunk.toolName,
343
+ input: chunk.input
344
+ });
345
+ parts.push({
346
+ type: "tool-call",
347
+ toolCallId: chunk.toolCallId,
348
+ toolName: chunk.toolName,
349
+ input: inputText.get(chunk.toolCallId) || JSON.stringify(chunk.input),
350
+ ...metadata
351
+ });
352
+ }
353
+ }
354
+ parts.push({
355
+ type: "finish",
356
+ finishReason: { unified: completed.finishReason ?? "other" },
357
+ usage: streamUsage(completed.usage)
358
+ });
359
+ calls.push({
360
+ kind: "step",
361
+ sse: sse(parts),
362
+ toolCalls
363
+ });
364
+ return calls;
365
+ }
366
+ function compactionSse(data) {
367
+ return sse([
368
+ {
369
+ type: "text-start",
370
+ id: "0"
371
+ },
372
+ {
373
+ type: "text-delta",
374
+ id: "0",
375
+ delta: data.summary ?? ""
376
+ },
377
+ {
378
+ type: "text-end",
379
+ id: "0"
380
+ },
381
+ {
382
+ type: "finish",
383
+ finishReason: { unified: "stop" },
384
+ usage: streamUsage(data.usage)
385
+ }
386
+ ]);
387
+ }
388
+ /** Streams one live step response as UI chunks and reports its finish. */
389
+ async function streamStep(body, emit, finished) {
390
+ for await (const part of sseParts(body)) {
391
+ const metadata = part["providerMetadata"] === void 0 ? {} : { providerMetadata: part["providerMetadata"] };
392
+ switch (part.type) {
393
+ case "text-start":
394
+ case "text-end":
395
+ case "reasoning-start":
396
+ case "reasoning-end":
397
+ emit({
398
+ type: part.type,
399
+ id: part["id"],
400
+ ...metadata
401
+ });
402
+ break;
403
+ case "text-delta":
404
+ case "reasoning-delta":
405
+ emit({
406
+ type: part.type,
407
+ id: part["id"],
408
+ delta: part["delta"],
409
+ ...metadata
410
+ });
411
+ break;
412
+ case "tool-input-start":
413
+ emit({
414
+ type: "tool-input-start",
415
+ toolCallId: part["id"],
416
+ toolName: part["toolName"],
417
+ ...metadata
418
+ });
419
+ break;
420
+ case "tool-input-delta":
421
+ emit({
422
+ type: "tool-input-delta",
423
+ toolCallId: part["id"],
424
+ inputTextDelta: part["delta"]
425
+ });
426
+ break;
427
+ case "tool-call": {
428
+ const text = part["input"];
429
+ let input;
430
+ try {
431
+ input = JSON.parse(text);
432
+ } catch (error) {
433
+ emit({
434
+ type: "tool-input-error",
435
+ toolCallId: part["toolCallId"],
436
+ toolName: part["toolName"],
437
+ input: text,
438
+ errorText: error instanceof Error ? error.message : String(error)
439
+ });
440
+ break;
441
+ }
442
+ emit({
443
+ type: "tool-input-available",
444
+ toolCallId: part["toolCallId"],
445
+ toolName: part["toolName"],
446
+ input,
447
+ ...metadata
448
+ });
449
+ break;
450
+ }
451
+ case "finish": {
452
+ const reason = part["finishReason"];
453
+ finished({
454
+ ...reason?.unified === void 0 ? {} : { finishReason: reason.unified },
455
+ usage: modelUsage(part["usage"])
456
+ });
457
+ break;
458
+ }
459
+ case "error": emit({
460
+ type: "error",
461
+ errorText: errorText(part["error"])
462
+ });
463
+ }
464
+ }
465
+ }
466
+ async function readParts(body) {
467
+ const all = [];
468
+ for await (const part of sseParts(body)) all.push(part);
469
+ return all;
470
+ }
471
+ function compactionData(all) {
472
+ const finish = all.find((part) => part.type === "finish");
473
+ return {
474
+ status: "completed",
475
+ summary: all.filter((part) => part.type === "text-delta").map((part) => part["delta"]).join(""),
476
+ ...finish === void 0 ? {} : { usage: modelUsage(finish["usage"]) }
477
+ };
478
+ }
479
+ async function* sseParts(body) {
480
+ const decoder = new TextDecoder();
481
+ let pendingText = "";
482
+ for await (const chunk of body) {
483
+ pendingText += decoder.decode(chunk, { stream: true });
484
+ let end;
485
+ while ((end = pendingText.indexOf("\n\n")) !== -1) {
486
+ const frame = pendingText.slice(0, end);
487
+ pendingText = pendingText.slice(end + 2);
488
+ if (frame.startsWith("data: {")) yield JSON.parse(frame.slice(6));
489
+ }
490
+ }
491
+ }
492
+ /**
493
+ * Hands a recorded response to the kernel frame by frame. Steering due after
494
+ * it is queued before its finish part, while the kernel still waits on it.
495
+ */
496
+ function relay(text, beforeFinish) {
497
+ const encoder = new TextEncoder();
498
+ const frames = text.split(/(?<=\n\n)/);
499
+ let index = 0;
500
+ let steered = false;
501
+ return new Response(new ReadableStream({ async pull(controller) {
502
+ const frame = frames[index++];
503
+ if (frame === void 0) {
504
+ controller.close();
505
+ return;
506
+ }
507
+ if (!steered && frame.includes("\"type\":\"finish\"")) {
508
+ steered = true;
509
+ await beforeFinish();
510
+ }
511
+ controller.enqueue(encoder.encode(frame));
512
+ } }), { headers: { "content-type": "text/event-stream" } });
513
+ }
514
+ async function loadLibfx() {
515
+ try {
516
+ return await import("libfx");
517
+ } catch (error) {
518
+ throw new Error("fx() needs the libfx package: install libfx", { cause: error });
519
+ }
520
+ }
521
+ function withAuth(headers, credential) {
522
+ const result = new Headers(headers);
523
+ result.set("ai-gateway-auth-method", credential.method);
524
+ return result;
525
+ }
526
+ function kernelModel(step) {
527
+ const effort = step.reasoning === void 0 || step.reasoning === "provider-default" ? void 0 : step.reasoning;
528
+ const fast = step.speed === "fast";
529
+ if (effort === void 0 && !fast) return step.model;
530
+ return {
531
+ id: step.model,
532
+ ...effort === void 0 ? {} : { effort },
533
+ ...fast ? { fast } : {}
534
+ };
535
+ }
536
+ async function kernelTools(tools, execute) {
537
+ return Promise.all(Object.entries(tools).filter(([, tool]) => tool.type !== "provider").map(async ([toolName, tool]) => ({
538
+ name: toolName,
539
+ description: typeof tool.description === "string" ? tool.description : toolName,
540
+ inputSchema: await asSchema(tool.inputSchema).jsonSchema,
541
+ execute: (input) => execute(toolName, input)
542
+ })));
543
+ }
544
+ const catalogCache = boundedCache({
545
+ maxEntries: 16,
546
+ maxBytes: 16
547
+ });
548
+ /**
549
+ * Model catalog reads, shared for an hour by every step in the process that
550
+ * uses the same credential; credentials can see different catalogs.
551
+ */
552
+ async function catalog(url, credential, headers) {
553
+ const key = `${url}\n${credential.token}`;
554
+ const now = Date.now();
555
+ let entry = catalogCache.get(key);
556
+ if (entry === void 0 || entry.expires <= now) {
557
+ const body = fetch(url, { headers }).then(async (response) => {
558
+ if (!response.ok) throw new Error(`catalog request failed: ${response.status}`);
559
+ return response.text();
560
+ });
561
+ entry = {
562
+ expires: now + CATALOG_TTL_MS,
563
+ body
564
+ };
565
+ catalogCache.set(key, entry, 1);
566
+ body.catch(() => catalogCache.delete(key));
567
+ }
568
+ return new Response(await entry.body, { headers: { "content-type": "application/json" } });
569
+ }
570
+ function savedCheckpoint(state) {
571
+ if (typeof state !== "object" || state === null) return void 0;
572
+ const { version, checkpoint } = state;
573
+ if (version !== SNAPSHOT_VERSION || typeof checkpoint !== "string") return void 0;
574
+ return new Uint8Array(Buffer.from(checkpoint, "base64"));
575
+ }
576
+ function isCompaction(body) {
577
+ return JSON.parse(body).toolChoice?.type === "none";
578
+ }
579
+ function decodeBody(body) {
580
+ if (typeof body === "string") return body;
581
+ if (body instanceof Uint8Array || body instanceof ArrayBuffer) return new TextDecoder().decode(body);
582
+ throw new TypeError("FX sent an unexpected request body");
583
+ }
584
+ function sameJson(a, b) {
585
+ return JSON.stringify(a) === JSON.stringify(b);
586
+ }
587
+ function sse(all) {
588
+ return all.map((part) => `data: ${JSON.stringify(part)}\n\n`).join("") + "data: [DONE]\n\n";
589
+ }
590
+ function errorText(error) {
591
+ if (typeof error === "string") return error;
592
+ if (error instanceof Error) return error.message;
593
+ return JSON.stringify(error);
594
+ }
595
+ /** A Gateway stream usage, as AI SDK normalizes it. */
596
+ function modelUsage(value) {
597
+ const usage = value ?? {};
598
+ const input = usage.inputTokens ?? {};
599
+ const output = usage.outputTokens ?? {};
600
+ return {
601
+ inputTokens: input.total,
602
+ inputTokenDetails: {
603
+ noCacheTokens: input.noCache,
604
+ cacheReadTokens: input.cacheRead,
605
+ cacheWriteTokens: input.cacheWrite
606
+ },
607
+ outputTokens: output.total,
608
+ outputTokenDetails: {
609
+ textTokens: output.text,
610
+ reasoningTokens: output.reasoning
611
+ },
612
+ totalTokens: input.total === void 0 && output.total === void 0 ? void 0 : (input.total ?? 0) + (output.total ?? 0)
613
+ };
614
+ }
615
+ /** The inverse of `modelUsage`, for replay. */
616
+ function streamUsage(usage) {
617
+ return {
618
+ inputTokens: {
619
+ total: usage?.inputTokens,
620
+ noCache: usage?.inputTokenDetails?.noCacheTokens,
621
+ cacheRead: usage?.inputTokenDetails?.cacheReadTokens,
622
+ cacheWrite: usage?.inputTokenDetails?.cacheWriteTokens
623
+ },
624
+ outputTokens: {
625
+ total: usage?.outputTokens,
626
+ text: usage?.outputTokenDetails?.textTokens,
627
+ reasoning: usage?.outputTokenDetails?.reasoningTokens
628
+ }
629
+ };
630
+ }
631
+ //#endregion
632
+ export { fx };
633
+
634
+ //# sourceMappingURL=ai-fx.js.map