agents 0.5.1 → 0.7.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-DnmmRjyv.d.ts +231 -0
- package/dist/{client-connection-CGMuV62J.js → client-connection-D3Wcd6Q6.js} +154 -23
- package/dist/client-connection-D3Wcd6Q6.js.map +1 -0
- package/dist/{client-storage-D633wI1S.d.ts → client-storage-tusTuoSF.d.ts} +262 -13
- package/dist/experimental/forever.d.ts +7 -18
- package/dist/experimental/forever.js +4 -38
- package/dist/experimental/forever.js.map +1 -1
- package/dist/experimental/memory/session/index.d.ts +251 -0
- package/dist/experimental/memory/session/index.js +385 -0
- package/dist/experimental/memory/session/index.js.map +1 -0
- package/dist/index.d.ts +84 -22
- package/dist/index.js +324 -250
- package/dist/index.js.map +1 -1
- package/dist/mcp/client.d.ts +51 -7
- package/dist/mcp/client.js +157 -32
- package/dist/mcp/client.js.map +1 -1
- package/dist/mcp/do-oauth-client-provider.js +1 -1
- package/dist/mcp/do-oauth-client-provider.js.map +1 -1
- package/dist/mcp/index.d.ts +27 -165
- package/dist/mcp/index.js +50 -10
- package/dist/mcp/index.js.map +1 -1
- package/dist/observability/index.d.ts +72 -6
- package/dist/observability/index.js +61 -16
- package/dist/observability/index.js.map +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/workflows.d.ts +15 -1
- package/dist/workflows.js +36 -3
- package/dist/workflows.js.map +1 -1
- package/package.json +16 -11
- package/dist/agent-B4_kEsdK.d.ts +0 -60
- package/dist/client-connection-CGMuV62J.js.map +0 -1
- package/dist/mcp-DA0kDE7K.d.ts +0 -61
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 {
|
|
6
|
+
import { a as RPC_DO_PREFIX, n as MCPConnectionState, s as DisposableStore } from "./client-connection-D3Wcd6Q6.js";
|
|
7
7
|
import { DurableObjectOAuthClientProvider } from "./mcp/do-oauth-client-provider.js";
|
|
8
8
|
import { MCPClientManager } from "./mcp/client.js";
|
|
9
9
|
import { genericObservability } from "./observability/index.js";
|
|
@@ -66,6 +66,7 @@ const unstable_callable = (metadata = {}) => {
|
|
|
66
66
|
function getNextCronTime(cron) {
|
|
67
67
|
return parseCronExpression(cron).getNextDate();
|
|
68
68
|
}
|
|
69
|
+
const KEEP_ALIVE_INTERVAL_MS = 3e4;
|
|
69
70
|
const STATE_ROW_ID = "cf_state_row_id";
|
|
70
71
|
const STATE_WAS_CHANGED = "cf_state_was_changed";
|
|
71
72
|
const DEFAULT_STATE = {};
|
|
@@ -106,12 +107,31 @@ function extractInternalFlags(raw) {
|
|
|
106
107
|
for (const key of Object.keys(raw)) if (CF_INTERNAL_KEYS.has(key)) result[key] = raw[key];
|
|
107
108
|
return result;
|
|
108
109
|
}
|
|
110
|
+
/** Max length for error strings broadcast to clients. */
|
|
111
|
+
const MAX_ERROR_STRING_LENGTH = 500;
|
|
112
|
+
/**
|
|
113
|
+
* Sanitize an error string before broadcasting to clients.
|
|
114
|
+
* MCP error strings may contain untrusted content from external OAuth
|
|
115
|
+
* providers — truncate and strip control characters to limit XSS risk.
|
|
116
|
+
*/
|
|
117
|
+
const CONTROL_CHAR_RE = /* @__PURE__ */ new RegExp("[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F]", "g");
|
|
118
|
+
function sanitizeErrorString(error) {
|
|
119
|
+
if (error === null) return null;
|
|
120
|
+
let sanitized = error.replace(CONTROL_CHAR_RE, "");
|
|
121
|
+
if (sanitized.length > MAX_ERROR_STRING_LENGTH) sanitized = sanitized.substring(0, MAX_ERROR_STRING_LENGTH) + "...";
|
|
122
|
+
return sanitized;
|
|
123
|
+
}
|
|
109
124
|
/**
|
|
110
125
|
* Tracks which agent constructors have already emitted the onStateUpdate
|
|
111
126
|
* deprecation warning, so it fires at most once per class.
|
|
112
127
|
*/
|
|
113
128
|
const _onStateUpdateWarnedClasses = /* @__PURE__ */ new WeakSet();
|
|
114
129
|
/**
|
|
130
|
+
* Tracks which agent constructors have already emitted the
|
|
131
|
+
* sendIdentityOnConnect deprecation warning, so it fires at most once per class.
|
|
132
|
+
*/
|
|
133
|
+
const _sendIdentityWarnedClasses = /* @__PURE__ */ new WeakSet();
|
|
134
|
+
/**
|
|
115
135
|
* Default options for Agent configuration.
|
|
116
136
|
* Child classes can override specific options without spreading.
|
|
117
137
|
*/
|
|
@@ -234,6 +254,17 @@ var Agent = class Agent extends Server {
|
|
|
234
254
|
return this._cachedOptions;
|
|
235
255
|
}
|
|
236
256
|
/**
|
|
257
|
+
* Emit an observability event with auto-generated timestamp.
|
|
258
|
+
* @internal
|
|
259
|
+
*/
|
|
260
|
+
_emit(type, payload = {}) {
|
|
261
|
+
this.observability?.emit({
|
|
262
|
+
type,
|
|
263
|
+
payload,
|
|
264
|
+
timestamp: Date.now()
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
237
268
|
* Execute SQL queries against the Agent's database
|
|
238
269
|
* @template T Type of the returned rows
|
|
239
270
|
* @param strings SQL query template strings
|
|
@@ -246,7 +277,7 @@ var Agent = class Agent extends Server {
|
|
|
246
277
|
query = strings.reduce((acc, str, i) => acc + str + (i < values.length ? "?" : ""), "");
|
|
247
278
|
return [...this.ctx.storage.sql.exec(query, ...values)];
|
|
248
279
|
} catch (e) {
|
|
249
|
-
throw
|
|
280
|
+
throw new SqlError(query, e);
|
|
250
281
|
}
|
|
251
282
|
}
|
|
252
283
|
constructor(ctx, env) {
|
|
@@ -291,29 +322,17 @@ var Agent = class Agent extends Server {
|
|
|
291
322
|
const { maxAttempts, baseDelayMs, maxDelayMs } = resolveRetryConfig(parseRetryOptions(row), this._resolvedOptions.retry);
|
|
292
323
|
const parsedPayload = JSON.parse(row.payload);
|
|
293
324
|
try {
|
|
294
|
-
this.
|
|
295
|
-
|
|
296
|
-
id:
|
|
297
|
-
|
|
298
|
-
callback: row.callback,
|
|
299
|
-
id: row.id
|
|
300
|
-
},
|
|
301
|
-
timestamp: Date.now(),
|
|
302
|
-
type: "schedule:execute"
|
|
303
|
-
}, this.ctx);
|
|
325
|
+
this._emit("schedule:execute", {
|
|
326
|
+
callback: row.callback,
|
|
327
|
+
id: row.id
|
|
328
|
+
});
|
|
304
329
|
await tryN(maxAttempts, async (attempt) => {
|
|
305
|
-
if (attempt > 1) this.
|
|
306
|
-
|
|
307
|
-
id:
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
attempt,
|
|
312
|
-
maxAttempts
|
|
313
|
-
},
|
|
314
|
-
timestamp: Date.now(),
|
|
315
|
-
type: "schedule:retry"
|
|
316
|
-
}, this.ctx);
|
|
330
|
+
if (attempt > 1) this._emit("schedule:retry", {
|
|
331
|
+
callback: row.callback,
|
|
332
|
+
id: row.id,
|
|
333
|
+
attempt,
|
|
334
|
+
maxAttempts
|
|
335
|
+
});
|
|
317
336
|
await callback.bind(this)(parsedPayload, row);
|
|
318
337
|
}, {
|
|
319
338
|
baseDelayMs,
|
|
@@ -321,6 +340,12 @@ var Agent = class Agent extends Server {
|
|
|
321
340
|
});
|
|
322
341
|
} catch (e) {
|
|
323
342
|
console.error(`error executing callback "${row.callback}" after ${maxAttempts} attempts`, e);
|
|
343
|
+
this._emit("schedule:error", {
|
|
344
|
+
callback: row.callback,
|
|
345
|
+
id: row.id,
|
|
346
|
+
error: e instanceof Error ? e.message : String(e),
|
|
347
|
+
attempts: maxAttempts
|
|
348
|
+
});
|
|
324
349
|
try {
|
|
325
350
|
await this.onError(e);
|
|
326
351
|
} catch {}
|
|
@@ -509,35 +534,27 @@ var Agent = class Agent extends Server {
|
|
|
509
534
|
const metadata = callableMetadata.get(methodFn);
|
|
510
535
|
if (metadata?.streaming) {
|
|
511
536
|
const stream = new StreamingResponse(connection, id);
|
|
512
|
-
this.
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
method,
|
|
517
|
-
streaming: true
|
|
518
|
-
},
|
|
519
|
-
timestamp: Date.now(),
|
|
520
|
-
type: "rpc"
|
|
521
|
-
}, this.ctx);
|
|
537
|
+
this._emit("rpc", {
|
|
538
|
+
method,
|
|
539
|
+
streaming: true
|
|
540
|
+
});
|
|
522
541
|
try {
|
|
523
542
|
await methodFn.apply(this, [stream, ...args]);
|
|
524
543
|
} catch (err) {
|
|
525
544
|
console.error(`Error in streaming method "${method}":`, err);
|
|
545
|
+
this._emit("rpc:error", {
|
|
546
|
+
method,
|
|
547
|
+
error: err instanceof Error ? err.message : String(err)
|
|
548
|
+
});
|
|
526
549
|
if (!stream.isClosed) stream.error(err instanceof Error ? err.message : String(err));
|
|
527
550
|
}
|
|
528
551
|
return;
|
|
529
552
|
}
|
|
530
553
|
const result = await methodFn.apply(this, args);
|
|
531
|
-
this.
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
method,
|
|
536
|
-
streaming: metadata?.streaming
|
|
537
|
-
},
|
|
538
|
-
timestamp: Date.now(),
|
|
539
|
-
type: "rpc"
|
|
540
|
-
}, this.ctx);
|
|
554
|
+
this._emit("rpc", {
|
|
555
|
+
method,
|
|
556
|
+
streaming: metadata?.streaming
|
|
557
|
+
});
|
|
541
558
|
const response = {
|
|
542
559
|
done: true,
|
|
543
560
|
id,
|
|
@@ -555,6 +572,10 @@ var Agent = class Agent extends Server {
|
|
|
555
572
|
};
|
|
556
573
|
connection.send(JSON.stringify(response));
|
|
557
574
|
console.error("RPC error:", e);
|
|
575
|
+
this._emit("rpc:error", {
|
|
576
|
+
method: parsed.method,
|
|
577
|
+
error: e instanceof Error ? e.message : String(e)
|
|
578
|
+
});
|
|
558
579
|
}
|
|
559
580
|
return;
|
|
560
581
|
}
|
|
@@ -572,11 +593,20 @@ var Agent = class Agent extends Server {
|
|
|
572
593
|
}, async () => {
|
|
573
594
|
if (this.shouldConnectionBeReadonly(connection, ctx)) this.setConnectionReadonly(connection, true);
|
|
574
595
|
if (this.shouldSendProtocolMessages(connection, ctx)) {
|
|
575
|
-
if (this._resolvedOptions.sendIdentityOnConnect)
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
596
|
+
if (this._resolvedOptions.sendIdentityOnConnect) {
|
|
597
|
+
const ctor = this.constructor;
|
|
598
|
+
if (ctor.options?.sendIdentityOnConnect === void 0 && !_sendIdentityWarnedClasses.has(ctor)) {
|
|
599
|
+
if (!new URL(ctx.request.url).pathname.includes(this.name)) {
|
|
600
|
+
_sendIdentityWarnedClasses.add(ctor);
|
|
601
|
+
console.warn(`[Agent] ${ctor.name}: sending instance name "${this.name}" to clients via sendIdentityOnConnect (the name is not visible in the URL with custom routing). If this name is sensitive, add \`static options = { sendIdentityOnConnect: false }\` to opt out. Set it to true to silence this message.`);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
connection.send(JSON.stringify({
|
|
605
|
+
name: this.name,
|
|
606
|
+
agent: camelCaseToKebabCase(this._ParentClass.name),
|
|
607
|
+
type: MessageType.CF_AGENT_IDENTITY
|
|
608
|
+
}));
|
|
609
|
+
}
|
|
580
610
|
if (this.state) connection.send(JSON.stringify({
|
|
581
611
|
state: this.state,
|
|
582
612
|
type: MessageType.CF_AGENT_STATE
|
|
@@ -586,13 +616,7 @@ var Agent = class Agent extends Server {
|
|
|
586
616
|
type: MessageType.CF_AGENT_MCP_SERVERS
|
|
587
617
|
}));
|
|
588
618
|
} else this._setConnectionNoProtocol(connection);
|
|
589
|
-
this.
|
|
590
|
-
displayMessage: "Connection established",
|
|
591
|
-
id: nanoid(),
|
|
592
|
-
payload: { connectionId: connection.id },
|
|
593
|
-
timestamp: Date.now(),
|
|
594
|
-
type: "connect"
|
|
595
|
-
}, this.ctx);
|
|
619
|
+
this._emit("connect", { connectionId: connection.id });
|
|
596
620
|
return this._tryCatch(() => _onConnect(connection, ctx));
|
|
597
621
|
});
|
|
598
622
|
};
|
|
@@ -606,6 +630,7 @@ var Agent = class Agent extends Server {
|
|
|
606
630
|
}, async () => {
|
|
607
631
|
await this._tryCatch(async () => {
|
|
608
632
|
await this.mcp.restoreConnectionsFromStorage(this.name);
|
|
633
|
+
await this._restoreRpcMcpServers();
|
|
609
634
|
this.broadcastMcpServers();
|
|
610
635
|
this._checkOrphanedWorkflows();
|
|
611
636
|
return _onStart(props);
|
|
@@ -671,13 +696,7 @@ var Agent = class Agent extends Server {
|
|
|
671
696
|
request,
|
|
672
697
|
email
|
|
673
698
|
}, async () => {
|
|
674
|
-
this.
|
|
675
|
-
displayMessage: "State updated",
|
|
676
|
-
id: nanoid(),
|
|
677
|
-
payload: {},
|
|
678
|
-
timestamp: Date.now(),
|
|
679
|
-
type: "state:update"
|
|
680
|
-
}, this.ctx);
|
|
699
|
+
this._emit("state:update");
|
|
681
700
|
await this._callStatePersistenceHook(nextState, source);
|
|
682
701
|
});
|
|
683
702
|
} catch (e) {
|
|
@@ -1079,18 +1098,12 @@ var Agent = class Agent extends Server {
|
|
|
1079
1098
|
const parsedPayload = JSON.parse(row.payload);
|
|
1080
1099
|
try {
|
|
1081
1100
|
await tryN(maxAttempts, async (attempt) => {
|
|
1082
|
-
if (attempt > 1) this.
|
|
1083
|
-
|
|
1084
|
-
id:
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
attempt,
|
|
1089
|
-
maxAttempts
|
|
1090
|
-
},
|
|
1091
|
-
timestamp: Date.now(),
|
|
1092
|
-
type: "queue:retry"
|
|
1093
|
-
}, this.ctx);
|
|
1101
|
+
if (attempt > 1) this._emit("queue:retry", {
|
|
1102
|
+
callback: row.callback,
|
|
1103
|
+
id: row.id,
|
|
1104
|
+
attempt,
|
|
1105
|
+
maxAttempts
|
|
1106
|
+
});
|
|
1094
1107
|
await callback.bind(this)(parsedPayload, row);
|
|
1095
1108
|
}, {
|
|
1096
1109
|
baseDelayMs,
|
|
@@ -1098,11 +1111,17 @@ var Agent = class Agent extends Server {
|
|
|
1098
1111
|
});
|
|
1099
1112
|
} catch (e) {
|
|
1100
1113
|
console.error(`queue callback "${row.callback}" failed after ${maxAttempts} attempts`, e);
|
|
1114
|
+
this._emit("queue:error", {
|
|
1115
|
+
callback: row.callback,
|
|
1116
|
+
id: row.id,
|
|
1117
|
+
error: e instanceof Error ? e.message : String(e),
|
|
1118
|
+
attempts: maxAttempts
|
|
1119
|
+
});
|
|
1101
1120
|
try {
|
|
1102
1121
|
await this.onError(e);
|
|
1103
1122
|
} catch {}
|
|
1104
1123
|
} finally {
|
|
1105
|
-
|
|
1124
|
+
this.dequeue(row.id);
|
|
1106
1125
|
}
|
|
1107
1126
|
});
|
|
1108
1127
|
}
|
|
@@ -1177,16 +1196,10 @@ var Agent = class Agent extends Server {
|
|
|
1177
1196
|
const id = nanoid(9);
|
|
1178
1197
|
if (options?.retry) validateRetryOptions(options.retry, this._resolvedOptions.retry);
|
|
1179
1198
|
const retryJson = options?.retry ? JSON.stringify(options.retry) : null;
|
|
1180
|
-
const emitScheduleCreate = (
|
|
1181
|
-
|
|
1182
|
-
id
|
|
1183
|
-
|
|
1184
|
-
callback,
|
|
1185
|
-
id
|
|
1186
|
-
},
|
|
1187
|
-
timestamp: Date.now(),
|
|
1188
|
-
type: "schedule:create"
|
|
1189
|
-
}, this.ctx);
|
|
1199
|
+
const emitScheduleCreate = () => this._emit("schedule:create", {
|
|
1200
|
+
callback,
|
|
1201
|
+
id
|
|
1202
|
+
});
|
|
1190
1203
|
if (typeof callback !== "string") throw new Error("Callback must be a string");
|
|
1191
1204
|
if (typeof this[callback] !== "function") throw new Error(`this.${callback} is not a function`);
|
|
1192
1205
|
if (when instanceof Date) {
|
|
@@ -1204,7 +1217,7 @@ var Agent = class Agent extends Server {
|
|
|
1204
1217
|
time: timestamp,
|
|
1205
1218
|
type: "scheduled"
|
|
1206
1219
|
};
|
|
1207
|
-
emitScheduleCreate(
|
|
1220
|
+
emitScheduleCreate();
|
|
1208
1221
|
return schedule;
|
|
1209
1222
|
}
|
|
1210
1223
|
if (typeof when === "number") {
|
|
@@ -1224,7 +1237,7 @@ var Agent = class Agent extends Server {
|
|
|
1224
1237
|
time: timestamp,
|
|
1225
1238
|
type: "delayed"
|
|
1226
1239
|
};
|
|
1227
|
-
emitScheduleCreate(
|
|
1240
|
+
emitScheduleCreate();
|
|
1228
1241
|
return schedule;
|
|
1229
1242
|
}
|
|
1230
1243
|
if (typeof when === "string") {
|
|
@@ -1244,7 +1257,7 @@ var Agent = class Agent extends Server {
|
|
|
1244
1257
|
time: timestamp,
|
|
1245
1258
|
type: "cron"
|
|
1246
1259
|
};
|
|
1247
|
-
emitScheduleCreate(
|
|
1260
|
+
emitScheduleCreate();
|
|
1248
1261
|
return schedule;
|
|
1249
1262
|
}
|
|
1250
1263
|
throw new Error(`Invalid schedule type: ${JSON.stringify(when)}(${typeof when}) trying to schedule ${callback}`);
|
|
@@ -1284,16 +1297,10 @@ var Agent = class Agent extends Server {
|
|
|
1284
1297
|
time: timestamp,
|
|
1285
1298
|
type: "interval"
|
|
1286
1299
|
};
|
|
1287
|
-
this.
|
|
1288
|
-
|
|
1289
|
-
id
|
|
1290
|
-
|
|
1291
|
-
callback,
|
|
1292
|
-
id
|
|
1293
|
-
},
|
|
1294
|
-
timestamp: Date.now(),
|
|
1295
|
-
type: "schedule:create"
|
|
1296
|
-
}, this.ctx);
|
|
1300
|
+
this._emit("schedule:create", {
|
|
1301
|
+
callback,
|
|
1302
|
+
id
|
|
1303
|
+
});
|
|
1297
1304
|
return schedule;
|
|
1298
1305
|
}
|
|
1299
1306
|
/**
|
|
@@ -1351,20 +1358,77 @@ var Agent = class Agent extends Server {
|
|
|
1351
1358
|
async cancelSchedule(id) {
|
|
1352
1359
|
const schedule = this.getSchedule(id);
|
|
1353
1360
|
if (!schedule) return false;
|
|
1354
|
-
this.
|
|
1355
|
-
|
|
1356
|
-
id:
|
|
1357
|
-
|
|
1358
|
-
callback: schedule.callback,
|
|
1359
|
-
id: schedule.id
|
|
1360
|
-
},
|
|
1361
|
-
timestamp: Date.now(),
|
|
1362
|
-
type: "schedule:cancel"
|
|
1363
|
-
}, this.ctx);
|
|
1361
|
+
this._emit("schedule:cancel", {
|
|
1362
|
+
callback: schedule.callback,
|
|
1363
|
+
id: schedule.id
|
|
1364
|
+
});
|
|
1364
1365
|
this.sql`DELETE FROM cf_agents_schedules WHERE id = ${id}`;
|
|
1365
1366
|
await this._scheduleNextAlarm();
|
|
1366
1367
|
return true;
|
|
1367
1368
|
}
|
|
1369
|
+
/**
|
|
1370
|
+
* Keep the Durable Object alive via alarm heartbeats.
|
|
1371
|
+
* Returns a disposer function that stops the heartbeat when called.
|
|
1372
|
+
*
|
|
1373
|
+
* Use this when you have long-running work and need to prevent the
|
|
1374
|
+
* DO from going idle (eviction after ~70-140s of inactivity).
|
|
1375
|
+
* The heartbeat fires every 30 seconds via the scheduling system.
|
|
1376
|
+
*
|
|
1377
|
+
* @experimental This API may change between releases.
|
|
1378
|
+
*
|
|
1379
|
+
* @example
|
|
1380
|
+
* ```ts
|
|
1381
|
+
* const dispose = await this.keepAlive();
|
|
1382
|
+
* try {
|
|
1383
|
+
* // ... long-running work ...
|
|
1384
|
+
* } finally {
|
|
1385
|
+
* dispose();
|
|
1386
|
+
* }
|
|
1387
|
+
* ```
|
|
1388
|
+
*/
|
|
1389
|
+
async keepAlive() {
|
|
1390
|
+
const heartbeatSeconds = Math.ceil(KEEP_ALIVE_INTERVAL_MS / 1e3);
|
|
1391
|
+
const schedule = await this.scheduleEvery(heartbeatSeconds, "_cf_keepAliveHeartbeat");
|
|
1392
|
+
let disposed = false;
|
|
1393
|
+
return () => {
|
|
1394
|
+
if (disposed) return;
|
|
1395
|
+
disposed = true;
|
|
1396
|
+
this.cancelSchedule(schedule.id);
|
|
1397
|
+
};
|
|
1398
|
+
}
|
|
1399
|
+
/**
|
|
1400
|
+
* Run an async function while keeping the Durable Object alive.
|
|
1401
|
+
* The heartbeat is automatically stopped when the function completes
|
|
1402
|
+
* (whether it succeeds or throws).
|
|
1403
|
+
*
|
|
1404
|
+
* This is the recommended way to use keepAlive — it guarantees cleanup
|
|
1405
|
+
* so you cannot forget to dispose the heartbeat.
|
|
1406
|
+
*
|
|
1407
|
+
* @experimental This API may change between releases.
|
|
1408
|
+
*
|
|
1409
|
+
* @example
|
|
1410
|
+
* ```ts
|
|
1411
|
+
* const result = await this.keepAliveWhile(async () => {
|
|
1412
|
+
* const data = await longRunningComputation();
|
|
1413
|
+
* return data;
|
|
1414
|
+
* });
|
|
1415
|
+
* ```
|
|
1416
|
+
*/
|
|
1417
|
+
async keepAliveWhile(fn) {
|
|
1418
|
+
const dispose = await this.keepAlive();
|
|
1419
|
+
try {
|
|
1420
|
+
return await fn();
|
|
1421
|
+
} finally {
|
|
1422
|
+
dispose();
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
/**
|
|
1426
|
+
* Internal no-op callback invoked by the keepAlive heartbeat schedule.
|
|
1427
|
+
* Its only purpose is to keep the DO alive — the alarm machinery
|
|
1428
|
+
* handles the rest.
|
|
1429
|
+
* @internal
|
|
1430
|
+
*/
|
|
1431
|
+
async _cf_keepAliveHeartbeat() {}
|
|
1368
1432
|
async _scheduleNextAlarm() {
|
|
1369
1433
|
const result = this.sql`
|
|
1370
1434
|
SELECT time FROM cf_agents_schedules
|
|
@@ -1395,13 +1459,7 @@ var Agent = class Agent extends Server {
|
|
|
1395
1459
|
setTimeout(() => {
|
|
1396
1460
|
this.ctx.abort("destroyed");
|
|
1397
1461
|
}, 0);
|
|
1398
|
-
this.
|
|
1399
|
-
displayMessage: "Agent destroyed",
|
|
1400
|
-
id: nanoid(),
|
|
1401
|
-
payload: {},
|
|
1402
|
-
timestamp: Date.now(),
|
|
1403
|
-
type: "destroy"
|
|
1404
|
-
}, this.ctx);
|
|
1462
|
+
this._emit("destroy");
|
|
1405
1463
|
}
|
|
1406
1464
|
/**
|
|
1407
1465
|
* Check if a method is callable
|
|
@@ -1481,16 +1539,10 @@ var Agent = class Agent extends Server {
|
|
|
1481
1539
|
if (e instanceof Error && e.message.includes("UNIQUE constraint failed")) throw new Error(`Workflow with ID "${workflowId}" is already being tracked`);
|
|
1482
1540
|
throw e;
|
|
1483
1541
|
}
|
|
1484
|
-
this.
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
workflowId: instance.id,
|
|
1489
|
-
workflowName
|
|
1490
|
-
},
|
|
1491
|
-
timestamp: Date.now(),
|
|
1492
|
-
type: "workflow:start"
|
|
1493
|
-
}, this.ctx);
|
|
1542
|
+
this._emit("workflow:start", {
|
|
1543
|
+
workflowId: instance.id,
|
|
1544
|
+
workflowName
|
|
1545
|
+
});
|
|
1494
1546
|
return instance.id;
|
|
1495
1547
|
}
|
|
1496
1548
|
/**
|
|
@@ -1519,16 +1571,10 @@ var Agent = class Agent extends Server {
|
|
|
1519
1571
|
baseDelayMs: 200,
|
|
1520
1572
|
maxDelayMs: 3e3
|
|
1521
1573
|
});
|
|
1522
|
-
this.
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
workflowId,
|
|
1527
|
-
eventType: event.type
|
|
1528
|
-
},
|
|
1529
|
-
timestamp: Date.now(),
|
|
1530
|
-
type: "workflow:event"
|
|
1531
|
-
}, this.ctx);
|
|
1574
|
+
this._emit("workflow:event", {
|
|
1575
|
+
workflowId,
|
|
1576
|
+
eventType: event.type
|
|
1577
|
+
});
|
|
1532
1578
|
}
|
|
1533
1579
|
/**
|
|
1534
1580
|
* Approve a waiting workflow.
|
|
@@ -1556,16 +1602,10 @@ var Agent = class Agent extends Server {
|
|
|
1556
1602
|
metadata: data?.metadata
|
|
1557
1603
|
}
|
|
1558
1604
|
});
|
|
1559
|
-
this.
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
workflowId,
|
|
1564
|
-
reason: data?.reason
|
|
1565
|
-
},
|
|
1566
|
-
timestamp: Date.now(),
|
|
1567
|
-
type: "workflow:approved"
|
|
1568
|
-
}, this.ctx);
|
|
1605
|
+
this._emit("workflow:approved", {
|
|
1606
|
+
workflowId,
|
|
1607
|
+
reason: data?.reason
|
|
1608
|
+
});
|
|
1569
1609
|
}
|
|
1570
1610
|
/**
|
|
1571
1611
|
* Reject a waiting workflow.
|
|
@@ -1591,16 +1631,10 @@ var Agent = class Agent extends Server {
|
|
|
1591
1631
|
reason: data?.reason
|
|
1592
1632
|
}
|
|
1593
1633
|
});
|
|
1594
|
-
this.
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
workflowId,
|
|
1599
|
-
reason: data?.reason
|
|
1600
|
-
},
|
|
1601
|
-
timestamp: Date.now(),
|
|
1602
|
-
type: "workflow:rejected"
|
|
1603
|
-
}, this.ctx);
|
|
1634
|
+
this._emit("workflow:rejected", {
|
|
1635
|
+
workflowId,
|
|
1636
|
+
reason: data?.reason
|
|
1637
|
+
});
|
|
1604
1638
|
}
|
|
1605
1639
|
/**
|
|
1606
1640
|
* Terminate a running workflow.
|
|
@@ -1637,16 +1671,10 @@ var Agent = class Agent extends Server {
|
|
|
1637
1671
|
}
|
|
1638
1672
|
const status = await instance.status();
|
|
1639
1673
|
this._updateWorkflowTracking(workflowId, status);
|
|
1640
|
-
this.
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
workflowId,
|
|
1645
|
-
workflowName: workflowInfo.workflowName
|
|
1646
|
-
},
|
|
1647
|
-
timestamp: Date.now(),
|
|
1648
|
-
type: "workflow:terminated"
|
|
1649
|
-
}, this.ctx);
|
|
1674
|
+
this._emit("workflow:terminated", {
|
|
1675
|
+
workflowId,
|
|
1676
|
+
workflowName: workflowInfo.workflowName
|
|
1677
|
+
});
|
|
1650
1678
|
}
|
|
1651
1679
|
/**
|
|
1652
1680
|
* Pause a running workflow.
|
|
@@ -1683,16 +1711,10 @@ var Agent = class Agent extends Server {
|
|
|
1683
1711
|
}
|
|
1684
1712
|
const status = await instance.status();
|
|
1685
1713
|
this._updateWorkflowTracking(workflowId, status);
|
|
1686
|
-
this.
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
workflowId,
|
|
1691
|
-
workflowName: workflowInfo.workflowName
|
|
1692
|
-
},
|
|
1693
|
-
timestamp: Date.now(),
|
|
1694
|
-
type: "workflow:paused"
|
|
1695
|
-
}, this.ctx);
|
|
1714
|
+
this._emit("workflow:paused", {
|
|
1715
|
+
workflowId,
|
|
1716
|
+
workflowName: workflowInfo.workflowName
|
|
1717
|
+
});
|
|
1696
1718
|
}
|
|
1697
1719
|
/**
|
|
1698
1720
|
* Resume a paused workflow.
|
|
@@ -1728,16 +1750,10 @@ var Agent = class Agent extends Server {
|
|
|
1728
1750
|
}
|
|
1729
1751
|
const status = await instance.status();
|
|
1730
1752
|
this._updateWorkflowTracking(workflowId, status);
|
|
1731
|
-
this.
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
workflowId,
|
|
1736
|
-
workflowName: workflowInfo.workflowName
|
|
1737
|
-
},
|
|
1738
|
-
timestamp: Date.now(),
|
|
1739
|
-
type: "workflow:resumed"
|
|
1740
|
-
}, this.ctx);
|
|
1753
|
+
this._emit("workflow:resumed", {
|
|
1754
|
+
workflowId,
|
|
1755
|
+
workflowName: workflowInfo.workflowName
|
|
1756
|
+
});
|
|
1741
1757
|
}
|
|
1742
1758
|
/**
|
|
1743
1759
|
* Restart a workflow instance.
|
|
@@ -1795,16 +1811,10 @@ var Agent = class Agent extends Server {
|
|
|
1795
1811
|
const status = await instance.status();
|
|
1796
1812
|
this._updateWorkflowTracking(workflowId, status);
|
|
1797
1813
|
}
|
|
1798
|
-
this.
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
workflowId,
|
|
1803
|
-
workflowName: workflowInfo.workflowName
|
|
1804
|
-
},
|
|
1805
|
-
timestamp: Date.now(),
|
|
1806
|
-
type: "workflow:restarted"
|
|
1807
|
-
}, this.ctx);
|
|
1814
|
+
this._emit("workflow:restarted", {
|
|
1815
|
+
workflowId,
|
|
1816
|
+
workflowName: workflowInfo.workflowName
|
|
1817
|
+
});
|
|
1808
1818
|
}
|
|
1809
1819
|
/**
|
|
1810
1820
|
* Find a workflow binding by its name.
|
|
@@ -2096,6 +2106,37 @@ var Agent = class Agent extends Server {
|
|
|
2096
2106
|
if (key === className || camelCaseToKebabCase(key) === camelCaseToKebabCase(className)) return key;
|
|
2097
2107
|
}
|
|
2098
2108
|
}
|
|
2109
|
+
_findBindingNameForNamespace(namespace) {
|
|
2110
|
+
for (const [key, value] of Object.entries(this.env)) if (value === namespace) return key;
|
|
2111
|
+
}
|
|
2112
|
+
async _restoreRpcMcpServers() {
|
|
2113
|
+
const rpcServers = this.mcp.getRpcServersFromStorage();
|
|
2114
|
+
for (const server of rpcServers) {
|
|
2115
|
+
if (this.mcp.mcpConnections[server.id]) continue;
|
|
2116
|
+
const opts = server.server_options ? JSON.parse(server.server_options) : {};
|
|
2117
|
+
const namespace = this.env[opts.bindingName];
|
|
2118
|
+
if (!namespace) {
|
|
2119
|
+
console.warn(`[Agent] Cannot restore RPC MCP server "${server.name}": binding "${opts.bindingName}" not found in env`);
|
|
2120
|
+
continue;
|
|
2121
|
+
}
|
|
2122
|
+
const normalizedName = server.server_url.replace(RPC_DO_PREFIX, "");
|
|
2123
|
+
try {
|
|
2124
|
+
await this.mcp.connect(`${RPC_DO_PREFIX}${normalizedName}`, {
|
|
2125
|
+
reconnect: { id: server.id },
|
|
2126
|
+
transport: {
|
|
2127
|
+
type: "rpc",
|
|
2128
|
+
namespace,
|
|
2129
|
+
name: normalizedName,
|
|
2130
|
+
props: opts.props
|
|
2131
|
+
}
|
|
2132
|
+
});
|
|
2133
|
+
const conn = this.mcp.mcpConnections[server.id];
|
|
2134
|
+
if (conn && conn.connectionState === MCPConnectionState.CONNECTED) await this.mcp.discoverIfConnected(server.id);
|
|
2135
|
+
} catch (error) {
|
|
2136
|
+
console.error(`[Agent] Error restoring RPC MCP server "${server.name}":`, error);
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2099
2140
|
/**
|
|
2100
2141
|
* Handle a callback from a workflow.
|
|
2101
2142
|
* Called when the Agent receives a callback at /_workflow/callback.
|
|
@@ -2202,63 +2243,87 @@ var Agent = class Agent extends Server {
|
|
|
2202
2243
|
});
|
|
2203
2244
|
} else if (action === "reset") this.setState(this.initialState);
|
|
2204
2245
|
}
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2246
|
+
async addMcpServer(serverName, urlOrBinding, callbackHostOrOptions, agentsPrefix, options) {
|
|
2247
|
+
const isHttpTransport = typeof urlOrBinding === "string";
|
|
2248
|
+
const normalizedUrl = isHttpTransport ? new URL(urlOrBinding).href : void 0;
|
|
2249
|
+
const existingServer = this.mcp.listServers().find((s) => s.name === serverName && (!isHttpTransport || new URL(s.server_url).href === normalizedUrl));
|
|
2250
|
+
if (existingServer && this.mcp.mcpConnections[existingServer.id]) {
|
|
2251
|
+
const conn = this.mcp.mcpConnections[existingServer.id];
|
|
2252
|
+
if (conn.connectionState === MCPConnectionState.AUTHENTICATING && conn.options.transport.authProvider?.authUrl) return {
|
|
2253
|
+
id: existingServer.id,
|
|
2254
|
+
state: MCPConnectionState.AUTHENTICATING,
|
|
2255
|
+
authUrl: conn.options.transport.authProvider.authUrl
|
|
2256
|
+
};
|
|
2257
|
+
if (conn.connectionState === MCPConnectionState.FAILED) throw new Error(`MCP server "${serverName}" is in failed state: ${conn.connectionError}`);
|
|
2258
|
+
return {
|
|
2259
|
+
id: existingServer.id,
|
|
2260
|
+
state: MCPConnectionState.READY
|
|
2261
|
+
};
|
|
2262
|
+
}
|
|
2263
|
+
if (typeof urlOrBinding !== "string") {
|
|
2264
|
+
const rpcOpts = callbackHostOrOptions;
|
|
2265
|
+
const normalizedName = serverName.toLowerCase().replace(/\s+/g, "-");
|
|
2266
|
+
const reconnectId = existingServer?.id;
|
|
2267
|
+
const { id } = await this.mcp.connect(`${RPC_DO_PREFIX}${normalizedName}`, {
|
|
2268
|
+
reconnect: reconnectId ? { id: reconnectId } : void 0,
|
|
2269
|
+
transport: {
|
|
2270
|
+
type: "rpc",
|
|
2271
|
+
namespace: urlOrBinding,
|
|
2272
|
+
name: normalizedName,
|
|
2273
|
+
props: rpcOpts?.props
|
|
2274
|
+
}
|
|
2275
|
+
});
|
|
2276
|
+
const conn = this.mcp.mcpConnections[id];
|
|
2277
|
+
if (conn && conn.connectionState === MCPConnectionState.CONNECTED) {
|
|
2278
|
+
const discoverResult = await this.mcp.discoverIfConnected(id);
|
|
2279
|
+
if (discoverResult && !discoverResult.success) throw new Error(`Failed to discover MCP server capabilities: ${discoverResult.error}`);
|
|
2280
|
+
} else if (conn && conn.connectionState === MCPConnectionState.FAILED) throw new Error(`Failed to connect to MCP server "${serverName}" via RPC: ${conn.connectionError}`);
|
|
2281
|
+
const bindingName = this._findBindingNameForNamespace(urlOrBinding);
|
|
2282
|
+
if (bindingName) this.mcp.saveRpcServerToStorage(id, serverName, normalizedName, bindingName, rpcOpts?.props);
|
|
2283
|
+
return {
|
|
2284
|
+
id,
|
|
2285
|
+
state: MCPConnectionState.READY
|
|
2286
|
+
};
|
|
2287
|
+
}
|
|
2288
|
+
const httpOptions = callbackHostOrOptions;
|
|
2231
2289
|
let resolvedCallbackHost;
|
|
2232
2290
|
let resolvedAgentsPrefix;
|
|
2233
2291
|
let resolvedOptions;
|
|
2234
2292
|
let resolvedCallbackPath;
|
|
2235
|
-
if (typeof
|
|
2236
|
-
resolvedCallbackHost =
|
|
2237
|
-
resolvedCallbackPath =
|
|
2238
|
-
resolvedAgentsPrefix =
|
|
2293
|
+
if (typeof httpOptions === "object" && httpOptions !== null) {
|
|
2294
|
+
resolvedCallbackHost = httpOptions.callbackHost;
|
|
2295
|
+
resolvedCallbackPath = httpOptions.callbackPath;
|
|
2296
|
+
resolvedAgentsPrefix = httpOptions.agentsPrefix ?? "agents";
|
|
2239
2297
|
resolvedOptions = {
|
|
2240
|
-
client:
|
|
2241
|
-
transport:
|
|
2242
|
-
retry:
|
|
2298
|
+
client: httpOptions.client,
|
|
2299
|
+
transport: httpOptions.transport,
|
|
2300
|
+
retry: httpOptions.retry
|
|
2243
2301
|
};
|
|
2244
2302
|
} else {
|
|
2245
|
-
resolvedCallbackHost =
|
|
2303
|
+
resolvedCallbackHost = httpOptions;
|
|
2246
2304
|
resolvedAgentsPrefix = agentsPrefix ?? "agents";
|
|
2247
2305
|
resolvedOptions = options;
|
|
2248
2306
|
}
|
|
2249
|
-
if (!this._resolvedOptions.sendIdentityOnConnect && !resolvedCallbackPath) throw new Error("callbackPath is required in addMcpServer options when sendIdentityOnConnect is false — the default callback URL would expose the instance name. Provide a callbackPath and route the callback request to this agent via getAgentByName.");
|
|
2307
|
+
if (!this._resolvedOptions.sendIdentityOnConnect && resolvedCallbackHost && !resolvedCallbackPath) throw new Error("callbackPath is required in addMcpServer options when sendIdentityOnConnect is false — the default callback URL would expose the instance name. Provide a callbackPath and route the callback request to this agent via getAgentByName.");
|
|
2250
2308
|
if (!resolvedCallbackHost) {
|
|
2251
2309
|
const { request } = getCurrentAgent();
|
|
2252
|
-
if (
|
|
2253
|
-
|
|
2254
|
-
|
|
2310
|
+
if (request) {
|
|
2311
|
+
const requestUrl = new URL(request.url);
|
|
2312
|
+
resolvedCallbackHost = `${requestUrl.protocol}//${requestUrl.host}`;
|
|
2313
|
+
}
|
|
2314
|
+
}
|
|
2315
|
+
let callbackUrl;
|
|
2316
|
+
if (resolvedCallbackHost) {
|
|
2317
|
+
const normalizedHost = resolvedCallbackHost.replace(/\/$/, "");
|
|
2318
|
+
callbackUrl = resolvedCallbackPath ? `${normalizedHost}/${resolvedCallbackPath.replace(/^\//, "")}` : `${normalizedHost}/${resolvedAgentsPrefix}/${camelCaseToKebabCase(this._ParentClass.name)}/${this.name}/callback`;
|
|
2255
2319
|
}
|
|
2256
|
-
const normalizedHost = resolvedCallbackHost.replace(/\/$/, "");
|
|
2257
|
-
const callbackUrl = resolvedCallbackPath ? `${normalizedHost}/${resolvedCallbackPath.replace(/^\//, "")}` : `${normalizedHost}/${resolvedAgentsPrefix}/${camelCaseToKebabCase(this._ParentClass.name)}/${this.name}/callback`;
|
|
2258
2320
|
await this.mcp.ensureJsonSchema();
|
|
2259
2321
|
const id = nanoid(8);
|
|
2260
|
-
|
|
2261
|
-
|
|
2322
|
+
let authProvider;
|
|
2323
|
+
if (callbackUrl) {
|
|
2324
|
+
authProvider = this.createMcpOAuthProvider(callbackUrl);
|
|
2325
|
+
authProvider.serverId = id;
|
|
2326
|
+
}
|
|
2262
2327
|
const transportType = resolvedOptions?.transport?.type ?? "auto";
|
|
2263
2328
|
let headerTransportOpts = {};
|
|
2264
2329
|
if (resolvedOptions?.transport?.headers) headerTransportOpts = {
|
|
@@ -2269,7 +2334,7 @@ var Agent = class Agent extends Server {
|
|
|
2269
2334
|
requestInit: { headers: resolvedOptions?.transport?.headers }
|
|
2270
2335
|
};
|
|
2271
2336
|
await this.mcp.registerServer(id, {
|
|
2272
|
-
url,
|
|
2337
|
+
url: normalizedUrl,
|
|
2273
2338
|
name: serverName,
|
|
2274
2339
|
callbackUrl,
|
|
2275
2340
|
client: resolvedOptions?.client,
|
|
@@ -2281,12 +2346,15 @@ var Agent = class Agent extends Server {
|
|
|
2281
2346
|
retry: resolvedOptions?.retry
|
|
2282
2347
|
});
|
|
2283
2348
|
const result = await this.mcp.connectToServer(id);
|
|
2284
|
-
if (result.state === MCPConnectionState.FAILED) throw new Error(`Failed to connect to MCP server at ${
|
|
2285
|
-
if (result.state === MCPConnectionState.AUTHENTICATING)
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2349
|
+
if (result.state === MCPConnectionState.FAILED) throw new Error(`Failed to connect to MCP server at ${normalizedUrl}: ${result.error}`);
|
|
2350
|
+
if (result.state === MCPConnectionState.AUTHENTICATING) {
|
|
2351
|
+
if (!callbackUrl) throw new Error("This MCP server requires OAuth authentication. Provide callbackHost in addMcpServer options to enable the OAuth flow.");
|
|
2352
|
+
return {
|
|
2353
|
+
id,
|
|
2354
|
+
state: result.state,
|
|
2355
|
+
authUrl: result.authUrl
|
|
2356
|
+
};
|
|
2357
|
+
}
|
|
2290
2358
|
const discoverResult = await this.mcp.discoverIfConnected(id);
|
|
2291
2359
|
if (discoverResult && !discoverResult.success) throw new Error(`Failed to discover MCP server capabilities: ${discoverResult.error}`);
|
|
2292
2360
|
return {
|
|
@@ -2312,7 +2380,7 @@ var Agent = class Agent extends Server {
|
|
|
2312
2380
|
mcpState.servers[server.id] = {
|
|
2313
2381
|
auth_url: server.auth_url,
|
|
2314
2382
|
capabilities: serverConn?.serverCapabilities ?? null,
|
|
2315
|
-
error: serverConn?.connectionError ?? null,
|
|
2383
|
+
error: sanitizeErrorString(serverConn?.connectionError ?? null),
|
|
2316
2384
|
instructions: serverConn?.instructions ?? null,
|
|
2317
2385
|
name: server.name,
|
|
2318
2386
|
server_url: server.server_url,
|
|
@@ -2431,16 +2499,22 @@ async function routeAgentEmail(email, env, options) {
|
|
|
2431
2499
|
}
|
|
2432
2500
|
if (!agentMapCache.has(env)) {
|
|
2433
2501
|
const map = {};
|
|
2502
|
+
const originalNames = [];
|
|
2434
2503
|
for (const [key, value] of Object.entries(env)) if (value && typeof value === "object" && "idFromName" in value && typeof value.idFromName === "function") {
|
|
2435
2504
|
map[key] = value;
|
|
2436
2505
|
map[camelCaseToKebabCase(key)] = value;
|
|
2506
|
+
map[key.toLowerCase()] = value;
|
|
2507
|
+
originalNames.push(key);
|
|
2437
2508
|
}
|
|
2438
|
-
agentMapCache.set(env,
|
|
2509
|
+
agentMapCache.set(env, {
|
|
2510
|
+
map,
|
|
2511
|
+
originalNames
|
|
2512
|
+
});
|
|
2439
2513
|
}
|
|
2440
|
-
const
|
|
2441
|
-
const namespace =
|
|
2514
|
+
const cached = agentMapCache.get(env);
|
|
2515
|
+
const namespace = cached.map[routingInfo.agentName];
|
|
2442
2516
|
if (!namespace) {
|
|
2443
|
-
const availableAgents =
|
|
2517
|
+
const availableAgents = cached.originalNames.join(", ");
|
|
2444
2518
|
throw new Error(`Agent namespace '${routingInfo.agentName}' not found in environment. Available agents: ${availableAgents}`);
|
|
2445
2519
|
}
|
|
2446
2520
|
const agent = await getAgentByName(namespace, routingInfo.agentId);
|