agents 0.7.5 → 0.7.6
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/{client-CgXIwdcc.js → client-K8Z-u76l.js} +38 -12
- package/dist/client-K8Z-u76l.js.map +1 -0
- package/dist/client.js.map +1 -1
- package/dist/experimental/forever.d.ts +1 -1
- package/dist/experimental/forever.js +1 -1
- package/dist/experimental/forever.js.map +1 -1
- package/dist/experimental/memory/session/index.js.map +1 -1
- package/dist/experimental/workspace.js +3 -1
- package/dist/experimental/workspace.js.map +1 -1
- package/dist/{index-p1XLNvwQ.d.ts → index-WBy5hmm3.d.ts} +183 -62
- package/dist/index.d.ts +13 -9
- package/dist/index.js +280 -135
- package/dist/index.js.map +1 -1
- package/dist/mcp/client.d.ts +1 -1
- package/dist/mcp/client.js +1 -1
- package/dist/mcp/index.d.ts +1 -1
- package/dist/mcp/index.js +59 -49
- package/dist/mcp/index.js.map +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/workflows.d.ts +1 -1
- package/dist/workflows.js +1 -1
- package/dist/workflows.js.map +1 -1
- package/package.json +12 -10
- package/dist/client-CgXIwdcc.js.map +0 -1
- package/dist/experimental/sub-agent.d.ts +0 -205
- package/dist/experimental/sub-agent.js +0 -190
- package/dist/experimental/sub-agent.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { camelCaseToKebabCase } from "./utils.js";
|
|
|
3
3
|
import { createHeaderBasedEmailResolver, signAgentHeaders } from "./email.js";
|
|
4
4
|
import { __DO_NOT_USE_WILL_BREAK__agentContext } from "./internal_context.js";
|
|
5
5
|
import { isErrorRetryable, tryN, validateRetryOptions } from "./retries.js";
|
|
6
|
-
import { o as RPC_DO_PREFIX, r as MCPConnectionState, s as DisposableStore, t as MCPClientManager } from "./client-
|
|
6
|
+
import { o as RPC_DO_PREFIX, r as MCPConnectionState, s as DisposableStore, t as MCPClientManager } from "./client-K8Z-u76l.js";
|
|
7
7
|
import { DurableObjectOAuthClientProvider } from "./mcp/do-oauth-client-provider.js";
|
|
8
8
|
import { genericObservability } from "./observability/index.js";
|
|
9
9
|
import { parseCronExpression } from "cron-schedule";
|
|
@@ -65,6 +65,14 @@ function getNextCronTime(cron) {
|
|
|
65
65
|
return parseCronExpression(cron).getNextDate();
|
|
66
66
|
}
|
|
67
67
|
const KEEP_ALIVE_INTERVAL_MS = 3e4;
|
|
68
|
+
/**
|
|
69
|
+
* Schema version for the Agent's internal SQLite tables.
|
|
70
|
+
* Bump this when adding new tables, columns, or migrations.
|
|
71
|
+
* The constructor stores this as a row in cf_agents_state and checks it
|
|
72
|
+
* on wake to skip DDL on established DOs.
|
|
73
|
+
*/
|
|
74
|
+
const CURRENT_SCHEMA_VERSION = 1;
|
|
75
|
+
const SCHEMA_VERSION_ROW_ID = "cf_schema_version";
|
|
68
76
|
const STATE_ROW_ID = "cf_state_row_id";
|
|
69
77
|
const STATE_WAS_CHANGED = "cf_state_was_changed";
|
|
70
78
|
const DEFAULT_STATE = {};
|
|
@@ -83,7 +91,11 @@ const CF_NO_PROTOCOL_KEY = "_cf_no_protocol";
|
|
|
83
91
|
* The set of all internal keys stored in connection state that must be
|
|
84
92
|
* hidden from user code and preserved across setState calls.
|
|
85
93
|
*/
|
|
86
|
-
const CF_INTERNAL_KEYS = new Set([
|
|
94
|
+
const CF_INTERNAL_KEYS = new Set([
|
|
95
|
+
CF_READONLY_KEY,
|
|
96
|
+
CF_NO_PROTOCOL_KEY,
|
|
97
|
+
"_cf_voiceInCall"
|
|
98
|
+
]);
|
|
87
99
|
/** Check if a raw connection state object contains any internal keys. */
|
|
88
100
|
function rawHasInternalKeys(raw) {
|
|
89
101
|
for (const key of Object.keys(raw)) if (CF_INTERNAL_KEYS.has(key)) return true;
|
|
@@ -205,14 +217,11 @@ var Agent = class Agent extends Server {
|
|
|
205
217
|
*/
|
|
206
218
|
get state() {
|
|
207
219
|
if (this._state !== DEFAULT_STATE) return this._state;
|
|
208
|
-
const wasChanged = this.sql`
|
|
209
|
-
SELECT state FROM cf_agents_state WHERE id = ${STATE_WAS_CHANGED}
|
|
210
|
-
`;
|
|
211
220
|
const result = this.sql`
|
|
212
221
|
SELECT state FROM cf_agents_state WHERE id = ${STATE_ROW_ID}
|
|
213
222
|
`;
|
|
214
|
-
if (
|
|
215
|
-
const state = result[0]
|
|
223
|
+
if (result.length > 0) {
|
|
224
|
+
const state = result[0].state;
|
|
216
225
|
try {
|
|
217
226
|
this._state = JSON.parse(state);
|
|
218
227
|
} catch (e) {
|
|
@@ -222,7 +231,6 @@ var Agent = class Agent extends Server {
|
|
|
222
231
|
this._setStateInternal(this.initialState);
|
|
223
232
|
} else {
|
|
224
233
|
this.sql`DELETE FROM cf_agents_state WHERE id = ${STATE_ROW_ID}`;
|
|
225
|
-
this.sql`DELETE FROM cf_agents_state WHERE id = ${STATE_WAS_CHANGED}`;
|
|
226
234
|
return;
|
|
227
235
|
}
|
|
228
236
|
}
|
|
@@ -232,9 +240,6 @@ var Agent = class Agent extends Server {
|
|
|
232
240
|
this._setStateInternal(this.initialState);
|
|
233
241
|
return this.initialState;
|
|
234
242
|
}
|
|
235
|
-
static {
|
|
236
|
-
this.options = { hibernate: true };
|
|
237
|
-
}
|
|
238
243
|
get _resolvedOptions() {
|
|
239
244
|
if (this._cachedOptions) return this._cachedOptions;
|
|
240
245
|
const ctor = this.constructor;
|
|
@@ -280,6 +285,138 @@ var Agent = class Agent extends Server {
|
|
|
280
285
|
throw new SqlError(query, e);
|
|
281
286
|
}
|
|
282
287
|
}
|
|
288
|
+
/**
|
|
289
|
+
* Create all internal tables and run migrations if needed.
|
|
290
|
+
* Called by the constructor on every wake. Idempotent — skips DDL when
|
|
291
|
+
* the stored schema version matches CURRENT_SCHEMA_VERSION.
|
|
292
|
+
*
|
|
293
|
+
* Protected so that test agents can re-run the real migration path
|
|
294
|
+
* after manipulating DB state (since ctx.abort() is unavailable in
|
|
295
|
+
* local dev and the constructor only runs once per DO instance).
|
|
296
|
+
*/
|
|
297
|
+
_ensureSchema() {
|
|
298
|
+
this.sql`
|
|
299
|
+
CREATE TABLE IF NOT EXISTS cf_agents_state (
|
|
300
|
+
id TEXT PRIMARY KEY NOT NULL,
|
|
301
|
+
state TEXT
|
|
302
|
+
)
|
|
303
|
+
`;
|
|
304
|
+
const versionRow = this.sql`
|
|
305
|
+
SELECT state FROM cf_agents_state WHERE id = ${SCHEMA_VERSION_ROW_ID}
|
|
306
|
+
`;
|
|
307
|
+
if ((versionRow.length > 0 ? Number(versionRow[0].state) : 0) < CURRENT_SCHEMA_VERSION) {
|
|
308
|
+
this.sql`
|
|
309
|
+
CREATE TABLE IF NOT EXISTS cf_agents_mcp_servers (
|
|
310
|
+
id TEXT PRIMARY KEY NOT NULL,
|
|
311
|
+
name TEXT NOT NULL,
|
|
312
|
+
server_url TEXT NOT NULL,
|
|
313
|
+
callback_url TEXT NOT NULL,
|
|
314
|
+
client_id TEXT,
|
|
315
|
+
auth_url TEXT,
|
|
316
|
+
server_options TEXT
|
|
317
|
+
)
|
|
318
|
+
`;
|
|
319
|
+
this.sql`
|
|
320
|
+
CREATE TABLE IF NOT EXISTS cf_agents_queues (
|
|
321
|
+
id TEXT PRIMARY KEY NOT NULL,
|
|
322
|
+
payload TEXT,
|
|
323
|
+
callback TEXT,
|
|
324
|
+
created_at INTEGER DEFAULT (unixepoch())
|
|
325
|
+
)
|
|
326
|
+
`;
|
|
327
|
+
this.sql`
|
|
328
|
+
CREATE TABLE IF NOT EXISTS cf_agents_schedules (
|
|
329
|
+
id TEXT PRIMARY KEY NOT NULL DEFAULT (randomblob(9)),
|
|
330
|
+
callback TEXT,
|
|
331
|
+
payload TEXT,
|
|
332
|
+
type TEXT NOT NULL CHECK(type IN ('scheduled', 'delayed', 'cron', 'interval')),
|
|
333
|
+
time INTEGER,
|
|
334
|
+
delayInSeconds INTEGER,
|
|
335
|
+
cron TEXT,
|
|
336
|
+
intervalSeconds INTEGER,
|
|
337
|
+
running INTEGER DEFAULT 0,
|
|
338
|
+
created_at INTEGER DEFAULT (unixepoch()),
|
|
339
|
+
execution_started_at INTEGER,
|
|
340
|
+
retry_options TEXT
|
|
341
|
+
)
|
|
342
|
+
`;
|
|
343
|
+
const addColumnIfNotExists = (sql) => {
|
|
344
|
+
try {
|
|
345
|
+
this.ctx.storage.sql.exec(sql);
|
|
346
|
+
} catch (e) {
|
|
347
|
+
if (!(e instanceof Error ? e.message : String(e)).toLowerCase().includes("duplicate column")) throw e;
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
addColumnIfNotExists("ALTER TABLE cf_agents_schedules ADD COLUMN intervalSeconds INTEGER");
|
|
351
|
+
addColumnIfNotExists("ALTER TABLE cf_agents_schedules ADD COLUMN running INTEGER DEFAULT 0");
|
|
352
|
+
addColumnIfNotExists("ALTER TABLE cf_agents_schedules ADD COLUMN execution_started_at INTEGER");
|
|
353
|
+
addColumnIfNotExists("ALTER TABLE cf_agents_schedules ADD COLUMN retry_options TEXT");
|
|
354
|
+
addColumnIfNotExists("ALTER TABLE cf_agents_queues ADD COLUMN retry_options TEXT");
|
|
355
|
+
{
|
|
356
|
+
const rows = this.ctx.storage.sql.exec("SELECT sql FROM sqlite_master WHERE type='table' AND name='cf_agents_schedules'").toArray();
|
|
357
|
+
if (rows.length > 0) {
|
|
358
|
+
if (!String(rows[0].sql).includes("'interval'")) {
|
|
359
|
+
this.ctx.storage.sql.exec("DROP TABLE IF EXISTS cf_agents_schedules_new");
|
|
360
|
+
this.ctx.storage.sql.exec(`
|
|
361
|
+
CREATE TABLE cf_agents_schedules_new (
|
|
362
|
+
id TEXT PRIMARY KEY NOT NULL DEFAULT (randomblob(9)),
|
|
363
|
+
callback TEXT,
|
|
364
|
+
payload TEXT,
|
|
365
|
+
type TEXT NOT NULL CHECK(type IN ('scheduled', 'delayed', 'cron', 'interval')),
|
|
366
|
+
time INTEGER,
|
|
367
|
+
delayInSeconds INTEGER,
|
|
368
|
+
cron TEXT,
|
|
369
|
+
intervalSeconds INTEGER,
|
|
370
|
+
running INTEGER DEFAULT 0,
|
|
371
|
+
created_at INTEGER DEFAULT (unixepoch()),
|
|
372
|
+
execution_started_at INTEGER,
|
|
373
|
+
retry_options TEXT
|
|
374
|
+
)
|
|
375
|
+
`);
|
|
376
|
+
this.ctx.storage.sql.exec(`
|
|
377
|
+
INSERT INTO cf_agents_schedules_new
|
|
378
|
+
(id, callback, payload, type, time, delayInSeconds, cron,
|
|
379
|
+
intervalSeconds, running, created_at, execution_started_at, retry_options)
|
|
380
|
+
SELECT id, callback, payload, type, time, delayInSeconds, cron,
|
|
381
|
+
intervalSeconds, running, created_at, execution_started_at, retry_options
|
|
382
|
+
FROM cf_agents_schedules
|
|
383
|
+
`);
|
|
384
|
+
this.ctx.storage.sql.exec("DROP TABLE cf_agents_schedules");
|
|
385
|
+
this.ctx.storage.sql.exec("ALTER TABLE cf_agents_schedules_new RENAME TO cf_agents_schedules");
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
this.sql`
|
|
390
|
+
CREATE TABLE IF NOT EXISTS cf_agents_workflows (
|
|
391
|
+
id TEXT PRIMARY KEY NOT NULL,
|
|
392
|
+
workflow_id TEXT NOT NULL UNIQUE,
|
|
393
|
+
workflow_name TEXT NOT NULL,
|
|
394
|
+
status TEXT NOT NULL CHECK(status IN (
|
|
395
|
+
'queued', 'running', 'paused', 'errored',
|
|
396
|
+
'terminated', 'complete', 'waiting',
|
|
397
|
+
'waitingForPause', 'unknown'
|
|
398
|
+
)),
|
|
399
|
+
metadata TEXT,
|
|
400
|
+
error_name TEXT,
|
|
401
|
+
error_message TEXT,
|
|
402
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
403
|
+
updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
404
|
+
completed_at INTEGER
|
|
405
|
+
)
|
|
406
|
+
`;
|
|
407
|
+
this.sql`
|
|
408
|
+
CREATE INDEX IF NOT EXISTS idx_workflows_status ON cf_agents_workflows(status)
|
|
409
|
+
`;
|
|
410
|
+
this.sql`
|
|
411
|
+
CREATE INDEX IF NOT EXISTS idx_workflows_name ON cf_agents_workflows(workflow_name)
|
|
412
|
+
`;
|
|
413
|
+
this.ctx.storage.sql.exec("DELETE FROM cf_agents_state WHERE id = ?", STATE_WAS_CHANGED);
|
|
414
|
+
this.sql`
|
|
415
|
+
INSERT OR REPLACE INTO cf_agents_state (id, state)
|
|
416
|
+
VALUES (${SCHEMA_VERSION_ROW_ID}, ${String(CURRENT_SCHEMA_VERSION)})
|
|
417
|
+
`;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
283
420
|
constructor(ctx, env) {
|
|
284
421
|
super(ctx, env);
|
|
285
422
|
this._state = DEFAULT_STATE;
|
|
@@ -287,6 +424,7 @@ var Agent = class Agent extends Server {
|
|
|
287
424
|
this._destroyed = false;
|
|
288
425
|
this._rawStateAccessors = /* @__PURE__ */ new WeakMap();
|
|
289
426
|
this._persistenceHookMode = "none";
|
|
427
|
+
this._isFacet = false;
|
|
290
428
|
this._ParentClass = Object.getPrototypeOf(this).constructor;
|
|
291
429
|
this.initialState = DEFAULT_STATE;
|
|
292
430
|
this.observability = genericObservability;
|
|
@@ -295,117 +433,7 @@ var Agent = class Agent extends Server {
|
|
|
295
433
|
this._autoWrapCustomMethods();
|
|
296
434
|
wrappedClasses.add(this.constructor);
|
|
297
435
|
}
|
|
298
|
-
this.
|
|
299
|
-
CREATE TABLE IF NOT EXISTS cf_agents_mcp_servers (
|
|
300
|
-
id TEXT PRIMARY KEY NOT NULL,
|
|
301
|
-
name TEXT NOT NULL,
|
|
302
|
-
server_url TEXT NOT NULL,
|
|
303
|
-
callback_url TEXT NOT NULL,
|
|
304
|
-
client_id TEXT,
|
|
305
|
-
auth_url TEXT,
|
|
306
|
-
server_options TEXT
|
|
307
|
-
)
|
|
308
|
-
`;
|
|
309
|
-
this.sql`
|
|
310
|
-
CREATE TABLE IF NOT EXISTS cf_agents_state (
|
|
311
|
-
id TEXT PRIMARY KEY NOT NULL,
|
|
312
|
-
state TEXT
|
|
313
|
-
)
|
|
314
|
-
`;
|
|
315
|
-
this.sql`
|
|
316
|
-
CREATE TABLE IF NOT EXISTS cf_agents_queues (
|
|
317
|
-
id TEXT PRIMARY KEY NOT NULL,
|
|
318
|
-
payload TEXT,
|
|
319
|
-
callback TEXT,
|
|
320
|
-
created_at INTEGER DEFAULT (unixepoch())
|
|
321
|
-
)
|
|
322
|
-
`;
|
|
323
|
-
this.sql`
|
|
324
|
-
CREATE TABLE IF NOT EXISTS cf_agents_schedules (
|
|
325
|
-
id TEXT PRIMARY KEY NOT NULL DEFAULT (randomblob(9)),
|
|
326
|
-
callback TEXT,
|
|
327
|
-
payload TEXT,
|
|
328
|
-
type TEXT NOT NULL CHECK(type IN ('scheduled', 'delayed', 'cron', 'interval')),
|
|
329
|
-
time INTEGER,
|
|
330
|
-
delayInSeconds INTEGER,
|
|
331
|
-
cron TEXT,
|
|
332
|
-
intervalSeconds INTEGER,
|
|
333
|
-
running INTEGER DEFAULT 0,
|
|
334
|
-
created_at INTEGER DEFAULT (unixepoch()),
|
|
335
|
-
execution_started_at INTEGER,
|
|
336
|
-
retry_options TEXT
|
|
337
|
-
)
|
|
338
|
-
`;
|
|
339
|
-
const addColumnIfNotExists = (sql) => {
|
|
340
|
-
try {
|
|
341
|
-
this.ctx.storage.sql.exec(sql);
|
|
342
|
-
} catch (e) {
|
|
343
|
-
if (!(e instanceof Error ? e.message : String(e)).toLowerCase().includes("duplicate column")) throw e;
|
|
344
|
-
}
|
|
345
|
-
};
|
|
346
|
-
addColumnIfNotExists("ALTER TABLE cf_agents_schedules ADD COLUMN intervalSeconds INTEGER");
|
|
347
|
-
addColumnIfNotExists("ALTER TABLE cf_agents_schedules ADD COLUMN running INTEGER DEFAULT 0");
|
|
348
|
-
addColumnIfNotExists("ALTER TABLE cf_agents_schedules ADD COLUMN execution_started_at INTEGER");
|
|
349
|
-
addColumnIfNotExists("ALTER TABLE cf_agents_schedules ADD COLUMN retry_options TEXT");
|
|
350
|
-
addColumnIfNotExists("ALTER TABLE cf_agents_queues ADD COLUMN retry_options TEXT");
|
|
351
|
-
{
|
|
352
|
-
const rows = this.ctx.storage.sql.exec("SELECT sql FROM sqlite_master WHERE type='table' AND name='cf_agents_schedules'").toArray();
|
|
353
|
-
if (rows.length > 0) {
|
|
354
|
-
if (!String(rows[0].sql).includes("'interval'")) {
|
|
355
|
-
this.ctx.storage.sql.exec("DROP TABLE IF EXISTS cf_agents_schedules_new");
|
|
356
|
-
this.ctx.storage.sql.exec(`
|
|
357
|
-
CREATE TABLE cf_agents_schedules_new (
|
|
358
|
-
id TEXT PRIMARY KEY NOT NULL DEFAULT (randomblob(9)),
|
|
359
|
-
callback TEXT,
|
|
360
|
-
payload TEXT,
|
|
361
|
-
type TEXT NOT NULL CHECK(type IN ('scheduled', 'delayed', 'cron', 'interval')),
|
|
362
|
-
time INTEGER,
|
|
363
|
-
delayInSeconds INTEGER,
|
|
364
|
-
cron TEXT,
|
|
365
|
-
intervalSeconds INTEGER,
|
|
366
|
-
running INTEGER DEFAULT 0,
|
|
367
|
-
created_at INTEGER DEFAULT (unixepoch()),
|
|
368
|
-
execution_started_at INTEGER,
|
|
369
|
-
retry_options TEXT
|
|
370
|
-
)
|
|
371
|
-
`);
|
|
372
|
-
this.ctx.storage.sql.exec(`
|
|
373
|
-
INSERT INTO cf_agents_schedules_new
|
|
374
|
-
(id, callback, payload, type, time, delayInSeconds, cron,
|
|
375
|
-
intervalSeconds, running, created_at, execution_started_at, retry_options)
|
|
376
|
-
SELECT id, callback, payload, type, time, delayInSeconds, cron,
|
|
377
|
-
intervalSeconds, running, created_at, execution_started_at, retry_options
|
|
378
|
-
FROM cf_agents_schedules
|
|
379
|
-
`);
|
|
380
|
-
this.ctx.storage.sql.exec("DROP TABLE cf_agents_schedules");
|
|
381
|
-
this.ctx.storage.sql.exec("ALTER TABLE cf_agents_schedules_new RENAME TO cf_agents_schedules");
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
this.sql`
|
|
386
|
-
CREATE TABLE IF NOT EXISTS cf_agents_workflows (
|
|
387
|
-
id TEXT PRIMARY KEY NOT NULL,
|
|
388
|
-
workflow_id TEXT NOT NULL UNIQUE,
|
|
389
|
-
workflow_name TEXT NOT NULL,
|
|
390
|
-
status TEXT NOT NULL CHECK(status IN (
|
|
391
|
-
'queued', 'running', 'paused', 'errored',
|
|
392
|
-
'terminated', 'complete', 'waiting',
|
|
393
|
-
'waitingForPause', 'unknown'
|
|
394
|
-
)),
|
|
395
|
-
metadata TEXT,
|
|
396
|
-
error_name TEXT,
|
|
397
|
-
error_message TEXT,
|
|
398
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
399
|
-
updated_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
400
|
-
completed_at INTEGER
|
|
401
|
-
)
|
|
402
|
-
`;
|
|
403
|
-
this.sql`
|
|
404
|
-
CREATE INDEX IF NOT EXISTS idx_workflows_status ON cf_agents_workflows(status)
|
|
405
|
-
`;
|
|
406
|
-
this.sql`
|
|
407
|
-
CREATE INDEX IF NOT EXISTS idx_workflows_name ON cf_agents_workflows(workflow_name)
|
|
408
|
-
`;
|
|
436
|
+
this._ensureSchema();
|
|
409
437
|
this.mcp = new MCPClientManager(this._ParentClass.name, "0.0.1", {
|
|
410
438
|
storage: this.ctx.storage,
|
|
411
439
|
createAuthProvider: (callbackUrl) => this.createMcpOAuthProvider(callbackUrl)
|
|
@@ -605,6 +633,7 @@ var Agent = class Agent extends Server {
|
|
|
605
633
|
request: void 0,
|
|
606
634
|
email: void 0
|
|
607
635
|
}, async () => {
|
|
636
|
+
if (await this.ctx.storage.get("cf_agents_is_facet")) this._isFacet = true;
|
|
608
637
|
await this._tryCatch(async () => {
|
|
609
638
|
await this.mcp.restoreConnectionsFromStorage(this.name);
|
|
610
639
|
await this._restoreRpcMcpServers();
|
|
@@ -655,10 +684,6 @@ var Agent = class Agent extends Server {
|
|
|
655
684
|
this.sql`
|
|
656
685
|
INSERT OR REPLACE INTO cf_agents_state (id, state)
|
|
657
686
|
VALUES (${STATE_ROW_ID}, ${JSON.stringify(nextState)})
|
|
658
|
-
`;
|
|
659
|
-
this.sql`
|
|
660
|
-
INSERT OR REPLACE INTO cf_agents_state (id, state)
|
|
661
|
-
VALUES (${STATE_WAS_CHANGED}, ${JSON.stringify(true)})
|
|
662
687
|
`;
|
|
663
688
|
this._broadcastProtocol(JSON.stringify({
|
|
664
689
|
state: nextState,
|
|
@@ -786,6 +811,44 @@ var Agent = class Agent extends Server {
|
|
|
786
811
|
return !!this._rawStateAccessors.get(connection).getRaw()?.[CF_READONLY_KEY];
|
|
787
812
|
}
|
|
788
813
|
/**
|
|
814
|
+
* ⚠️ INTERNAL — DO NOT USE IN APPLICATION CODE. ⚠️
|
|
815
|
+
*
|
|
816
|
+
* Read an internal `_cf_`-prefixed flag from the raw connection state,
|
|
817
|
+
* bypassing the user-facing state wrapper that strips internal keys.
|
|
818
|
+
*
|
|
819
|
+
* This exists for framework mixins (e.g. voice) that need to persist
|
|
820
|
+
* flags in the connection attachment across hibernation. Application
|
|
821
|
+
* code should use `connection.state` and `connection.setState()` instead.
|
|
822
|
+
*
|
|
823
|
+
* @internal
|
|
824
|
+
*/
|
|
825
|
+
_unsafe_getConnectionFlag(connection, key) {
|
|
826
|
+
this._ensureConnectionWrapped(connection);
|
|
827
|
+
return this._rawStateAccessors.get(connection).getRaw()?.[key];
|
|
828
|
+
}
|
|
829
|
+
/**
|
|
830
|
+
* ⚠️ INTERNAL — DO NOT USE IN APPLICATION CODE. ⚠️
|
|
831
|
+
*
|
|
832
|
+
* Write an internal `_cf_`-prefixed flag to the raw connection state,
|
|
833
|
+
* bypassing the user-facing state wrapper. The key must be registered
|
|
834
|
+
* in `CF_INTERNAL_KEYS` so it is preserved across user `setState` calls
|
|
835
|
+
* and hidden from `connection.state`.
|
|
836
|
+
*
|
|
837
|
+
* @internal
|
|
838
|
+
*/
|
|
839
|
+
_unsafe_setConnectionFlag(connection, key, value) {
|
|
840
|
+
this._ensureConnectionWrapped(connection);
|
|
841
|
+
const accessors = this._rawStateAccessors.get(connection);
|
|
842
|
+
const raw = accessors.getRaw() ?? {};
|
|
843
|
+
if (value === void 0) {
|
|
844
|
+
const { [key]: _, ...rest } = raw;
|
|
845
|
+
accessors.setRaw(Object.keys(rest).length > 0 ? rest : null);
|
|
846
|
+
} else accessors.setRaw({
|
|
847
|
+
...raw,
|
|
848
|
+
[key]: value
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
/**
|
|
789
852
|
* Override this method to determine if a connection should be readonly on connect
|
|
790
853
|
* @param _connection The connection that is being established
|
|
791
854
|
* @param _ctx Connection context
|
|
@@ -1185,6 +1248,7 @@ var Agent = class Agent extends Server {
|
|
|
1185
1248
|
* @returns Schedule object representing the scheduled task
|
|
1186
1249
|
*/
|
|
1187
1250
|
async schedule(when, callback, payload, options) {
|
|
1251
|
+
if (this._isFacet) throw new Error("Scheduling is not supported in sub-agents. Schedule from the parent agent instead.");
|
|
1188
1252
|
const id = nanoid(9);
|
|
1189
1253
|
if (options?.retry) validateRetryOptions(options.retry, this._resolvedOptions.retry);
|
|
1190
1254
|
const retryJson = options?.retry ? JSON.stringify(options.retry) : null;
|
|
@@ -1281,6 +1345,7 @@ var Agent = class Agent extends Server {
|
|
|
1281
1345
|
* @returns Schedule object representing the scheduled task
|
|
1282
1346
|
*/
|
|
1283
1347
|
async scheduleEvery(intervalSeconds, callback, payload, options) {
|
|
1348
|
+
if (this._isFacet) throw new Error("Scheduling is not supported in sub-agents. Schedule from the parent agent instead.");
|
|
1284
1349
|
const MAX_INTERVAL_SECONDS = 720 * 60 * 60;
|
|
1285
1350
|
if (typeof intervalSeconds !== "number" || intervalSeconds <= 0) throw new Error("intervalSeconds must be a positive number");
|
|
1286
1351
|
if (intervalSeconds > MAX_INTERVAL_SECONDS) throw new Error(`intervalSeconds cannot exceed ${MAX_INTERVAL_SECONDS} seconds (30 days)`);
|
|
@@ -1388,6 +1453,7 @@ var Agent = class Agent extends Server {
|
|
|
1388
1453
|
* @returns true if the task was cancelled, false if the task was not found
|
|
1389
1454
|
*/
|
|
1390
1455
|
async cancelSchedule(id) {
|
|
1456
|
+
if (this._isFacet) throw new Error("Scheduling is not supported in sub-agents. Schedule from the parent agent instead.");
|
|
1391
1457
|
const schedule = this.getSchedule(id);
|
|
1392
1458
|
if (!schedule) return false;
|
|
1393
1459
|
this._emit("schedule:cancel", {
|
|
@@ -1419,6 +1485,7 @@ var Agent = class Agent extends Server {
|
|
|
1419
1485
|
* ```
|
|
1420
1486
|
*/
|
|
1421
1487
|
async keepAlive() {
|
|
1488
|
+
if (this._isFacet) throw new Error("keepAlive() is not supported in sub-agents. Use keepAlive() from the parent agent instead.");
|
|
1422
1489
|
const heartbeatSeconds = Math.ceil(KEEP_ALIVE_INTERVAL_MS / 1e3);
|
|
1423
1490
|
const schedule = await this.scheduleEvery(heartbeatSeconds, "_cf_keepAliveHeartbeat", void 0, { _idempotent: false });
|
|
1424
1491
|
let disposed = false;
|
|
@@ -1469,10 +1536,10 @@ var Agent = class Agent extends Server {
|
|
|
1469
1536
|
LIMIT 1
|
|
1470
1537
|
`;
|
|
1471
1538
|
if (!result) return;
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1539
|
+
let nextTimeMs = null;
|
|
1540
|
+
if (result.length > 0 && "time" in result[0]) nextTimeMs = result[0].time * 1e3;
|
|
1541
|
+
if (nextTimeMs !== null) await this.ctx.storage.setAlarm(nextTimeMs);
|
|
1542
|
+
else await this.ctx.storage.deleteAlarm();
|
|
1476
1543
|
}
|
|
1477
1544
|
/**
|
|
1478
1545
|
* Override PartyServer's onAlarm hook as a no-op.
|
|
@@ -1573,6 +1640,81 @@ var Agent = class Agent extends Server {
|
|
|
1573
1640
|
await this._scheduleNextAlarm();
|
|
1574
1641
|
}
|
|
1575
1642
|
/**
|
|
1643
|
+
* Marks this agent as running inside a facet (sub-agent). Once set,
|
|
1644
|
+
* scheduling methods throw a clear error instead of crashing on
|
|
1645
|
+
* `setAlarm()` (which is not supported in facets).
|
|
1646
|
+
* @internal
|
|
1647
|
+
*/
|
|
1648
|
+
async _cf_markAsFacet() {
|
|
1649
|
+
this._isFacet = true;
|
|
1650
|
+
await this.ctx.storage.put("cf_agents_is_facet", true);
|
|
1651
|
+
}
|
|
1652
|
+
/**
|
|
1653
|
+
* Get or create a named sub-agent — a child Durable Object (facet)
|
|
1654
|
+
* with its own isolated SQLite storage running on the same machine.
|
|
1655
|
+
*
|
|
1656
|
+
* The child class must extend `Agent` and be exported from the worker
|
|
1657
|
+
* entry point. The first call for a given name triggers the child's
|
|
1658
|
+
* `onStart()`. Subsequent calls return the existing instance.
|
|
1659
|
+
*
|
|
1660
|
+
* @experimental Requires the `"experimental"` compatibility flag.
|
|
1661
|
+
*
|
|
1662
|
+
* @param cls The Agent subclass (must be exported from the worker)
|
|
1663
|
+
* @param name Unique name for this child instance
|
|
1664
|
+
* @returns A typed RPC stub for calling methods on the child
|
|
1665
|
+
*
|
|
1666
|
+
* @example
|
|
1667
|
+
* ```typescript
|
|
1668
|
+
* const searcher = await this.subAgent(SearchAgent, "main-search");
|
|
1669
|
+
* const results = await searcher.search("cloudflare agents");
|
|
1670
|
+
* ```
|
|
1671
|
+
*/
|
|
1672
|
+
async subAgent(cls, name) {
|
|
1673
|
+
const ctx = this.ctx;
|
|
1674
|
+
if (!ctx.facets || !ctx.exports) throw new Error("subAgent() requires the \"experimental\" compatibility flag. Add it to your wrangler.jsonc compatibility_flags.");
|
|
1675
|
+
if (!ctx.exports[cls.name]) throw new Error(`Sub-agent class "${cls.name}" not found in worker exports. Make sure the class is exported from your worker entry point and that the export name matches the class name.`);
|
|
1676
|
+
const facetKey = `${cls.name}\0${name}`;
|
|
1677
|
+
const stub = ctx.facets.get(facetKey, () => ({ class: ctx.exports[cls.name] }));
|
|
1678
|
+
const req = new Request("http://dummy-example.cloudflare.com/cdn-cgi/partyserver/set-name/");
|
|
1679
|
+
req.headers.set("x-partykit-room", name);
|
|
1680
|
+
await stub.fetch(req).then((res) => res.text());
|
|
1681
|
+
await stub._cf_markAsFacet();
|
|
1682
|
+
return stub;
|
|
1683
|
+
}
|
|
1684
|
+
/**
|
|
1685
|
+
* Forcefully abort a running sub-agent. The child stops executing
|
|
1686
|
+
* immediately and will be restarted on next {@link subAgent} call.
|
|
1687
|
+
* Pending RPC calls receive the reason as an error.
|
|
1688
|
+
* Transitively aborts the child's own children.
|
|
1689
|
+
*
|
|
1690
|
+
* @experimental Requires the `"experimental"` compatibility flag.
|
|
1691
|
+
*
|
|
1692
|
+
* @param cls The Agent subclass used when creating the child
|
|
1693
|
+
* @param name Name of the child to abort
|
|
1694
|
+
* @param reason Error thrown to pending/future RPC callers
|
|
1695
|
+
*/
|
|
1696
|
+
abortSubAgent(cls, name, reason) {
|
|
1697
|
+
const ctx = this.ctx;
|
|
1698
|
+
if (!ctx.facets) throw new Error("abortSubAgent() requires the \"experimental\" compatibility flag.");
|
|
1699
|
+
const facetKey = `${cls.name}\0${name}`;
|
|
1700
|
+
ctx.facets.abort(facetKey, reason);
|
|
1701
|
+
}
|
|
1702
|
+
/**
|
|
1703
|
+
* Delete a sub-agent: abort it if running, then permanently wipe its
|
|
1704
|
+
* storage. Transitively deletes the child's own children.
|
|
1705
|
+
*
|
|
1706
|
+
* @experimental Requires the `"experimental"` compatibility flag.
|
|
1707
|
+
*
|
|
1708
|
+
* @param cls The Agent subclass used when creating the child
|
|
1709
|
+
* @param name Name of the child to delete
|
|
1710
|
+
*/
|
|
1711
|
+
deleteSubAgent(cls, name) {
|
|
1712
|
+
const ctx = this.ctx;
|
|
1713
|
+
if (!ctx.facets) throw new Error("deleteSubAgent() requires the \"experimental\" compatibility flag.");
|
|
1714
|
+
const facetKey = `${cls.name}\0${name}`;
|
|
1715
|
+
ctx.facets.delete(facetKey);
|
|
1716
|
+
}
|
|
1717
|
+
/**
|
|
1576
1718
|
* Destroy the Agent, removing all state and scheduled tasks
|
|
1577
1719
|
*/
|
|
1578
1720
|
async destroy() {
|
|
@@ -1581,7 +1723,7 @@ var Agent = class Agent extends Server {
|
|
|
1581
1723
|
this.sql`DROP TABLE IF EXISTS cf_agents_schedules`;
|
|
1582
1724
|
this.sql`DROP TABLE IF EXISTS cf_agents_queues`;
|
|
1583
1725
|
this.sql`DROP TABLE IF EXISTS cf_agents_workflows`;
|
|
1584
|
-
await this.ctx.storage.deleteAlarm();
|
|
1726
|
+
if (!this._isFacet) await this.ctx.storage.deleteAlarm();
|
|
1585
1727
|
await this.ctx.storage.deleteAll();
|
|
1586
1728
|
this._disposables.dispose();
|
|
1587
1729
|
await this.mcp.dispose();
|
|
@@ -2317,7 +2459,7 @@ var Agent = class Agent extends Server {
|
|
|
2317
2459
|
* @param workflowId - ID of the workflow
|
|
2318
2460
|
* @param progress - Typed progress data (default: DefaultProgress)
|
|
2319
2461
|
*/
|
|
2320
|
-
async onWorkflowProgress(
|
|
2462
|
+
async onWorkflowProgress(workflowName, workflowId, progress) {}
|
|
2321
2463
|
/**
|
|
2322
2464
|
* Called when a workflow completes successfully.
|
|
2323
2465
|
* Override to handle completion.
|
|
@@ -2326,7 +2468,7 @@ var Agent = class Agent extends Server {
|
|
|
2326
2468
|
* @param workflowId - ID of the workflow
|
|
2327
2469
|
* @param result - Optional result data
|
|
2328
2470
|
*/
|
|
2329
|
-
async onWorkflowComplete(
|
|
2471
|
+
async onWorkflowComplete(workflowName, workflowId, result) {}
|
|
2330
2472
|
/**
|
|
2331
2473
|
* Called when a workflow encounters an error.
|
|
2332
2474
|
* Override to handle errors.
|
|
@@ -2335,7 +2477,9 @@ var Agent = class Agent extends Server {
|
|
|
2335
2477
|
* @param workflowId - ID of the workflow
|
|
2336
2478
|
* @param error - Error message
|
|
2337
2479
|
*/
|
|
2338
|
-
async onWorkflowError(
|
|
2480
|
+
async onWorkflowError(workflowName, workflowId, error) {
|
|
2481
|
+
console.error(`Workflow error [${workflowName}/${workflowId}]: ${error}\nOverride onWorkflowError() in your Agent to handle workflow errors.`);
|
|
2482
|
+
}
|
|
2339
2483
|
/**
|
|
2340
2484
|
* Called when a workflow sends a custom event.
|
|
2341
2485
|
* Override to handle custom events.
|
|
@@ -2344,7 +2488,7 @@ var Agent = class Agent extends Server {
|
|
|
2344
2488
|
* @param workflowId - ID of the workflow
|
|
2345
2489
|
* @param event - Custom event payload
|
|
2346
2490
|
*/
|
|
2347
|
-
async onWorkflowEvent(
|
|
2491
|
+
async onWorkflowEvent(workflowName, workflowId, event) {}
|
|
2348
2492
|
/**
|
|
2349
2493
|
* Handle a workflow callback via RPC.
|
|
2350
2494
|
* @internal - Called by AgentWorkflow, do not call directly
|
|
@@ -2601,6 +2745,7 @@ var Agent = class Agent extends Server {
|
|
|
2601
2745
|
return Response.redirect(baseOrigin);
|
|
2602
2746
|
}
|
|
2603
2747
|
};
|
|
2748
|
+
Agent.options = { hibernate: true };
|
|
2604
2749
|
const wrappedClasses = /* @__PURE__ */ new Set();
|
|
2605
2750
|
/**
|
|
2606
2751
|
* Route a request to the appropriate Agent
|