agents 0.23.0 → 0.25.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 (187) hide show
  1. package/dist/{agent-routing-DE5zmCQ8.d.ts → agent-routing-0DR0Tk2v.d.ts} +270 -190
  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 +13 -7
  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-DtXMTDGM.d.ts → agent-tools-YrATZJu2.d.ts} +29 -15
  10. package/dist/agent-tools.d.ts +6 -4
  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-BjSKYpzg.js → capability-B4WbF81e.js} +2 -1
  32. package/dist/capability-B4WbF81e.js.map +1 -0
  33. package/dist/{capability-runner-Be_-PLR1.d.ts → capability-runner-DBAnGr6L.d.ts} +45 -15
  34. package/dist/chat/index.d.ts +341 -232
  35. package/dist/chat/index.js +602 -72
  36. package/dist/chat/index.js.map +1 -1
  37. package/dist/chat/react.d.ts +42 -2
  38. package/dist/chat/react.js +191 -35
  39. package/dist/chat/react.js.map +1 -1
  40. package/dist/chat/transport.d.ts +1 -1
  41. package/dist/chat/transport.js +1 -1
  42. package/dist/chat-sdk/index.d.ts +17 -3
  43. package/dist/chat-sdk/index.js +28 -2
  44. package/dist/chat-sdk/index.js.map +1 -1
  45. package/dist/client-4OlMIJx-.js +591 -0
  46. package/dist/client-4OlMIJx-.js.map +1 -0
  47. package/dist/{client-jagG8a9_.js → client-fyQrLGkD.js} +2 -2
  48. package/dist/{client-jagG8a9_.js.map → client-fyQrLGkD.js.map} +1 -1
  49. package/dist/client-je3koD9Z.d.ts +446 -0
  50. package/dist/client.d.ts +29 -283
  51. package/dist/client.js +2 -274
  52. package/dist/{connector-CptFKzRh.js → connector-58XDGvc3.js} +251 -120
  53. package/dist/connector-58XDGvc3.js.map +1 -0
  54. package/dist/{connector-CkQD4MK3.d.ts → connector-Cl-VqPmA.d.ts} +244 -146
  55. package/dist/context/index.d.ts +29 -0
  56. package/dist/context/index.js +64 -17
  57. package/dist/context/index.js.map +1 -1
  58. package/dist/current-agent-DhoDkSnH.js.map +1 -1
  59. package/dist/current-agent-vAk_5JMt.d.ts +93 -0
  60. package/dist/{current-agent-Da_C9a3b.d.ts → durable-object-lifecycle-HhgOMWq9.d.ts} +26 -127
  61. package/dist/harnesses/pi/index.d.ts +348 -0
  62. package/dist/harnesses/pi/index.js +754 -0
  63. package/dist/harnesses/pi/index.js.map +1 -0
  64. package/dist/images-iiTDSyRu.js +1631 -0
  65. package/dist/images-iiTDSyRu.js.map +1 -0
  66. package/dist/index-BreL0YrQ.d.ts +78 -0
  67. package/dist/index-kbSUIer3.d.ts +343 -0
  68. package/dist/index.d.ts +43 -41
  69. package/dist/index.js +2 -2
  70. package/dist/internal_context.d.ts +1 -1
  71. package/dist/lifecycle/index.d.ts +19 -17
  72. package/dist/lifecycle/index.js +2 -2
  73. package/dist/{lifecycle-CMRGjZdw.js → lifecycle-CFu6OV03.js} +73 -29
  74. package/dist/lifecycle-CFu6OV03.js.map +1 -0
  75. package/dist/mcp/client/index.d.ts +20 -20
  76. package/dist/mcp/client/index.js +1 -1
  77. package/dist/mcp/client/x402.js +21 -19
  78. package/dist/mcp/client/x402.js.map +1 -1
  79. package/dist/mcp/index.d.ts +35 -35
  80. package/dist/mcp/index.js +2 -2
  81. package/dist/{message-builder-BymO4N_D.js → message-builder-DAl9AhHH.js} +89 -4
  82. package/dist/message-builder-DAl9AhHH.js.map +1 -0
  83. package/dist/models/ai-sdk/index.d.ts +353 -0
  84. package/dist/models/ai-sdk/index.js +2443 -0
  85. package/dist/models/ai-sdk/index.js.map +1 -0
  86. package/dist/models/pi-ai/index.d.ts +313 -0
  87. package/dist/models/pi-ai/index.js +1795 -0
  88. package/dist/models/pi-ai/index.js.map +1 -0
  89. package/dist/observability/ai/index.js +20 -4
  90. package/dist/observability/ai/index.js.map +1 -1
  91. package/dist/{protocol-B0nh6KNf.js → protocol-kHsr-f0X.js} +26 -12
  92. package/dist/{protocol-B0nh6KNf.js.map → protocol-kHsr-f0X.js.map} +1 -1
  93. package/dist/queue/index.d.ts +123 -0
  94. package/dist/queue/index.js +2 -0
  95. package/dist/queue-BB-zfIYF.js +406 -0
  96. package/dist/queue-BB-zfIYF.js.map +1 -0
  97. package/dist/react.d.ts +31 -13
  98. package/dist/react.js +190 -71
  99. package/dist/react.js.map +1 -1
  100. package/dist/{retries-D9Ds-1lz.d.ts → retries-CV6AkXmQ.d.ts} +23 -8
  101. package/dist/retries.d.ts +12 -8
  102. package/dist/retries.js +19 -1
  103. package/dist/retries.js.map +1 -1
  104. package/dist/routing/index.d.ts +2 -2
  105. package/dist/routing/index.js +1 -1
  106. package/dist/{scheduler-DD9NdYbF.js → scheduler-DZ3xQfeK.js} +4 -11
  107. package/dist/scheduler-DZ3xQfeK.js.map +1 -0
  108. package/dist/{scheduler-Dwh85ZGl.d.ts → scheduler-RdACNXDP.d.ts} +6 -6
  109. package/dist/schedules/index.d.ts +1 -1
  110. package/dist/schedules/index.js +1 -1
  111. package/dist/serializable.d.ts +1 -1
  112. package/dist/sessions/index.d.ts +70 -3
  113. package/dist/sessions/index.js +276 -67
  114. package/dist/sessions/index.js.map +1 -1
  115. package/dist/skills/index.d.ts +29 -314
  116. package/dist/socket-address.d.ts +16 -0
  117. package/dist/socket-address.js +19 -0
  118. package/dist/socket-address.js.map +1 -0
  119. package/dist/{src-DlSHshb2.js → src-BZeu-SCD.js} +541 -500
  120. package/dist/src-BZeu-SCD.js.map +1 -0
  121. package/dist/state/index.d.ts +6 -0
  122. package/dist/state/index.js +137 -0
  123. package/dist/state/index.js.map +1 -0
  124. package/dist/streams/index.d.ts +1 -1
  125. package/dist/streams/index.js +1 -1
  126. package/dist/{streams-D6tJ0NN9.d.ts → streams-Cmkj9QfW.d.ts} +4 -2
  127. package/dist/{streams-DZKgAj9b.js → streams-DW-UCOUc.js} +9 -2
  128. package/dist/streams-DW-UCOUc.js.map +1 -0
  129. package/dist/sub-routing.d.ts +12 -12
  130. package/dist/tasks/index.d.ts +1 -1
  131. package/dist/tasks/index.js +1 -1
  132. package/dist/{tasks-ylZgBjhj.js → tasks-68DESxf0.js} +3 -3
  133. package/dist/{tasks-ylZgBjhj.js.map → tasks-68DESxf0.js.map} +1 -1
  134. package/dist/{tasks-BRJ5zgya.d.ts → tasks-9YxYtByh.d.ts} +5 -5
  135. package/dist/transport-PfFnt-Ld.d.ts +525 -0
  136. package/dist/transport-protocol-BxyngQ_R.js +25 -0
  137. package/dist/transport-protocol-BxyngQ_R.js.map +1 -0
  138. package/dist/transport-protocol-ZTew5Wso.d.ts +27 -0
  139. package/dist/types-BA4anrjI.d.ts +70 -0
  140. package/dist/voice/index.d.ts +2 -2
  141. package/dist/websockets/index.d.ts +195 -69
  142. package/dist/websockets/index.js +3 -2
  143. package/dist/websockets-Ci-LZFHH.js +1030 -0
  144. package/dist/websockets-Ci-LZFHH.js.map +1 -0
  145. package/dist/wire-types-CnMt6_HR.js.map +1 -1
  146. package/dist/wire-types-CrjKvDyi.d.ts +300 -0
  147. package/dist/workflow-types.d.ts +25 -25
  148. package/dist/workflows.d.ts +21 -21
  149. package/dist/workflows.js +1 -1
  150. package/dist/{ws-chat-transport-UNRIS2xl.d.ts → ws-chat-transport-BbT1A5Lc.d.ts} +51 -2
  151. package/dist/{ws-chat-transport-rWwta645.js → ws-chat-transport-cqMKq3IY.js} +143 -16
  152. package/dist/ws-chat-transport-cqMKq3IY.js.map +1 -0
  153. package/docs/agent-class.md +2 -2
  154. package/docs/agent-tools.md +29 -4
  155. package/docs/browse-the-web.md +87 -5
  156. package/docs/chat-agents.md +124 -16
  157. package/docs/client-sdk.md +30 -0
  158. package/docs/context.md +27 -0
  159. package/docs/durable-execution.md +2 -0
  160. package/docs/harnesses/pi.md +91 -0
  161. package/docs/human-in-the-loop.md +4 -0
  162. package/docs/index.md +4 -1
  163. package/docs/lifecycle.md +114 -23
  164. package/docs/models-pi-ai.md +325 -0
  165. package/docs/models.md +504 -0
  166. package/docs/queue.md +135 -241
  167. package/docs/resumable-streaming.md +2 -0
  168. package/docs/retries.md +1 -1
  169. package/docs/routing.md +13 -13
  170. package/docs/sessions.md +36 -1
  171. package/docs/state.md +1 -0
  172. package/docs/sub-agents.md +12 -3
  173. package/package.json +51 -3
  174. package/dist/agent-tools-y7zLfw4Q.js.map +0 -1
  175. package/dist/browser/ai.js.map +0 -1
  176. package/dist/capability-BjSKYpzg.js.map +0 -1
  177. package/dist/client-tools-aIBO0Fk7.d.ts +0 -53
  178. package/dist/client.js.map +0 -1
  179. package/dist/connector-CptFKzRh.js.map +0 -1
  180. package/dist/lifecycle-CMRGjZdw.js.map +0 -1
  181. package/dist/message-builder-BymO4N_D.js.map +0 -1
  182. package/dist/scheduler-DD9NdYbF.js.map +0 -1
  183. package/dist/src-DlSHshb2.js.map +0 -1
  184. package/dist/streams-DZKgAj9b.js.map +0 -1
  185. package/dist/websockets-DUfRHPRq.js +0 -502
  186. package/dist/websockets-DUfRHPRq.js.map +0 -1
  187. package/dist/ws-chat-transport-rWwta645.js.map +0 -1
@@ -0,0 +1,754 @@
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 { ConversationConfig, 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/harnesses/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/harnesses/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 _sql = /* @__PURE__ */ new WeakMap();
85
+ var _query = /* @__PURE__ */ new WeakMap();
86
+ var DurableObjectSqliteStatement = class {
87
+ constructor(sql, query) {
88
+ _classPrivateFieldInitSpec(this, _sql, void 0);
89
+ _classPrivateFieldInitSpec(this, _query, void 0);
90
+ _classPrivateFieldSet2(_sql, this, sql);
91
+ _classPrivateFieldSet2(_query, this, query);
92
+ }
93
+ run(...params) {
94
+ _classPrivateFieldGet2(_sql, this).exec(_classPrivateFieldGet2(_query, this), ...params.map(binding));
95
+ }
96
+ get(...params) {
97
+ const first = _classPrivateFieldGet2(_sql, this).exec(_classPrivateFieldGet2(_query, this), ...params.map(binding)).next();
98
+ return first.done ? void 0 : row(first.value);
99
+ }
100
+ all(...params) {
101
+ return _classPrivateFieldGet2(_sql, this).exec(_classPrivateFieldGet2(_query, this), ...params.map(binding)).toArray().map((raw) => row(raw));
102
+ }
103
+ };
104
+ var _storage = /* @__PURE__ */ new WeakMap();
105
+ var _prefixer = /* @__PURE__ */ new WeakMap();
106
+ /**
107
+ * pi's `SqliteDatabase` facade over `DurableObjectStorage`.
108
+ *
109
+ * Durable Objects reject `BEGIN`, so transactions go through
110
+ * `transactionSync`, which rolls back when the callback throws and rethrows
111
+ * the same error, as pi's contract requires. Durable Objects have no
112
+ * prepared statements either; a statement is the query text, executed on
113
+ * each call.
114
+ */
115
+ var DurableObjectSqliteDatabase = class {
116
+ constructor(storage, options = {}) {
117
+ _classPrivateFieldInitSpec(this, _storage, void 0);
118
+ _classPrivateFieldInitSpec(this, _prefixer, void 0);
119
+ _classPrivateFieldSet2(_storage, this, storage);
120
+ _classPrivateFieldSet2(_prefixer, this, new Prefixer(options.prefix ?? DEFAULT_PREFIX));
121
+ }
122
+ exec(sql) {
123
+ _classPrivateFieldGet2(_storage, this).sql.exec(_classPrivateFieldGet2(_prefixer, this).rewrite(sql));
124
+ }
125
+ prepare(sql) {
126
+ return new DurableObjectSqliteStatement(_classPrivateFieldGet2(_storage, this).sql, _classPrivateFieldGet2(_prefixer, this).rewrite(sql));
127
+ }
128
+ transaction(callback) {
129
+ return _classPrivateFieldGet2(_storage, this).transactionSync(callback);
130
+ }
131
+ /** The object owns the database; there is nothing to close. */
132
+ close() {}
133
+ };
134
+ //#endregion
135
+ //#region src/harnesses/pi/harness.ts
136
+ const BG = BACKGROUND_CONTEXT;
137
+ /** The root session's id. */
138
+ const ROOT_SESSION = String(ROOT_CONVERSATION_ID);
139
+ /**
140
+ * Defaults for the three wake timings; a host overrides them with
141
+ * `timing`, which is what the tests do to avoid the real heartbeat.
142
+ *
143
+ * A pi long wait further away than this is handed to the alarm, so the
144
+ * alarm, not pi's in-memory timer, is what wakes the object.
145
+ */
146
+ const SLEEP_THRESHOLD_MS = 6e4;
147
+ /**
148
+ * Longest one wake waits on pi. It waits inside an alarm invocation, which
149
+ * has a 15 minute wall-time limit, so a longer run is waited on across
150
+ * several alarms.
151
+ */
152
+ const WAIT_BUDGET_MS = 10 * 6e4;
153
+ /**
154
+ * The wake job's heartbeat while it waits, and how often it re-checks work
155
+ * it cannot wait on, such as background tasks. If the object is evicted, the
156
+ * job is still due and its alarm restarts the object.
157
+ */
158
+ const HEARTBEAT_MS = 3e4;
159
+ const WAKE_FN = "wake";
160
+ function wakeJobId(session) {
161
+ return `pi-wake:${session}`;
162
+ }
163
+ function sessionOfJob(payload) {
164
+ return typeof payload === "object" && payload !== null && "session" in payload && typeof payload.session === "string" ? payload.session : void 0;
165
+ }
166
+ function conversationId(session) {
167
+ const id = Number(session);
168
+ if (!Number.isSafeInteger(id) || id < 1) throw new Error(`Invalid pi session ${JSON.stringify(session)}`);
169
+ return id;
170
+ }
171
+ /** The text of an assistant entry, for an operation's result. */
172
+ function assistantText(entry) {
173
+ const message = entry?.model?.[0];
174
+ if (message?.role !== "assistant") return "";
175
+ return message.content.map((part) => part.type === "text" ? part.text : "").join("");
176
+ }
177
+ function signalContext(signal) {
178
+ return signal ? withAbortSignal(signal, BG) : BG;
179
+ }
180
+ var _options = /* @__PURE__ */ new WeakMap();
181
+ var _waits = /* @__PURE__ */ new WeakMap();
182
+ var _admitting = /* @__PURE__ */ new WeakMap();
183
+ var _sleepThresholdMs = /* @__PURE__ */ new WeakMap();
184
+ var _waitBudgetMs = /* @__PURE__ */ new WeakMap();
185
+ var _heartbeatMs = /* @__PURE__ */ new WeakMap();
186
+ var _opening = /* @__PURE__ */ new WeakMap();
187
+ var _PiHarness_brand = /* @__PURE__ */ new WeakSet();
188
+ /**
189
+ * pi-durable hosted in a Durable Object, behind a small harness interface:
190
+ * `harness.prompt()`, `harness.sessions`, `harness.session(id)`. How a session reaches a
191
+ * client (sockets, SSE, RPC) is the host's glue, built on `session.events()`.
192
+ *
193
+ * pi owns everything about a run: the transcript, the inbox of steers and
194
+ * follow-ups, generation and tool tasks, retries, crash recovery, and the
195
+ * live view. It keeps all of it in its own tables in this object's SQLite
196
+ * database (see `session-store.ts`).
197
+ *
198
+ * What pi cannot do on a Durable Object is wake itself: its scheduler runs
199
+ * in memory, and an evicted object has no memory. The harness is that wake,
200
+ * with one Lifecycle job per session. Input goes to pi once, in `submit()`,
201
+ * after the session's job is scheduled. The job waits while pi has live
202
+ * tasks in the session, rescheduling itself as a heartbeat, and completes
203
+ * when there are none. An eviction mid-run leaves the job due, so its alarm
204
+ * restarts the object, pi reopens and resumes its own tasks, and the job
205
+ * waits again.
206
+ *
207
+ * @experimental The API may change between releases.
208
+ */
209
+ var PiHarness = class extends LifecycleCapability {
210
+ constructor(options) {
211
+ super("pi-harness");
212
+ _classPrivateMethodInitSpec(this, _PiHarness_brand);
213
+ _classPrivateFieldInitSpec(this, _options, void 0);
214
+ _classPrivateFieldInitSpec(this, _waits, /* @__PURE__ */ new Map());
215
+ _classPrivateFieldInitSpec(this, _admitting, /* @__PURE__ */ new Map());
216
+ _classPrivateFieldInitSpec(this, _sleepThresholdMs, void 0);
217
+ _classPrivateFieldInitSpec(this, _waitBudgetMs, void 0);
218
+ _classPrivateFieldInitSpec(this, _heartbeatMs, void 0);
219
+ _classPrivateFieldInitSpec(this, _opening, void 0);
220
+ _classPrivateFieldSet2(_options, this, options);
221
+ _classPrivateFieldSet2(_sleepThresholdMs, this, options.timing?.sleepThresholdMs ?? SLEEP_THRESHOLD_MS);
222
+ _classPrivateFieldSet2(_waitBudgetMs, this, options.timing?.waitBudgetMs ?? WAIT_BUDGET_MS);
223
+ _classPrivateFieldSet2(_heartbeatMs, this, options.timing?.heartbeatMs ?? HEARTBEAT_MS);
224
+ for (const [name, value] of [
225
+ ["sleepThresholdMs", _classPrivateFieldGet2(_sleepThresholdMs, this)],
226
+ ["waitBudgetMs", _classPrivateFieldGet2(_waitBudgetMs, this)],
227
+ ["heartbeatMs", _classPrivateFieldGet2(_heartbeatMs, this)]
228
+ ]) if (!Number.isFinite(value) || value <= 0) throw new Error(`PiHarness timing.${name} must be a positive number`);
229
+ this.sessions = new PiSessions(this);
230
+ }
231
+ async onStart(_context) {
232
+ const { pi } = await _assertClassBrand(_PiHarness_brand, this, _open).call(this);
233
+ const inspection = await pi.inspect(BG);
234
+ const sessions = new Set(inspection.tasks.map((task) => String(task.record.conversationId)));
235
+ for (const session of sessions) await _assertClassBrand(_PiHarness_brand, this, _wake).call(this, session);
236
+ }
237
+ async onJob(context) {
238
+ if (context.job.fn !== WAKE_FN) return;
239
+ const session = sessionOfJob(context.job.payload);
240
+ if (session !== void 0) return _assertClassBrand(_PiHarness_brand, this, _wakeStep).call(this, session);
241
+ }
242
+ /** Close pi's in-memory resources. Durable state is untouched. */
243
+ async dispose() {
244
+ const opening = _classPrivateFieldGet2(_opening, this);
245
+ _classPrivateFieldSet2(_opening, this, void 0);
246
+ await (await opening?.catch(() => void 0))?.pi.close(BG);
247
+ }
248
+ /** A handle on one session. No I/O until you call it. */
249
+ session(id = ROOT_SESSION) {
250
+ return new PiSession(this, id);
251
+ }
252
+ /** Submit a prompt and wait for its answer. */
253
+ prompt(input, options = {}) {
254
+ return this.session(options.session).prompt(input, options);
255
+ }
256
+ /** Durably submit a prompt. Resolves before the model runs. */
257
+ submit(input, options = {}) {
258
+ return this.session(options.session).submit(input, options);
259
+ }
260
+ /** Stop one operation, or everything running in a session. */
261
+ abort(options = {}) {
262
+ return this.session(options.session).abort(options.operationId);
263
+ }
264
+ wait(operationId, options = {}) {
265
+ return this.session(options.session).wait(operationId, options.signal);
266
+ }
267
+ /** The session's active transcript, as pi's entries. */
268
+ messages(options = {}) {
269
+ return this.session(options.session).messages();
270
+ }
271
+ /** Submissions pi has not settled yet, oldest first. */
272
+ async pending(options = {}) {
273
+ const { pi } = await _assertClassBrand(_PiHarness_brand, this, _open).call(this);
274
+ const id = options.session === void 0 ? void 0 : conversationId(options.session);
275
+ return (await pi.inspect(BG)).submissions.filter((record) => record.requestId !== void 0 && (id === void 0 || record.conversationId === id)).map((record) => ({
276
+ operationId: record.requestId,
277
+ session: String(record.conversationId),
278
+ status: record.status === "queued" ? "queued" : "running"
279
+ }));
280
+ }
281
+ /** The opened pi Harness, for anything the interface does not cover. */
282
+ async pi() {
283
+ return (await _assertClassBrand(_PiHarness_brand, this, _open).call(this)).pi;
284
+ }
285
+ /** @internal */
286
+ async conversation(session) {
287
+ const { pi } = await _assertClassBrand(_PiHarness_brand, this, _open).call(this);
288
+ const conversation = await pi.conversation(conversationId(session), BG);
289
+ if (!conversation) throw new Error(`Unknown pi session ${session}`);
290
+ return conversation;
291
+ }
292
+ /** @internal */
293
+ async storage() {
294
+ return (await _assertClassBrand(_PiHarness_brand, this, _open).call(this)).storage;
295
+ }
296
+ /** @internal Configure a new session like the root. */
297
+ initSession(tx, id) {
298
+ return _assertClassBrand(_PiHarness_brand, this, _init).call(this, tx, id);
299
+ }
300
+ /** @internal */
301
+ async enqueue(session, input, options) {
302
+ const operationId = options.operationId ?? crypto.randomUUID();
303
+ const { storage } = await _assertClassBrand(_PiHarness_brand, this, _open).call(this);
304
+ const conversation = await this.conversation(session);
305
+ _classPrivateFieldGet2(_admitting, this).set(session, (_classPrivateFieldGet2(_admitting, this).get(session) ?? 0) + 1);
306
+ let accepted;
307
+ try {
308
+ await _assertClassBrand(_PiHarness_brand, this, _wake).call(this, session);
309
+ accepted = await storage.submissionByRequest(conversation.id, operationId, BG) === void 0;
310
+ await conversation.submit({
311
+ type: "input",
312
+ content: input,
313
+ whenBusy: options.whenBusy ?? "followUp",
314
+ requestId: operationId
315
+ }, BG);
316
+ } finally {
317
+ const left = (_classPrivateFieldGet2(_admitting, this).get(session) ?? 1) - 1;
318
+ if (left === 0) _classPrivateFieldGet2(_admitting, this).delete(session);
319
+ else _classPrivateFieldGet2(_admitting, this).set(session, left);
320
+ }
321
+ await _assertClassBrand(_PiHarness_brand, this, _wake).call(this, session);
322
+ return {
323
+ operationId,
324
+ session,
325
+ accepted
326
+ };
327
+ }
328
+ /** @internal Withdraw a queued input, or abort the run it joined. */
329
+ async withdraw(session, operationId) {
330
+ const { pi, storage } = await _assertClassBrand(_PiHarness_brand, this, _open).call(this);
331
+ const id = conversationId(session);
332
+ const record = await storage.submissionByRequest(id, operationId, BG);
333
+ if (!record || record.status === "done" || record.status === "unanswered") return false;
334
+ const withdrawn = await pi.abortSubmission(record.id, BG, id);
335
+ if (withdrawn === "already_placed") await (await pi.conversation(id, BG))?.abort(BG);
336
+ return withdrawn !== "settled" && withdrawn !== "not_found";
337
+ }
338
+ /** @internal Wait for pi to settle an operation, by its request id. */
339
+ async settled(session, operationId, signal) {
340
+ const conversation = await this.conversation(session);
341
+ const context = signalContext(signal);
342
+ const submission = await _assertClassBrand(_PiHarness_brand, this, _findSubmission).call(this, conversation.id, operationId, context);
343
+ if (!submission) return {
344
+ operationId,
345
+ session,
346
+ status: "unanswered",
347
+ reason: "not_found"
348
+ };
349
+ return _assertClassBrand(_PiHarness_brand, this, _result).call(this, session, operationId, await submission.wait(context), context);
350
+ }
351
+ };
352
+ /** Schedule the session's wake job now, or pull it forward. */
353
+ function _wake(session, time = Date.now()) {
354
+ return this.lifecycle.jobs.push({
355
+ id: wakeJobId(session),
356
+ fn: WAKE_FN,
357
+ time,
358
+ payload: { session },
359
+ singleflight: true,
360
+ recoveryLoop: true
361
+ });
362
+ }
363
+ /**
364
+ * One run of a session's wake job. It never admits or replays anything:
365
+ * it waits while pi has live tasks in the session and completes when it
366
+ * has none. pi does all the work in between.
367
+ */
368
+ async function _wakeStep(session) {
369
+ const heartbeat = { rescheduleAt: Date.now() + _classPrivateFieldGet2(_heartbeatMs, this) };
370
+ if (_classPrivateFieldGet2(_waits, this).has(session)) return heartbeat;
371
+ const { pi } = await _assertClassBrand(_PiHarness_brand, this, _open).call(this);
372
+ const conversation = await pi.conversation(conversationId(session), BG);
373
+ if (!conversation) return void 0;
374
+ const tasks = (await pi.inspect(BG)).tasks.filter((task) => task.record.conversationId === conversation.id);
375
+ if (tasks.length === 0) return _classPrivateFieldGet2(_admitting, this).has(session) ? heartbeat : void 0;
376
+ const wakeAt = await _assertClassBrand(_PiHarness_brand, this, _longWait).call(this, pi, conversation.id, BG);
377
+ if (wakeAt !== void 0) return { rescheduleAt: wakeAt };
378
+ if (tasks.every((task) => task.record.background)) return heartbeat;
379
+ const wait = _assertClassBrand(_PiHarness_brand, this, _waitForIdle).call(this, conversation).finally(() => {
380
+ _classPrivateFieldGet2(_waits, this).delete(session);
381
+ _assertClassBrand(_PiHarness_brand, this, _wake).call(this, session);
382
+ });
383
+ _classPrivateFieldGet2(_waits, this).set(session, wait);
384
+ this.lifecycle.trackAlarmWork(wait);
385
+ return heartbeat;
386
+ }
387
+ async function _waitForIdle(conversation) {
388
+ const budget = new AbortController();
389
+ const timer = setTimeout(() => budget.abort(), _classPrivateFieldGet2(_waitBudgetMs, this));
390
+ try {
391
+ await conversation.waitForIdle(withAbortSignal(budget.signal, BG));
392
+ } catch (error) {
393
+ if (!budget.signal.aborted) this.lifecycle.events.emit("pi:wake_error", {
394
+ session: String(conversation.id),
395
+ error: error instanceof Error ? error.message : String(error)
396
+ });
397
+ } finally {
398
+ clearTimeout(timer);
399
+ }
400
+ }
401
+ function _open() {
402
+ _classPrivateFieldGet2(_opening, this) ?? _classPrivateFieldSet2(_opening, this, _assertClassBrand(_PiHarness_brand, this, _doOpen).call(this).catch((error) => {
403
+ _classPrivateFieldSet2(_opening, this, void 0);
404
+ throw error;
405
+ }));
406
+ return _classPrivateFieldGet2(_opening, this);
407
+ }
408
+ async function _doOpen() {
409
+ const storage = await openPiSessionStore(this.lifecycle.storage, _classPrivateFieldGet2(_options, this).store);
410
+ const pi = await _classPrivateFieldGet2(_options, this).harness({
411
+ storage,
412
+ context: BG
413
+ });
414
+ await pi.root(BG, { init: (tx, id) => _assertClassBrand(_PiHarness_brand, this, _init).call(this, tx, id) });
415
+ pi.resume();
416
+ return {
417
+ pi,
418
+ storage
419
+ };
420
+ }
421
+ async function _init(tx, id) {
422
+ const config = await tx.doc(ConversationConfig, id);
423
+ const defaults = _classPrivateFieldGet2(_options, this).defaults;
424
+ if (defaults?.model) config.model = { ...defaults.model };
425
+ if (defaults?.thinkingLevel) config.thinkingLevel = defaults.thinkingLevel;
426
+ if (defaults?.retry) config.retry = { ...defaults.retry };
427
+ }
428
+ async function _findSubmission(id, operationId, context) {
429
+ const { pi, storage } = await _assertClassBrand(_PiHarness_brand, this, _open).call(this);
430
+ const record = await storage.submissionByRequest(id, operationId, context);
431
+ return record ? pi.submission(record.id, context) : void 0;
432
+ }
433
+ /** When pi's current wait is far enough away to hand to the alarm. */
434
+ async function _longWait(pi, id, context) {
435
+ const live = await pi.snapshot(LiveDoc, id, context);
436
+ const at = live?.generation?.deferred?.pollAt ?? live?.generation?.retry?.at;
437
+ return at !== void 0 && at - Date.now() > _classPrivateFieldGet2(_sleepThresholdMs, this) ? at : void 0;
438
+ }
439
+ async function _result(session, operationId, settled, context) {
440
+ if (settled.status === "unanswered") return {
441
+ operationId,
442
+ session,
443
+ status: "unanswered",
444
+ reason: settled.reason
445
+ };
446
+ if (settled.type !== "input") return {
447
+ operationId,
448
+ session,
449
+ status: "done"
450
+ };
451
+ return {
452
+ operationId,
453
+ session,
454
+ status: "done",
455
+ text: assistantText((await (await this.conversation(session)).entries({
456
+ minEntryId: settled.answer,
457
+ maxEntryId: settled.answer
458
+ }, 1, void 0, context)).items[0])
459
+ };
460
+ }
461
+ var _harness = /* @__PURE__ */ new WeakMap();
462
+ /** One pi conversation, addressed through the harness. */
463
+ var PiSession = class {
464
+ constructor(harness, id) {
465
+ _classPrivateFieldInitSpec(this, _harness, void 0);
466
+ _classPrivateFieldSet2(_harness, this, harness);
467
+ this.id = id;
468
+ }
469
+ /** Durably submit a prompt. Resolves before the model runs. */
470
+ submit(input, options = {}) {
471
+ return _classPrivateFieldGet2(_harness, this).enqueue(this.id, input, options);
472
+ }
473
+ /** Submit and wait for the answer and the updated transcript. */
474
+ async prompt(input, options = {}) {
475
+ const receipt = await this.submit(input, options);
476
+ return {
477
+ ...await this.wait(receipt.operationId),
478
+ messages: await this.messages()
479
+ };
480
+ }
481
+ /** Join the running work after its current tool round. */
482
+ steer(input, options = {}) {
483
+ return this.submit(input, {
484
+ ...options,
485
+ whenBusy: "steer"
486
+ });
487
+ }
488
+ /** Wait for an operation to settle. Aborting `signal` stops only the wait. */
489
+ wait(operationId, signal) {
490
+ return _classPrivateFieldGet2(_harness, this).settled(this.id, operationId, signal);
491
+ }
492
+ /**
493
+ * Withdraw one queued operation (or abort the run it joined), or, with no
494
+ * id, abort the session: pi withdraws queued inputs and aborts the
495
+ * running work.
496
+ */
497
+ async abort(operationId) {
498
+ if (operationId !== void 0) return _classPrivateFieldGet2(_harness, this).withdraw(this.id, operationId);
499
+ await (await _classPrivateFieldGet2(_harness, this).conversation(this.id)).abort(BG);
500
+ return true;
501
+ }
502
+ /** Start a new context, optionally from a handoff note. */
503
+ async reset(handoff) {
504
+ await (await _classPrivateFieldGet2(_harness, this).conversation(this.id)).reset(handoff, BG);
505
+ }
506
+ async setModel(model) {
507
+ await (await _classPrivateFieldGet2(_harness, this).conversation(this.id)).setModel(model, BG);
508
+ }
509
+ /** The active transcript, as pi's entries since the newest reset. */
510
+ async messages() {
511
+ return [...(await (await _classPrivateFieldGet2(_harness, this).conversation(this.id)).context(BG)).entries];
512
+ }
513
+ /** pi's agent events for this session: a snapshot, then one batch per commit. */
514
+ async events(context = BG) {
515
+ return watchEvents(await _classPrivateFieldGet2(_harness, this).pi(), conversationId(this.id), context);
516
+ }
517
+ async busy() {
518
+ return (await (await _classPrivateFieldGet2(_harness, this).pi()).snapshot(LiveDoc, conversationId(this.id), BG))?.run !== void 0;
519
+ }
520
+ };
521
+ var _harness2 = /* @__PURE__ */ new WeakMap();
522
+ /** Every pi conversation in this object. */
523
+ var PiSessions = class {
524
+ constructor(harness) {
525
+ _classPrivateFieldInitSpec(this, _harness2, void 0);
526
+ _classPrivateFieldSet2(_harness2, this, harness);
527
+ }
528
+ get(id) {
529
+ return _classPrivateFieldGet2(_harness2, this).session(id);
530
+ }
531
+ /** A new top-level session, configured like the root. */
532
+ async create() {
533
+ const conversation = await (await _classPrivateFieldGet2(_harness2, this).pi()).createConversation({
534
+ ownership: { kind: "ownerless" },
535
+ init: (tx, id) => _classPrivateFieldGet2(_harness2, this).initSession(tx, id)
536
+ }, BG);
537
+ return _classPrivateFieldGet2(_harness2, this).session(String(conversation.id));
538
+ }
539
+ /** A new session that sees `from`'s history up to its newest entry. */
540
+ async fork(from) {
541
+ const conversation = await _classPrivateFieldGet2(_harness2, this).conversation(from);
542
+ const at = (await conversation.entries({}, 1, void 0, BG)).items[0];
543
+ if (!at) throw new Error(`Session ${from} has no entries to fork from`);
544
+ const fork = await conversation.fork(at.id, { ownership: { kind: "ownerless" } }, BG);
545
+ return _classPrivateFieldGet2(_harness2, this).session(String(fork.id));
546
+ }
547
+ /**
548
+ * Every conversation, including ones subagent tools own. Read from pi's
549
+ * storage directly: the Harness has no conversation listing.
550
+ */
551
+ async list() {
552
+ const storage = await _classPrivateFieldGet2(_harness2, this).storage();
553
+ const pi = await _classPrivateFieldGet2(_harness2, this).pi();
554
+ const sessions = [];
555
+ let cursor;
556
+ for (;;) {
557
+ const page = await storage.scanConversations({}, 100, cursor, BG);
558
+ for (const record of page.items) {
559
+ const live = await pi.snapshot(LiveDoc, record.id, BG);
560
+ sessions.push({
561
+ id: String(record.id),
562
+ ...record.parent ? { parent: String(record.parent.conversationId) } : {},
563
+ busy: live?.run !== void 0
564
+ });
565
+ }
566
+ if (page.next === void 0) return sessions;
567
+ cursor = page.next;
568
+ }
569
+ }
570
+ };
571
+ //#endregion
572
+ //#region src/harnesses/pi/skills.ts
573
+ function skillsFingerprint(sources) {
574
+ return sources.map((source) => `${source.id}:${source.fingerprint}`).join("|");
575
+ }
576
+ function textResult(text) {
577
+ return { content: [{
578
+ type: "text",
579
+ text
580
+ }] };
581
+ }
582
+ /** pi validates arguments against the schema before `execute` runs. */
583
+ function argsOf(args) {
584
+ return args;
585
+ }
586
+ function attributes(pairs) {
587
+ return pairs.filter((pair) => pair[1] !== void 0).map(([key, value]) => ` ${key}="${value.replace(/"/g, "&quot;")}"`).join("");
588
+ }
589
+ function describeResource(resource) {
590
+ const details = [
591
+ resource.kind,
592
+ resource.encoding ?? "text",
593
+ resource.mimeType,
594
+ resource.size === void 0 ? void 0 : `${resource.size} bytes`
595
+ ].filter((value) => value !== void 0);
596
+ return `- ${resource.path} (${details.join(", ")})`;
597
+ }
598
+ function renderSkillContent(skill) {
599
+ const resources = skill.content.resources ?? [];
600
+ const lines = [`<skill_content${attributes([["name", skill.descriptor.name], ["version", skill.descriptor.version]])}>`, skill.content.body.trim()];
601
+ if (resources.length > 0) {
602
+ lines.push("<skill_resources>");
603
+ for (const resource of resources) lines.push(` <file${attributes([
604
+ ["kind", resource.kind],
605
+ ["encoding", resource.encoding ?? "text"],
606
+ ["size", resource.size === void 0 ? void 0 : String(resource.size)]
607
+ ])}>${resource.path}</file>`);
608
+ lines.push("</skill_resources>");
609
+ }
610
+ lines.push("</skill_content>", "");
611
+ lines.push(resources.length === 0 ? "No bundled resources." : ["Bundled resources:", ...resources.map(describeResource)].join("\n"));
612
+ return lines.join("\n");
613
+ }
614
+ /**
615
+ * Resolve `agents/skills` sources into a system-prompt catalog and two
616
+ * tools.
617
+ *
618
+ * The first source to list a name wins, as in `SkillRegistry`. pi-durable
619
+ * has no skill concept of its own, so the model activates skills through
620
+ * `activate_skill` and reads bundled files through `read_skill_resource` —
621
+ * the same tools `@cloudflare/think` offers, so skills authored for one
622
+ * framework work in the other.
623
+ */
624
+ async function resolveSkillSources(sources) {
625
+ const fingerprint = skillsFingerprint(sources);
626
+ const warnings = [];
627
+ const resolved = /* @__PURE__ */ new Map();
628
+ for (const source of sources) {
629
+ let descriptors;
630
+ try {
631
+ descriptors = await source.list();
632
+ } catch (error) {
633
+ warnings.push(`Skill source "${source.id}" failed to list skills: ${error instanceof Error ? error.message : String(error)}`);
634
+ continue;
635
+ }
636
+ for (const descriptor of descriptors) {
637
+ if (resolved.has(descriptor.name)) {
638
+ warnings.push(`Skill "${descriptor.name}" from source "${source.id}" is shadowed by an earlier source`);
639
+ continue;
640
+ }
641
+ let content;
642
+ try {
643
+ content = await source.load(descriptor.name);
644
+ } catch (error) {
645
+ warnings.push(`Skill "${descriptor.name}" from source "${source.id}" failed to load: ${error instanceof Error ? error.message : String(error)}`);
646
+ continue;
647
+ }
648
+ if (!content) continue;
649
+ resolved.set(descriptor.name, {
650
+ descriptor,
651
+ content,
652
+ source
653
+ });
654
+ }
655
+ }
656
+ const visible = [...resolved.values()].filter((skill) => skill.descriptor.metadata?.["disable-model-invocation"] !== true);
657
+ if (visible.length === 0) return {
658
+ fingerprint,
659
+ tools: [],
660
+ catalog: null,
661
+ warnings
662
+ };
663
+ const names = visible.map((skill) => skill.descriptor.name);
664
+ const nameSchema = Type.Union(names.map((name) => Type.Literal(name)));
665
+ const byName = new Map(visible.map((skill) => [skill.descriptor.name, skill]));
666
+ const activateSkill = {
667
+ name: "activate_skill",
668
+ 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.",
669
+ parameters: Type.Object({ name: nameSchema }),
670
+ replay: "safe",
671
+ async execute(args) {
672
+ const input = argsOf(args);
673
+ const skill = byName.get(input.name);
674
+ return textResult(skill ? renderSkillContent(skill) : `Skill not found: ${input.name}`);
675
+ }
676
+ };
677
+ const readResource = {
678
+ name: "read_skill_resource",
679
+ 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.",
680
+ parameters: Type.Object({
681
+ name: Type.Optional(nameSchema),
682
+ path: Type.String({ minLength: 1 })
683
+ }),
684
+ replay: "safe",
685
+ async execute(args) {
686
+ const input = argsOf(args);
687
+ const target = resolveResourceTarget(byName, input.name, input.path);
688
+ if (!target) return textResult(`Skill resource not found: ${input.name ? `${input.name}/` : ""}${input.path}`);
689
+ const resource = await target.skill.source.readResource?.(target.skill.descriptor.name, target.path).catch(() => null);
690
+ if (!resource) return textResult(`Skill resource not found: ${target.skill.descriptor.name}/${target.path}`);
691
+ return textResult([
692
+ `<skill_resource${attributes([
693
+ ["name", target.skill.descriptor.name],
694
+ ["path", resource.path],
695
+ ["kind", resource.kind],
696
+ ["encoding", resource.encoding ?? "text"],
697
+ ["mimeType", resource.mimeType]
698
+ ])}>`,
699
+ resource.content,
700
+ "</skill_resource>"
701
+ ].join("\n"));
702
+ }
703
+ };
704
+ const catalog = [
705
+ "Available skills. When a task matches a skill, use activate_skill with its name before proceeding.",
706
+ "",
707
+ ...visible.map((skill) => `- ${skill.descriptor.name}: ${skill.descriptor.description}`)
708
+ ].join("\n");
709
+ return {
710
+ fingerprint,
711
+ tools: [activateSkill, readResource],
712
+ catalog,
713
+ warnings
714
+ };
715
+ }
716
+ function resolveResourceTarget(byName, name, path) {
717
+ if (path.split("/").some((segment) => segment === "..")) return void 0;
718
+ if (name !== void 0) {
719
+ const skill = byName.get(name);
720
+ return skill && hasResource(skill, path) ? {
721
+ skill,
722
+ path
723
+ } : void 0;
724
+ }
725
+ const [head, ...rest] = path.split("/");
726
+ const skill = head === void 0 ? void 0 : byName.get(head);
727
+ const relative = rest.join("/");
728
+ return skill && relative && hasResource(skill, relative) ? {
729
+ skill,
730
+ path: relative
731
+ } : void 0;
732
+ }
733
+ function hasResource(skill, path) {
734
+ return (skill.content.resources ?? []).some((resource) => resource.path === path);
735
+ }
736
+ /**
737
+ * Register `agents/skills` sources on a pi registry: the activation tools
738
+ * and a "skills" system prompt section. Resolution reads the sources, so a
739
+ * host awaits this in its `harness` factory, before `Harness.open`.
740
+ */
741
+ async function addSkills(registry, sources) {
742
+ const resolved = await resolveSkillSources(sources);
743
+ for (const warning of resolved.warnings) console.warn(`pi skills: ${warning}`);
744
+ const catalog = resolved.catalog;
745
+ registry.batch(() => {
746
+ for (const tool of resolved.tools) registry.tools.add(tool);
747
+ if (catalog) registry.systemPrompt.section("skills", () => catalog);
748
+ });
749
+ return resolved;
750
+ }
751
+ //#endregion
752
+ export { PiHarness, PiSession, PiSessions, ROOT_SESSION, addSkills, openPiSessionStore };
753
+
754
+ //# sourceMappingURL=index.js.map