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.
- package/CHANGELOG.md +21 -0
- package/README.md +1 -1
- package/dist/{actor-DBbe5xeE.d.ts → actor-Bjce9_5U.d.ts} +5 -5
- package/dist/{actor-DBbe5xeE.d.ts.map → actor-Bjce9_5U.d.ts.map} +1 -1
- package/dist/actor-client.d.ts +1 -1
- package/dist/actor-react.d.ts +3 -3
- package/dist/actor-react.js +1 -1
- package/dist/actor.d.ts +1 -1
- package/dist/actor.js +2 -2
- package/dist/{ai-UrWfKeuv.js → ai-BpOGm-zB.js} +2 -2
- package/dist/{ai-UrWfKeuv.js.map → ai-BpOGm-zB.js.map} +1 -1
- package/dist/{ai-vpGtcaCB.d.ts → ai-DrUaMyRt.d.ts} +3 -3
- package/dist/{ai-vpGtcaCB.d.ts.map → ai-DrUaMyRt.d.ts.map} +1 -1
- package/dist/ai-client.d.ts +2 -2
- package/dist/ai-client.js +2 -2
- package/dist/ai-fx.browser.d.ts +1 -0
- package/dist/ai-fx.browser.js +6 -0
- package/dist/ai-fx.browser.js.map +1 -0
- package/dist/ai-fx.d.ts +10 -0
- package/dist/ai-fx.d.ts.map +1 -0
- package/dist/ai-fx.js +634 -0
- package/dist/ai-fx.js.map +1 -0
- package/dist/ai-harness-BuLh_EYi.js +273 -0
- package/dist/ai-harness-BuLh_EYi.js.map +1 -0
- package/dist/ai-harness-DvsPeJeB.d.ts +26 -0
- package/dist/ai-harness-DvsPeJeB.d.ts.map +1 -0
- package/dist/ai-pi.browser.d.ts +1 -0
- package/dist/ai-pi.browser.js +6 -0
- package/dist/ai-pi.browser.js.map +1 -0
- package/dist/ai-pi.d.ts +17 -0
- package/dist/ai-pi.d.ts.map +1 -0
- package/dist/ai-pi.js +904 -0
- package/dist/ai-pi.js.map +1 -0
- package/dist/ai-react.d.ts +5 -5
- package/dist/ai-react.js +2 -2
- package/dist/ai-server.d.ts +47 -25
- package/dist/ai-server.d.ts.map +1 -1
- package/dist/ai-server.js +106 -37
- package/dist/ai-server.js.map +1 -1
- package/dist/ai.d.ts +1 -1
- package/dist/ai.js +1 -1
- package/dist/bounded-cache-CPHWF-8h.js +41 -0
- package/dist/bounded-cache-CPHWF-8h.js.map +1 -0
- package/dist/{client-COaiaDub.js → client-BReKqwZC.js} +3 -3
- package/dist/{client-COaiaDub.js.map → client-BReKqwZC.js.map} +1 -1
- package/dist/{client-qRnRUzBS.d.ts → client-h_DKFAjj.d.ts} +3 -3
- package/dist/{client-qRnRUzBS.d.ts.map → client-h_DKFAjj.d.ts.map} +1 -1
- package/dist/client.d.ts +3 -3
- package/dist/client.js +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/{initial-snapshot-BrLso5x_.d.ts → initial-snapshot-Bx-c0pDP.d.ts} +2 -2
- package/dist/{initial-snapshot-BrLso5x_.d.ts.map → initial-snapshot-Bx-c0pDP.d.ts.map} +1 -1
- package/dist/{internal-CbVvEwad.js → internal-B2T3U4Rq.js} +8 -2
- package/dist/internal-B2T3U4Rq.js.map +1 -0
- package/dist/otel.d.ts +1 -1
- package/dist/{react-BbJ-g6Uu.d.ts → react-BBXgHWFc.d.ts} +4 -4
- package/dist/{react-BbJ-g6Uu.d.ts.map → react-BBXgHWFc.d.ts.map} +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/reducer-BAXc8JyF.js.map +1 -1
- package/dist/{reducer-BYu6DT7h.d.ts → reducer-CqA59Q6U.d.ts} +2 -2
- package/dist/{reducer-BYu6DT7h.d.ts.map → reducer-CqA59Q6U.d.ts.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-Rj64fNvX.js → server-Dw_k_7kO.js} +93 -60
- package/dist/server-Dw_k_7kO.js.map +1 -0
- package/dist/{server-CGM8WzRG.d.ts → server-En7cbi3v.d.ts} +36 -7
- package/dist/server-En7cbi3v.d.ts.map +1 -0
- package/dist/server.d.ts +4 -4
- package/dist/server.js +1 -1
- package/dist/{session-clear-Dshpuhff.d.ts → session-clear-Bo-XTFEy.d.ts} +2 -2
- package/dist/session-clear-Bo-XTFEy.d.ts.map +1 -0
- package/dist/{store-Gpkq7WBo.d.ts → store-Djdv0bnZ.d.ts} +31 -2
- package/dist/store-Djdv0bnZ.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.js +32 -6
- package/dist/store-memory.js.map +1 -1
- package/dist/{store-polling-4_y8lETm.js → store-polling-B5FcwJfo.js} +2 -2
- package/dist/{store-polling-4_y8lETm.js.map → store-polling-B5FcwJfo.js.map} +1 -1
- package/dist/store-postgres.d.ts +1 -1
- package/dist/store-postgres.js +54 -3
- package/dist/store-postgres.js.map +1 -1
- package/dist/{store-presence-polling-Q1C-k2dd.js → store-presence-polling-BAwS8RWn.js} +3 -3
- package/dist/{store-presence-polling-Q1C-k2dd.js.map → store-presence-polling-BAwS8RWn.js.map} +1 -1
- package/dist/store-redis-http.d.ts +1 -1
- package/dist/store-redis-http.js +3 -3
- package/dist/{store-redis-notify-tqWNnMRu.js → store-redis-notify-BRITti_o.js} +187 -68
- package/dist/store-redis-notify-BRITti_o.js.map +1 -0
- package/dist/store-redis.d.ts +1 -1
- package/dist/store-redis.js +1 -1
- package/dist/store-sqlite.d.ts +1 -1
- package/dist/store-sqlite.js +32 -2
- package/dist/store-sqlite.js.map +1 -1
- package/dist/{telemetry-cZH-cibj.d.ts → telemetry-Cyfqo6Qt.d.ts} +2 -2
- package/dist/telemetry-Cyfqo6Qt.d.ts.map +1 -0
- package/dist/{wire-DjfHOvdW.js → wire-rSUglYo8.js} +2 -2
- package/dist/{wire-DjfHOvdW.js.map → wire-rSUglYo8.js.map} +1 -1
- package/docs/01-quickstart.mdx +49 -83
- package/docs/{guides/07-examples.mdx → 02-examples.mdx} +10 -20
- package/docs/actors/01-introduction.mdx +48 -86
- package/docs/actors/02-concurrency.mdx +43 -70
- package/docs/actors/03-timers.mdx +28 -42
- package/docs/actors/04-routes.mdx +110 -150
- package/docs/actors/meta.ts +1 -1
- package/docs/agents/01-introduction.mdx +257 -0
- package/docs/agents/02-tools.mdx +247 -0
- package/docs/agents/03-conversations.mdx +538 -0
- package/docs/agents/04-harnesses.mdx +136 -0
- package/docs/agents/meta.ts +1 -0
- package/docs/client/01-react.mdx +294 -0
- package/docs/client/02-transports.mdx +86 -0
- package/docs/client/03-presence.mdx +224 -0
- package/docs/client/meta.ts +1 -0
- package/docs/contract/01-events.mdx +57 -0
- package/docs/contract/02-state.mdx +142 -0
- package/docs/contract/03-sessions.mdx +175 -0
- package/docs/index.mdx +108 -144
- package/docs/reference/01-core.mdx +139 -0
- package/docs/reference/02-server.mdx +855 -0
- package/docs/reference/03-client.mdx +301 -0
- package/docs/reference/04-ai.mdx +722 -0
- package/docs/reference/05-adapters.mdx +351 -0
- package/docs/reference/06-errors.mdx +138 -0
- package/docs/reference/meta.ts +1 -1
- package/docs/server/01-handlers.mdx +200 -0
- package/docs/server/02-timers.mdx +130 -0
- package/docs/server/03-cancellation.mdx +141 -0
- package/docs/server/04-routes.mdx +243 -0
- package/docs/server/05-production.mdx +322 -0
- package/docs/server/06-guarantees.mdx +123 -0
- package/examples/playground/DEPLOY.md +1 -1
- package/examples/playground/app/a0/[appId]/page.tsx +11 -6
- package/examples/playground/app/a0/app-list.tsx +9 -2
- package/examples/playground/app/a0/app-view.tsx +9 -1
- package/examples/playground/app/a0/apps/[appId]/route.ts +10 -5
- package/examples/playground/app/a0/apps.test.ts +12 -0
- package/examples/playground/app/a0/apps.ts +22 -7
- package/examples/playground/app/a0/chat.tsx +47 -1
- package/examples/playground/app/a0/fx/events/route.ts +9 -0
- package/examples/playground/app/a0/fx/scheduler/route.ts +7 -0
- package/examples/playground/app/a0/fx/short/events/route.ts +11 -0
- package/examples/playground/app/a0/model.test.ts +18 -1
- package/examples/playground/app/a0/model.ts +47 -10
- package/examples/playground/app/a0/no-photon.cjs +4 -0
- package/examples/playground/app/a0/no-quickjs.cjs +3 -0
- package/examples/playground/app/a0/pi/events/route.ts +9 -0
- package/examples/playground/app/a0/pi/scheduler/route.ts +7 -0
- package/examples/playground/app/a0/pi/short/events/route.ts +11 -0
- package/examples/playground/app/a0/sandbox-log.ts +10 -4
- package/examples/playground/app/a0/sandbox-session.ts +152 -0
- package/examples/playground/app/a0/scheduler/route.ts +2 -2
- package/examples/playground/app/a0/server.ts +100 -45
- package/examples/playground/app/a0/session.ts +17 -5
- package/examples/playground/app/a0/shell.tsx +11 -3
- package/examples/playground/app/agent/ingress.test.ts +6 -3
- package/examples/playground/app/agent/server.ts +9 -6
- package/examples/playground/app/agent/telemetry.test.ts +71 -50
- package/examples/playground/app/canvas/[canvasId]/canvas-client.tsx +1 -1
- package/examples/playground/app/chat/server.test.ts +30 -26
- package/examples/playground/app/chat/server.ts +46 -46
- package/examples/playground/next.config.ts +17 -5
- package/examples/playground/package.json +4 -1
- package/examples/playground/vercel.json +18 -0
- package/package.json +27 -3
- package/src/ai-agent.ts +261 -87
- package/src/ai-fx.browser.ts +5 -0
- package/src/ai-fx.ts +833 -0
- package/src/ai-harness.ts +518 -0
- package/src/ai-model-metadata.ts +11 -6
- package/src/ai-pi.browser.ts +5 -0
- package/src/ai-pi.ts +1263 -0
- package/src/ai-sdk-step.ts +2 -2
- package/src/ai-server.ts +3 -1
- package/src/internal.ts +7 -0
- package/src/libfx.d.ts +34 -0
- package/src/reducer.ts +1 -1
- package/src/server.ts +177 -19
- package/src/store-memory.ts +42 -7
- package/src/store-postgres.ts +80 -0
- package/src/store-redis-core.ts +210 -79
- package/src/store-redis-notify.ts +2 -0
- package/src/store-sqlite.ts +60 -0
- package/src/store.ts +33 -0
- package/src/telemetry.ts +1 -0
- package/dist/internal-CbVvEwad.js.map +0 -1
- package/dist/server-CGM8WzRG.d.ts.map +0 -1
- package/dist/server-Rj64fNvX.js.map +0 -1
- package/dist/session-clear-Dshpuhff.d.ts.map +0 -1
- package/dist/store-Gpkq7WBo.d.ts.map +0 -1
- package/dist/store-redis-notify-tqWNnMRu.js.map +0 -1
- package/dist/telemetry-cZH-cibj.d.ts.map +0 -1
- package/docs/concepts/01-contracts.mdx +0 -237
- package/docs/concepts/02-handlers.mdx +0 -284
- package/docs/concepts/03-durability.mdx +0 -314
- package/docs/concepts/04-state.mdx +0 -209
- package/docs/guides/01-timers.mdx +0 -193
- package/docs/guides/02-cancellation.mdx +0 -161
- package/docs/guides/03-react.mdx +0 -324
- package/docs/guides/04-local-first.mdx +0 -47
- package/docs/guides/05-production.mdx +0 -534
- package/docs/guides/06-ai-agents.mdx +0 -1408
- package/docs/guides/08-application-data.mdx +0 -113
- package/docs/guides/09-presence.mdx +0 -262
- package/docs/guides/10-transports.mdx +0 -221
- package/docs/reference/01-api.mdx +0 -2358
- package/docs/reference/02-errors.mdx +0 -150
- /package/docs/{concepts → contract}/meta.ts +0 -0
- /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
|