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.
- package/dist/{agent-routing-BMvWTFEp.d.ts → agent-routing-CDbpVjYq.d.ts} +7 -8
- package/dist/agent-routing.d.ts +1 -1
- package/dist/agent-tool-types.d.ts +1 -1
- package/dist/{agent-tools-ohoewW-B.d.ts → agent-tools-C8DH8Yii.d.ts} +2 -2
- package/dist/agent-tools.d.ts +1 -1
- package/dist/{ai-ahruGw7e.d.ts → ai-iXw0uOGj.d.ts} +3 -3
- package/dist/{ai-sdk-C2iTAwUd.js → ai-sdk-DRPh2rRa.js} +185 -125
- package/dist/ai-sdk-DRPh2rRa.js.map +1 -0
- package/dist/browser/ai.d.ts +1 -1
- package/dist/browser/ai.js +2 -2
- package/dist/browser/index.d.ts +8 -8
- package/dist/browser/index.js +3 -3
- package/dist/browser/tools/ai-sdk.d.ts +2 -2
- package/dist/browser/tools/ai-sdk.js +1 -1
- package/dist/browser/tools/pi.d.ts +7 -3
- package/dist/browser/tools/pi.js +9 -5
- package/dist/browser/tools/pi.js.map +1 -1
- package/dist/browser/tools/tanstack-ai.d.ts +2 -2
- package/dist/browser/tools/tanstack-ai.js +1 -1
- package/dist/{browser-CEdNlsaD.js → browser-DccPw_wW.js} +130 -79
- package/dist/browser-DccPw_wW.js.map +1 -0
- package/dist/{browser-D30VlpcA.d.ts → browser-DlXhSAdf.d.ts} +41 -37
- package/dist/{browser-tool-4iomavs_.js → browser-tool-B1mhifin.js} +3 -3
- package/dist/{browser-tool-4iomavs_.js.map → browser-tool-B1mhifin.js.map} +1 -1
- package/dist/{browser-tool-vtrPOV8e.d.ts → browser-tool-Cxjk5sgG.d.ts} +2 -2
- package/dist/chat/index.d.ts +143 -71
- package/dist/chat/index.js +269 -110
- package/dist/chat/index.js.map +1 -1
- package/dist/chat-sdk/index.d.ts +1 -1
- package/dist/chat-sdk/index.js +1 -1
- package/dist/cli.js +53 -52
- package/dist/{client-Bp-JwL3p.d.ts → client-C27CtRZr.d.ts} +2 -2
- package/dist/client.d.ts +1 -1
- package/dist/{connector-DEXzVoaY.d.ts → connector-20DJ6p4k.d.ts} +2 -2
- package/dist/{connector-CtZylXdk.js → connector-DAXBOIIi.js} +2 -2
- package/dist/{connector-CtZylXdk.js.map → connector-DAXBOIIi.js.map} +1 -1
- package/dist/experimental/channels/web/index.js +1 -1
- package/dist/harness/container/index.js +1 -1
- package/dist/harness/container/index.js.map +1 -1
- package/dist/harness/opencode/index.js +1 -1
- package/dist/harness/pi/index.d.ts +70 -40
- package/dist/harness/pi/index.js +266 -330
- package/dist/harness/pi/index.js.map +1 -1
- package/dist/{images-C4hiIexM.js → images-xaDv2Fqe.js} +636 -168
- package/dist/images-xaDv2Fqe.js.map +1 -0
- package/dist/index-DCxk3OSJ.d.ts +378 -0
- package/dist/{index-kbSUIer3.d.ts → index-iv9FTXX9.d.ts} +80 -112
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/{input-schema-xBofjBKC.js → input-schema-B0Etuo-o.js} +2 -2
- package/dist/{input-schema-xBofjBKC.js.map → input-schema-B0Etuo-o.js.map} +1 -1
- package/dist/lifecycle/index.js +1 -1
- package/dist/{lifecycle-DGorpAmA.js → lifecycle-v1rKHV2C.js} +67 -19
- package/dist/lifecycle-v1rKHV2C.js.map +1 -0
- package/dist/mcp/client/index.d.ts +1 -1
- package/dist/mcp/index.d.ts +1 -1
- package/dist/mcp/index.js +1 -1
- package/dist/models/ai-sdk/index.d.ts +29 -352
- package/dist/models/ai-sdk/index.js +1 -1
- package/dist/models/opencode/index.d.ts +25 -13
- package/dist/models/opencode/index.js +16 -11
- package/dist/models/opencode/index.js.map +1 -1
- package/dist/models/pi-ai/index.d.ts +13 -14
- package/dist/models/pi-ai/index.js +87 -34
- package/dist/models/pi-ai/index.js.map +1 -1
- package/dist/react.d.ts +2 -2
- package/dist/routing/index.d.ts +1 -1
- package/dist/serializable.d.ts +1 -1
- package/dist/{session-store-DoPY12xM.d.ts → session-store-_IK6dV-w.d.ts} +14 -2
- package/dist/{session-store-Do4xkDcf.js → session-store-yvwq1f9x.js} +55 -2
- package/dist/session-store-yvwq1f9x.js.map +1 -0
- package/dist/{settings-BjAiPd6Z.js → settings-Dk2il2Hf.js} +4 -1
- package/dist/settings-Dk2il2Hf.js.map +1 -0
- package/dist/skills/index.d.ts +22 -28
- package/dist/skills/index.js +183 -143
- package/dist/skills/index.js.map +1 -1
- package/dist/{src-CQxJFv4l.js → src-DfLzdwlK.js} +3 -3
- package/dist/src-DfLzdwlK.js.map +1 -0
- package/dist/sub-routing.d.ts +1 -1
- package/dist/{tool-VF0qiO7q.js → tool-DkT_DJBm.js} +2 -2
- package/dist/tool-DkT_DJBm.js.map +1 -0
- package/dist/transport-CPh7YGiw.d.ts +623 -0
- package/dist/voice/index.d.ts +1 -1
- package/dist/web/fetch/tools/pi.d.ts +10 -6
- package/dist/web/fetch/tools/pi.js +12 -8
- package/dist/web/fetch/tools/pi.js.map +1 -1
- package/dist/web/search/index.js +1 -1
- package/dist/web/search/tools/ai-sdk.js +2 -2
- package/dist/web/search/tools/pi.d.ts +10 -6
- package/dist/web/search/tools/pi.js +13 -9
- package/dist/web/search/tools/pi.js.map +1 -1
- package/dist/web/search/tools/tanstack-ai.js +2 -2
- package/dist/websockets/index.js +1 -1
- package/dist/{websockets-C3zy8YR0.js → websockets-CyoeosdO.js} +2 -2
- package/dist/{websockets-C3zy8YR0.js.map → websockets-CyoeosdO.js.map} +1 -1
- package/dist/workflow-types.d.ts +1 -1
- package/dist/workflows.d.ts +1 -1
- package/dist/workflows.js +1 -1
- package/docs/browse-the-web.md +1 -1
- package/docs/chat-agents.md +19 -18
- package/docs/harnesses/pi.md +101 -38
- package/docs/lifecycle.md +7 -1
- package/docs/models-pi-ai.md +66 -20
- package/docs/models.md +197 -172
- package/package.json +12 -7
- package/dist/ai-sdk-C2iTAwUd.js.map +0 -1
- package/dist/browser-CEdNlsaD.js.map +0 -1
- package/dist/images-C4hiIexM.js.map +0 -1
- package/dist/lifecycle-DGorpAmA.js.map +0 -1
- package/dist/session-store-Do4xkDcf.js.map +0 -1
- package/dist/settings-BjAiPd6Z.js.map +0 -1
- package/dist/settings-DnD__xuE.d.ts +0 -349
- package/dist/src-CQxJFv4l.js.map +0 -1
- package/dist/tool-VF0qiO7q.js.map +0 -1
- package/dist/transport-DCyarnwm.d.ts +0 -200
package/dist/harness/pi/index.js
CHANGED
|
@@ -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-
|
|
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 {
|
|
8
|
-
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
|
250
|
-
*
|
|
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
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
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
|
|
83
|
+
var _waiting = /* @__PURE__ */ new WeakMap();
|
|
84
|
+
var _changed = /* @__PURE__ */ new WeakMap();
|
|
299
85
|
var _admitting = /* @__PURE__ */ new WeakMap();
|
|
300
|
-
var
|
|
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
|
|
318
|
-
* after the
|
|
319
|
-
*
|
|
320
|
-
*
|
|
321
|
-
*
|
|
322
|
-
*
|
|
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,
|
|
332
|
-
_classPrivateFieldInitSpec(this,
|
|
333
|
-
_classPrivateFieldInitSpec(this,
|
|
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
|
-
|
|
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
|
-
|
|
349
|
-
if (
|
|
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
|
-
|
|
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
|
-
/**
|
|
466
|
-
|
|
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:
|
|
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
|
|
478
|
-
*
|
|
479
|
-
*
|
|
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(
|
|
340
|
+
async function _wakeStep() {
|
|
482
341
|
const heartbeat = { rescheduleAt: Date.now() + _classPrivateFieldGet2(_heartbeatMs, this) };
|
|
483
|
-
if (_classPrivateFieldGet2(
|
|
342
|
+
if (_classPrivateFieldGet2(_waiting, this)) return heartbeat;
|
|
484
343
|
const { pi } = await _assertClassBrand(_PiHarness_brand, this, _open).call(this);
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
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
|
-
|
|
501
|
-
|
|
502
|
-
|
|
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
|
-
|
|
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 (!
|
|
507
|
-
|
|
508
|
-
|
|
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)
|
|
524
|
-
|
|
525
|
-
|
|
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
|
-
|
|
536
|
-
|
|
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 {
|
|
631
|
+
return { output: [{
|
|
696
632
|
type: "text",
|
|
697
633
|
text
|
|
698
634
|
}] };
|