agents 0.24.0 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. package/dist/{agent-routing-B2XLNMxq.d.ts → agent-routing-0DR0Tk2v.d.ts} +205 -132
  2. package/dist/agent-routing.d.ts +1 -1
  3. package/dist/agent-routing.js.map +1 -1
  4. package/dist/agent-tool-types.d.ts +28 -22
  5. package/dist/agent-tool-types.js +23 -1
  6. package/dist/agent-tool-types.js.map +1 -1
  7. package/dist/{agent-tools-y7zLfw4Q.js → agent-tools-CdJC-8Dc.js} +72 -9
  8. package/dist/agent-tools-CdJC-8Dc.js.map +1 -0
  9. package/dist/{agent-tools-Dj5Mzlw7.d.ts → agent-tools-YrATZJu2.d.ts} +34 -20
  10. package/dist/agent-tools.d.ts +20 -18
  11. package/dist/agent-tools.js.map +1 -1
  12. package/dist/ai-fm7xx1BK.js +312 -0
  13. package/dist/ai-fm7xx1BK.js.map +1 -0
  14. package/dist/base64-redaction-C1Uu29t5.js +59 -0
  15. package/dist/base64-redaction-C1Uu29t5.js.map +1 -0
  16. package/dist/browser/ai-sdk.d.ts +90 -0
  17. package/dist/browser/ai-sdk.js +51 -0
  18. package/dist/browser/ai-sdk.js.map +1 -0
  19. package/dist/browser/ai.d.ts +4 -4
  20. package/dist/browser/ai.js +1 -347
  21. package/dist/browser/index.d.ts +81 -79
  22. package/dist/browser/index.js +361 -2
  23. package/dist/browser/index.js.map +1 -0
  24. package/dist/browser/tanstack-ai.d.ts +96 -1
  25. package/dist/browser/tanstack-ai.js +62 -2
  26. package/dist/browser/tanstack-ai.js.map +1 -1
  27. package/dist/browser-DE3d-RLk.d.ts +275 -0
  28. package/dist/browser-tool-CQ10e6Kj.js +480 -0
  29. package/dist/browser-tool-CQ10e6Kj.js.map +1 -0
  30. package/dist/browser-tool-DuNdlwZ9.d.ts +73 -0
  31. package/dist/capability-B4WbF81e.js.map +1 -1
  32. package/dist/{capability-runner-BUBa6Ake.d.ts → capability-runner-DBAnGr6L.d.ts} +7 -2
  33. package/dist/chat/index.d.ts +340 -234
  34. package/dist/chat/index.js +592 -66
  35. package/dist/chat/index.js.map +1 -1
  36. package/dist/chat/react.d.ts +42 -2
  37. package/dist/chat/react.js +191 -35
  38. package/dist/chat/react.js.map +1 -1
  39. package/dist/chat/transport.d.ts +1 -1
  40. package/dist/chat/transport.js +1 -1
  41. package/dist/chat-sdk/index.d.ts +17 -3
  42. package/dist/chat-sdk/index.js +28 -2
  43. package/dist/chat-sdk/index.js.map +1 -1
  44. package/dist/{client-C9DAjZIY.js → client-4OlMIJx-.js} +102 -29
  45. package/dist/client-4OlMIJx-.js.map +1 -0
  46. package/dist/{client-BxdkCuVy.d.ts → client-je3koD9Z.d.ts} +45 -10
  47. package/dist/client.d.ts +7 -5
  48. package/dist/client.js +2 -2
  49. package/dist/{connector-CptFKzRh.js → connector-58XDGvc3.js} +251 -120
  50. package/dist/connector-58XDGvc3.js.map +1 -0
  51. package/dist/{connector-CkQD4MK3.d.ts → connector-Cl-VqPmA.d.ts} +244 -146
  52. package/dist/context/index.d.ts +29 -0
  53. package/dist/context/index.js +64 -17
  54. package/dist/context/index.js.map +1 -1
  55. package/dist/current-agent-DhoDkSnH.js.map +1 -1
  56. package/dist/{current-agent-C4w86kcN.d.ts → current-agent-vAk_5JMt.d.ts} +4 -4
  57. package/dist/{durable-object-lifecycle-ei7M7lqB.d.ts → durable-object-lifecycle-HhgOMWq9.d.ts} +15 -7
  58. package/dist/harness/pi/index.d.ts +344 -0
  59. package/dist/harness/pi/index.js +873 -0
  60. package/dist/harness/pi/index.js.map +1 -0
  61. package/dist/images-iiTDSyRu.js +1631 -0
  62. package/dist/images-iiTDSyRu.js.map +1 -0
  63. package/dist/{index-Dlgtd3Mj.d.ts → index-BreL0YrQ.d.ts} +2 -2
  64. package/dist/index-kbSUIer3.d.ts +343 -0
  65. package/dist/index.d.ts +46 -44
  66. package/dist/index.js +1 -1
  67. package/dist/internal_context.d.ts +1 -1
  68. package/dist/lifecycle/index.d.ts +3 -3
  69. package/dist/lifecycle/index.js +1 -1
  70. package/dist/{lifecycle-Mm_jQh7r.js → lifecycle-CFu6OV03.js} +39 -10
  71. package/dist/{lifecycle-Mm_jQh7r.js.map → lifecycle-CFu6OV03.js.map} +1 -1
  72. package/dist/mcp/client/index.d.ts +20 -20
  73. package/dist/mcp/client/x402.js +21 -19
  74. package/dist/mcp/client/x402.js.map +1 -1
  75. package/dist/mcp/index.d.ts +35 -35
  76. package/dist/mcp/index.js +1 -1
  77. package/dist/{message-builder-BymO4N_D.js → message-builder-DAl9AhHH.js} +89 -4
  78. package/dist/message-builder-DAl9AhHH.js.map +1 -0
  79. package/dist/models/ai-sdk/index.d.ts +353 -0
  80. package/dist/models/ai-sdk/index.js +2443 -0
  81. package/dist/models/ai-sdk/index.js.map +1 -0
  82. package/dist/models/pi-ai/index.d.ts +313 -0
  83. package/dist/models/pi-ai/index.js +1795 -0
  84. package/dist/models/pi-ai/index.js.map +1 -0
  85. package/dist/observability/ai/index.js +20 -4
  86. package/dist/observability/ai/index.js.map +1 -1
  87. package/dist/{protocol-B0nh6KNf.js → protocol-kHsr-f0X.js} +26 -12
  88. package/dist/{protocol-B0nh6KNf.js.map → protocol-kHsr-f0X.js.map} +1 -1
  89. package/dist/queue/index.d.ts +3 -3
  90. package/dist/queue/index.js +1 -1
  91. package/dist/{queue-BC9F5cUo.js → queue-BB-zfIYF.js} +2 -2
  92. package/dist/{queue-BC9F5cUo.js.map → queue-BB-zfIYF.js.map} +1 -1
  93. package/dist/react.d.ts +6 -6
  94. package/dist/react.js +160 -64
  95. package/dist/react.js.map +1 -1
  96. package/dist/{retries-D9B2UCq3.d.ts → retries-CV6AkXmQ.d.ts} +18 -9
  97. package/dist/retries.d.ts +11 -9
  98. package/dist/retries.js +11 -1
  99. package/dist/retries.js.map +1 -1
  100. package/dist/routing/index.d.ts +2 -2
  101. package/dist/routing/index.js +1 -1
  102. package/dist/{scheduler-D_KHqxBa.js → scheduler-DZ3xQfeK.js} +2 -2
  103. package/dist/{scheduler-D_KHqxBa.js.map → scheduler-DZ3xQfeK.js.map} +1 -1
  104. package/dist/{scheduler-BGq6M5Kd.d.ts → scheduler-RdACNXDP.d.ts} +3 -3
  105. package/dist/schedules/index.d.ts +1 -1
  106. package/dist/schedules/index.js +1 -1
  107. package/dist/serializable.d.ts +1 -1
  108. package/dist/sessions/index.d.ts +70 -3
  109. package/dist/sessions/index.js +276 -67
  110. package/dist/sessions/index.js.map +1 -1
  111. package/dist/skills/index.d.ts +29 -314
  112. package/dist/socket-address.d.ts +16 -0
  113. package/dist/socket-address.js +19 -0
  114. package/dist/socket-address.js.map +1 -0
  115. package/dist/{src-BNU3ZiJM.js → src-BZeu-SCD.js} +423 -115
  116. package/dist/src-BZeu-SCD.js.map +1 -0
  117. package/dist/state/index.d.ts +1 -1
  118. package/dist/state/index.js +1 -1
  119. package/dist/streams/index.d.ts +1 -1
  120. package/dist/streams/index.js +1 -1
  121. package/dist/{streams-BcE8JP2X.d.ts → streams-Cmkj9QfW.d.ts} +4 -2
  122. package/dist/{streams-CCPRV6dt.js → streams-DW-UCOUc.js} +9 -2
  123. package/dist/streams-DW-UCOUc.js.map +1 -0
  124. package/dist/sub-routing.d.ts +12 -12
  125. package/dist/tasks/index.d.ts +1 -1
  126. package/dist/tasks/index.js +1 -1
  127. package/dist/{tasks-D4nLqVSI.js → tasks-68DESxf0.js} +2 -2
  128. package/dist/{tasks-D4nLqVSI.js.map → tasks-68DESxf0.js.map} +1 -1
  129. package/dist/{tasks-V2lCT8ZV.d.ts → tasks-9YxYtByh.d.ts} +2 -2
  130. package/dist/transport-PfFnt-Ld.d.ts +525 -0
  131. package/dist/{types-DCD6BNZH.d.ts → types-BA4anrjI.d.ts} +2 -2
  132. package/dist/voice/index.d.ts +2 -2
  133. package/dist/websockets/index.d.ts +23 -6
  134. package/dist/websockets/index.js +1 -1
  135. package/dist/{websockets-D7IzWbZb.js → websockets-Ci-LZFHH.js} +45 -20
  136. package/dist/{websockets-D7IzWbZb.js.map → websockets-Ci-LZFHH.js.map} +1 -1
  137. package/dist/wire-types-CnMt6_HR.js.map +1 -1
  138. package/dist/wire-types-CrjKvDyi.d.ts +300 -0
  139. package/dist/workflow-types.d.ts +25 -25
  140. package/dist/workflows.d.ts +21 -21
  141. package/dist/workflows.js +1 -1
  142. package/dist/{ws-chat-transport-UNRIS2xl.d.ts → ws-chat-transport-BbT1A5Lc.d.ts} +51 -2
  143. package/dist/{ws-chat-transport-rWwta645.js → ws-chat-transport-cqMKq3IY.js} +143 -16
  144. package/dist/ws-chat-transport-cqMKq3IY.js.map +1 -0
  145. package/docs/agent-tools.md +29 -4
  146. package/docs/browse-the-web.md +87 -5
  147. package/docs/chat-agents.md +124 -16
  148. package/docs/context.md +27 -0
  149. package/docs/durable-execution.md +2 -0
  150. package/docs/harnesses/pi.md +173 -0
  151. package/docs/human-in-the-loop.md +4 -0
  152. package/docs/index.md +3 -0
  153. package/docs/lifecycle.md +20 -4
  154. package/docs/models-pi-ai.md +325 -0
  155. package/docs/models.md +504 -0
  156. package/docs/resumable-streaming.md +2 -0
  157. package/docs/routing.md +13 -13
  158. package/docs/sessions.md +36 -1
  159. package/docs/state.md +1 -0
  160. package/docs/sub-agents.md +9 -0
  161. package/package.json +40 -2
  162. package/dist/agent-tools-y7zLfw4Q.js.map +0 -1
  163. package/dist/browser/ai.js.map +0 -1
  164. package/dist/client-C9DAjZIY.js.map +0 -1
  165. package/dist/client-tools-aIBO0Fk7.d.ts +0 -53
  166. package/dist/connector-CptFKzRh.js.map +0 -1
  167. package/dist/message-builder-BymO4N_D.js.map +0 -1
  168. package/dist/src-BNU3ZiJM.js.map +0 -1
  169. package/dist/streams-CCPRV6dt.js.map +0 -1
  170. package/dist/ws-chat-transport-rWwta645.js.map +0 -1
@@ -0,0 +1,873 @@
1
+ import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldSet2, r as _assertClassBrand, t as _classPrivateFieldGet2 } from "../../classPrivateFieldGet2-DZBYAB34.js";
2
+ import { t as _classPrivateMethodInitSpec } from "../../classPrivateMethodInitSpec-qMjJ6sHQ.js";
3
+ import { t as LifecycleCapability } from "../../capability-B4WbF81e.js";
4
+ import "../../lifecycle-CFu6OV03.js";
5
+ import { LiveDoc, ROOT_CONVERSATION_ID, watchEvents } from "@earendil-works/pi-durable";
6
+ import { SQLITE_MIGRATIONS, SqliteStorage } from "@earendil-works/pi-durable/storage/sqlite";
7
+ import { Type } from "@earendil-works/pi-ai";
8
+ //#region src/harness/pi/context.ts
9
+ /** chord's `BACKGROUND_CONTEXT`: no values, never cancelled. */
10
+ const BACKGROUND_CONTEXT = Object.freeze({
11
+ abortSignal: void 0,
12
+ value: () => void 0,
13
+ toString: () => "[Context pi-harness background]"
14
+ });
15
+ /**
16
+ * chord's `withAbortSignal`: a context cancelled by either the parent's
17
+ * signal or `signal`, carrying the parent's values.
18
+ */
19
+ function withAbortSignal(signal, parent) {
20
+ const abortSignal = parent.abortSignal === void 0 ? signal : AbortSignal.any([parent.abortSignal, signal]);
21
+ return Object.freeze({
22
+ abortSignal,
23
+ value: parent.value.bind(parent),
24
+ toString: () => `${parent}.WithAbortSignal`
25
+ });
26
+ }
27
+ //#endregion
28
+ //#region src/harness/pi/session-store.ts
29
+ const DEFAULT_PREFIX = "pi_";
30
+ /** Open pi-durable's storage over this object's SQLite database. */
31
+ function openPiSessionStore(storage, options = {}) {
32
+ return SqliteStorage.open(new DurableObjectSqliteDatabase(storage, options));
33
+ }
34
+ /** Names pi's migrations create: every table and index, in any version. */
35
+ function schemaNames() {
36
+ const names = /* @__PURE__ */ new Set(["durable_schema"]);
37
+ const pattern = /\bCREATE\s+(?:UNIQUE\s+)?(?:TABLE|INDEX)\s+(?:IF\s+NOT\s+EXISTS\s+)?([A-Za-z_][A-Za-z0-9_]*)/gi;
38
+ for (const migration of SQLITE_MIGRATIONS) for (const statement of migration.statements) for (const match of statement.matchAll(pattern)) names.add(match[1]);
39
+ return [...names];
40
+ }
41
+ var _pattern = /* @__PURE__ */ new WeakMap();
42
+ var _prefix = /* @__PURE__ */ new WeakMap();
43
+ var _cache = /* @__PURE__ */ new WeakMap();
44
+ /**
45
+ * Rewrites pi's schema identifiers outside string literals. Identifiers are
46
+ * matched on word boundaries, so column names such as `record_type` or
47
+ * `document_id` are left alone.
48
+ */
49
+ var Prefixer = class {
50
+ constructor(prefix) {
51
+ _classPrivateFieldInitSpec(this, _pattern, void 0);
52
+ _classPrivateFieldInitSpec(this, _prefix, void 0);
53
+ _classPrivateFieldInitSpec(this, _cache, /* @__PURE__ */ new Map());
54
+ if (!/^[A-Za-z][A-Za-z0-9_]*$/.test(prefix)) throw new Error(`Invalid pi session store prefix ${JSON.stringify(prefix)}`);
55
+ if (prefix.startsWith("_cf_")) throw new Error("The pi session store prefix must not start with _cf_");
56
+ _classPrivateFieldSet2(_prefix, this, prefix);
57
+ _classPrivateFieldSet2(_pattern, this, new RegExp(`\\b(${schemaNames().join("|")})\\b`, "g"));
58
+ }
59
+ rewrite(sql) {
60
+ const cached = _classPrivateFieldGet2(_cache, this).get(sql);
61
+ if (cached !== void 0) return cached;
62
+ const rewritten = sql.split(/('(?:[^']|'')*')/).map((part, index) => index % 2 === 1 ? part : part.replace(_classPrivateFieldGet2(_pattern, this), (name) => `${_classPrivateFieldGet2(_prefix, this)}${name}`)).join("");
63
+ _classPrivateFieldGet2(_cache, this).set(sql, rewritten);
64
+ return rewritten;
65
+ }
66
+ };
67
+ /** Durable Objects bind strings, numbers, null, and ArrayBuffers. */
68
+ function binding(value) {
69
+ if (typeof value === "bigint") {
70
+ if (value > BigInt(Number.MAX_SAFE_INTEGER) || value < BigInt(Number.MIN_SAFE_INTEGER)) throw new RangeError(`SQLite integer ${value} is outside the safe range`);
71
+ return Number(value);
72
+ }
73
+ if (value instanceof Uint8Array) return value.buffer.slice(value.byteOffset, value.byteOffset + value.byteLength);
74
+ return value;
75
+ }
76
+ /** Blobs come back as ArrayBuffers; pi's contract reads Uint8Arrays. */
77
+ function row(raw) {
78
+ for (const key of Object.keys(raw)) {
79
+ const value = raw[key];
80
+ if (value instanceof ArrayBuffer) raw[key] = new Uint8Array(value);
81
+ }
82
+ return raw;
83
+ }
84
+ var _tail = /* @__PURE__ */ new WeakMap();
85
+ var _pending = /* @__PURE__ */ new WeakMap();
86
+ var _OperationQueue_brand = /* @__PURE__ */ new WeakSet();
87
+ /**
88
+ * Orders calls on the one SQLite connection. A statement runs at once,
89
+ * directly, when nothing is waiting; while a transaction is open, it and
90
+ * every call after it wait, in call order, for the transaction to settle,
91
+ * so none runs inside it. A failure does not stop the calls behind it.
92
+ *
93
+ * The queue holds calls, not data: a caller has no result until its call
94
+ * runs, so an isolate that dies loses only calls nobody has seen succeed,
95
+ * as it would without the queue. pi's transactions await only their own
96
+ * synchronous statements, so a transaction holds the queue for microtasks,
97
+ * never for I/O.
98
+ */
99
+ var OperationQueue = class {
100
+ constructor() {
101
+ _classPrivateMethodInitSpec(this, _OperationQueue_brand);
102
+ _classPrivateFieldInitSpec(this, _tail, Promise.resolve());
103
+ _classPrivateFieldInitSpec(this, _pending, 0);
104
+ }
105
+ /** A synchronous statement: runs now unless something is ahead of it. */
106
+ run(statement) {
107
+ if (_classPrivateFieldGet2(_pending, this) > 0) return _assertClassBrand(_OperationQueue_brand, this, _enqueue).call(this, statement);
108
+ try {
109
+ return Promise.resolve(statement());
110
+ } catch (error) {
111
+ return Promise.reject(error);
112
+ }
113
+ }
114
+ /** An asynchronous call that holds the queue until it settles. */
115
+ hold(call) {
116
+ return _assertClassBrand(_OperationQueue_brand, this, _enqueue).call(this, call);
117
+ }
118
+ };
119
+ function _enqueue(call) {
120
+ _classPrivateFieldSet2(_pending, this, _classPrivateFieldGet2(_pending, this) + 1);
121
+ const settled = _classPrivateFieldGet2(_tail, this).then(call).finally(() => {
122
+ _classPrivateFieldSet2(_pending, this, _classPrivateFieldGet2(_pending, this) - 1);
123
+ });
124
+ _classPrivateFieldSet2(_tail, this, settled.then(() => void 0, () => void 0));
125
+ return settled;
126
+ }
127
+ var _sql = /* @__PURE__ */ new WeakMap();
128
+ var _prefixer = /* @__PURE__ */ new WeakMap();
129
+ /** One statement on a Durable Object's synchronous SQL API. */
130
+ var Statements = class {
131
+ constructor(sql, prefixer) {
132
+ _classPrivateFieldInitSpec(this, _sql, void 0);
133
+ _classPrivateFieldInitSpec(this, _prefixer, void 0);
134
+ _classPrivateFieldSet2(_sql, this, sql);
135
+ _classPrivateFieldSet2(_prefixer, this, prefixer);
136
+ }
137
+ exec(sql) {
138
+ _classPrivateFieldGet2(_sql, this).exec(_classPrivateFieldGet2(_prefixer, this).rewrite(sql));
139
+ }
140
+ run(sql, params) {
141
+ _classPrivateFieldGet2(_sql, this).exec(_classPrivateFieldGet2(_prefixer, this).rewrite(sql), ...params.map(binding));
142
+ }
143
+ get(sql, params) {
144
+ const first = _classPrivateFieldGet2(_sql, this).exec(_classPrivateFieldGet2(_prefixer, this).rewrite(sql), ...params.map(binding)).next();
145
+ return first.done ? void 0 : row(first.value);
146
+ }
147
+ all(sql, params) {
148
+ return _classPrivateFieldGet2(_sql, this).exec(_classPrivateFieldGet2(_prefixer, this).rewrite(sql), ...params.map(binding)).toArray().map((raw) => row(raw));
149
+ }
150
+ };
151
+ var _statements = /* @__PURE__ */ new WeakMap();
152
+ var _active = /* @__PURE__ */ new WeakMap();
153
+ var _DurableObjectSqliteTransaction_brand = /* @__PURE__ */ new WeakSet();
154
+ /**
155
+ * The handle pi's transaction callback gets. It runs statements directly:
156
+ * the transaction holds the database's queue for as long as it is open.
157
+ */
158
+ var DurableObjectSqliteTransaction = class {
159
+ constructor(statements) {
160
+ _classPrivateMethodInitSpec(this, _DurableObjectSqliteTransaction_brand);
161
+ _classPrivateFieldInitSpec(this, _statements, void 0);
162
+ _classPrivateFieldInitSpec(this, _active, true);
163
+ _classPrivateFieldSet2(_statements, this, statements);
164
+ }
165
+ /** Called once the transaction settles; the handle is unusable after. */
166
+ end() {
167
+ _classPrivateFieldSet2(_active, this, false);
168
+ }
169
+ async exec(sql) {
170
+ _assertClassBrand(_DurableObjectSqliteTransaction_brand, this, _assertActive).call(this);
171
+ _classPrivateFieldGet2(_statements, this).exec(sql);
172
+ }
173
+ async run(sql, ...params) {
174
+ _assertClassBrand(_DurableObjectSqliteTransaction_brand, this, _assertActive).call(this);
175
+ _classPrivateFieldGet2(_statements, this).run(sql, params);
176
+ }
177
+ async get(sql, ...params) {
178
+ _assertClassBrand(_DurableObjectSqliteTransaction_brand, this, _assertActive).call(this);
179
+ return _classPrivateFieldGet2(_statements, this).get(sql, params);
180
+ }
181
+ async all(sql, ...params) {
182
+ _assertClassBrand(_DurableObjectSqliteTransaction_brand, this, _assertActive).call(this);
183
+ return _classPrivateFieldGet2(_statements, this).all(sql, params);
184
+ }
185
+ };
186
+ function _assertActive() {
187
+ if (!_classPrivateFieldGet2(_active, this)) throw new Error("The pi SQLite transaction is no longer active");
188
+ }
189
+ var _storage = /* @__PURE__ */ new WeakMap();
190
+ var _statements2 = /* @__PURE__ */ new WeakMap();
191
+ var _queue = /* @__PURE__ */ new WeakMap();
192
+ /**
193
+ * pi's `SqliteDatabase` facade over `DurableObjectStorage`.
194
+ *
195
+ * Durable Object SQL is synchronous, but pi's facade is asynchronous and a
196
+ * transaction's callback awaits between statements. A statement issued
197
+ * outside the transaction while it waits would run inside it: it would see
198
+ * uncommitted rows, and roll back with it. pi's contract therefore has the
199
+ * adapter queue every other call until the transaction settles: every
200
+ * call goes through one `OperationQueue`, which runs a statement at once
201
+ * when no transaction is open. As the contract says, a call on the database from inside a
202
+ * transaction's callback waits for that transaction and never settles; the
203
+ * callback must use its handle.
204
+ *
205
+ * Transactions run in `storage.transaction()`, which rolls back when the
206
+ * callback rejects and rejects with the same error.
207
+ */
208
+ var DurableObjectSqliteDatabase = class {
209
+ constructor(storage, options = {}) {
210
+ _classPrivateFieldInitSpec(this, _storage, void 0);
211
+ _classPrivateFieldInitSpec(this, _statements2, void 0);
212
+ _classPrivateFieldInitSpec(this, _queue, new OperationQueue());
213
+ _classPrivateFieldSet2(_storage, this, storage);
214
+ _classPrivateFieldSet2(_statements2, this, new Statements(storage.sql, new Prefixer(options.prefix ?? DEFAULT_PREFIX)));
215
+ }
216
+ exec(sql) {
217
+ return _classPrivateFieldGet2(_queue, this).run(() => _classPrivateFieldGet2(_statements2, this).exec(sql));
218
+ }
219
+ run(sql, ...params) {
220
+ return _classPrivateFieldGet2(_queue, this).run(() => _classPrivateFieldGet2(_statements2, this).run(sql, params));
221
+ }
222
+ get(sql, ...params) {
223
+ return _classPrivateFieldGet2(_queue, this).run(() => _classPrivateFieldGet2(_statements2, this).get(sql, params));
224
+ }
225
+ all(sql, ...params) {
226
+ return _classPrivateFieldGet2(_queue, this).run(() => _classPrivateFieldGet2(_statements2, this).all(sql, params));
227
+ }
228
+ transaction(callback) {
229
+ return _classPrivateFieldGet2(_queue, this).hold(() => _classPrivateFieldGet2(_storage, this).transaction(async () => {
230
+ const transaction = new DurableObjectSqliteTransaction(_classPrivateFieldGet2(_statements2, this));
231
+ try {
232
+ return await callback(transaction);
233
+ } finally {
234
+ transaction.end();
235
+ }
236
+ }));
237
+ }
238
+ /** The object owns the database; there is nothing to close. */
239
+ close() {
240
+ return _classPrivateFieldGet2(_queue, this).run(() => void 0);
241
+ }
242
+ };
243
+ //#endregion
244
+ //#region src/harness/pi/harness.ts
245
+ const BG = BACKGROUND_CONTEXT;
246
+ /** The root session's id. */
247
+ const ROOT_SESSION = String(ROOT_CONVERSATION_ID);
248
+ /**
249
+ * The three wake timings. They suit a real deployment; only tests change
250
+ * them, with `setWakeTimingForTests`, so a suite does not sit on the real
251
+ * heartbeat.
252
+ *
253
+ * A pi long wait further away than this is handed to the alarm, so the
254
+ * alarm, not pi's in-memory timer, is what wakes the object.
255
+ */
256
+ const SLEEP_THRESHOLD_MS = 6e4;
257
+ /**
258
+ * Longest one wake waits on pi. It waits inside an alarm invocation, which
259
+ * has a 15 minute wall-time limit, so a longer run is waited on across
260
+ * several alarms.
261
+ */
262
+ const WAIT_BUDGET_MS = 10 * 6e4;
263
+ /**
264
+ * The wake job's heartbeat while it waits, and how often it re-checks work
265
+ * it cannot wait on, such as background tasks. If the object is evicted, the
266
+ * job is still due and its alarm restarts the object.
267
+ */
268
+ const HEARTBEAT_MS = 3e4;
269
+ const WAKE_FN = "wake";
270
+ function wakeJobId(session) {
271
+ return `pi-wake:${session}`;
272
+ }
273
+ function sessionOfJob(payload) {
274
+ return typeof payload === "object" && payload !== null && "session" in payload && typeof payload.session === "string" ? payload.session : void 0;
275
+ }
276
+ /** pi-durable's stored reference to a pi-ai model. */
277
+ function modelRef(model) {
278
+ return {
279
+ provider: model.provider,
280
+ modelId: model.id
281
+ };
282
+ }
283
+ function conversationId(session) {
284
+ const id = Number(session);
285
+ if (!Number.isSafeInteger(id) || id < 1) throw new Error(`Invalid pi session ${JSON.stringify(session)}`);
286
+ return id;
287
+ }
288
+ /** The text of an assistant entry, for an operation's result. */
289
+ function assistantText(entry) {
290
+ const message = entry?.model?.[0];
291
+ if (message?.role !== "assistant") return "";
292
+ return message.content.map((part) => part.type === "text" ? part.text : "").join("");
293
+ }
294
+ function signalContext(signal) {
295
+ return signal ? withAbortSignal(signal, BG) : BG;
296
+ }
297
+ var _options = /* @__PURE__ */ new WeakMap();
298
+ var _waits = /* @__PURE__ */ new WeakMap();
299
+ var _admitting = /* @__PURE__ */ new WeakMap();
300
+ var _sleepThresholdMs = /* @__PURE__ */ new WeakMap();
301
+ var _waitBudgetMs = /* @__PURE__ */ new WeakMap();
302
+ var _heartbeatMs = /* @__PURE__ */ new WeakMap();
303
+ var _opening = /* @__PURE__ */ new WeakMap();
304
+ var _PiHarness_brand = /* @__PURE__ */ new WeakSet();
305
+ /**
306
+ * pi-durable hosted in a Durable Object, behind a small harness interface:
307
+ * `harness.prompt()`, `harness.sessions`, `harness.session(id)`. How a session reaches a
308
+ * client (sockets, SSE, RPC) is the host's glue, built on `session.events()`.
309
+ *
310
+ * pi owns everything about a run: the transcript, the inbox of steers and
311
+ * follow-ups, generation and tool tasks, retries, crash recovery, and the
312
+ * live view. It keeps all of it in its own tables in this object's SQLite
313
+ * database (see `session-store.ts`).
314
+ *
315
+ * What pi cannot do on a Durable Object is wake itself: its scheduler runs
316
+ * in memory, and an evicted object has no memory. The harness is that wake,
317
+ * with one Lifecycle job per session. Input goes to pi once, in `submit()`,
318
+ * after the session's job is scheduled. The job waits while pi has live
319
+ * tasks in the session, rescheduling itself as a heartbeat, and completes
320
+ * when there are none. An eviction mid-run leaves the job due, so its alarm
321
+ * restarts the object, pi reopens and resumes its own tasks, and the job
322
+ * waits again.
323
+ *
324
+ * @beta The API may change between releases.
325
+ */
326
+ var PiHarness = class extends LifecycleCapability {
327
+ constructor(options) {
328
+ super("pi-harness");
329
+ _classPrivateMethodInitSpec(this, _PiHarness_brand);
330
+ _classPrivateFieldInitSpec(this, _options, void 0);
331
+ _classPrivateFieldInitSpec(this, _waits, /* @__PURE__ */ new Map());
332
+ _classPrivateFieldInitSpec(this, _admitting, /* @__PURE__ */ new Map());
333
+ _classPrivateFieldInitSpec(this, _sleepThresholdMs, SLEEP_THRESHOLD_MS);
334
+ _classPrivateFieldInitSpec(this, _waitBudgetMs, WAIT_BUDGET_MS);
335
+ _classPrivateFieldInitSpec(this, _heartbeatMs, HEARTBEAT_MS);
336
+ _classPrivateFieldInitSpec(this, _opening, void 0);
337
+ _classPrivateFieldSet2(_options, this, options);
338
+ this.sessions = new PiSessions(this);
339
+ }
340
+ async onStart(_context) {
341
+ const { pi } = await _assertClassBrand(_PiHarness_brand, this, _open).call(this);
342
+ const inspection = await pi.inspect(BG);
343
+ const sessions = new Set(inspection.tasks.map((task) => String(task.record.conversationId)));
344
+ for (const session of sessions) await _assertClassBrand(_PiHarness_brand, this, _wake).call(this, session);
345
+ }
346
+ async onJob(context) {
347
+ if (context.job.fn !== WAKE_FN) return;
348
+ const session = sessionOfJob(context.job.payload);
349
+ if (session !== void 0) return _assertClassBrand(_PiHarness_brand, this, _wakeStep).call(this, session);
350
+ }
351
+ /** Close pi's in-memory resources. Durable state is untouched. */
352
+ async dispose() {
353
+ const opening = _classPrivateFieldGet2(_opening, this);
354
+ _classPrivateFieldSet2(_opening, this, void 0);
355
+ await (await opening?.catch(() => void 0))?.pi.close(BG);
356
+ }
357
+ /** A handle on one session. No I/O until you call it. */
358
+ session(id = ROOT_SESSION) {
359
+ return new PiSession(this, id);
360
+ }
361
+ /** Submit a prompt and wait for its answer. */
362
+ prompt(input, options = {}) {
363
+ return this.session(options.session).prompt(input, options);
364
+ }
365
+ /** Durably submit a prompt. Resolves before the model runs. */
366
+ submit(input, options = {}) {
367
+ return this.session(options.session).submit(input, options);
368
+ }
369
+ /** Stop one operation, or everything running in a session. */
370
+ abort(options = {}) {
371
+ return this.session(options.session).abort(options.operationId);
372
+ }
373
+ wait(operationId, options = {}) {
374
+ return this.session(options.session).wait(operationId, options.signal);
375
+ }
376
+ /** The session's active transcript, as pi's entries. */
377
+ messages(options = {}) {
378
+ return this.session(options.session).messages();
379
+ }
380
+ /** Submissions pi has not settled yet, oldest first. */
381
+ async pending(options = {}) {
382
+ const { pi } = await _assertClassBrand(_PiHarness_brand, this, _open).call(this);
383
+ const id = options.session === void 0 ? void 0 : conversationId(options.session);
384
+ return (await pi.inspect(BG)).submissions.filter((record) => record.requestId !== void 0 && (id === void 0 || record.conversationId === id)).map((record) => ({
385
+ operationId: record.requestId,
386
+ session: String(record.conversationId),
387
+ status: record.status === "queued" ? "queued" : "running"
388
+ }));
389
+ }
390
+ /** The opened pi Harness, for anything the interface does not cover. */
391
+ async pi() {
392
+ return (await _assertClassBrand(_PiHarness_brand, this, _open).call(this)).pi;
393
+ }
394
+ /** @internal */
395
+ async conversation(session) {
396
+ const { pi } = await _assertClassBrand(_PiHarness_brand, this, _open).call(this);
397
+ const conversation = await pi.conversation(conversationId(session), BG);
398
+ if (!conversation) throw new Error(`Unknown pi session ${session}`);
399
+ return conversation;
400
+ }
401
+ /** @internal */
402
+ async storage() {
403
+ return (await _assertClassBrand(_PiHarness_brand, this, _open).call(this)).storage;
404
+ }
405
+ /** @internal */
406
+ async enqueue(session, input, options) {
407
+ const operationId = options.operationId ?? crypto.randomUUID();
408
+ const { storage } = await _assertClassBrand(_PiHarness_brand, this, _open).call(this);
409
+ const conversation = await this.conversation(session);
410
+ _classPrivateFieldGet2(_admitting, this).set(session, (_classPrivateFieldGet2(_admitting, this).get(session) ?? 0) + 1);
411
+ let accepted;
412
+ try {
413
+ await _assertClassBrand(_PiHarness_brand, this, _wake).call(this, session);
414
+ accepted = await storage.submissionByRequest(conversation.id, operationId, BG) === void 0;
415
+ await conversation.submit({
416
+ type: "input",
417
+ content: input,
418
+ whenBusy: options.whenBusy ?? "followUp",
419
+ requestId: operationId
420
+ }, BG);
421
+ } finally {
422
+ const left = (_classPrivateFieldGet2(_admitting, this).get(session) ?? 1) - 1;
423
+ if (left === 0) _classPrivateFieldGet2(_admitting, this).delete(session);
424
+ else _classPrivateFieldGet2(_admitting, this).set(session, left);
425
+ }
426
+ await _assertClassBrand(_PiHarness_brand, this, _wake).call(this, session);
427
+ return {
428
+ operationId,
429
+ session,
430
+ accepted
431
+ };
432
+ }
433
+ /** @internal Withdraw a queued input, or abort the run it joined. */
434
+ async withdraw(session, operationId) {
435
+ const { pi, storage } = await _assertClassBrand(_PiHarness_brand, this, _open).call(this);
436
+ const id = conversationId(session);
437
+ const record = await storage.submissionByRequest(id, operationId, BG);
438
+ if (!record || record.status === "done" || record.status === "unanswered") return false;
439
+ const withdrawn = await pi.abortSubmission(record.id, BG, id);
440
+ if (withdrawn === "already_placed") await (await pi.conversation(id, BG))?.abort(BG);
441
+ return withdrawn !== "settled" && withdrawn !== "not_found";
442
+ }
443
+ /** @internal Wait for pi to settle an operation, by its request id. */
444
+ async settled(session, operationId, signal) {
445
+ const conversation = await this.conversation(session);
446
+ const context = signalContext(signal);
447
+ const submission = await _assertClassBrand(_PiHarness_brand, this, _findSubmission).call(this, conversation.id, operationId, context);
448
+ if (!submission) return {
449
+ operationId,
450
+ session,
451
+ status: "unanswered",
452
+ reason: "not_found"
453
+ };
454
+ return _assertClassBrand(_PiHarness_brand, this, _result).call(this, session, operationId, await submission.wait(context), context);
455
+ }
456
+ /** @internal The per-conversation defaults supported by pi-durable. */
457
+ agentDefaults() {
458
+ const defaults = _classPrivateFieldGet2(_options, this).defaults;
459
+ return {
460
+ ...defaults?.model === void 0 ? {} : { model: modelRef(defaults.model) },
461
+ ...defaults?.thinkingLevel === void 0 ? {} : { thinkingLevel: defaults.thinkingLevel }
462
+ };
463
+ }
464
+ };
465
+ /** Schedule the session's wake job now, or pull it forward. */
466
+ function _wake(session, time = Date.now()) {
467
+ return this.lifecycle.jobs.push({
468
+ id: wakeJobId(session),
469
+ fn: WAKE_FN,
470
+ time,
471
+ payload: { session },
472
+ singleflight: true,
473
+ recoveryLoop: true
474
+ });
475
+ }
476
+ /**
477
+ * One run of a session's wake job. It never admits or replays anything:
478
+ * it waits while pi has live tasks in the session and completes when it
479
+ * has none. pi does all the work in between.
480
+ */
481
+ async function _wakeStep(session) {
482
+ const heartbeat = { rescheduleAt: Date.now() + _classPrivateFieldGet2(_heartbeatMs, this) };
483
+ if (_classPrivateFieldGet2(_waits, this).has(session)) return heartbeat;
484
+ const { pi } = await _assertClassBrand(_PiHarness_brand, this, _open).call(this);
485
+ const conversation = await pi.conversation(conversationId(session), BG);
486
+ if (!conversation) return void 0;
487
+ const tasks = (await pi.inspect(BG)).tasks.filter((task) => task.record.conversationId === conversation.id);
488
+ if (tasks.length === 0) return _classPrivateFieldGet2(_admitting, this).has(session) ? heartbeat : void 0;
489
+ const wakeAt = await _assertClassBrand(_PiHarness_brand, this, _longWait).call(this, pi, conversation.id, BG);
490
+ if (wakeAt !== void 0) return { rescheduleAt: wakeAt };
491
+ if (tasks.every((task) => task.record.background)) return heartbeat;
492
+ const wait = _assertClassBrand(_PiHarness_brand, this, _waitForIdle).call(this, conversation).finally(() => {
493
+ _classPrivateFieldGet2(_waits, this).delete(session);
494
+ _assertClassBrand(_PiHarness_brand, this, _wake).call(this, session);
495
+ });
496
+ _classPrivateFieldGet2(_waits, this).set(session, wait);
497
+ this.lifecycle.trackAlarmWork(wait);
498
+ return heartbeat;
499
+ }
500
+ async function _waitForIdle(conversation) {
501
+ const budget = new AbortController();
502
+ const timer = setTimeout(() => budget.abort(), _classPrivateFieldGet2(_waitBudgetMs, this));
503
+ try {
504
+ await conversation.waitForIdle(withAbortSignal(budget.signal, BG));
505
+ } catch (error) {
506
+ if (!budget.signal.aborted) this.lifecycle.events.emit("pi:wake_error", {
507
+ session: String(conversation.id),
508
+ error: error instanceof Error ? error.message : String(error)
509
+ });
510
+ } finally {
511
+ clearTimeout(timer);
512
+ }
513
+ }
514
+ /**
515
+ * pi, opened. Waits for Lifecycle startup first, so pi is only ever
516
+ * opened inside it: startup holds the input gate (`blockConcurrencyWhile`)
517
+ * and awaits the open in `onStart`, so an open begun before startup would
518
+ * be awaited behind a closed gate, with its timers and I/O held back, and
519
+ * never finish.
520
+ */
521
+ async function _open() {
522
+ await this.lifecycle.ready();
523
+ _classPrivateFieldGet2(_opening, this) ?? _classPrivateFieldSet2(_opening, this, _assertClassBrand(_PiHarness_brand, this, _doOpen).call(this).catch((error) => {
524
+ _classPrivateFieldSet2(_opening, this, void 0);
525
+ throw error;
526
+ }));
527
+ return _classPrivateFieldGet2(_opening, this);
528
+ }
529
+ async function _doOpen() {
530
+ const storage = await openPiSessionStore(this.lifecycle.storage);
531
+ const pi = await _classPrivateFieldGet2(_options, this).harness({
532
+ storage,
533
+ context: BG
534
+ });
535
+ await pi.root(BG, { agent: this.agentDefaults() });
536
+ pi.resume();
537
+ return {
538
+ pi,
539
+ storage
540
+ };
541
+ }
542
+ async function _findSubmission(id, operationId, context) {
543
+ const { pi, storage } = await _assertClassBrand(_PiHarness_brand, this, _open).call(this);
544
+ const record = await storage.submissionByRequest(id, operationId, context);
545
+ return record ? pi.submission(record.id, context) : void 0;
546
+ }
547
+ /** When pi's current wait is far enough away to hand to the alarm. */
548
+ async function _longWait(pi, id, context) {
549
+ const live = await pi.snapshot(LiveDoc, id, context);
550
+ const at = live?.generation?.deferred?.pollAt ?? live?.generation?.retry?.at;
551
+ return at !== void 0 && at - Date.now() > _classPrivateFieldGet2(_sleepThresholdMs, this) ? at : void 0;
552
+ }
553
+ async function _result(session, operationId, settled, context) {
554
+ if (settled.status === "unanswered") return {
555
+ operationId,
556
+ session,
557
+ status: "unanswered",
558
+ reason: settled.reason
559
+ };
560
+ if (settled.type !== "input") return {
561
+ operationId,
562
+ session,
563
+ status: "done"
564
+ };
565
+ return {
566
+ operationId,
567
+ session,
568
+ status: "done",
569
+ text: assistantText((await (await this.conversation(session)).entries({
570
+ minEntryId: settled.answer,
571
+ maxEntryId: settled.answer
572
+ }, 1, void 0, context)).items[0])
573
+ };
574
+ }
575
+ var _harness = /* @__PURE__ */ new WeakMap();
576
+ /** One pi conversation, addressed through the harness. */
577
+ var PiSession = class {
578
+ constructor(harness, id) {
579
+ _classPrivateFieldInitSpec(this, _harness, void 0);
580
+ _classPrivateFieldSet2(_harness, this, harness);
581
+ this.id = id;
582
+ }
583
+ /** Durably submit a prompt. Resolves before the model runs. */
584
+ submit(input, options = {}) {
585
+ return _classPrivateFieldGet2(_harness, this).enqueue(this.id, input, options);
586
+ }
587
+ /** Submit and wait for the answer and the updated transcript. */
588
+ async prompt(input, options = {}) {
589
+ const receipt = await this.submit(input, options);
590
+ return {
591
+ ...await this.wait(receipt.operationId),
592
+ messages: await this.messages()
593
+ };
594
+ }
595
+ /** Join the running work after its current tool round. */
596
+ steer(input, options = {}) {
597
+ return this.submit(input, {
598
+ ...options,
599
+ whenBusy: "steer"
600
+ });
601
+ }
602
+ /** Wait for an operation to settle. Aborting `signal` stops only the wait. */
603
+ wait(operationId, signal) {
604
+ return _classPrivateFieldGet2(_harness, this).settled(this.id, operationId, signal);
605
+ }
606
+ /**
607
+ * Withdraw one queued operation (or abort the run it joined), or, with no
608
+ * id, abort the session: pi withdraws queued inputs and aborts the
609
+ * running work.
610
+ */
611
+ async abort(operationId) {
612
+ if (operationId !== void 0) return _classPrivateFieldGet2(_harness, this).withdraw(this.id, operationId);
613
+ await (await _classPrivateFieldGet2(_harness, this).conversation(this.id)).abort(BG);
614
+ return true;
615
+ }
616
+ /** Start a new context, optionally from a handoff note. */
617
+ async reset(handoff) {
618
+ await (await _classPrivateFieldGet2(_harness, this).conversation(this.id)).reset(handoff, BG);
619
+ }
620
+ /**
621
+ * Change this session's model, by its `provider` and `id`; it must be on
622
+ * the `Models` the factory opened pi with (see `PiModel`).
623
+ */
624
+ async setModel(model) {
625
+ await (await _classPrivateFieldGet2(_harness, this).conversation(this.id)).configure({ model: modelRef(model) }, BG);
626
+ }
627
+ /** The active transcript, as pi's entries since the newest reset. */
628
+ async messages() {
629
+ return [...(await (await _classPrivateFieldGet2(_harness, this).conversation(this.id)).context(BG)).entries];
630
+ }
631
+ /** pi's agent events for this session: a snapshot, then one batch per commit. */
632
+ async events(context = BG) {
633
+ return watchEvents(await _classPrivateFieldGet2(_harness, this).pi(), conversationId(this.id), context);
634
+ }
635
+ async busy() {
636
+ return (await (await _classPrivateFieldGet2(_harness, this).pi()).snapshot(LiveDoc, conversationId(this.id), BG))?.run !== void 0;
637
+ }
638
+ };
639
+ var _harness2 = /* @__PURE__ */ new WeakMap();
640
+ /** Every pi conversation in this object. */
641
+ var PiSessions = class {
642
+ constructor(harness) {
643
+ _classPrivateFieldInitSpec(this, _harness2, void 0);
644
+ _classPrivateFieldSet2(_harness2, this, harness);
645
+ }
646
+ get(id) {
647
+ return _classPrivateFieldGet2(_harness2, this).session(id);
648
+ }
649
+ /** A new top-level session, configured like the root. */
650
+ async create() {
651
+ const conversation = await (await _classPrivateFieldGet2(_harness2, this).pi()).createConversation({
652
+ ownership: { kind: "ownerless" },
653
+ agent: _classPrivateFieldGet2(_harness2, this).agentDefaults()
654
+ }, BG);
655
+ return _classPrivateFieldGet2(_harness2, this).session(String(conversation.id));
656
+ }
657
+ /** A new session that sees `from`'s history up to its newest entry. */
658
+ async fork(from) {
659
+ const conversation = await _classPrivateFieldGet2(_harness2, this).conversation(from);
660
+ const at = (await conversation.entries({}, 1, void 0, BG)).items[0];
661
+ if (!at) throw new Error(`Session ${from} has no entries to fork from`);
662
+ const fork = await conversation.fork(at.id, { ownership: { kind: "ownerless" } }, BG);
663
+ return _classPrivateFieldGet2(_harness2, this).session(String(fork.id));
664
+ }
665
+ /**
666
+ * Every conversation, including ones subagent tools own. Read from pi's
667
+ * storage directly: the Harness has no conversation listing.
668
+ */
669
+ async list() {
670
+ const storage = await _classPrivateFieldGet2(_harness2, this).storage();
671
+ const pi = await _classPrivateFieldGet2(_harness2, this).pi();
672
+ const sessions = [];
673
+ let cursor;
674
+ for (;;) {
675
+ const page = await storage.scanConversations({}, 100, cursor, BG);
676
+ for (const record of page.items) {
677
+ const live = await pi.snapshot(LiveDoc, record.id, BG);
678
+ sessions.push({
679
+ id: String(record.id),
680
+ ...record.parent ? { parent: String(record.parent.conversationId) } : {},
681
+ busy: live?.run !== void 0
682
+ });
683
+ }
684
+ if (page.next === void 0) return sessions;
685
+ cursor = page.next;
686
+ }
687
+ }
688
+ };
689
+ //#endregion
690
+ //#region src/harness/pi/skills.ts
691
+ function skillsFingerprint(sources) {
692
+ return sources.map((source) => `${source.id}:${source.fingerprint}`).join("|");
693
+ }
694
+ function textResult(text) {
695
+ return { content: [{
696
+ type: "text",
697
+ text
698
+ }] };
699
+ }
700
+ function attributes(pairs) {
701
+ return pairs.filter((pair) => pair[1] !== void 0).map(([key, value]) => ` ${key}="${value.replace(/"/g, "&quot;")}"`).join("");
702
+ }
703
+ function describeResource(resource) {
704
+ const details = [
705
+ resource.kind,
706
+ resource.encoding ?? "text",
707
+ resource.mimeType,
708
+ resource.size === void 0 ? void 0 : `${resource.size} bytes`
709
+ ].filter((value) => value !== void 0);
710
+ return `- ${resource.path} (${details.join(", ")})`;
711
+ }
712
+ function renderSkillContent(skill) {
713
+ const resources = skill.content.resources ?? [];
714
+ const lines = [`<skill_content${attributes([["name", skill.descriptor.name], ["version", skill.descriptor.version]])}>`, skill.content.body.trim()];
715
+ if (resources.length > 0) {
716
+ lines.push("<skill_resources>");
717
+ for (const resource of resources) lines.push(` <file${attributes([
718
+ ["kind", resource.kind],
719
+ ["encoding", resource.encoding ?? "text"],
720
+ ["size", resource.size === void 0 ? void 0 : String(resource.size)]
721
+ ])}>${resource.path}</file>`);
722
+ lines.push("</skill_resources>");
723
+ }
724
+ lines.push("</skill_content>", "");
725
+ lines.push(resources.length === 0 ? "No bundled resources." : ["Bundled resources:", ...resources.map(describeResource)].join("\n"));
726
+ return lines.join("\n");
727
+ }
728
+ /**
729
+ * Resolve `agents/skills` sources into a system-prompt catalog and two
730
+ * tools.
731
+ *
732
+ * The first source to list a name wins, as in `SkillRegistry`. pi-durable
733
+ * has no skill concept of its own, so the model activates skills through
734
+ * `activate_skill` and reads bundled files through `read_skill_resource` —
735
+ * the same tools `@cloudflare/think` offers, so skills authored for one
736
+ * framework work in the other.
737
+ */
738
+ async function resolveSkillSources(sources) {
739
+ const fingerprint = skillsFingerprint(sources);
740
+ const warnings = [];
741
+ const resolved = /* @__PURE__ */ new Map();
742
+ for (const source of sources) {
743
+ let descriptors;
744
+ try {
745
+ descriptors = await source.list();
746
+ } catch (error) {
747
+ warnings.push(`Skill source "${source.id}" failed to list skills: ${error instanceof Error ? error.message : String(error)}`);
748
+ continue;
749
+ }
750
+ for (const descriptor of descriptors) {
751
+ if (resolved.has(descriptor.name)) {
752
+ warnings.push(`Skill "${descriptor.name}" from source "${source.id}" is shadowed by an earlier source`);
753
+ continue;
754
+ }
755
+ let content;
756
+ try {
757
+ content = await source.load(descriptor.name);
758
+ } catch (error) {
759
+ warnings.push(`Skill "${descriptor.name}" from source "${source.id}" failed to load: ${error instanceof Error ? error.message : String(error)}`);
760
+ continue;
761
+ }
762
+ if (!content) continue;
763
+ resolved.set(descriptor.name, {
764
+ descriptor,
765
+ content,
766
+ source
767
+ });
768
+ }
769
+ }
770
+ const visible = [...resolved.values()].filter((skill) => skill.descriptor.metadata?.["disable-model-invocation"] !== true);
771
+ if (visible.length === 0) return {
772
+ fingerprint,
773
+ tools: [],
774
+ catalog: null,
775
+ warnings
776
+ };
777
+ const names = visible.map((skill) => skill.descriptor.name);
778
+ const nameSchema = Type.Union(names.map((name) => Type.Literal(name)));
779
+ const byName = new Map(visible.map((skill) => [skill.descriptor.name, skill]));
780
+ const activateSkill = {
781
+ name: "activate_skill",
782
+ description: "Activate a skill by name. Use this when the user's task matches one of the available skills; the response contains the skill's full instructions.",
783
+ parameters: Type.Object({ name: nameSchema }),
784
+ replay: "safe",
785
+ async execute(input) {
786
+ const skill = byName.get(input.name);
787
+ return textResult(skill ? renderSkillContent(skill) : `Skill not found: ${input.name}`);
788
+ }
789
+ };
790
+ const readResource = {
791
+ name: "read_skill_resource",
792
+ description: "Read a file bundled with a skill, such as a reference document or template. Provide the skill name and the file's path from the skill's resource list.",
793
+ parameters: Type.Object({
794
+ name: Type.Optional(nameSchema),
795
+ path: Type.String({ minLength: 1 })
796
+ }),
797
+ replay: "safe",
798
+ async execute(input) {
799
+ const target = resolveResourceTarget(byName, input.name, input.path);
800
+ if (!target) return textResult(`Skill resource not found: ${input.name ? `${input.name}/` : ""}${input.path}`);
801
+ const resource = await target.skill.source.readResource?.(target.skill.descriptor.name, target.path).catch(() => null);
802
+ if (!resource) return textResult(`Skill resource not found: ${target.skill.descriptor.name}/${target.path}`);
803
+ return textResult([
804
+ `<skill_resource${attributes([
805
+ ["name", target.skill.descriptor.name],
806
+ ["path", resource.path],
807
+ ["kind", resource.kind],
808
+ ["encoding", resource.encoding ?? "text"],
809
+ ["mimeType", resource.mimeType]
810
+ ])}>`,
811
+ resource.content,
812
+ "</skill_resource>"
813
+ ].join("\n"));
814
+ }
815
+ };
816
+ const catalog = [
817
+ "Available skills. When a task matches a skill, use activate_skill with its name before proceeding.",
818
+ "",
819
+ ...visible.map((skill) => `- ${skill.descriptor.name}: ${skill.descriptor.description}`)
820
+ ].join("\n");
821
+ return {
822
+ fingerprint,
823
+ tools: [activateSkill, readResource],
824
+ catalog,
825
+ warnings
826
+ };
827
+ }
828
+ function resolveResourceTarget(byName, name, path) {
829
+ if (path.split("/").some((segment) => segment === "..")) return void 0;
830
+ if (name !== void 0) {
831
+ const skill = byName.get(name);
832
+ return skill && hasResource(skill, path) ? {
833
+ skill,
834
+ path
835
+ } : void 0;
836
+ }
837
+ const [head, ...rest] = path.split("/");
838
+ const skill = head === void 0 ? void 0 : byName.get(head);
839
+ const relative = rest.join("/");
840
+ return skill && relative && hasResource(skill, relative) ? {
841
+ skill,
842
+ path: relative
843
+ } : void 0;
844
+ }
845
+ function hasResource(skill, path) {
846
+ return (skill.content.resources ?? []).some((resource) => resource.path === path);
847
+ }
848
+ /**
849
+ * `agents/skills` sources as a pi extension, named `agents.skills`: the
850
+ * activation tools and a `skills` system prompt section. Install it on
851
+ * pi's registry in the harness factory.
852
+ *
853
+ * ```ts
854
+ * registry.install(await skills(sources));
855
+ * ```
856
+ */
857
+ async function skills(sources) {
858
+ const resolved = await resolveSkillSources(sources);
859
+ for (const warning of resolved.warnings) console.warn(`pi skills: ${warning}`);
860
+ const catalog = resolved.catalog;
861
+ return {
862
+ name: "agents.skills",
863
+ tools: resolved.tools,
864
+ sections: catalog === null ? [] : [{
865
+ key: "skills",
866
+ render: () => catalog
867
+ }]
868
+ };
869
+ }
870
+ //#endregion
871
+ export { PiHarness, PiSession, PiSessions, ROOT_SESSION, openPiSessionStore, skills };
872
+
873
+ //# sourceMappingURL=index.js.map