agents 0.28.1 → 0.30.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 (115) hide show
  1. package/dist/{agent-routing-BMvWTFEp.d.ts → agent-routing-CDbpVjYq.d.ts} +7 -8
  2. package/dist/agent-routing.d.ts +1 -1
  3. package/dist/agent-tool-types.d.ts +1 -1
  4. package/dist/{agent-tools-ohoewW-B.d.ts → agent-tools-C8DH8Yii.d.ts} +2 -2
  5. package/dist/agent-tools.d.ts +1 -1
  6. package/dist/{ai-ahruGw7e.d.ts → ai-iXw0uOGj.d.ts} +3 -3
  7. package/dist/{ai-sdk-C2iTAwUd.js → ai-sdk-DRPh2rRa.js} +185 -125
  8. package/dist/ai-sdk-DRPh2rRa.js.map +1 -0
  9. package/dist/browser/ai.d.ts +1 -1
  10. package/dist/browser/ai.js +2 -2
  11. package/dist/browser/index.d.ts +8 -8
  12. package/dist/browser/index.js +3 -3
  13. package/dist/browser/tools/ai-sdk.d.ts +2 -2
  14. package/dist/browser/tools/ai-sdk.js +1 -1
  15. package/dist/browser/tools/pi.d.ts +7 -3
  16. package/dist/browser/tools/pi.js +9 -5
  17. package/dist/browser/tools/pi.js.map +1 -1
  18. package/dist/browser/tools/tanstack-ai.d.ts +2 -2
  19. package/dist/browser/tools/tanstack-ai.js +1 -1
  20. package/dist/{browser-CEdNlsaD.js → browser-DccPw_wW.js} +130 -79
  21. package/dist/browser-DccPw_wW.js.map +1 -0
  22. package/dist/{browser-D30VlpcA.d.ts → browser-DlXhSAdf.d.ts} +41 -37
  23. package/dist/{browser-tool-4iomavs_.js → browser-tool-B1mhifin.js} +3 -3
  24. package/dist/{browser-tool-4iomavs_.js.map → browser-tool-B1mhifin.js.map} +1 -1
  25. package/dist/{browser-tool-vtrPOV8e.d.ts → browser-tool-Cxjk5sgG.d.ts} +2 -2
  26. package/dist/chat/index.d.ts +143 -71
  27. package/dist/chat/index.js +269 -110
  28. package/dist/chat/index.js.map +1 -1
  29. package/dist/chat-sdk/index.d.ts +1 -1
  30. package/dist/chat-sdk/index.js +1 -1
  31. package/dist/cli.js +53 -52
  32. package/dist/{client-Bp-JwL3p.d.ts → client-C27CtRZr.d.ts} +2 -2
  33. package/dist/client.d.ts +1 -1
  34. package/dist/{connector-DEXzVoaY.d.ts → connector-20DJ6p4k.d.ts} +2 -2
  35. package/dist/{connector-CtZylXdk.js → connector-DAXBOIIi.js} +2 -2
  36. package/dist/{connector-CtZylXdk.js.map → connector-DAXBOIIi.js.map} +1 -1
  37. package/dist/experimental/channels/web/index.js +1 -1
  38. package/dist/harness/container/index.js +1 -1
  39. package/dist/harness/container/index.js.map +1 -1
  40. package/dist/harness/opencode/index.js +1 -1
  41. package/dist/harness/pi/index.d.ts +70 -40
  42. package/dist/harness/pi/index.js +266 -330
  43. package/dist/harness/pi/index.js.map +1 -1
  44. package/dist/{images-C4hiIexM.js → images-xaDv2Fqe.js} +636 -168
  45. package/dist/images-xaDv2Fqe.js.map +1 -0
  46. package/dist/index-DCxk3OSJ.d.ts +378 -0
  47. package/dist/{index-kbSUIer3.d.ts → index-iv9FTXX9.d.ts} +80 -112
  48. package/dist/index.d.ts +1 -1
  49. package/dist/index.js +1 -1
  50. package/dist/{input-schema-xBofjBKC.js → input-schema-B0Etuo-o.js} +2 -2
  51. package/dist/{input-schema-xBofjBKC.js.map → input-schema-B0Etuo-o.js.map} +1 -1
  52. package/dist/lifecycle/index.js +1 -1
  53. package/dist/{lifecycle-DGorpAmA.js → lifecycle-v1rKHV2C.js} +67 -19
  54. package/dist/lifecycle-v1rKHV2C.js.map +1 -0
  55. package/dist/mcp/client/index.d.ts +1 -1
  56. package/dist/mcp/index.d.ts +1 -1
  57. package/dist/mcp/index.js +1 -1
  58. package/dist/models/ai-sdk/index.d.ts +29 -352
  59. package/dist/models/ai-sdk/index.js +1 -1
  60. package/dist/models/opencode/index.d.ts +25 -13
  61. package/dist/models/opencode/index.js +16 -11
  62. package/dist/models/opencode/index.js.map +1 -1
  63. package/dist/models/pi-ai/index.d.ts +13 -14
  64. package/dist/models/pi-ai/index.js +87 -34
  65. package/dist/models/pi-ai/index.js.map +1 -1
  66. package/dist/react.d.ts +2 -2
  67. package/dist/routing/index.d.ts +1 -1
  68. package/dist/serializable.d.ts +1 -1
  69. package/dist/{session-store-DoPY12xM.d.ts → session-store-_IK6dV-w.d.ts} +14 -2
  70. package/dist/{session-store-Do4xkDcf.js → session-store-yvwq1f9x.js} +55 -2
  71. package/dist/session-store-yvwq1f9x.js.map +1 -0
  72. package/dist/{settings-BjAiPd6Z.js → settings-Dk2il2Hf.js} +4 -1
  73. package/dist/settings-Dk2il2Hf.js.map +1 -0
  74. package/dist/skills/index.d.ts +22 -28
  75. package/dist/skills/index.js +183 -143
  76. package/dist/skills/index.js.map +1 -1
  77. package/dist/{src-CQxJFv4l.js → src-DfLzdwlK.js} +3 -3
  78. package/dist/src-DfLzdwlK.js.map +1 -0
  79. package/dist/sub-routing.d.ts +1 -1
  80. package/dist/{tool-VF0qiO7q.js → tool-DkT_DJBm.js} +2 -2
  81. package/dist/tool-DkT_DJBm.js.map +1 -0
  82. package/dist/transport-CPh7YGiw.d.ts +623 -0
  83. package/dist/voice/index.d.ts +1 -1
  84. package/dist/web/fetch/tools/pi.d.ts +10 -6
  85. package/dist/web/fetch/tools/pi.js +12 -8
  86. package/dist/web/fetch/tools/pi.js.map +1 -1
  87. package/dist/web/search/index.js +1 -1
  88. package/dist/web/search/tools/ai-sdk.js +2 -2
  89. package/dist/web/search/tools/pi.d.ts +10 -6
  90. package/dist/web/search/tools/pi.js +13 -9
  91. package/dist/web/search/tools/pi.js.map +1 -1
  92. package/dist/web/search/tools/tanstack-ai.js +2 -2
  93. package/dist/websockets/index.js +1 -1
  94. package/dist/{websockets-C3zy8YR0.js → websockets-CyoeosdO.js} +2 -2
  95. package/dist/{websockets-C3zy8YR0.js.map → websockets-CyoeosdO.js.map} +1 -1
  96. package/dist/workflow-types.d.ts +1 -1
  97. package/dist/workflows.d.ts +1 -1
  98. package/dist/workflows.js +1 -1
  99. package/docs/browse-the-web.md +1 -1
  100. package/docs/chat-agents.md +19 -18
  101. package/docs/harnesses/pi.md +101 -38
  102. package/docs/lifecycle.md +7 -1
  103. package/docs/models-pi-ai.md +66 -20
  104. package/docs/models.md +197 -172
  105. package/package.json +12 -7
  106. package/dist/ai-sdk-C2iTAwUd.js.map +0 -1
  107. package/dist/browser-CEdNlsaD.js.map +0 -1
  108. package/dist/images-C4hiIexM.js.map +0 -1
  109. package/dist/lifecycle-DGorpAmA.js.map +0 -1
  110. package/dist/session-store-Do4xkDcf.js.map +0 -1
  111. package/dist/settings-BjAiPd6Z.js.map +0 -1
  112. package/dist/settings-DnD__xuE.d.ts +0 -349
  113. package/dist/src-CQxJFv4l.js.map +0 -1
  114. package/dist/tool-VF0qiO7q.js.map +0 -1
  115. package/dist/transport-DCyarnwm.d.ts +0 -200
@@ -1,260 +1,30 @@
1
1
  import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldSet2, r as _assertClassBrand, t as _classPrivateFieldGet2 } from "../../classPrivateFieldGet2-DZBYAB34.js";
2
2
  import { t as _classPrivateMethodInitSpec } from "../../classPrivateMethodInitSpec-qMjJ6sHQ.js";
3
3
  import { t as LifecycleCapability } from "../../capability-DBAKc6UZ.js";
4
- import "../../lifecycle-DGorpAmA.js";
4
+ import "../../lifecycle-v1rKHV2C.js";
5
+ import { isDurableObjectMemoryLimitReset } from "../../retries.js";
5
6
  import { Type } from "@earendil-works/pi-ai";
6
7
  import { LiveDoc, ROOT_CONVERSATION_ID, watchEvents } from "@earendil-works/pi-durable";
7
- import { SQLITE_MIGRATIONS, SqliteStorage } from "@earendil-works/pi-durable/storage/sqlite";
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
8
+ import { BACKGROUND_CONTEXT, withAbortSignal } from "@earendil-works/chord/context";
9
+ import { openDurableObjectSqliteStorage } from "@earendil-works/pi-durable/storage/sqlite/cloudflare";
28
10
  //#region src/harness/pi/session-store.ts
29
11
  const DEFAULT_PREFIX = "pi_";
30
12
  /** Open pi-durable's storage over this object's SQLite database. */
31
13
  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");
14
+ const prefix = options.prefix ?? DEFAULT_PREFIX;
15
+ if (prefix.toLowerCase().startsWith("_cf_")) return Promise.reject(/* @__PURE__ */ new Error("The pi session store prefix must not start with _cf_"));
16
+ return openDurableObjectSqliteStorage(storage, { prefix });
188
17
  }
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
18
  //#endregion
244
19
  //#region src/harness/pi/harness.ts
245
20
  const BG = BACKGROUND_CONTEXT;
246
21
  /** The root session's id. */
247
22
  const ROOT_SESSION = String(ROOT_CONVERSATION_ID);
248
23
  /**
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
24
+ * The wake timings. They suit a real deployment; only tests change them,
25
+ * with `setWakeTimingForTests`, so a suite does not sit on the real
251
26
  * heartbeat.
252
27
  *
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
28
  * Longest one wake waits on pi. It waits inside an alarm invocation, which
259
29
  * has a 15 minute wall-time limit, so a longer run is waited on across
260
30
  * several alarms.
@@ -266,13 +36,28 @@ const WAIT_BUDGET_MS = 10 * 6e4;
266
36
  * job is still due and its alarm restarts the object.
267
37
  */
268
38
  const HEARTBEAT_MS = 3e4;
39
+ /**
40
+ * How often a wait re-reads `pi.wake()` when the factory did not pass
41
+ * `onWake`, so nothing tells the harness when it changes.
42
+ */
43
+ const POLL_MS = 1e3;
44
+ /**
45
+ * The longest a wake job whose dispatch keeps failing waits before it tries
46
+ * again. Its delay starts at the heartbeat and doubles up to this.
47
+ */
48
+ const MAX_WAKE_BACKOFF_MS = 10 * 6e4;
49
+ /**
50
+ * Wake dispatches that failed in a row. Stored, so the backoff holds across
51
+ * restarts, and outside the job: a push that saved it in the job's payload
52
+ * could overwrite a submit's due-now push.
53
+ */
54
+ const WAKE_ERRORS_KEY = "pi-harness:wake-errors";
269
55
  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
- }
56
+ /**
57
+ * The one wake job. Before Pi 1.4 there was one per session, with ids
58
+ * `pi-wake:<session>`; a left-over one hands over to this one.
59
+ */
60
+ const WAKE_JOB_ID = "pi-wake";
276
61
  /** pi-durable's stored reference to a pi-ai model. */
277
62
  function modelRef(model) {
278
63
  return {
@@ -295,13 +80,18 @@ function signalContext(signal) {
295
80
  return signal ? withAbortSignal(signal, BG) : BG;
296
81
  }
297
82
  var _options = /* @__PURE__ */ new WeakMap();
298
- var _waits = /* @__PURE__ */ new WeakMap();
83
+ var _waiting = /* @__PURE__ */ new WeakMap();
84
+ var _changed = /* @__PURE__ */ new WeakMap();
299
85
  var _admitting = /* @__PURE__ */ new WeakMap();
300
- var _sleepThresholdMs = /* @__PURE__ */ new WeakMap();
86
+ var _onWakeWired = /* @__PURE__ */ new WeakMap();
301
87
  var _waitBudgetMs = /* @__PURE__ */ new WeakMap();
302
88
  var _heartbeatMs = /* @__PURE__ */ new WeakMap();
89
+ var _pollMs = /* @__PURE__ */ new WeakMap();
303
90
  var _opening = /* @__PURE__ */ new WeakMap();
91
+ var _lastClose = /* @__PURE__ */ new WeakMap();
92
+ var _closing = /* @__PURE__ */ new WeakMap();
304
93
  var _PiHarness_brand = /* @__PURE__ */ new WeakSet();
94
+ var _onWake = /* @__PURE__ */ new WeakMap();
305
95
  /**
306
96
  * pi-durable hosted in a Durable Object, behind a small harness interface:
307
97
  * `harness.prompt()`, `harness.sessions`, `harness.session(id)`. How a session reaches a
@@ -314,12 +104,14 @@ var _PiHarness_brand = /* @__PURE__ */ new WeakSet();
314
104
  *
315
105
  * What pi cannot do on a Durable Object is wake itself: its scheduler runs
316
106
  * 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.
107
+ * with one Lifecycle job driven by `pi.wake()`. Input goes to pi once, in
108
+ * `submit()`, after the job is scheduled. While pi has work running, the
109
+ * job waits on it, rescheduling itself as a heartbeat. While pi's tasks
110
+ * sleep, the job is due when the first one wakes; pi tells the harness
111
+ * through the `onWake` the factory passes to `Harness.open`. When pi has
112
+ * nothing to do, the job completes. An eviction mid-run leaves the job due,
113
+ * so its alarm restarts the object, pi reopens and resumes its own tasks,
114
+ * and the job waits again.
323
115
  *
324
116
  * @beta The API may change between releases.
325
117
  */
@@ -328,25 +120,54 @@ var PiHarness = class extends LifecycleCapability {
328
120
  super("pi-harness");
329
121
  _classPrivateMethodInitSpec(this, _PiHarness_brand);
330
122
  _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);
123
+ _classPrivateFieldInitSpec(this, _waiting, void 0);
124
+ _classPrivateFieldInitSpec(this, _changed, void 0);
125
+ _classPrivateFieldInitSpec(this, _admitting, 0);
126
+ _classPrivateFieldInitSpec(this, _onWakeWired, false);
334
127
  _classPrivateFieldInitSpec(this, _waitBudgetMs, WAIT_BUDGET_MS);
335
128
  _classPrivateFieldInitSpec(this, _heartbeatMs, HEARTBEAT_MS);
129
+ _classPrivateFieldInitSpec(this, _pollMs, POLL_MS);
336
130
  _classPrivateFieldInitSpec(this, _opening, void 0);
131
+ _classPrivateFieldInitSpec(this, _lastClose, Promise.resolve());
132
+ _classPrivateFieldInitSpec(this, _closing, /* @__PURE__ */ new WeakSet());
133
+ _classPrivateFieldInitSpec(this, _onWake, (wake) => {
134
+ _classPrivateFieldSet2(_onWakeWired, this, true);
135
+ _classPrivateFieldGet2(_changed, this)?.call(this);
136
+ if (_classPrivateFieldGet2(_waiting, this)) return;
137
+ _assertClassBrand(_PiHarness_brand, this, _wakeFor).call(this, wake).catch((error) => _assertClassBrand(_PiHarness_brand, this, _wakeError).call(this, error));
138
+ });
337
139
  _classPrivateFieldSet2(_options, this, options);
338
140
  this.sessions = new PiSessions(this);
339
141
  }
340
142
  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);
143
+ await _assertClassBrand(_PiHarness_brand, this, _open).call(this);
345
144
  }
346
145
  async onJob(context) {
347
146
  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);
147
+ let outcome;
148
+ if (context.job.id !== WAKE_JOB_ID) {
149
+ await _assertClassBrand(_PiHarness_brand, this, _wake).call(this);
150
+ outcome = void 0;
151
+ } else outcome = await _assertClassBrand(_PiHarness_brand, this, _wakeStep).call(this);
152
+ const kv = this.lifecycle.storage.kv;
153
+ if (kv.get(WAKE_ERRORS_KEY) !== void 0) kv.delete(WAKE_ERRORS_KEY);
154
+ return outcome;
155
+ }
156
+ /**
157
+ * A wake dispatch that failed through Lifecycle's retries, as when pi
158
+ * cannot be opened. Lifecycle would complete the job, and pi's work would
159
+ * never be woken again, so try again later: at the heartbeat, doubling
160
+ * while the failures continue, up to 10 minutes.
161
+ */
162
+ onJobError(context, error) {
163
+ if (context.job.fn !== WAKE_FN) return;
164
+ console.error(`pi wake job "${context.job.id}" failed after ${context.attempt} attempts; retrying with backoff`, error);
165
+ _assertClassBrand(_PiHarness_brand, this, _wakeError).call(this, error);
166
+ const kv = this.lifecycle.storage.kv;
167
+ const errors = (kv.get(WAKE_ERRORS_KEY) ?? 0) + 1;
168
+ kv.put(WAKE_ERRORS_KEY, errors);
169
+ const delay = Math.min(_classPrivateFieldGet2(_heartbeatMs, this) * 2 ** (errors - 1), MAX_WAKE_BACKOFF_MS);
170
+ return { rescheduleAt: Date.now() + delay };
350
171
  }
351
172
  /** Close pi's in-memory resources. Durable state is untouched. */
352
173
  async dispose() {
@@ -370,6 +191,13 @@ var PiHarness = class extends LifecycleCapability {
370
191
  abort(options = {}) {
371
192
  return this.session(options.session).abort(options.operationId);
372
193
  }
194
+ /**
195
+ * Run again the tasks pi's crash-loop guard parked in a session's work.
196
+ * False when nothing was parked.
197
+ */
198
+ retry(options = {}) {
199
+ return this.session(options.session).retry();
200
+ }
373
201
  wait(operationId, options = {}) {
374
202
  return this.session(options.session).wait(operationId, options.signal);
375
203
  }
@@ -381,10 +209,12 @@ var PiHarness = class extends LifecycleCapability {
381
209
  async pending(options = {}) {
382
210
  const { pi } = await _assertClassBrand(_PiHarness_brand, this, _open).call(this);
383
211
  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) => ({
212
+ const inspection = await pi.inspect(BG);
213
+ const parked = await _assertClassBrand(_PiHarness_brand, this, _parked).call(this, inspection);
214
+ return inspection.submissions.filter((record) => record.requestId !== void 0 && (id === void 0 || record.conversationId === id)).map((record) => ({
385
215
  operationId: record.requestId,
386
216
  session: String(record.conversationId),
387
- status: record.status === "queued" ? "queued" : "running"
217
+ status: record.status === "queued" ? "queued" : parked.some((task) => task.sessions.has(record.conversationId)) ? "parked" : "running"
388
218
  }));
389
219
  }
390
220
  /** The opened pi Harness, for anything the interface does not cover. */
@@ -407,29 +237,33 @@ var PiHarness = class extends LifecycleCapability {
407
237
  const operationId = options.operationId ?? crypto.randomUUID();
408
238
  const { storage } = await _assertClassBrand(_PiHarness_brand, this, _open).call(this);
409
239
  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
240
  return {
428
241
  operationId,
429
242
  session,
430
- accepted
243
+ accepted: await _assertClassBrand(_PiHarness_brand, this, _admit).call(this, async () => {
244
+ const accepted = await storage.submissionByRequest(conversation.id, operationId, BG) === void 0;
245
+ await conversation.submit({
246
+ type: "input",
247
+ content: input,
248
+ whenBusy: options.whenBusy ?? "followUp",
249
+ requestId: operationId,
250
+ ...options.entry ? { entry: options.entry } : {}
251
+ }, BG);
252
+ return accepted;
253
+ })
431
254
  };
432
255
  }
256
+ /** @internal Run again the tasks pi's restart limit parked in a session. */
257
+ async retryParked(session) {
258
+ const { pi } = await _assertClassBrand(_PiHarness_brand, this, _open).call(this);
259
+ const id = conversationId(session);
260
+ const parked = (await _assertClassBrand(_PiHarness_brand, this, _parked).call(this, await pi.inspect(BG))).filter((task) => task.sessions.has(id));
261
+ if (parked.length === 0) return false;
262
+ await _assertClassBrand(_PiHarness_brand, this, _admit).call(this, async () => {
263
+ for (const task of parked) await pi.retryTask(task.id, BG);
264
+ });
265
+ return true;
266
+ }
433
267
  /** @internal Withdraw a queued input, or abort the run it joined. */
434
268
  async withdraw(session, operationId) {
435
269
  const { pi, storage } = await _assertClassBrand(_PiHarness_brand, this, _open).call(this);
@@ -462,94 +296,188 @@ var PiHarness = class extends LifecycleCapability {
462
296
  };
463
297
  }
464
298
  };
465
- /** Schedule the session's wake job now, or pull it forward. */
466
- function _wake(session, time = Date.now()) {
299
+ /**
300
+ * Hand pi work inside the wake. The wake is scheduled first, so if the
301
+ * object dies after pi has the work, that job restarts it; while `hand`
302
+ * is in flight the step will not complete. The wake steps again after, so
303
+ * it sees the work even if it just completed.
304
+ */
305
+ async function _admit(hand) {
306
+ var _this$admitting;
307
+ _classPrivateFieldSet2(_admitting, this, (_this$admitting = _classPrivateFieldGet2(_admitting, this), _this$admitting++, _this$admitting));
308
+ let result;
309
+ try {
310
+ await _assertClassBrand(_PiHarness_brand, this, _wake).call(this);
311
+ result = await hand();
312
+ } finally {
313
+ var _this$admitting3;
314
+ _classPrivateFieldSet2(_admitting, this, (_this$admitting3 = _classPrivateFieldGet2(_admitting, this), _this$admitting3--, _this$admitting3));
315
+ }
316
+ await _assertClassBrand(_PiHarness_brand, this, _wake).call(this);
317
+ return result;
318
+ }
319
+ /** Schedule the wake job now, or at `time`. */
320
+ function _wake(time = Date.now()) {
467
321
  return this.lifecycle.jobs.push({
468
- id: wakeJobId(session),
322
+ id: WAKE_JOB_ID,
469
323
  fn: WAKE_FN,
470
324
  time,
471
- payload: { session },
472
325
  singleflight: true,
473
326
  recoveryLoop: true
474
327
  });
475
328
  }
329
+ /** Schedule the wake job for what `pi.wake()` says, if pi needs one. */
330
+ async function _wakeFor(wake) {
331
+ if (wake.active) await _assertClassBrand(_PiHarness_brand, this, _wake).call(this);
332
+ else if (wake.at !== void 0) await _assertClassBrand(_PiHarness_brand, this, _wake).call(this, wake.at);
333
+ }
476
334
  /**
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.
335
+ * One run of the wake job. It never admits or replays anything: it reads
336
+ * `pi.wake()`, which is all of pi's sessions at once. Running work is
337
+ * waited on; a sleep reschedules the job for when it ends; no work
338
+ * completes the job. pi does all the work in between.
480
339
  */
481
- async function _wakeStep(session) {
340
+ async function _wakeStep() {
482
341
  const heartbeat = { rescheduleAt: Date.now() + _classPrivateFieldGet2(_heartbeatMs, this) };
483
- if (_classPrivateFieldGet2(_waits, this).has(session)) return heartbeat;
342
+ if (_classPrivateFieldGet2(_waiting, this)) return heartbeat;
484
343
  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;
344
+ pi.resume();
345
+ const wake = pi.wake();
346
+ if (wake.active) {
347
+ const wait = _assertClassBrand(_PiHarness_brand, this, _waitWhileActive).call(this, pi).then((ended) => {
348
+ _classPrivateFieldSet2(_waiting, this, void 0);
349
+ if (ended) _assertClassBrand(_PiHarness_brand, this, _wake).call(this);
350
+ }, (error) => {
351
+ _classPrivateFieldSet2(_waiting, this, void 0);
352
+ throw error;
353
+ });
354
+ _classPrivateFieldSet2(_waiting, this, wait);
355
+ if (!this.lifecycle.trackAlarmWork(wait)) wait.catch(() => {});
356
+ return heartbeat;
357
+ }
358
+ if (_classPrivateFieldGet2(_admitting, this) > 0) return heartbeat;
359
+ if (wake.at !== void 0) return { rescheduleAt: wake.at };
499
360
  }
500
- async function _waitForIdle(conversation) {
501
- const budget = new AbortController();
502
- const timer = setTimeout(() => budget.abort(), _classPrivateFieldGet2(_waitBudgetMs, this));
361
+ /**
362
+ * Wait while pi has work running, up to the wait budget. With `onWake`, a
363
+ * change ends each sleep early, and so does pi closing; without it, the
364
+ * sleep is `pollMs`. Whether the wait ended, with pi idle or at the
365
+ * budget, rather than failing. Rejects with a memory-limit reset.
366
+ */
367
+ async function _waitWhileActive(pi) {
368
+ const deadline = Date.now() + _classPrivateFieldGet2(_waitBudgetMs, this);
503
369
  try {
504
- await conversation.waitForIdle(withAbortSignal(budget.signal, BG));
370
+ while (pi.wake().active) {
371
+ const left = deadline - Date.now();
372
+ if (left <= 0) return true;
373
+ await new Promise((resolve) => {
374
+ const timer = setTimeout(resolve, _classPrivateFieldGet2(_onWakeWired, this) ? left : Math.min(left, _classPrivateFieldGet2(_pollMs, this)));
375
+ _classPrivateFieldSet2(_changed, this, () => {
376
+ clearTimeout(timer);
377
+ resolve();
378
+ });
379
+ });
380
+ _classPrivateFieldSet2(_changed, this, void 0);
381
+ }
382
+ return true;
505
383
  } 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);
384
+ if (!_classPrivateFieldGet2(_closing, this).has(pi)) _assertClassBrand(_PiHarness_brand, this, _wakeError).call(this, error);
385
+ if (isDurableObjectMemoryLimitReset(error)) throw error;
386
+ return false;
512
387
  }
513
388
  }
389
+ function _wakeError(error) {
390
+ this.lifecycle.events.emit("pi:wake_error", { error: error instanceof Error ? error.message : String(error) });
391
+ }
514
392
  /**
515
393
  * pi, opened. Waits for Lifecycle startup first, so pi is only ever
516
394
  * opened inside it: startup holds the input gate (`blockConcurrencyWhile`)
517
395
  * and awaits the open in `onStart`, so an open begun before startup would
518
396
  * be awaited behind a closed gate, with its timers and I/O held back, and
519
397
  * never finish.
398
+ *
399
+ * pi stays open until `dispose()`, or until it closes on its own: any
400
+ * storage error fails pi's Session for good. Then the next call, or the
401
+ * wake job's next step, opens pi again once the failed pi has closed, and
402
+ * pi recovers from what it had committed. Closing waits for running task
403
+ * code to return, so code that ignores its abort signal delays the reopen.
404
+ * An open that fails is not kept either, so the next call tries again.
520
405
  */
521
406
  async function _open() {
522
407
  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
- }));
408
+ if (_classPrivateFieldGet2(_opening, this) === void 0) {
409
+ const opening = _assertClassBrand(_PiHarness_brand, this, _doOpen).call(this, (pi, closed) => _assertClassBrand(_PiHarness_brand, this, _piClosing).call(this, opening, pi, closed)).catch((error) => {
410
+ if (_classPrivateFieldGet2(_opening, this) === opening) _classPrivateFieldSet2(_opening, this, void 0);
411
+ throw error;
412
+ });
413
+ _classPrivateFieldSet2(_opening, this, opening);
414
+ }
527
415
  return _classPrivateFieldGet2(_opening, this);
528
416
  }
529
- async function _doOpen() {
417
+ async function _doOpen(onClose) {
418
+ await _classPrivateFieldGet2(_lastClose, this);
530
419
  const storage = await openPiSessionStore(this.lifecycle.storage);
531
420
  const pi = await _classPrivateFieldGet2(_options, this).harness({
532
421
  storage,
533
- context: BG
422
+ context: BG,
423
+ onWake: _classPrivateFieldGet2(_onWake, this)
534
424
  });
535
- await pi.root(BG, { agent: this.agentDefaults() });
536
- pi.resume();
425
+ try {
426
+ const closed = pi.closed;
427
+ pi.subscribeClose(() => onClose(pi, closed));
428
+ await pi.root(BG, { agent: this.agentDefaults() });
429
+ pi.resume();
430
+ await new Promise((resolve) => setTimeout(resolve, 0));
431
+ if (!_classPrivateFieldGet2(_onWakeWired, this)) console.warn("PiHarness: the harness factory did not pass onWake to Harness.open, so pi's timers keep this Durable Object in memory while a task sleeps. Pass it: harness: ({ storage, context, onWake }) => Harness.open(storage, { ...options, onWake }, context)");
432
+ await _assertClassBrand(_PiHarness_brand, this, _wakeFor).call(this, pi.wake());
433
+ } catch (error) {
434
+ pi.close(BG).catch(() => {});
435
+ throw error;
436
+ }
537
437
  return {
538
438
  pi,
539
439
  storage
540
440
  };
541
441
  }
442
+ /**
443
+ * pi began closing: on its own, as a storage error makes it, or by
444
+ * `dispose()`.
445
+ */
446
+ function _piClosing(opening, pi, closed) {
447
+ _classPrivateFieldGet2(_closing, this).add(pi);
448
+ _classPrivateFieldGet2(_changed, this)?.call(this);
449
+ if (_classPrivateFieldGet2(_opening, this) !== opening) return;
450
+ _classPrivateFieldSet2(_opening, this, void 0);
451
+ _classPrivateFieldSet2(_lastClose, this, closed);
452
+ }
453
+ /**
454
+ * Tasks pi's restart limit parked, each with the sessions whose work it
455
+ * is: its own, and every session that owns that one, such as the session
456
+ * of a tool that started a subagent.
457
+ */
458
+ async function _parked(inspection) {
459
+ const { storage } = await _assertClassBrand(_PiHarness_brand, this, _open).call(this);
460
+ const parked = [];
461
+ for (const { record, state } of inspection.tasks) {
462
+ if (state.kind !== "blocked" || state.reason !== "restart_limit") continue;
463
+ const sessions = /* @__PURE__ */ new Set();
464
+ let id = record.conversationId;
465
+ while (id !== void 0 && !sessions.has(id)) {
466
+ sessions.add(id);
467
+ id = (await storage.conversation(id, BG))?.owner?.conversationId;
468
+ }
469
+ parked.push({
470
+ id: record.id,
471
+ sessions
472
+ });
473
+ }
474
+ return parked;
475
+ }
542
476
  async function _findSubmission(id, operationId, context) {
543
477
  const { pi, storage } = await _assertClassBrand(_PiHarness_brand, this, _open).call(this);
544
478
  const record = await storage.submissionByRequest(id, operationId, context);
545
479
  return record ? pi.submission(record.id, context) : void 0;
546
480
  }
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
481
  async function _result(session, operationId, settled, context) {
554
482
  if (settled.status === "unanswered") return {
555
483
  operationId,
@@ -613,6 +541,14 @@ var PiSession = class {
613
541
  await (await _classPrivateFieldGet2(_harness, this).conversation(this.id)).abort(BG);
614
542
  return true;
615
543
  }
544
+ /**
545
+ * Run again the tasks pi's crash-loop guard (`settings.restartLimit`)
546
+ * parked in this session's work, including subagent sessions it owns.
547
+ * False when nothing was parked.
548
+ */
549
+ retry() {
550
+ return _classPrivateFieldGet2(_harness, this).retryParked(this.id);
551
+ }
616
552
  /** Start a new context, optionally from a handoff note. */
617
553
  async reset(handoff) {
618
554
  await (await _classPrivateFieldGet2(_harness, this).conversation(this.id)).reset(handoff, BG);
@@ -692,7 +628,7 @@ function skillsFingerprint(sources) {
692
628
  return sources.map((source) => `${source.id}:${source.fingerprint}`).join("|");
693
629
  }
694
630
  function textResult(text) {
695
- return { content: [{
631
+ return { output: [{
696
632
  type: "text",
697
633
  text
698
634
  }] };