agents 0.9.0 → 0.10.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/chat/index.d.ts +151 -2
- package/dist/chat/index.js +210 -11
- package/dist/chat/index.js.map +1 -1
- package/dist/{client-BwgM3cRz.js → client-QBjFV5de.js} +161 -49
- package/dist/client-QBjFV5de.js.map +1 -0
- package/dist/client.d.ts +2 -2
- package/dist/{compaction-helpers-BFTBIzpK.js → compaction-helpers-BPE1_ziA.js} +1 -1
- package/dist/{compaction-helpers-BFTBIzpK.js.map → compaction-helpers-BPE1_ziA.js.map} +1 -1
- package/dist/{compaction-helpers-DkJreaDR.d.ts → compaction-helpers-CHNQeyRm.d.ts} +1 -1
- package/dist/{do-oauth-client-provider-C2jurFjW.d.ts → do-oauth-client-provider-31gqR33H.d.ts} +1 -1
- package/dist/{email-DwPlM0bQ.d.ts → email-Cql45SKP.d.ts} +1 -1
- package/dist/email.d.ts +2 -2
- package/dist/experimental/memory/session/index.d.ts +153 -82
- package/dist/experimental/memory/session/index.js +257 -24
- package/dist/experimental/memory/session/index.js.map +1 -1
- package/dist/experimental/memory/utils/index.d.ts +1 -1
- package/dist/experimental/memory/utils/index.js +1 -1
- package/dist/{index-BtHngIIG.d.ts → index-BPkkIqMn.d.ts} +204 -74
- package/dist/{index-Ua2Nfvbm.d.ts → index-DDSX-g7W.d.ts} +11 -1
- package/dist/index.d.ts +30 -26
- package/dist/index.js +2 -3049
- package/dist/{internal_context-DT8RxmAN.d.ts → internal_context-DuQZFvWI.d.ts} +1 -1
- package/dist/internal_context.d.ts +1 -1
- package/dist/mcp/client.d.ts +2 -2
- package/dist/mcp/client.js +1 -1
- package/dist/mcp/do-oauth-client-provider.d.ts +1 -1
- package/dist/mcp/index.d.ts +1 -1
- package/dist/mcp/index.js +2 -2
- package/dist/observability/index.d.ts +1 -1
- package/dist/react.d.ts +3 -1
- package/dist/react.js +3 -0
- package/dist/react.js.map +1 -1
- package/dist/{retries-DXMQGhG3.d.ts → retries-B_CN5KM9.d.ts} +1 -1
- package/dist/retries.d.ts +1 -1
- package/dist/{serializable-8Jt1B04R.d.ts → serializable-DGdO8CDh.d.ts} +1 -1
- package/dist/serializable.d.ts +1 -1
- package/dist/src-B8NZxxsO.js +3217 -0
- package/dist/src-B8NZxxsO.js.map +1 -0
- package/dist/{types-C-m0II8i.d.ts → types-B9A8AU7B.d.ts} +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/{workflow-types-CZNXKj_D.d.ts → workflow-types-XmOkuI7A.d.ts} +1 -1
- package/dist/workflow-types.d.ts +1 -1
- package/dist/workflows.d.ts +2 -2
- package/dist/workflows.js +1 -1
- package/package.json +12 -16
- package/dist/client-BwgM3cRz.js.map +0 -1
- package/dist/experimental/forever.d.ts +0 -64
- package/dist/experimental/forever.js +0 -338
- package/dist/experimental/forever.js.map +0 -1
- package/dist/index.js.map +0 -1
|
@@ -1,338 +0,0 @@
|
|
|
1
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
-
import { nanoid } from "nanoid";
|
|
3
|
-
//#region src/experimental/forever.ts
|
|
4
|
-
/**
|
|
5
|
-
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
6
|
-
* !! WARNING: EXPERIMENTAL — DO NOT USE IN PRODUCTION !!
|
|
7
|
-
* !! !!
|
|
8
|
-
* !! This API is under active development and WILL break between !!
|
|
9
|
-
* !! releases. Method names, types, behavior, and the mixin signature !!
|
|
10
|
-
* !! are all subject to change without notice. !!
|
|
11
|
-
* !! !!
|
|
12
|
-
* !! If you use this, pin your agents version and expect to rewrite !!
|
|
13
|
-
* !! your code when upgrading. !!
|
|
14
|
-
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
15
|
-
*
|
|
16
|
-
* Experimental fiber mixin for durable long-running execution.
|
|
17
|
-
*
|
|
18
|
-
* Usage:
|
|
19
|
-
* import { Agent } from "agents";
|
|
20
|
-
* import { withFibers } from "agents/experimental/forever";
|
|
21
|
-
*
|
|
22
|
-
* class MyAgent extends withFibers(Agent)<Env, State> {
|
|
23
|
-
* async doWork(payload, fiberCtx) { ... }
|
|
24
|
-
* }
|
|
25
|
-
*
|
|
26
|
-
* This mixin adds:
|
|
27
|
-
* - keepAlive() — keep the DO alive via scheduled heartbeats
|
|
28
|
-
* - spawnFiber() — fire-and-forget durable execution
|
|
29
|
-
* - stashFiber() — checkpoint progress that survives eviction
|
|
30
|
-
* - cancelFiber() / getFiber() — manage running fibers
|
|
31
|
-
* - onFiberComplete / onFiberRecovered / onFibersRecovered — lifecycle hooks
|
|
32
|
-
*
|
|
33
|
-
* @experimental This API is not yet stable and may change.
|
|
34
|
-
*/
|
|
35
|
-
const FIBER_CLEANUP_INTERVAL_MS = 600 * 1e3;
|
|
36
|
-
const FIBER_CLEANUP_COMPLETED_MS = 1440 * 60 * 1e3;
|
|
37
|
-
const FIBER_CLEANUP_FAILED_MS = 10080 * 60 * 1e3;
|
|
38
|
-
const fiberContext = new AsyncLocalStorage();
|
|
39
|
-
function withFibers(Base, options) {
|
|
40
|
-
const debugEnabled = options?.debugFibers ?? false;
|
|
41
|
-
class FiberAgent extends Base {
|
|
42
|
-
constructor(...args) {
|
|
43
|
-
super(...args);
|
|
44
|
-
this._fiberActiveFibers = /* @__PURE__ */ new Set();
|
|
45
|
-
this._fiberRecoveryInProgress = false;
|
|
46
|
-
this._fiberLastCleanupTime = 0;
|
|
47
|
-
console.warn("[agents/experimental/forever] WARNING: You are using an experimental API that WILL break between releases. Use with caution.");
|
|
48
|
-
this.sql`
|
|
49
|
-
CREATE TABLE IF NOT EXISTS cf_agents_fibers (
|
|
50
|
-
id TEXT PRIMARY KEY NOT NULL,
|
|
51
|
-
callback TEXT NOT NULL,
|
|
52
|
-
payload TEXT,
|
|
53
|
-
snapshot TEXT,
|
|
54
|
-
status TEXT NOT NULL DEFAULT 'running'
|
|
55
|
-
CHECK(status IN ('running', 'completed', 'failed', 'interrupted', 'cancelled')),
|
|
56
|
-
retry_count INTEGER NOT NULL DEFAULT 0,
|
|
57
|
-
max_retries INTEGER NOT NULL DEFAULT 3,
|
|
58
|
-
result TEXT,
|
|
59
|
-
error TEXT,
|
|
60
|
-
started_at INTEGER,
|
|
61
|
-
updated_at INTEGER,
|
|
62
|
-
completed_at INTEGER,
|
|
63
|
-
created_at INTEGER NOT NULL
|
|
64
|
-
)
|
|
65
|
-
`;
|
|
66
|
-
}
|
|
67
|
-
/** @internal */ _fiberDebug(msg, ...args) {
|
|
68
|
-
if (debugEnabled) console.debug(`[fiber] ${msg}`, ...args);
|
|
69
|
-
}
|
|
70
|
-
/** @internal */ async _onAlarmHousekeeping() {
|
|
71
|
-
await this._checkInterruptedFibers();
|
|
72
|
-
}
|
|
73
|
-
spawnFiber(methodName, payload, options) {
|
|
74
|
-
this._maybeCleanupFibers();
|
|
75
|
-
const name = methodName;
|
|
76
|
-
if (typeof this[methodName] !== "function") throw new Error(`this.${name} is not a function`);
|
|
77
|
-
const id = nanoid();
|
|
78
|
-
const now = Date.now();
|
|
79
|
-
const maxRetries = options?.maxRetries ?? 3;
|
|
80
|
-
this.sql`
|
|
81
|
-
INSERT INTO cf_agents_fibers (id, callback, payload, status, max_retries, retry_count, started_at, updated_at, created_at)
|
|
82
|
-
VALUES (${id}, ${name}, ${JSON.stringify(payload ?? null)}, 'running', ${maxRetries}, 0, ${now}, ${now}, ${now})
|
|
83
|
-
`;
|
|
84
|
-
this._fiberActiveFibers.add(id);
|
|
85
|
-
this._fiberDebug("spawned fiber=%s method=%s maxRetries=%d", id, name, maxRetries);
|
|
86
|
-
this._startFiber(id, name, payload, maxRetries).catch((e) => {
|
|
87
|
-
console.error(`Unhandled error in fiber ${id}:`, e);
|
|
88
|
-
});
|
|
89
|
-
return id;
|
|
90
|
-
}
|
|
91
|
-
stashFiber(data) {
|
|
92
|
-
const ctx = fiberContext.getStore();
|
|
93
|
-
if (!ctx) throw new Error("stashFiber() can only be called within a fiber execution context");
|
|
94
|
-
const now = Date.now();
|
|
95
|
-
this.sql`
|
|
96
|
-
UPDATE cf_agents_fibers
|
|
97
|
-
SET snapshot = ${JSON.stringify(data)}, updated_at = ${now}
|
|
98
|
-
WHERE id = ${ctx.fiberId}
|
|
99
|
-
`;
|
|
100
|
-
this._fiberDebug("stash fiber=%s", ctx.fiberId);
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Note: cancellation is cooperative. The status is set to 'cancelled'
|
|
104
|
-
* in SQLite, and the _runFiber retry loop checks for this status at
|
|
105
|
-
* the top of each iteration.
|
|
106
|
-
*/
|
|
107
|
-
cancelFiber(fiberId) {
|
|
108
|
-
const fiber = this._getRawFiber(fiberId);
|
|
109
|
-
if (!fiber) return false;
|
|
110
|
-
if (fiber.status === "completed" || fiber.status === "failed" || fiber.status === "cancelled") return false;
|
|
111
|
-
const now = Date.now();
|
|
112
|
-
this.sql`
|
|
113
|
-
UPDATE cf_agents_fibers
|
|
114
|
-
SET status = 'cancelled', updated_at = ${now}
|
|
115
|
-
WHERE id = ${fiberId}
|
|
116
|
-
`;
|
|
117
|
-
this._fiberActiveFibers.delete(fiberId);
|
|
118
|
-
this._fiberDebug("cancelled fiber=%s", fiberId);
|
|
119
|
-
return true;
|
|
120
|
-
}
|
|
121
|
-
getFiber(fiberId) {
|
|
122
|
-
const raw = this._getRawFiber(fiberId);
|
|
123
|
-
if (!raw) return null;
|
|
124
|
-
return this._toFiberState(raw);
|
|
125
|
-
}
|
|
126
|
-
restartFiber(fiberId) {
|
|
127
|
-
const fiber = this._getRawFiber(fiberId);
|
|
128
|
-
if (!fiber) throw new Error(`Fiber ${fiberId} not found`);
|
|
129
|
-
const now = Date.now();
|
|
130
|
-
this.sql`
|
|
131
|
-
UPDATE cf_agents_fibers
|
|
132
|
-
SET status = 'running', started_at = ${now}, updated_at = ${now}
|
|
133
|
-
WHERE id = ${fiberId}
|
|
134
|
-
`;
|
|
135
|
-
this._fiberActiveFibers.add(fiberId);
|
|
136
|
-
this._fiberDebug("restarting fiber=%s method=%s retryCount=%d", fiberId, fiber.callback, fiber.retry_count);
|
|
137
|
-
const parsedPayload = fiber.payload ? JSON.parse(fiber.payload) : void 0;
|
|
138
|
-
this._startFiber(fiberId, fiber.callback, parsedPayload, fiber.max_retries).catch((e) => {
|
|
139
|
-
console.error(`Error restarting fiber ${fiberId}:`, e);
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* Manually trigger fiber recovery check.
|
|
144
|
-
* In production, this runs automatically via the heartbeat schedule.
|
|
145
|
-
* Useful for testing or when you need immediate recovery after
|
|
146
|
-
* detecting an eviction.
|
|
147
|
-
*/
|
|
148
|
-
async checkFibers() {
|
|
149
|
-
await this._checkInterruptedFibers();
|
|
150
|
-
}
|
|
151
|
-
onFiberComplete(_ctx) {}
|
|
152
|
-
onFiberRecovered(ctx) {
|
|
153
|
-
this.restartFiber(ctx.id);
|
|
154
|
-
}
|
|
155
|
-
async onFibersRecovered(fibers) {
|
|
156
|
-
for (const fiber of fibers) await this.onFiberRecovered(fiber);
|
|
157
|
-
}
|
|
158
|
-
/** @internal */ _getRawFiber(fiberId) {
|
|
159
|
-
const result = this.sql`
|
|
160
|
-
SELECT * FROM cf_agents_fibers WHERE id = ${fiberId}
|
|
161
|
-
`;
|
|
162
|
-
return result && result.length > 0 ? result[0] : null;
|
|
163
|
-
}
|
|
164
|
-
/** @internal */ _safeJsonParse(value) {
|
|
165
|
-
if (value === null) return null;
|
|
166
|
-
try {
|
|
167
|
-
return JSON.parse(value);
|
|
168
|
-
} catch {
|
|
169
|
-
return null;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
/** @internal */ _toFiberState(raw) {
|
|
173
|
-
return {
|
|
174
|
-
id: raw.id,
|
|
175
|
-
callback: raw.callback,
|
|
176
|
-
payload: this._safeJsonParse(raw.payload),
|
|
177
|
-
snapshot: this._safeJsonParse(raw.snapshot),
|
|
178
|
-
status: raw.status,
|
|
179
|
-
retryCount: raw.retry_count,
|
|
180
|
-
maxRetries: raw.max_retries,
|
|
181
|
-
result: this._safeJsonParse(raw.result),
|
|
182
|
-
error: raw.error,
|
|
183
|
-
startedAt: raw.started_at,
|
|
184
|
-
updatedAt: raw.updated_at,
|
|
185
|
-
completedAt: raw.completed_at,
|
|
186
|
-
createdAt: raw.created_at
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
/** @internal */ async _startFiber(id, methodName, payload, maxRetries) {
|
|
190
|
-
const disposeKeepAlive = await this.keepAlive();
|
|
191
|
-
await this._runFiber(id, methodName, payload, maxRetries, disposeKeepAlive);
|
|
192
|
-
}
|
|
193
|
-
/** @internal */ async _runFiber(id, methodName, payload, maxRetries, disposeKeepAlive) {
|
|
194
|
-
try {
|
|
195
|
-
while (true) {
|
|
196
|
-
const fiber = this._getRawFiber(id);
|
|
197
|
-
if (!fiber || fiber.status === "cancelled") {
|
|
198
|
-
this._fiberDebug("fiber=%s exiting: %s", id, !fiber ? "not found" : "cancelled");
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
try {
|
|
202
|
-
await fiberContext.run({ fiberId: id }, async () => {
|
|
203
|
-
const snapshot = this._safeJsonParse(fiber.snapshot);
|
|
204
|
-
const retryCount = fiber.retry_count;
|
|
205
|
-
const callback = this[methodName];
|
|
206
|
-
if (typeof callback !== "function") throw new Error(`Fiber method ${methodName} not found`);
|
|
207
|
-
const result = await callback.call(this, payload, {
|
|
208
|
-
id,
|
|
209
|
-
snapshot,
|
|
210
|
-
retryCount
|
|
211
|
-
});
|
|
212
|
-
const now = Date.now();
|
|
213
|
-
this.sql`
|
|
214
|
-
UPDATE cf_agents_fibers
|
|
215
|
-
SET status = 'completed',
|
|
216
|
-
result = ${JSON.stringify(result ?? null)},
|
|
217
|
-
completed_at = ${now},
|
|
218
|
-
updated_at = ${now}
|
|
219
|
-
WHERE id = ${id}
|
|
220
|
-
`;
|
|
221
|
-
this._fiberDebug("fiber=%s completed method=%s", id, methodName);
|
|
222
|
-
try {
|
|
223
|
-
await this.onFiberComplete({
|
|
224
|
-
id,
|
|
225
|
-
methodName,
|
|
226
|
-
payload,
|
|
227
|
-
result
|
|
228
|
-
});
|
|
229
|
-
} catch (e) {
|
|
230
|
-
console.error("Error in onFiberComplete:", e);
|
|
231
|
-
}
|
|
232
|
-
});
|
|
233
|
-
return;
|
|
234
|
-
} catch (e) {
|
|
235
|
-
const now = Date.now();
|
|
236
|
-
const newRetryCount = (this._getRawFiber(id)?.retry_count ?? 0) + 1;
|
|
237
|
-
if (newRetryCount > maxRetries) {
|
|
238
|
-
const errorMsg = e instanceof Error ? e.message : String(e);
|
|
239
|
-
this.sql`
|
|
240
|
-
UPDATE cf_agents_fibers
|
|
241
|
-
SET status = 'failed',
|
|
242
|
-
error = ${errorMsg},
|
|
243
|
-
retry_count = ${newRetryCount},
|
|
244
|
-
updated_at = ${now}
|
|
245
|
-
WHERE id = ${id}
|
|
246
|
-
`;
|
|
247
|
-
this._fiberDebug("fiber=%s failed after %d retries: %s", id, newRetryCount, errorMsg);
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
250
|
-
this.sql`
|
|
251
|
-
UPDATE cf_agents_fibers
|
|
252
|
-
SET retry_count = ${newRetryCount}, updated_at = ${now}
|
|
253
|
-
WHERE id = ${id}
|
|
254
|
-
`;
|
|
255
|
-
this._fiberDebug("fiber=%s retrying (%d/%d)", id, newRetryCount, maxRetries);
|
|
256
|
-
continue;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
} finally {
|
|
260
|
-
this._fiberActiveFibers.delete(id);
|
|
261
|
-
disposeKeepAlive();
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
/** @internal */ async _checkInterruptedFibers() {
|
|
265
|
-
if (this._fiberRecoveryInProgress) return;
|
|
266
|
-
this._fiberRecoveryInProgress = true;
|
|
267
|
-
try {
|
|
268
|
-
const runningFibers = this.sql`
|
|
269
|
-
SELECT * FROM cf_agents_fibers
|
|
270
|
-
WHERE status = 'running'
|
|
271
|
-
ORDER BY created_at ASC
|
|
272
|
-
`;
|
|
273
|
-
if (!runningFibers || runningFibers.length === 0) return;
|
|
274
|
-
const interrupted = [];
|
|
275
|
-
for (const fiber of runningFibers) {
|
|
276
|
-
if (this._fiberActiveFibers.has(fiber.id)) continue;
|
|
277
|
-
const newRetryCount = fiber.retry_count + 1;
|
|
278
|
-
const now = Date.now();
|
|
279
|
-
if (newRetryCount > fiber.max_retries) {
|
|
280
|
-
this.sql`
|
|
281
|
-
UPDATE cf_agents_fibers
|
|
282
|
-
SET status = 'failed',
|
|
283
|
-
error = 'max retries exceeded (eviction recovery)',
|
|
284
|
-
retry_count = ${newRetryCount},
|
|
285
|
-
updated_at = ${now}
|
|
286
|
-
WHERE id = ${fiber.id}
|
|
287
|
-
`;
|
|
288
|
-
this._fiberDebug("fiber=%s max retries exceeded on recovery", fiber.id);
|
|
289
|
-
} else {
|
|
290
|
-
this.sql`
|
|
291
|
-
UPDATE cf_agents_fibers
|
|
292
|
-
SET status = 'interrupted',
|
|
293
|
-
retry_count = ${newRetryCount},
|
|
294
|
-
updated_at = ${now}
|
|
295
|
-
WHERE id = ${fiber.id}
|
|
296
|
-
`;
|
|
297
|
-
interrupted.push({
|
|
298
|
-
id: fiber.id,
|
|
299
|
-
methodName: fiber.callback,
|
|
300
|
-
payload: this._safeJsonParse(fiber.payload),
|
|
301
|
-
snapshot: this._safeJsonParse(fiber.snapshot),
|
|
302
|
-
retryCount: newRetryCount
|
|
303
|
-
});
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
if (interrupted.length > 0) {
|
|
307
|
-
this._fiberDebug("recovering %d interrupted fibers", interrupted.length);
|
|
308
|
-
try {
|
|
309
|
-
await this.onFibersRecovered(interrupted);
|
|
310
|
-
} catch (e) {
|
|
311
|
-
console.error("Error in onFibersRecovered:", e);
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
} finally {
|
|
315
|
-
this._fiberRecoveryInProgress = false;
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
/** @internal */ _maybeCleanupFibers() {
|
|
319
|
-
const now = Date.now();
|
|
320
|
-
if (now - this._fiberLastCleanupTime < FIBER_CLEANUP_INTERVAL_MS) return;
|
|
321
|
-
this._fiberLastCleanupTime = now;
|
|
322
|
-
const completedCutoff = now - FIBER_CLEANUP_COMPLETED_MS;
|
|
323
|
-
const failedCutoff = now - FIBER_CLEANUP_FAILED_MS;
|
|
324
|
-
this.sql`
|
|
325
|
-
DELETE FROM cf_agents_fibers
|
|
326
|
-
WHERE (status = 'completed' AND completed_at < ${completedCutoff})
|
|
327
|
-
OR (status = 'failed' AND updated_at < ${failedCutoff})
|
|
328
|
-
OR (status = 'cancelled' AND updated_at < ${completedCutoff})
|
|
329
|
-
`;
|
|
330
|
-
this._fiberDebug("cleanup: checked for old completed/failed/cancelled fibers");
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
return FiberAgent;
|
|
334
|
-
}
|
|
335
|
-
//#endregion
|
|
336
|
-
export { withFibers };
|
|
337
|
-
|
|
338
|
-
//# sourceMappingURL=forever.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"forever.js","names":[],"sources":["../../src/experimental/forever.ts"],"sourcesContent":["/**\n * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n * !! WARNING: EXPERIMENTAL — DO NOT USE IN PRODUCTION !!\n * !! !!\n * !! This API is under active development and WILL break between !!\n * !! releases. Method names, types, behavior, and the mixin signature !!\n * !! are all subject to change without notice. !!\n * !! !!\n * !! If you use this, pin your agents version and expect to rewrite !!\n * !! your code when upgrading. !!\n * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n *\n * Experimental fiber mixin for durable long-running execution.\n *\n * Usage:\n * import { Agent } from \"agents\";\n * import { withFibers } from \"agents/experimental/forever\";\n *\n * class MyAgent extends withFibers(Agent)<Env, State> {\n * async doWork(payload, fiberCtx) { ... }\n * }\n *\n * This mixin adds:\n * - keepAlive() — keep the DO alive via scheduled heartbeats\n * - spawnFiber() — fire-and-forget durable execution\n * - stashFiber() — checkpoint progress that survives eviction\n * - cancelFiber() / getFiber() — manage running fibers\n * - onFiberComplete / onFiberRecovered / onFibersRecovered — lifecycle hooks\n *\n * @experimental This API is not yet stable and may change.\n */\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { nanoid } from \"nanoid\";\nimport type { Agent } from \"../index\";\n\n// ── Types ─────────────────────────────────────────────────────────────\n\nexport type FiberState = {\n id: string;\n callback: string;\n payload: unknown;\n snapshot: unknown | null;\n status: \"running\" | \"completed\" | \"failed\" | \"interrupted\" | \"cancelled\";\n retryCount: number;\n maxRetries: number;\n result: unknown | null;\n error: string | null;\n startedAt: number | null;\n updatedAt: number | null;\n completedAt: number | null;\n createdAt: number;\n};\n\nexport type FiberRecoveryContext = {\n id: string;\n methodName: string;\n payload: unknown;\n snapshot: unknown | null;\n retryCount: number;\n};\n\nexport type FiberContext = {\n id: string;\n snapshot: unknown | null;\n retryCount: number;\n};\n\nexport type FiberCompleteContext = {\n id: string;\n methodName: string;\n payload: unknown;\n result: unknown;\n};\n\n// ── Internal types ────────────────────────────────────────────────────\n\ntype RawFiberRow = {\n id: string;\n callback: string;\n payload: string | null;\n snapshot: string | null;\n status: string;\n retry_count: number;\n max_retries: number;\n result: string | null;\n error: string | null;\n started_at: number | null;\n updated_at: number | null;\n completed_at: number | null;\n created_at: number;\n};\n\n// ── Constants ─────────────────────────────────────────────────────────\n\nconst FIBER_CLEANUP_INTERVAL_MS = 10 * 60 * 1000;\nconst FIBER_CLEANUP_COMPLETED_MS = 24 * 60 * 60 * 1000;\nconst FIBER_CLEANUP_FAILED_MS = 7 * 24 * 60 * 60 * 1000;\n\nconst fiberContext = new AsyncLocalStorage<{ fiberId: string }>();\n\n// ── Mixin ─────────────────────────────────────────────────────────────\n\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any -- mixin constructor pattern\ntype AgentConstructor = new (...args: any[]) => Agent;\n\nexport function withFibers<TBase extends typeof Agent>(\n Base: TBase,\n options?: { debugFibers?: boolean }\n) {\n const debugEnabled = options?.debugFibers ?? false;\n\n class FiberAgent extends (Base as AgentConstructor) {\n // ── Fiber state ───────────────────────────────────────────────\n\n /** @internal */ _fiberActiveFibers = new Set<string>();\n /** @internal */ _fiberRecoveryInProgress = false;\n /** @internal */ _fiberLastCleanupTime = 0;\n\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any -- mixin constructor\n constructor(...args: any[]) {\n super(...args);\n\n console.warn(\n \"[agents/experimental/forever] WARNING: You are using an experimental API that WILL break between releases. Use with caution.\"\n );\n\n // Create the fibers table\n this.sql`\n CREATE TABLE IF NOT EXISTS cf_agents_fibers (\n id TEXT PRIMARY KEY NOT NULL,\n callback TEXT NOT NULL,\n payload TEXT,\n snapshot TEXT,\n status TEXT NOT NULL DEFAULT 'running'\n CHECK(status IN ('running', 'completed', 'failed', 'interrupted', 'cancelled')),\n retry_count INTEGER NOT NULL DEFAULT 0,\n max_retries INTEGER NOT NULL DEFAULT 3,\n result TEXT,\n error TEXT,\n started_at INTEGER,\n updated_at INTEGER,\n completed_at INTEGER,\n created_at INTEGER NOT NULL\n )\n `;\n }\n\n // ── Debug logging ─────────────────────────────────────────────\n\n /** @internal */ _fiberDebug(msg: string, ...args: unknown[]) {\n if (debugEnabled) {\n console.debug(`[fiber] ${msg}`, ...args);\n }\n }\n\n // ── Alarm housekeeping override ─────────────────────────────────\n\n /** @internal */ async _onAlarmHousekeeping() {\n await this._checkInterruptedFibers();\n }\n\n // ── Public API ────────────────────────────────────────────────\n\n spawnFiber(\n methodName: keyof this,\n payload?: unknown,\n options?: { maxRetries?: number }\n ): string {\n this._maybeCleanupFibers();\n\n const name = methodName as string;\n if (typeof this[methodName] !== \"function\") {\n throw new Error(`this.${name} is not a function`);\n }\n\n const id = nanoid();\n const now = Date.now();\n const maxRetries = options?.maxRetries ?? 3;\n\n this.sql`\n INSERT INTO cf_agents_fibers (id, callback, payload, status, max_retries, retry_count, started_at, updated_at, created_at)\n VALUES (${id}, ${name}, ${JSON.stringify(payload ?? null)}, 'running', ${maxRetries}, 0, ${now}, ${now}, ${now})\n `;\n\n this._fiberActiveFibers.add(id);\n this._fiberDebug(\n \"spawned fiber=%s method=%s maxRetries=%d\",\n id,\n name,\n maxRetries\n );\n\n void this._startFiber(id, name, payload, maxRetries).catch((e) => {\n console.error(`Unhandled error in fiber ${id}:`, e);\n });\n\n return id;\n }\n\n stashFiber(data: unknown): void {\n const ctx = fiberContext.getStore();\n if (!ctx) {\n throw new Error(\n \"stashFiber() can only be called within a fiber execution context\"\n );\n }\n const now = Date.now();\n this.sql`\n UPDATE cf_agents_fibers\n SET snapshot = ${JSON.stringify(data)}, updated_at = ${now}\n WHERE id = ${ctx.fiberId}\n `;\n this._fiberDebug(\"stash fiber=%s\", ctx.fiberId);\n }\n\n /**\n * Note: cancellation is cooperative. The status is set to 'cancelled'\n * in SQLite, and the _runFiber retry loop checks for this status at\n * the top of each iteration.\n */\n cancelFiber(fiberId: string): boolean {\n const fiber = this._getRawFiber(fiberId);\n if (!fiber) return false;\n if (\n fiber.status === \"completed\" ||\n fiber.status === \"failed\" ||\n fiber.status === \"cancelled\"\n ) {\n return false;\n }\n\n const now = Date.now();\n this.sql`\n UPDATE cf_agents_fibers\n SET status = 'cancelled', updated_at = ${now}\n WHERE id = ${fiberId}\n `;\n this._fiberActiveFibers.delete(fiberId);\n this._fiberDebug(\"cancelled fiber=%s\", fiberId);\n return true;\n }\n\n getFiber(fiberId: string): FiberState | null {\n const raw = this._getRawFiber(fiberId);\n if (!raw) return null;\n return this._toFiberState(raw);\n }\n\n restartFiber(fiberId: string): void {\n const fiber = this._getRawFiber(fiberId);\n if (!fiber) {\n throw new Error(`Fiber ${fiberId} not found`);\n }\n\n const now = Date.now();\n this.sql`\n UPDATE cf_agents_fibers\n SET status = 'running', started_at = ${now}, updated_at = ${now}\n WHERE id = ${fiberId}\n `;\n\n this._fiberActiveFibers.add(fiberId);\n this._fiberDebug(\n \"restarting fiber=%s method=%s retryCount=%d\",\n fiberId,\n fiber.callback,\n fiber.retry_count\n );\n\n const parsedPayload = fiber.payload\n ? JSON.parse(fiber.payload)\n : undefined;\n\n void this._startFiber(\n fiberId,\n fiber.callback,\n parsedPayload,\n fiber.max_retries\n ).catch((e) => {\n console.error(`Error restarting fiber ${fiberId}:`, e);\n });\n }\n\n // ── Lifecycle hooks (override in subclass) ────────────────────\n\n /**\n * Manually trigger fiber recovery check.\n * In production, this runs automatically via the heartbeat schedule.\n * Useful for testing or when you need immediate recovery after\n * detecting an eviction.\n */\n async checkFibers(): Promise<void> {\n await this._checkInterruptedFibers();\n }\n\n // oxlint-disable-next-line @typescript-eslint/no-unused-vars -- overridable hook\n onFiberComplete(_ctx: FiberCompleteContext): void | Promise<void> {}\n\n onFiberRecovered(ctx: FiberRecoveryContext): void | Promise<void> {\n this.restartFiber(ctx.id);\n }\n\n async onFibersRecovered(fibers: FiberRecoveryContext[]): Promise<void> {\n for (const fiber of fibers) {\n await this.onFiberRecovered(fiber);\n }\n }\n\n // ── Private implementation ────────────────────────────────────\n\n /** @internal */ _getRawFiber(fiberId: string): RawFiberRow | null {\n const result = this.sql<RawFiberRow>`\n SELECT * FROM cf_agents_fibers WHERE id = ${fiberId}\n `;\n return result && result.length > 0 ? result[0] : null;\n }\n\n /** @internal */ _safeJsonParse(value: string | null): unknown {\n if (value === null) return null;\n try {\n return JSON.parse(value);\n } catch {\n return null;\n }\n }\n\n /** @internal */ _toFiberState(raw: RawFiberRow): FiberState {\n return {\n id: raw.id,\n callback: raw.callback,\n payload: this._safeJsonParse(raw.payload),\n snapshot: this._safeJsonParse(raw.snapshot),\n status: raw.status as FiberState[\"status\"],\n retryCount: raw.retry_count,\n maxRetries: raw.max_retries,\n result: this._safeJsonParse(raw.result),\n error: raw.error,\n startedAt: raw.started_at,\n updatedAt: raw.updated_at,\n completedAt: raw.completed_at,\n createdAt: raw.created_at\n };\n }\n\n /** @internal */ async _startFiber(\n id: string,\n methodName: string,\n payload: unknown,\n maxRetries: number\n ): Promise<void> {\n const disposeKeepAlive = await this.keepAlive();\n await this._runFiber(\n id,\n methodName,\n payload,\n maxRetries,\n disposeKeepAlive\n );\n }\n\n /** @internal */ async _runFiber(\n id: string,\n methodName: string,\n payload: unknown,\n maxRetries: number,\n disposeKeepAlive: () => void\n ): Promise<void> {\n try {\n while (true) {\n const fiber = this._getRawFiber(id);\n if (!fiber || fiber.status === \"cancelled\") {\n this._fiberDebug(\n \"fiber=%s exiting: %s\",\n id,\n !fiber ? \"not found\" : \"cancelled\"\n );\n return;\n }\n\n try {\n await fiberContext.run({ fiberId: id }, async () => {\n const snapshot = this._safeJsonParse(fiber.snapshot);\n const retryCount = fiber.retry_count;\n\n const callback = this[methodName as keyof this];\n if (typeof callback !== \"function\") {\n throw new Error(`Fiber method ${methodName} not found`);\n }\n\n const result = await (\n callback as (p: unknown, ctx: FiberContext) => Promise<unknown>\n ).call(this, payload, { id, snapshot, retryCount });\n\n const now = Date.now();\n this.sql`\n UPDATE cf_agents_fibers\n SET status = 'completed',\n result = ${JSON.stringify(result ?? null)},\n completed_at = ${now},\n updated_at = ${now}\n WHERE id = ${id}\n `;\n\n this._fiberDebug(\"fiber=%s completed method=%s\", id, methodName);\n\n try {\n await this.onFiberComplete({\n id,\n methodName,\n payload,\n result\n });\n } catch (e) {\n console.error(\"Error in onFiberComplete:\", e);\n }\n });\n\n return;\n } catch (e) {\n const now = Date.now();\n const currentFiber = this._getRawFiber(id);\n const newRetryCount = (currentFiber?.retry_count ?? 0) + 1;\n\n if (newRetryCount > maxRetries) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n this.sql`\n UPDATE cf_agents_fibers\n SET status = 'failed',\n error = ${errorMsg},\n retry_count = ${newRetryCount},\n updated_at = ${now}\n WHERE id = ${id}\n `;\n this._fiberDebug(\n \"fiber=%s failed after %d retries: %s\",\n id,\n newRetryCount,\n errorMsg\n );\n return;\n }\n\n this.sql`\n UPDATE cf_agents_fibers\n SET retry_count = ${newRetryCount}, updated_at = ${now}\n WHERE id = ${id}\n `;\n this._fiberDebug(\n \"fiber=%s retrying (%d/%d)\",\n id,\n newRetryCount,\n maxRetries\n );\n continue;\n }\n }\n } finally {\n this._fiberActiveFibers.delete(id);\n disposeKeepAlive();\n }\n }\n\n /** @internal */ async _checkInterruptedFibers(): Promise<void> {\n if (this._fiberRecoveryInProgress) return;\n this._fiberRecoveryInProgress = true;\n\n try {\n const runningFibers = this.sql<RawFiberRow>`\n SELECT * FROM cf_agents_fibers\n WHERE status = 'running'\n ORDER BY created_at ASC\n `;\n\n if (!runningFibers || runningFibers.length === 0) return;\n\n const interrupted: FiberRecoveryContext[] = [];\n\n for (const fiber of runningFibers) {\n if (this._fiberActiveFibers.has(fiber.id)) continue;\n\n const newRetryCount = fiber.retry_count + 1;\n const now = Date.now();\n\n if (newRetryCount > fiber.max_retries) {\n this.sql`\n UPDATE cf_agents_fibers\n SET status = 'failed',\n error = 'max retries exceeded (eviction recovery)',\n retry_count = ${newRetryCount},\n updated_at = ${now}\n WHERE id = ${fiber.id}\n `;\n this._fiberDebug(\n \"fiber=%s max retries exceeded on recovery\",\n fiber.id\n );\n } else {\n this.sql`\n UPDATE cf_agents_fibers\n SET status = 'interrupted',\n retry_count = ${newRetryCount},\n updated_at = ${now}\n WHERE id = ${fiber.id}\n `;\n\n interrupted.push({\n id: fiber.id,\n methodName: fiber.callback,\n payload: this._safeJsonParse(fiber.payload),\n snapshot: this._safeJsonParse(fiber.snapshot),\n retryCount: newRetryCount\n });\n }\n }\n\n if (interrupted.length > 0) {\n this._fiberDebug(\n \"recovering %d interrupted fibers\",\n interrupted.length\n );\n\n try {\n await this.onFibersRecovered(interrupted);\n } catch (e) {\n console.error(\"Error in onFibersRecovered:\", e);\n }\n }\n } finally {\n this._fiberRecoveryInProgress = false;\n }\n }\n\n /** @internal */ _maybeCleanupFibers() {\n const now = Date.now();\n if (now - this._fiberLastCleanupTime < FIBER_CLEANUP_INTERVAL_MS) {\n return;\n }\n this._fiberLastCleanupTime = now;\n\n const completedCutoff = now - FIBER_CLEANUP_COMPLETED_MS;\n const failedCutoff = now - FIBER_CLEANUP_FAILED_MS;\n\n this.sql`\n DELETE FROM cf_agents_fibers\n WHERE (status = 'completed' AND completed_at < ${completedCutoff})\n OR (status = 'failed' AND updated_at < ${failedCutoff})\n OR (status = 'cancelled' AND updated_at < ${completedCutoff})\n `;\n\n this._fiberDebug(\n \"cleanup: checked for old completed/failed/cancelled fibers\"\n );\n }\n }\n\n return FiberAgent as unknown as FiberAgentClass;\n}\n\n// ── Return type ──────────────────────────────────────────────────────\n//\n// Explicit generic constructor so consumers can write:\n// const FA = withFibers(Agent);\n// class MyAgent extends FA<Env> { ... }\n// and still see all FiberMethods on `this`.\n//\n// We define this as a standalone constructor (not intersected with\n// typeof Agent) to avoid overload-resolution issues where TypeScript\n// would pick Agent's constructor and lose FiberMethods.\n\ntype FiberAgentClass = {\n new <\n Env extends Cloudflare.Env = Cloudflare.Env,\n State = unknown,\n Props extends Record<string, unknown> = Record<string, unknown>\n >(\n ctx: DurableObjectState,\n env: Env\n ): Agent<Env, State, Props> & FiberMethods;\n};\n\n// ── FiberMethods interface ───────────────────────────────────────────\n//\n// Describes the methods added by the mixin. Exported so users can\n// reference the shape in their own type annotations if needed.\n\nexport interface FiberMethods {\n spawnFiber(\n methodName: string,\n payload?: unknown,\n options?: { maxRetries?: number }\n ): string;\n stashFiber(data: unknown): void;\n cancelFiber(fiberId: string): boolean;\n getFiber(fiberId: string): FiberState | null;\n restartFiber(fiberId: string): void;\n checkFibers(): Promise<void>;\n onFiberComplete(ctx: FiberCompleteContext): void | Promise<void>;\n onFiberRecovered(ctx: FiberRecoveryContext): void | Promise<void>;\n onFibersRecovered(fibers: FiberRecoveryContext[]): Promise<void>;\n\n /** @internal */ _fiberActiveFibers: Set<string>;\n /** @internal */ _fiberRecoveryInProgress: boolean;\n /** @internal */ _fiberLastCleanupTime: number;\n}\n"],"mappings":"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"}
|