experimental-a2 0.17.0 → 0.18.1

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