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-pi.js ADDED
@@ -0,0 +1,950 @@
1
+ import { u as ambientToolScopeStorage } from "./internal-CscSY7K_.js";
2
+ import { t as boundedCache } from "./bounded-cache-CPHWF-8h.js";
3
+ 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";
4
+ import { t as schemaInput } from "./ai-tool-input-DmSqMUQi.js";
5
+ import { createHash } from "node:crypto";
6
+ import { asSchema, jsonSchema } from "ai";
7
+ import { readFile, unlink } from "node:fs/promises";
8
+ import { tmpdir } from "node:os";
9
+ import { basename, dirname, join } from "node:path";
10
+ //#region src/ai-pi.ts
11
+ /**
12
+ * experimental-a2/ai/pi — `pi()`: the Pi harness for `agent({ harness })`.
13
+ * Server-only; Pi comes from the optional `@earendil-works/pi-coding-agent`
14
+ * and `@earendil-works/pi-ai` peer dependencies.
15
+ */
16
+ /**
17
+ * The Pi harness. Every model call Pi makes is one durable step; A2 rebuilds
18
+ * Pi's session for each step from its latest snapshot and the log since.
19
+ */
20
+ function pi(options) {
21
+ if (typeof options?.sandbox !== "function") throw new TypeError("pi() requires sandbox");
22
+ return defineHarness("pi", {
23
+ sandbox: options.sandbox,
24
+ tools: builtinTools,
25
+ step: (step) => runStep(step, async () => options.sandbox({
26
+ session: { id: step.sessionId },
27
+ signal: step.signal
28
+ }))
29
+ });
30
+ }
31
+ const SNAPSHOT_VERSION = 1;
32
+ const GATEWAY = "vercel-ai-gateway";
33
+ const MAX_TRANSFER_BYTES = 4194304;
34
+ const MAX_TRANSFER = "4 MiB";
35
+ const BUILTINS = [
36
+ "read",
37
+ "write",
38
+ "edit",
39
+ "bash"
40
+ ];
41
+ function endStep(state) {
42
+ state.over = true;
43
+ state.stepOver.resolve();
44
+ }
45
+ const diverged = (message) => new Divergence("Pi", message);
46
+ async function runStep(step, sandbox) {
47
+ const modules = await loadPi();
48
+ const credential = gatewayCredential("pi");
49
+ const name = `${step.agentName}.a2.harness.pi`;
50
+ const { replayAll, ...first } = await readHarness({
51
+ step,
52
+ name,
53
+ parse: savedSession
54
+ });
55
+ let read = first;
56
+ let results = read.results;
57
+ const cwd = read.saved?.cwd ?? await sessionCwd({
58
+ key: cwdKey(step.agentName, step.sessionId),
59
+ sandbox
60
+ });
61
+ let completion = {};
62
+ let session;
63
+ let compacting = false;
64
+ let turn;
65
+ const fail = (state, error) => {
66
+ state.failure ??= error;
67
+ endStep(state);
68
+ session?.abort();
69
+ throw state.failure;
70
+ };
71
+ let controller;
72
+ let closed = false;
73
+ const emit = (chunk) => {
74
+ if (!closed) controller.enqueue(chunk);
75
+ };
76
+ const record = (data) => emit({
77
+ type: "data-a2-pi",
78
+ data,
79
+ transient: true
80
+ });
81
+ const answer = async (out, model, context, options) => {
82
+ const state = turn;
83
+ if (state.failure) throw state.failure;
84
+ if (state.over) {
85
+ replay(out, interrupted(model));
86
+ return;
87
+ }
88
+ const kind = compacting ? "compaction" : "step";
89
+ const recorded = state.calls[state.next];
90
+ if (recorded !== void 0) {
91
+ if (recorded.kind !== kind) fail(state, diverged(`call ${state.next} is not a ${kind}`));
92
+ state.next++;
93
+ for (const text of recorded.steers) await session.steer(text);
94
+ replay(out, recorded.response);
95
+ return;
96
+ }
97
+ if (state.plan.end === "stop") fail(state, diverged("Pi continued a turn the log ended"));
98
+ if (state.plan.end === "interrupted") {
99
+ replay(out, interrupted(model));
100
+ return;
101
+ }
102
+ if (kind === "step" && state.liveStep) {
103
+ endStep(state);
104
+ replay(out, interrupted(model));
105
+ return;
106
+ }
107
+ if (kind === "step") state.liveStep = true;
108
+ const upstream = realStream(model, context, {
109
+ ...options,
110
+ headers: {
111
+ ...options?.headers,
112
+ ...gatewayHeaders(credential)
113
+ }
114
+ });
115
+ const live = kind === "step" ? liveStep() : void 0;
116
+ for await (const event of upstream) {
117
+ await live?.(event);
118
+ if (event.type === "done" || event.type === "error") {
119
+ const response = event.type === "done" ? event.message : event.error;
120
+ if (kind === "compaction") state.compactions.push(response);
121
+ if (event.type === "error" && event.reason === "error") {
122
+ state.failure ??= new Error(response.errorMessage ?? "the Pi model call failed");
123
+ endStep(state);
124
+ }
125
+ }
126
+ out.push(event);
127
+ }
128
+ };
129
+ /**
130
+ * A live call's input as Pi's loop, then the AI SDK step, read it. Pi
131
+ * answers a call to no tool, or whose arguments it cannot prepare or
132
+ * validate, itself. The agent's tool reads the arguments Pi accepts through
133
+ * its own schema; A2 runs Pi's own tools from the model's arguments, as
134
+ * Pi's loop does.
135
+ */
136
+ const callInput = async (block) => {
137
+ const definition = tools.find((tool) => tool.name === block.name);
138
+ if (definition === void 0) return {
139
+ success: false,
140
+ errorText: `Tool ${block.name} not found`
141
+ };
142
+ let args;
143
+ try {
144
+ const prepared = definition.prepareArguments?.(block.arguments);
145
+ args = modules.ai.validateToolArguments(definition, prepared === void 0 ? block : {
146
+ ...block,
147
+ arguments: prepared
148
+ });
149
+ } catch (error) {
150
+ return {
151
+ success: false,
152
+ errorText: asError(error).message
153
+ };
154
+ }
155
+ if (builtinOf(step.tools, block.name) !== void 0) return {
156
+ success: true,
157
+ value: block.arguments
158
+ };
159
+ return schemaInput({
160
+ tools: step.tools,
161
+ toolName: block.name,
162
+ value: args
163
+ });
164
+ };
165
+ /** Streams one live step response as UI chunks and records it. */
166
+ const liveStep = () => {
167
+ const ref = (index) => `pi:${step.requestIndex}:${index}`;
168
+ const started = /* @__PURE__ */ new Set();
169
+ return async (event) => {
170
+ switch (event.type) {
171
+ case "start":
172
+ emit({ type: "start-step" });
173
+ break;
174
+ case "text_start":
175
+ emit({
176
+ type: "text-start",
177
+ id: ref(event.contentIndex)
178
+ });
179
+ break;
180
+ case "text_delta":
181
+ emit({
182
+ type: "text-delta",
183
+ id: ref(event.contentIndex),
184
+ delta: event.delta
185
+ });
186
+ break;
187
+ case "text_end":
188
+ emit({
189
+ type: "text-end",
190
+ id: ref(event.contentIndex)
191
+ });
192
+ break;
193
+ case "thinking_start":
194
+ emit({
195
+ type: "reasoning-start",
196
+ id: ref(event.contentIndex)
197
+ });
198
+ break;
199
+ case "thinking_delta":
200
+ emit({
201
+ type: "reasoning-delta",
202
+ id: ref(event.contentIndex),
203
+ delta: event.delta
204
+ });
205
+ break;
206
+ case "thinking_end":
207
+ emit({
208
+ type: "reasoning-end",
209
+ id: ref(event.contentIndex)
210
+ });
211
+ break;
212
+ case "toolcall_start":
213
+ case "toolcall_delta": {
214
+ const call = event.partial.content[event.contentIndex];
215
+ if (!call.id) break;
216
+ if (!started.has(call.id)) {
217
+ started.add(call.id);
218
+ emit({
219
+ type: "tool-input-start",
220
+ toolCallId: call.id,
221
+ toolName: call.name
222
+ });
223
+ }
224
+ if (event.type === "toolcall_delta") emit({
225
+ type: "tool-input-delta",
226
+ toolCallId: call.id,
227
+ inputTextDelta: event.delta
228
+ });
229
+ break;
230
+ }
231
+ case "done":
232
+ case "error": {
233
+ const response = event.type === "done" ? event.message : event.error;
234
+ const changed = /* @__PURE__ */ new Set();
235
+ if (event.type === "done") for (const block of response.content) {
236
+ if (block.type !== "toolCall") continue;
237
+ if (!started.has(block.id)) emit({
238
+ type: "tool-input-start",
239
+ toolCallId: block.id,
240
+ toolName: block.name
241
+ });
242
+ const input = response.stopReason === "length" ? {
243
+ success: false,
244
+ errorText: "The response hit the output token limit before this call was complete."
245
+ } : await callInput(block);
246
+ if (input.success && JSON.stringify(input.value) !== JSON.stringify(block.arguments)) changed.add(block.id);
247
+ emit(input.success ? {
248
+ type: "tool-input-available",
249
+ toolCallId: block.id,
250
+ toolName: block.name,
251
+ input: input.value
252
+ } : {
253
+ type: "tool-input-error",
254
+ toolCallId: block.id,
255
+ toolName: block.name,
256
+ input: block.arguments,
257
+ errorText: input.errorText
258
+ });
259
+ }
260
+ record({
261
+ call: "step",
262
+ response: storedResponse(response, ref, changed)
263
+ });
264
+ completion = {
265
+ finishReason: finishReason(response.stopReason),
266
+ usage: modelUsage(response.usage)
267
+ };
268
+ break;
269
+ }
270
+ }
271
+ };
272
+ };
273
+ const toolResult = async (toolCallId, signal, shape) => {
274
+ const state = turn;
275
+ if (state.failure) throw state.failure;
276
+ const result = results.get(toolCallId);
277
+ if (result !== void 0) return answeredTool(result, shape);
278
+ if (state.plan.end === "live" && state.liveStep) {
279
+ endStep(state);
280
+ return pending(signal);
281
+ }
282
+ if (state.plan.end === "interrupted") {
283
+ session.abort();
284
+ return pending(signal);
285
+ }
286
+ return fail(state, diverged(`no recorded result for ${toolCallId}`));
287
+ };
288
+ const runtime = await modules.agent.ModelRuntime.create({
289
+ credentials: new modules.ai.InMemoryCredentialStore(),
290
+ modelsPath: null,
291
+ allowModelNetwork: false,
292
+ refreshOnCreate: false
293
+ });
294
+ await runtime.setRuntimeApiKey(GATEWAY, credential.token);
295
+ const model = runtime.getModel(GATEWAY, step.model);
296
+ if (model === void 0) throw new Error(`Pi does not know the Gateway model ${step.model}`);
297
+ const realStream = runtime.streamSimple.bind(runtime);
298
+ runtime.streamSimple = (requested, context, options) => {
299
+ const out = modules.ai.createAssistantMessageEventStream();
300
+ answer(out, requested, context, options).catch((error) => {
301
+ const state = turn;
302
+ if (state) state.failure ??= asError(error);
303
+ replay(out, {
304
+ ...interrupted(requested),
305
+ stopReason: "error"
306
+ });
307
+ });
308
+ return out;
309
+ };
310
+ const tools = await sessionTools(modules, step.tools, cwd, toolResult);
311
+ const createSession = async (from) => {
312
+ const sessionManager = from === void 0 ? modules.agent.SessionManager.inMemory(cwd, { id: sessionUuid(step.agentName, step.sessionId) }) : modules.agent.SessionManager.inMemory(cwd, { id: from.header.id }, [from.header, ...from.entries]);
313
+ const created = await modules.agent.createAgentSession({
314
+ cwd,
315
+ agentDir: join(tmpdir(), "a2-pi"),
316
+ model,
317
+ ...thinkingLevel(step),
318
+ modelRuntime: runtime,
319
+ resourceLoader: resources(modules, step.instructions),
320
+ sessionManager,
321
+ settingsManager: modules.agent.SettingsManager.inMemory({
322
+ retry: {
323
+ enabled: false,
324
+ maxRetries: 0,
325
+ baseDelayMs: 0
326
+ },
327
+ cacheWarming: "off"
328
+ }),
329
+ customTools: tools,
330
+ tools: tools.map((definition) => definition.name)
331
+ });
332
+ created.session.subscribe((event) => {
333
+ if (event.type === "compaction_start") compacting = true;
334
+ if (event.type !== "compaction_end") return;
335
+ compacting = false;
336
+ const state = turn;
337
+ if (state === void 0 || state.over || state.compactions.length === 0) return;
338
+ for (const response of state.compactions.splice(0)) record({
339
+ call: "compaction",
340
+ response
341
+ });
342
+ emit({
343
+ type: "data-a2-compaction",
344
+ id: `pi-compaction:${step.requestIndex}:${compactions++}`,
345
+ data: compactionData(event)
346
+ });
347
+ });
348
+ return created.session;
349
+ };
350
+ let compactions = 0;
351
+ const saveSnapshot = async () => {
352
+ const manager = session.sessionManager;
353
+ const header = manager.getHeader();
354
+ const entries = manager.getEntries();
355
+ const last = entries.findLast((entry) => entry.type === "message" && entry.message.role === "assistant");
356
+ const stop = last?.type === "message" && last.message.role === "assistant" ? last.message.stopReason : void 0;
357
+ if (header === null || step.signal.aborted || stop === "aborted" || stop === "error") return;
358
+ await saveHarness({
359
+ step,
360
+ name,
361
+ state: {
362
+ version: SNAPSHOT_VERSION,
363
+ cwd,
364
+ header,
365
+ entries
366
+ }
367
+ });
368
+ };
369
+ const run = async () => {
370
+ emit({
371
+ type: "start",
372
+ messageId: step.responseMessageId
373
+ });
374
+ try {
375
+ session = await createSession(read.saved);
376
+ } catch (error) {
377
+ if (read.saved === void 0) throw error;
378
+ read = await replayAll();
379
+ results = read.results;
380
+ session = await createSession(void 0);
381
+ }
382
+ for (const plan of read.turns) {
383
+ if (step.signal.aborted) return;
384
+ const state = {
385
+ plan,
386
+ calls: recordedTurn(plan),
387
+ next: 0,
388
+ liveStep: false,
389
+ over: false,
390
+ stepOver: deferred(),
391
+ compactions: []
392
+ };
393
+ turn = state;
394
+ const running = session.prompt(plan.prompt, { expandPromptTemplates: false });
395
+ if (plan.end !== "live") {
396
+ await running.catch((error) => {
397
+ throw state.failure ?? error;
398
+ });
399
+ if (state.failure) throw state.failure;
400
+ continue;
401
+ }
402
+ const ended = await Promise.race([running.then(() => true), state.stepOver.promise.then(() => false)]).catch((error) => {
403
+ throw state.failure ?? error;
404
+ });
405
+ if (!ended) {
406
+ await session.abort();
407
+ await running.catch(() => {});
408
+ }
409
+ if (state.failure) throw state.failure;
410
+ if (ended) await saveSnapshot();
411
+ emit({ type: "finish-step" });
412
+ emit({
413
+ type: "finish",
414
+ ...completion.finishReason === void 0 ? {} : { finishReason: completion.finishReason }
415
+ });
416
+ }
417
+ };
418
+ return {
419
+ stream: new ReadableStream({
420
+ start(streamController) {
421
+ controller = streamController;
422
+ const abort = () => void session?.abort();
423
+ step.signal.addEventListener("abort", abort, { once: true });
424
+ (async () => {
425
+ try {
426
+ await run();
427
+ closed = true;
428
+ streamController.close();
429
+ } catch (error) {
430
+ closed = true;
431
+ streamController.error(step.signal.aborted ? step.signal.reason : error);
432
+ } finally {
433
+ step.signal.removeEventListener("abort", abort);
434
+ session?.dispose();
435
+ }
436
+ })();
437
+ },
438
+ cancel() {
439
+ closed = true;
440
+ session?.abort();
441
+ }
442
+ }),
443
+ completion: () => completion
444
+ };
445
+ }
446
+ /** A turn's recorded model calls, in the order Pi made them. */
447
+ function recordedTurn(turn) {
448
+ const calls = [];
449
+ turn.generations.forEach((generation, index) => {
450
+ calls.push(...recordedCalls(generation));
451
+ const steers = turn.steers.get(index);
452
+ const last = calls.findLast((call) => call.kind === "step");
453
+ if (steers !== void 0 && last !== void 0) last.steers.push(...steers);
454
+ });
455
+ return calls;
456
+ }
457
+ function recordedCalls(generation) {
458
+ const texts = /* @__PURE__ */ new Map();
459
+ const inputs = /* @__PURE__ */ new Map();
460
+ const calls = [];
461
+ for (const chunk of generation.chunks) switch (chunk.type) {
462
+ case "text-delta":
463
+ case "reasoning-delta":
464
+ texts.set(chunk.id, (texts.get(chunk.id) ?? "") + chunk.delta);
465
+ break;
466
+ case "tool-input-available":
467
+ case "tool-input-error":
468
+ inputs.set(chunk.toolCallId, chunk.input);
469
+ break;
470
+ case "data-a2-pi": {
471
+ const record = chunk.data;
472
+ calls.push({
473
+ kind: record.call,
474
+ response: record.call === "step" ? restoredResponse(record.response, texts, inputs) : record.response,
475
+ steers: []
476
+ });
477
+ break;
478
+ }
479
+ }
480
+ if (!calls.some((call) => call.kind === "step")) throw diverged("a completed generation has no recorded response");
481
+ return calls;
482
+ }
483
+ function storedResponse(response, ref, changed) {
484
+ return {
485
+ ...response,
486
+ content: response.content.map((block, index) => {
487
+ switch (block.type) {
488
+ case "text": {
489
+ const { text: _, ...rest } = block;
490
+ return {
491
+ ...rest,
492
+ ref: ref(index)
493
+ };
494
+ }
495
+ case "thinking": {
496
+ const { thinking: _, ...rest } = block;
497
+ return {
498
+ ...rest,
499
+ ref: ref(index)
500
+ };
501
+ }
502
+ case "toolCall": {
503
+ if (changed.has(block.id)) return block;
504
+ const { arguments: _, ...rest } = block;
505
+ return rest;
506
+ }
507
+ }
508
+ })
509
+ };
510
+ }
511
+ function restoredResponse(stored, texts, inputs) {
512
+ return {
513
+ ...stored,
514
+ content: stored.content.map((block) => {
515
+ if (block.type === "toolCall") return {
516
+ ...block,
517
+ arguments: block.arguments ?? inputs.get(block.id) ?? {}
518
+ };
519
+ const { ref, ...rest } = block;
520
+ const text = texts.get(ref) ?? "";
521
+ return rest.type === "text" ? {
522
+ ...rest,
523
+ text
524
+ } : {
525
+ ...rest,
526
+ thinking: text
527
+ };
528
+ })
529
+ };
530
+ }
531
+ /** Hands a recorded response to Pi's loop. */
532
+ function replay(out, response) {
533
+ out.push({
534
+ type: "start",
535
+ partial: response
536
+ });
537
+ const reason = response.stopReason;
538
+ if (reason === "error" || reason === "aborted") out.push({
539
+ type: "error",
540
+ reason,
541
+ error: response
542
+ });
543
+ else if (reason === "pending") out.push({
544
+ type: "done",
545
+ reason: "stop",
546
+ message: response
547
+ });
548
+ else out.push({
549
+ type: "done",
550
+ reason,
551
+ message: response
552
+ });
553
+ }
554
+ /** The response of a model call that A2 interrupted. */
555
+ function interrupted(model) {
556
+ return {
557
+ role: "assistant",
558
+ content: [],
559
+ api: model.api,
560
+ provider: model.provider,
561
+ model: model.id,
562
+ usage: {
563
+ input: 0,
564
+ output: 0,
565
+ cacheRead: 0,
566
+ cacheWrite: 0,
567
+ totalTokens: 0,
568
+ cost: {
569
+ input: 0,
570
+ output: 0,
571
+ cacheRead: 0,
572
+ cacheWrite: 0,
573
+ total: 0
574
+ }
575
+ },
576
+ stopReason: "aborted",
577
+ timestamp: Date.now()
578
+ };
579
+ }
580
+ const builtinTools = Object.fromEntries(BUILTINS.map((name) => [name, {
581
+ inputSchema: jsonSchema(async () => builtinDefinition(await loadPi(), name, "/").parameters),
582
+ execute: (input, options) => runBuiltin(name, input, options)
583
+ }]));
584
+ /** Runs one of Pi's tools in the session's sandbox, as Pi's loop would. */
585
+ async function runBuiltin(name, input, options) {
586
+ const sandbox = options.experimental_sandbox;
587
+ if (sandbox === void 0) throw new Error(`pi() runs ${name} in the agent's sandbox`);
588
+ const modules = await loadPi();
589
+ const scope = ambientToolScopeStorage()?.getStore();
590
+ const event = (scope?.context)?.event;
591
+ const definition = builtinDefinition(modules, name, await sessionCwd({
592
+ key: scope?.contract !== void 0 && typeof event?.sessionId === "string" ? cwdKey(scope.contract.name, event.sessionId) : void 0,
593
+ sandbox: async () => sandbox
594
+ }), {
595
+ sandbox,
596
+ signal: options.abortSignal
597
+ });
598
+ const args = modules.ai.validateToolArguments(definition, {
599
+ type: "toolCall",
600
+ id: options.toolCallId,
601
+ name,
602
+ arguments: definition.prepareArguments?.(input) ?? input
603
+ });
604
+ let outputFile;
605
+ try {
606
+ const result = await definition.execute(options.toolCallId, args, options.abortSignal, (update) => {
607
+ outputFile ??= fullOutputPath(update);
608
+ }, void 0);
609
+ outputFile ??= fullOutputPath(result);
610
+ return await recordedResult(result, sandbox);
611
+ } catch (error) {
612
+ outputFile ??= namedOutputFile(error);
613
+ throw error;
614
+ } finally {
615
+ if (outputFile !== void 0) await unlink(outputFile).catch(() => {});
616
+ }
617
+ }
618
+ const fullOutputPath = (result) => {
619
+ const path = result.details?.fullOutputPath;
620
+ return typeof path === "string" ? path : void 0;
621
+ };
622
+ /** The full-output file an aborted or timed-out bash call's error names. */
623
+ function namedOutputFile(error) {
624
+ if (!(error instanceof Error)) return void 0;
625
+ const path = /Full output: ([^\n]+)\]\n\nCommand [^\n]+$/.exec(error.message)?.[1];
626
+ return path !== void 0 && dirname(path) === tmpdir() && /^pi-bash-[0-9a-f]{16}\.log$/.test(basename(path)) ? path : void 0;
627
+ }
628
+ /**
629
+ * What the log keeps of a result: what Pi's tool-result message holds. A
630
+ * long output's full text moves to the path its result names in the sandbox,
631
+ * where the model's `read` finds it.
632
+ */
633
+ async function recordedResult(result, sandbox) {
634
+ const { structuredContent: _, ...kept } = result;
635
+ const details = kept.details;
636
+ const path = fullOutputPath(kept);
637
+ if (path !== void 0) try {
638
+ await sandbox.writeBinaryFile({
639
+ path,
640
+ content: await readFile(path)
641
+ });
642
+ } catch {}
643
+ if (details?.truncation === void 0) return kept;
644
+ const { content: __, ...truncation } = details.truncation;
645
+ return {
646
+ ...kept,
647
+ details: {
648
+ ...details,
649
+ truncation
650
+ }
651
+ };
652
+ }
653
+ /** The Pi tool a name in the agent's tool set runs, unless the agent replaced it. */
654
+ const builtinOf = (tools, name) => BUILTINS.find((candidate) => candidate === name && Object.hasOwn(tools, name) && tools[name] === builtinTools[name]);
655
+ const untyped = (definition) => definition;
656
+ function builtinDefinition(modules, name, cwd, call) {
657
+ const sandbox = call?.sandbox;
658
+ const files = call === void 0 ? void 0 : { operations: sandboxFiles(call) };
659
+ switch (name) {
660
+ case "read": return untyped(modules.agent.createReadToolDefinition(cwd, files));
661
+ case "write": return untyped(modules.agent.createWriteToolDefinition(cwd, files));
662
+ case "edit": return untyped(modules.agent.createEditToolDefinition(cwd, files));
663
+ case "bash": return untyped(modules.agent.createBashToolDefinition(cwd, {
664
+ exposeSessionEnvironment: false,
665
+ ...sandbox === void 0 ? {} : { operations: sandboxBash(sandbox) }
666
+ }));
667
+ }
668
+ }
669
+ /**
670
+ * The tools Pi's session declares: Pi's own, and the agent's. Their results
671
+ * come from A2's log; A2's tool handlers ran them.
672
+ */
673
+ async function sessionTools(modules, tools, cwd, result) {
674
+ return Promise.all(Object.entries(tools).filter(([, tool]) => tool.type !== "provider").map(async ([name, tool]) => {
675
+ const builtin = builtinOf(tools, name);
676
+ if (builtin !== void 0) return Object.assign(builtinDefinition(modules, builtin, cwd), { execute: (toolCallId, _params, signal) => result(toolCallId, signal, "pi") });
677
+ return {
678
+ name,
679
+ label: name,
680
+ description: typeof tool.description === "string" ? tool.description : "",
681
+ parameters: await asSchema(tool.inputSchema).jsonSchema,
682
+ execute: (toolCallId, _params, signal) => result(toolCallId, signal, "tool")
683
+ };
684
+ }));
685
+ }
686
+ function answeredTool(result, shape) {
687
+ if (result.error !== void 0) throw new Error(result.error);
688
+ if (result.denied === true) throw new Error("The tool call was denied.");
689
+ if (shape === "pi") return result.output;
690
+ const output = result.output;
691
+ return {
692
+ content: [{
693
+ type: "text",
694
+ text: typeof output === "string" ? output : JSON.stringify(output) ?? ""
695
+ }],
696
+ details: void 0
697
+ };
698
+ }
699
+ /** Pi's file operations, against the sandbox. */
700
+ function sandboxFiles({ sandbox, signal }) {
701
+ const abortSignal = signal === void 0 ? {} : { abortSignal: signal };
702
+ let checked;
703
+ const readFile = async (path) => {
704
+ if (checked?.path === path) {
705
+ const { bytes } = checked;
706
+ checked = void 0;
707
+ return bytes;
708
+ }
709
+ const stream = await sandbox.readFile({
710
+ path,
711
+ ...abortSignal
712
+ });
713
+ if (stream === null) throw Object.assign(/* @__PURE__ */ new Error(`ENOENT: no such file or directory, open '${path}'`), { code: "ENOENT" });
714
+ const chunks = [];
715
+ let size = 0;
716
+ for await (const chunk of stream) {
717
+ size += chunk.byteLength;
718
+ if (size > MAX_TRANSFER_BYTES) throw new Error(`${path} is larger than ${MAX_TRANSFER}. Read or change parts of it with bash, using sed, head, or grep.`);
719
+ chunks.push(chunk);
720
+ }
721
+ return Buffer.concat(chunks);
722
+ };
723
+ return {
724
+ readFile,
725
+ access: async (path) => {
726
+ checked = {
727
+ path,
728
+ bytes: await readFile(path)
729
+ };
730
+ },
731
+ writeFile: async (path, content) => {
732
+ checked = void 0;
733
+ await sandbox.writeTextFile({
734
+ path,
735
+ content,
736
+ ...abortSignal
737
+ });
738
+ },
739
+ mkdir: async () => {}
740
+ };
741
+ }
742
+ /** Pi's shell operations, against the sandbox. */
743
+ const sandboxBash = (sandbox) => ({ exec: (command, cwd, { onData, signal, timeout }) => sandboxExec({
744
+ sandbox,
745
+ command,
746
+ workingDirectory: cwd,
747
+ onData,
748
+ signal,
749
+ timeout
750
+ }) });
751
+ /**
752
+ * Runs a command in the sandbox with Pi's error contract. At most
753
+ * `MAX_TRANSFER_BYTES` of its output reach the server; past that, the
754
+ * command is stopped.
755
+ */
756
+ async function sandboxExec({ sandbox, command, workingDirectory, onData, signal, timeout }) {
757
+ if (signal?.aborted) throw new Error("aborted");
758
+ const stop = new AbortController();
759
+ const abort = () => stop.abort();
760
+ signal?.addEventListener("abort", abort, { once: true });
761
+ let timedOut = false;
762
+ const timer = timeout === void 0 || timeout <= 0 ? void 0 : setTimeout(() => {
763
+ timedOut = true;
764
+ stop.abort();
765
+ }, timeout * 1e3);
766
+ let size = 0;
767
+ let overflowed = false;
768
+ const overflow = (options) => new Error(`The command's output passed ${MAX_TRANSFER}, so it was stopped. Write long output to a file and read parts of it with head, tail, or grep.`, options);
769
+ let exitCode;
770
+ try {
771
+ const process = await sandbox.spawn({
772
+ command,
773
+ ...workingDirectory === void 0 ? {} : { workingDirectory },
774
+ abortSignal: stop.signal
775
+ });
776
+ const pipe = async (stream) => {
777
+ for await (const chunk of process[stream]) {
778
+ size += chunk.byteLength;
779
+ if (size > MAX_TRANSFER_BYTES) {
780
+ overflowed = true;
781
+ stop.abort();
782
+ return;
783
+ }
784
+ onData(Buffer.from(chunk), stream);
785
+ }
786
+ };
787
+ const [exited] = await Promise.all([
788
+ process.wait(),
789
+ pipe("stdout"),
790
+ pipe("stderr")
791
+ ]);
792
+ exitCode = exited.exitCode;
793
+ } catch (error) {
794
+ if (signal?.aborted) throw new Error("aborted", { cause: error });
795
+ if (timedOut) throw new Error(`timeout:${timeout}`, { cause: error });
796
+ if (overflowed) throw overflow({ cause: error });
797
+ throw error;
798
+ } finally {
799
+ clearTimeout(timer);
800
+ signal?.removeEventListener("abort", abort);
801
+ }
802
+ if (signal?.aborted) throw new Error("aborted");
803
+ if (timedOut) throw new Error(`timeout:${timeout}`);
804
+ if (overflowed) throw overflow();
805
+ return { exitCode };
806
+ }
807
+ const cwds = boundedCache({
808
+ maxEntries: 1e3,
809
+ maxBytes: 1e3
810
+ });
811
+ const cwdKey = (agentName, sessionId) => `${agentName}\u001f${sessionId}`;
812
+ /** A session's working directory in its sandbox: Pi's cwd. */
813
+ function sessionCwd({ key, sandbox }) {
814
+ const cached = key === void 0 ? void 0 : cwds.get(key);
815
+ if (cached !== void 0) return cached;
816
+ const cwd = sandbox().then(async (resolved) => {
817
+ const output = {
818
+ stdout: "",
819
+ stderr: ""
820
+ };
821
+ const decoder = {
822
+ stdout: new TextDecoder(),
823
+ stderr: new TextDecoder()
824
+ };
825
+ const { exitCode } = await sandboxExec({
826
+ sandbox: resolved,
827
+ command: "pwd",
828
+ onData: (data, stream) => {
829
+ output[stream] += decoder[stream].decode(data, { stream: true });
830
+ }
831
+ }).catch((error) => {
832
+ throw new Error("pi() could not read the sandbox's working directory", { cause: error });
833
+ });
834
+ const path = output.stdout.trim();
835
+ if (exitCode !== 0 || !path.startsWith("/")) throw new Error(`pi() could not read the sandbox's working directory: ${(output.stderr || output.stdout).slice(0, 1e3)}`);
836
+ return path;
837
+ });
838
+ if (key !== void 0) {
839
+ cwds.set(key, cwd, 1);
840
+ cwd.catch(() => cwds.delete(key));
841
+ }
842
+ return cwd;
843
+ }
844
+ let loading;
845
+ function loadPi() {
846
+ loading ??= Promise.all([import("@earendil-works/pi-coding-agent"), import("@earendil-works/pi-ai")]).then(([agent, ai]) => ({
847
+ agent,
848
+ ai
849
+ }), (error) => {
850
+ loading = void 0;
851
+ throw new Error("pi() needs @earendil-works/pi-coding-agent and @earendil-works/pi-ai: install them", { cause: error });
852
+ });
853
+ return loading;
854
+ }
855
+ /** Pi's resources: nothing discovered from disk, the agent's instructions appended. */
856
+ function resources(modules, instructions) {
857
+ const runtime = modules.agent.createExtensionRuntime();
858
+ return {
859
+ getExtensions: () => ({
860
+ extensions: [],
861
+ errors: [],
862
+ runtime
863
+ }),
864
+ getSkills: () => ({
865
+ skills: [],
866
+ diagnostics: []
867
+ }),
868
+ getPrompts: () => ({
869
+ prompts: [],
870
+ diagnostics: []
871
+ }),
872
+ getThemes: () => ({
873
+ themes: [],
874
+ diagnostics: []
875
+ }),
876
+ getAgentsFiles: () => ({ agentsFiles: [] }),
877
+ getSystemPrompt: () => void 0,
878
+ getSystemPromptSource: () => void 0,
879
+ getAppendSystemPrompt: () => instructions,
880
+ getAppendSystemPromptSources: () => [],
881
+ extendResources: () => {},
882
+ reload: async () => {}
883
+ };
884
+ }
885
+ function thinkingLevel(step) {
886
+ if (step.reasoning === void 0 || step.reasoning === "provider-default") return {};
887
+ return { thinkingLevel: step.reasoning === "none" ? "off" : step.reasoning };
888
+ }
889
+ function gatewayHeaders(credential) {
890
+ return {
891
+ authorization: `Bearer ${credential.token}`,
892
+ "ai-gateway-auth-method": credential.method
893
+ };
894
+ }
895
+ /** A stable Pi session ID for an A2 session, so Pi's cache affinity holds. */
896
+ function sessionUuid(agentName, sessionId) {
897
+ const hex = createHash("sha256").update(`${agentName}\u001f${sessionId}`).digest("hex");
898
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-8${hex.slice(13, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
899
+ }
900
+ function savedSession(state) {
901
+ if (typeof state !== "object" || state === null) return void 0;
902
+ const { version, cwd, header, entries } = state;
903
+ if (version !== SNAPSHOT_VERSION || typeof cwd !== "string" || typeof header !== "object" || header === null || !Array.isArray(entries)) return void 0;
904
+ return {
905
+ cwd,
906
+ header,
907
+ entries
908
+ };
909
+ }
910
+ function compactionData(event) {
911
+ if (event.result === void 0) return { status: event.aborted ? "interrupted" : "failed" };
912
+ return {
913
+ status: "completed",
914
+ inputTokens: event.result.tokensBefore,
915
+ summary: event.result.summary,
916
+ ...event.result.usage === void 0 ? {} : { usage: modelUsage(event.result.usage) }
917
+ };
918
+ }
919
+ function finishReason(reason) {
920
+ switch (reason) {
921
+ case "stop": return "stop";
922
+ case "length": return "length";
923
+ case "toolUse": return "tool-calls";
924
+ case "error": return "error";
925
+ default: return "other";
926
+ }
927
+ }
928
+ function modelUsage(usage) {
929
+ return {
930
+ inputTokens: usage.input + usage.cacheRead + usage.cacheWrite,
931
+ inputTokenDetails: {
932
+ noCacheTokens: usage.input,
933
+ cacheReadTokens: usage.cacheRead,
934
+ cacheWriteTokens: usage.cacheWrite
935
+ },
936
+ outputTokens: usage.output,
937
+ outputTokenDetails: {
938
+ textTokens: usage.reasoning === void 0 ? void 0 : usage.output - usage.reasoning,
939
+ reasoningTokens: usage.reasoning
940
+ },
941
+ totalTokens: usage.totalTokens
942
+ };
943
+ }
944
+ function asError(error) {
945
+ return error instanceof Error ? error : new Error(String(error));
946
+ }
947
+ //#endregion
948
+ export { pi };
949
+
950
+ //# sourceMappingURL=ai-pi.js.map