@standardagents/builder 0.37.8 → 0.38.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/built-in-routes.js +1246 -518
  2. package/dist/built-in-routes.js.map +1 -1
  3. package/dist/client/{ApiKeysView-L85eJb6G.js → ApiKeysView-AH5i-H6j.js} +1 -1
  4. package/dist/client/CenteredContentView-DaBp0K-A.js +1 -0
  5. package/dist/client/{CompositionView-BGp0EJCk.js → CompositionView-yY1Q4M23.js} +1 -1
  6. package/dist/client/{ConfirmDialog.vue_vue_type_script_setup_true_lang-DgwqZrLV.js → ConfirmDialog.vue_vue_type_script_setup_true_lang-Bwo5CJzF.js} +1 -1
  7. package/dist/client/{CopyButton.vue_vue_type_script_setup_true_lang-B8cGgU2J.js → CopyButton.vue_vue_type_script_setup_true_lang-D0WYwWyA.js} +1 -1
  8. package/dist/client/{DataTable.vue_vue_type_script_setup_true_lang-CRBXym9D.js → DataTable.vue_vue_type_script_setup_true_lang-1wwPn2aY.js} +1 -1
  9. package/dist/client/{JsonViewer-DkHnIfq3.js → JsonViewer-Bi8Zdmir.js} +1 -1
  10. package/dist/client/LoginView-BIKahEU6.js +1 -0
  11. package/dist/client/{MarketplaceView-DEnujUBs.js → MarketplaceView-DPDPVH68.js} +1 -1
  12. package/dist/client/{Modal.vue_vue_type_script_setup_true_lang-keKEwm3K.js → Modal.vue_vue_type_script_setup_true_lang-D1_rfOsy.js} +1 -1
  13. package/dist/client/{ModelModal.vue_vue_type_script_setup_true_lang-t1n0bbJe.js → ModelModal.vue_vue_type_script_setup_true_lang-DSLeOdxr.js} +1 -1
  14. package/dist/client/{ModelsView-BkCLr8sp.js → ModelsView-Q2WXu1Gw.js} +1 -1
  15. package/dist/client/{PromptEditView-BjCfsJQE.js → PromptEditView-BzA-3n1w.js} +1 -1
  16. package/dist/client/{PromptModal-nxu_LIv6.js → PromptModal-CJWh_cru.js} +1 -1
  17. package/dist/client/{PromptsView-1yXGCmEZ.js → PromptsView-BezNl-Et.js} +1 -1
  18. package/dist/client/{ProvidersView-DxF6gafk.js → ProvidersView-BuzSKxuT.js} +1 -1
  19. package/dist/client/ThreadInspectorPane.vue_vue_type_script_setup_true_lang-DOxNp_3C.js +22 -0
  20. package/dist/client/{ThreadsView-CBup_w8u.js → ThreadsView-DPCpZA4J.js} +1 -1
  21. package/dist/client/{ToolsView-B30TfXU9.js → ToolsView-DMjFMAhH.js} +1 -1
  22. package/dist/client/{UiCodeBlock.vue_vue_type_script_setup_true_lang-BdyzSxZZ.js → UiCodeBlock.vue_vue_type_script_setup_true_lang-7FgZmkfs.js} +1 -1
  23. package/dist/client/{UsersView-kE7Xr4f3.js → UsersView-B7jXLNQs.js} +1 -1
  24. package/dist/client/{VariablesView-yYuynG0M.js → VariablesView-CF7x0G8g.js} +1 -1
  25. package/dist/client/assets/{ThreadInspectorPane-BRGO_P0_.css → ThreadInspectorPane-CZMdV0Tb.css} +1 -1
  26. package/dist/client/assets/{index-B-69GGhJ.css → index-DAABRRAA.css} +1 -1
  27. package/dist/client/{check-YKi1o6p6.js → check-CD2lvcVi.js} +1 -1
  28. package/dist/client/{chevron-right-T0zzq_GX.js → chevron-right-bWZrmJ5a.js} +1 -1
  29. package/dist/client/{copy-C_gUGAj1.js → copy-B7nifl6a.js} +1 -1
  30. package/dist/client/{formkit-CE26dtFp.js → formkit-DJxUzqRQ.js} +1 -1
  31. package/dist/client/index-eGECJFYe.js +6 -0
  32. package/dist/client/index.html +6 -6
  33. package/dist/client/{package-B70IFSDB.js → package-Dyv7fTIQ.js} +1 -1
  34. package/dist/client/{plus-BN9IHYdt.js → plus-XkSslfF4.js} +1 -1
  35. package/dist/client/{syntax-D-ZcovVx.js → syntax-D68HCEYm.js} +1 -1
  36. package/dist/client/{timeAgo-G1ovbWPH.js → timeAgo-DRBdu9ed.js} +1 -1
  37. package/dist/client/{useActionFeedback-7pfPjKMi.js → useActionFeedback-FN4t6ezB.js} +1 -1
  38. package/dist/client/{vendor-TPZjEwCU.js → vendor-c_uwtQ-S.js} +13 -13
  39. package/dist/client/{vue-CZ-ORfS3.js → vue-BuvSxRmI.js} +1 -1
  40. package/dist/{index-Lfan8QMN.d.ts → index-CF-lgN47.d.ts} +83 -12
  41. package/dist/index.d.ts +2 -2
  42. package/dist/index.js +1155 -360
  43. package/dist/index.js.map +1 -1
  44. package/dist/plugin.js +131 -72
  45. package/dist/plugin.js.map +1 -1
  46. package/dist/runtime.d.ts +69 -11
  47. package/dist/runtime.js +1018 -280
  48. package/dist/runtime.js.map +1 -1
  49. package/dist/test.d.ts +1 -1
  50. package/package.json +7 -6
  51. package/dist/client/CenteredContentView-D9jkWoLc.js +0 -1
  52. package/dist/client/LoginView-BXD9Odc-.js +0 -1
  53. package/dist/client/ThreadInspectorPane.vue_vue_type_script_setup_true_lang-CjwWJ3uw.js +0 -22
  54. package/dist/client/index-AUGwDdSH.js +0 -6
package/dist/runtime.js CHANGED
@@ -28,6 +28,14 @@ var __copyProps = (to, from, except, desc) => {
28
28
  };
29
29
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
30
 
31
+ // src/skills/scope.ts
32
+ var SHARED_SKILL_OWNER;
33
+ var init_scope = __esm({
34
+ "src/skills/scope.ts"() {
35
+ SHARED_SKILL_OWNER = "__instance_shared__";
36
+ }
37
+ });
38
+
31
39
  // src/agents/client-forwarding.ts
32
40
  function asForwarding(state) {
33
41
  return state;
@@ -3150,6 +3158,104 @@ var init_sessionTools = __esm({
3150
3158
  }
3151
3159
  });
3152
3160
 
3161
+ // src/utils/remoteAttachment.ts
3162
+ function parseAllowedHosts(value) {
3163
+ if (typeof value !== "string") {
3164
+ return /* @__PURE__ */ new Set();
3165
+ }
3166
+ return new Set(
3167
+ value.split(",").map((host) => host.trim().toLowerCase().replace(/\.$/, "")).filter(Boolean)
3168
+ );
3169
+ }
3170
+ function isForbiddenHostname(hostname) {
3171
+ const normalized = hostname.toLowerCase().replace(/\.$/, "");
3172
+ if (/^\d{1,3}(?:\.\d{1,3}){3}$/.test(normalized)) {
3173
+ return true;
3174
+ }
3175
+ if (normalized.startsWith("[") && normalized.endsWith("]")) {
3176
+ return true;
3177
+ }
3178
+ if (!normalized.includes(".")) {
3179
+ return true;
3180
+ }
3181
+ return FORBIDDEN_HOST_SUFFIXES.some(
3182
+ (suffix) => normalized === suffix.slice(1) || normalized.endsWith(suffix)
3183
+ );
3184
+ }
3185
+ function validateRemoteAttachmentUrl(value, allowedHostsValue) {
3186
+ let url;
3187
+ try {
3188
+ url = new URL(value);
3189
+ } catch {
3190
+ return null;
3191
+ }
3192
+ if (url.protocol !== "https:" || url.username !== "" || url.password !== "" || url.port !== "" && url.port !== "443") {
3193
+ return null;
3194
+ }
3195
+ const hostname = url.hostname.toLowerCase().replace(/\.$/, "");
3196
+ if (isForbiddenHostname(hostname)) {
3197
+ return null;
3198
+ }
3199
+ const allowedHosts = parseAllowedHosts(allowedHostsValue);
3200
+ if (!allowedHosts.has(hostname)) {
3201
+ return null;
3202
+ }
3203
+ return url;
3204
+ }
3205
+ async function fetchRemoteAttachment(location, allowedHostsValue, fetchImpl = fetch) {
3206
+ let url = validateRemoteAttachmentUrl(location, allowedHostsValue);
3207
+ if (!url) {
3208
+ return null;
3209
+ }
3210
+ for (let redirectCount = 0; redirectCount <= MAX_REDIRECTS; redirectCount++) {
3211
+ let response;
3212
+ try {
3213
+ response = await fetchImpl(url.toString(), {
3214
+ redirect: "manual"
3215
+ });
3216
+ } catch {
3217
+ return null;
3218
+ }
3219
+ if (!REDIRECT_STATUSES.has(response.status)) {
3220
+ return response.ok ? response : null;
3221
+ }
3222
+ await response.body?.cancel().catch(() => void 0);
3223
+ if (redirectCount === MAX_REDIRECTS) {
3224
+ return null;
3225
+ }
3226
+ const redirectLocation = response.headers.get("Location");
3227
+ if (!redirectLocation) {
3228
+ return null;
3229
+ }
3230
+ let resolvedLocation;
3231
+ try {
3232
+ resolvedLocation = new URL(redirectLocation, url).toString();
3233
+ } catch {
3234
+ return null;
3235
+ }
3236
+ url = validateRemoteAttachmentUrl(resolvedLocation, allowedHostsValue);
3237
+ if (!url) {
3238
+ return null;
3239
+ }
3240
+ }
3241
+ return null;
3242
+ }
3243
+ var REDIRECT_STATUSES, MAX_REDIRECTS, FORBIDDEN_HOST_SUFFIXES;
3244
+ var init_remoteAttachment = __esm({
3245
+ "src/utils/remoteAttachment.ts"() {
3246
+ REDIRECT_STATUSES = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
3247
+ MAX_REDIRECTS = 3;
3248
+ FORBIDDEN_HOST_SUFFIXES = [
3249
+ ".internal",
3250
+ ".lan",
3251
+ ".local",
3252
+ ".localhost",
3253
+ ".home",
3254
+ ".test"
3255
+ ];
3256
+ }
3257
+ });
3258
+
3153
3259
  // src/durable-objects/files.ts
3154
3260
  var files_exports = {};
3155
3261
  __export(files_exports, {
@@ -3887,6 +3993,7 @@ var init_ToolExecutor = __esm({
3887
3993
  init_types();
3888
3994
  init_ThreadStateImpl();
3889
3995
  init_sessionTools();
3996
+ init_remoteAttachment();
3890
3997
  SUBAGENT_CREATE_TOOL = "subagent_create";
3891
3998
  SUBAGENT_MESSAGE_TOOL = "subagent_message";
3892
3999
  SUBAGENTS_LIST_TOOL = "subagents";
@@ -4935,7 +5042,8 @@ Message one with subagent_message(referenceId, message).`
4935
5042
  stub,
4936
5043
  state.thread.instance,
4937
5044
  childResult.attachments,
4938
- `from-subagent-${childThreadId}`
5045
+ `from-subagent-${childThreadId}`,
5046
+ state.env.AGENT_BUILDER_ATTACHMENT_FETCH_HOSTS
4939
5047
  );
4940
5048
  if (resumable) {
4941
5049
  await this.settleResumableChildAfterRun(
@@ -4968,7 +5076,8 @@ Message one with subagent_message(referenceId, message).`
4968
5076
  stub,
4969
5077
  state.thread.instance,
4970
5078
  childResult.attachments,
4971
- `from-subagent-${childThreadId}`
5079
+ `from-subagent-${childThreadId}`,
5080
+ state.env.AGENT_BUILDER_ATTACHMENT_FETCH_HOSTS
4972
5081
  );
4973
5082
  const messageContent = this.formatSubagentTerminalMessage(
4974
5083
  childThreadId,
@@ -5382,7 +5491,8 @@ Message one with subagent_message(referenceId, message).`
5382
5491
  state.thread.instance,
5383
5492
  childThread,
5384
5493
  parentAttachments,
5385
- `from-parent-${state.threadId}`
5494
+ `from-parent-${state.threadId}`,
5495
+ state.env.AGENT_BUILDER_ATTACHMENT_FETCH_HOSTS
5386
5496
  );
5387
5497
  const targetRole = receivesMessages === "side_a" ? "user" : "assistant";
5388
5498
  const baseTimestamp = Date.now() * 1e3;
@@ -5642,7 +5752,7 @@ Reference: ${referenceId}` : `Reference: ${referenceId}`;
5642
5752
  const shortId = crypto.randomUUID().slice(0, 8);
5643
5753
  return `/attachments/${normalizedStem}-${shortId}.${finalExt}`;
5644
5754
  }
5645
- static async copyAttachmentsBetweenThreads(sourceThread, targetThread, attachments, _pathPrefix) {
5755
+ static async copyAttachmentsBetweenThreads(sourceThread, targetThread, attachments, _pathPrefix, allowedAttachmentFetchHosts) {
5646
5756
  if (!attachments || attachments.length === 0) {
5647
5757
  return [];
5648
5758
  }
@@ -5670,7 +5780,8 @@ Reference: ${referenceId}` : `Reference: ${referenceId}`;
5670
5780
  const copiedData = await this.readAttachmentDataFromSource(
5671
5781
  sourceThread,
5672
5782
  attachment.path,
5673
- sourceFile
5783
+ sourceFile,
5784
+ allowedAttachmentFetchHosts
5674
5785
  );
5675
5786
  if (!copiedData) {
5676
5787
  continue;
@@ -5727,7 +5838,7 @@ Reference: ${referenceId}` : `Reference: ${referenceId}`;
5727
5838
  }
5728
5839
  return copied;
5729
5840
  }
5730
- static async readAttachmentDataFromSource(sourceThread, path, sourceFile) {
5841
+ static async readAttachmentDataFromSource(sourceThread, path, sourceFile, allowedAttachmentFetchHosts) {
5731
5842
  const sourceData = await sourceThread.readFile(path);
5732
5843
  if (sourceData.success && sourceData.data) {
5733
5844
  return sourceData.data;
@@ -5736,14 +5847,17 @@ Reference: ${referenceId}` : `Reference: ${referenceId}`;
5736
5847
  return null;
5737
5848
  }
5738
5849
  const location = sourceFile.location;
5739
- if (!location || !/^https?:\/\//i.test(location)) {
5850
+ if (!location) {
5851
+ return null;
5852
+ }
5853
+ const response = await fetchRemoteAttachment(
5854
+ location,
5855
+ allowedAttachmentFetchHosts
5856
+ );
5857
+ if (!response) {
5740
5858
  return null;
5741
5859
  }
5742
5860
  try {
5743
- const response = await fetch(location);
5744
- if (!response.ok) {
5745
- return null;
5746
- }
5747
5861
  const buffer = await response.arrayBuffer();
5748
5862
  return this.arrayBufferToBase64(buffer);
5749
5863
  } catch {
@@ -5997,7 +6111,8 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
5997
6111
  state.thread.instance,
5998
6112
  childStub,
5999
6113
  sourceAttachments,
6000
- `from-parent-${state.threadId}`
6114
+ `from-parent-${state.threadId}`,
6115
+ state.env.AGENT_BUILDER_ATTACHMENT_FETCH_HOSTS
6001
6116
  );
6002
6117
  await childStub.queueMessage(referenceId, {
6003
6118
  role: receivesMessages === "side_a" ? "user" : "assistant",
@@ -12979,6 +13094,32 @@ var ThreadStateImpl_exports = {};
12979
13094
  __export(ThreadStateImpl_exports, {
12980
13095
  ThreadStateImpl: () => ThreadStateImpl
12981
13096
  });
13097
+ function isRuntimeDurableNamespace(value) {
13098
+ return value !== null && typeof value === "object" && "idFromName" in value && typeof value.idFromName === "function" && "get" in value && typeof value.get === "function";
13099
+ }
13100
+ function isThreadStateRuntimeEnvironment(value) {
13101
+ return value !== null && typeof value === "object" && "AGENT_BUILDER" in value && isRuntimeDurableNamespace(value.AGENT_BUILDER) && (!("AGENT_BUILDER_USER_HUB" in value) || value.AGENT_BUILDER_USER_HUB === void 0 || isRuntimeDurableNamespace(value.AGENT_BUILDER_USER_HUB));
13102
+ }
13103
+ function confinedUserEndpointUrl(userId, endpoint) {
13104
+ const base = new URL(
13105
+ `https://user-hub/users/${encodeURIComponent(userId)}/endpoints/`
13106
+ );
13107
+ const relative = endpoint.replace(/^\/+/, "");
13108
+ let decoded;
13109
+ try {
13110
+ decoded = decodeURIComponent(relative);
13111
+ } catch {
13112
+ throw new Error("Invalid user endpoint path: malformed percent encoding.");
13113
+ }
13114
+ if (decoded.includes("\\") || decoded.includes("?") || decoded.includes("#") || /[\u0000-\u001f\u007f]/.test(decoded) || decoded.split("/").some((segment) => segment === "." || segment === "..")) {
13115
+ throw new Error("Invalid user endpoint path: traversal and URL syntax are not allowed.");
13116
+ }
13117
+ const resolved = new URL(relative, base);
13118
+ if (resolved.origin !== base.origin || !resolved.pathname.startsWith(base.pathname)) {
13119
+ throw new Error("Invalid user endpoint path: endpoint escaped the user namespace.");
13120
+ }
13121
+ return resolved.toString();
13122
+ }
12982
13123
  function hasLocalCodeExecutionRuntime(threadInstance) {
12983
13124
  const candidate = threadInstance;
12984
13125
  return Boolean(candidate.env && candidate.ctx);
@@ -13159,6 +13300,7 @@ function runCodeViaThreadRpc(threadInstance, threadId, source, options) {
13159
13300
  var ThreadStateImpl, ExecutionStateImpl;
13160
13301
  var init_ThreadStateImpl = __esm({
13161
13302
  "src/agents/ThreadStateImpl.ts"() {
13303
+ init_scope();
13162
13304
  init_client_forwarding();
13163
13305
  init_types();
13164
13306
  init_utilities();
@@ -13392,6 +13534,14 @@ var init_ThreadStateImpl = __esm({
13392
13534
  }
13393
13535
  return value;
13394
13536
  }
13537
+ async instanceEnv(propertyName) {
13538
+ const agentBuilder = this._getAgentBuilderStub();
13539
+ if (typeof agentBuilder.getInstanceEnv !== "function") {
13540
+ throw new Error("instanceEnv is not supported by this runtime");
13541
+ }
13542
+ const values = await agentBuilder.getInstanceEnv();
13543
+ return values[propertyName];
13544
+ }
13395
13545
  async envType(propertyName) {
13396
13546
  const agentBuilder = this._getAgentBuilderStub();
13397
13547
  if (typeof agentBuilder.resolveThreadEnvType === "function") {
@@ -14422,12 +14572,11 @@ var init_ThreadStateImpl = __esm({
14422
14572
  * dispatcher recorded in the runtime context instead.
14423
14573
  */
14424
14574
  _getRuntimeEnv() {
14425
- const contextEnv = this._runtimeContext.env;
14426
- if (contextEnv?.AGENT_BUILDER) {
14427
- return contextEnv;
14575
+ if (isThreadStateRuntimeEnvironment(this._runtimeContext.env)) {
14576
+ return this._runtimeContext.env;
14428
14577
  }
14429
14578
  const instanceEnv = this._threadInstance.env;
14430
- if (!instanceEnv?.AGENT_BUILDER) {
14579
+ if (!isThreadStateRuntimeEnvironment(instanceEnv)) {
14431
14580
  throw new Error(
14432
14581
  "No runtime environment available on this ThreadState. Stub-backed states must be constructed with runtimeContext.env."
14433
14582
  );
@@ -14444,9 +14593,7 @@ var init_ThreadStateImpl = __esm({
14444
14593
  return namespace.get(namespace.idFromName(userId));
14445
14594
  }
14446
14595
  // ─────────────────────────────────────────────────────────────────────────
14447
- // Skills — instance-global Agent Skills library, backed by the
14448
- // DurableAgentBuilder singleton (skills are never stored per-thread or on
14449
- // a client machine).
14596
+ // Skills — owner-scoped library plus explicitly shared administrator skills.
14450
14597
  // ─────────────────────────────────────────────────────────────────────────
14451
14598
  /**
14452
14599
  * The profile of the user this thread belongs to (identity fields only —
@@ -14469,9 +14616,8 @@ var init_ThreadStateImpl = __esm({
14469
14616
  "Reentrant user-scope call: this ThreadState was raised from user-scope code; state.user().call() is unavailable here."
14470
14617
  );
14471
14618
  }
14472
- const path = endpoint.replace(/^\/+/, "");
14473
14619
  return hub().fetch(
14474
- new Request(`https://user-hub/users/${encodeURIComponent(userId)}/endpoints/${path}`, {
14620
+ new Request(confinedUserEndpointUrl(userId, endpoint), {
14475
14621
  method: "POST",
14476
14622
  headers: {
14477
14623
  "Content-Type": "application/json",
@@ -14531,18 +14677,42 @@ var init_ThreadStateImpl = __esm({
14531
14677
  }
14532
14678
  get skills() {
14533
14679
  const stub = this._getAgentBuilderStub();
14680
+ const ownerId = this._metadata.user_id ?? SHARED_SKILL_OWNER;
14681
+ const scope = {
14682
+ ownerId,
14683
+ includeShared: ownerId !== SHARED_SKILL_OWNER
14684
+ };
14685
+ const requireMutationAdmin = async () => {
14686
+ const userId = this._metadata.user_id;
14687
+ if (!userId) {
14688
+ throw new Error("Skill mutations require an authenticated instance admin.");
14689
+ }
14690
+ const user = await stub.getUserById(userId);
14691
+ if (user?.role !== "admin") {
14692
+ throw new Error("Skill mutations require an instance admin.");
14693
+ }
14694
+ };
14534
14695
  return {
14535
- list: (options) => stub.listSkills(options),
14696
+ list: (options) => stub.listSkills(scope, options),
14536
14697
  get: async (name) => {
14537
- const metadata = await stub.getSkill(name);
14698
+ const metadata = await stub.getSkill(scope, name);
14538
14699
  if (!metadata) return null;
14539
- const skillMd = await stub.getSkillFile(name, "SKILL.md") ?? "";
14700
+ const skillMd = await stub.getSkillFile(scope, name, "SKILL.md") ?? "";
14540
14701
  return { metadata, skillMd };
14541
14702
  },
14542
- readFile: (name, path) => stub.getSkillFile(name, path),
14543
- install: (files) => stub.upsertSkill(files),
14544
- setEnabled: (name, enabled) => stub.setSkillEnabled(name, enabled),
14545
- remove: (name) => stub.deleteSkill(name)
14703
+ readFile: (name, path) => stub.getSkillFile(scope, name, path),
14704
+ install: async (files) => {
14705
+ await requireMutationAdmin();
14706
+ return stub.upsertSkill(scope, files);
14707
+ },
14708
+ setEnabled: async (name, enabled) => {
14709
+ await requireMutationAdmin();
14710
+ return stub.setSkillEnabled(scope, name, enabled);
14711
+ },
14712
+ remove: async (name) => {
14713
+ await requireMutationAdmin();
14714
+ return stub.deleteSkill(scope, name);
14715
+ }
14546
14716
  };
14547
14717
  }
14548
14718
  // ─────────────────────────────────────────────────────────────────────────
@@ -14703,56 +14873,9 @@ var init_image_processing = __esm({
14703
14873
 
14704
14874
  // src/router/index.ts
14705
14875
  init_ThreadStateImpl();
14706
- function defineController2(controller) {
14707
- return controller;
14708
- }
14709
- function createThreadEndpointHandler(handler) {
14710
- return (async ({ req, params, env }) => {
14711
- const threadId = params.threadId ?? params.id;
14712
- if (!threadId) {
14713
- return Response.json({ error: "Thread ID required" }, { status: 400 });
14714
- }
14715
- try {
14716
- const agentBuilderId = env.AGENT_BUILDER.idFromName("singleton");
14717
- const agentBuilder = env.AGENT_BUILDER.get(agentBuilderId);
14718
- const metadata = await agentBuilder.getThread(threadId);
14719
- if (!metadata) {
14720
- return Response.json(
14721
- { error: `Thread not found: ${threadId}` },
14722
- { status: 404 }
14723
- );
14724
- }
14725
- const meta = metadata;
14726
- if (!meta.agent_id && meta.agent_name) {
14727
- meta.agent_id = meta.agent_name;
14728
- }
14729
- const durableId = env.AGENT_BUILDER_THREAD.idFromName(threadId);
14730
- const stub = env.AGENT_BUILDER_THREAD.get(durableId);
14731
- const instance = stub;
14732
- const threadState = ThreadStateImpl.fromThreadInstance(instance, metadata, {
14733
- env
14734
- });
14735
- const endpointParams = {};
14736
- for (const [key, value] of Object.entries(params)) {
14737
- if (key === "threadId" || key === "id" && !params.threadId) {
14738
- continue;
14739
- }
14740
- endpointParams[key === "_" ? "*" : key] = value;
14741
- }
14742
- const response = await handler(req, threadState, endpointParams);
14743
- await threadState.flushPendingEventDeliveries();
14744
- return response;
14745
- } catch (error) {
14746
- console.error(`Error in thread endpoint for ${threadId}:`, error);
14747
- return Response.json(
14748
- { error: error.message || "Internal server error" },
14749
- { status: 500 }
14750
- );
14751
- }
14752
- });
14753
- }
14754
14876
 
14755
14877
  // src/utils/auth.ts
14878
+ var SUPER_ADMIN_TOKEN_TTL_SECONDS = 12 * 60 * 60;
14756
14879
  async function hashToken(token) {
14757
14880
  const encoder = new TextEncoder();
14758
14881
  const data = encoder.encode(token);
@@ -14818,9 +14941,12 @@ async function verifySuperAdminToken(token, encryptionKey) {
14818
14941
  return false;
14819
14942
  }
14820
14943
  const timestamp = parseInt(parts[1], 10);
14944
+ if (!Number.isFinite(timestamp)) {
14945
+ return false;
14946
+ }
14821
14947
  const age = Date.now() - timestamp;
14822
- const maxAge = 30 * 24 * 60 * 60 * 1e3;
14823
- if (age > maxAge) {
14948
+ const maxAge = SUPER_ADMIN_TOKEN_TTL_SECONDS * 1e3;
14949
+ if (age < 0 || age > maxAge) {
14824
14950
  return false;
14825
14951
  }
14826
14952
  return true;
@@ -14834,13 +14960,191 @@ function base64ToBuffer(base64) {
14834
14960
  return bytes;
14835
14961
  }
14836
14962
 
14837
- // src/middleware/auth.ts
14838
- var CORS_HEADERS = {
14963
+ // src/routing/access-policy.ts
14964
+ var PUBLIC_ROUTES = /* @__PURE__ */ new Set([
14965
+ "/api/auth/bootstrap",
14966
+ "/api/auth/login",
14967
+ "/api/auth/config",
14968
+ "/api/auth/brolly-control",
14969
+ "/api/auth/platform-cleanup",
14970
+ "/api/auth/platform-replica",
14971
+ "/api/auth/sa/start",
14972
+ "/api/auth/sa/callback",
14973
+ "/api/config",
14974
+ "/api/auth/oauth/github",
14975
+ "/api/auth/oauth/google",
14976
+ "/api/auth/oauth/github/callback",
14977
+ "/api/auth/oauth/google/callback"
14978
+ ]);
14979
+ function isProviderIconRoute(routePath) {
14980
+ return /^\/api\/providers\/[^/]+\/icon$/.test(routePath);
14981
+ }
14982
+ function isHostedInstance(env) {
14983
+ const value = env && typeof env === "object" ? Reflect.get(env, "STANDARD_AGENTS_HOSTED") : void 0;
14984
+ if (typeof value === "string") {
14985
+ const normalized = value.trim().toLowerCase();
14986
+ return normalized !== "" && normalized !== "0" && normalized !== "false";
14987
+ }
14988
+ return Boolean(value);
14989
+ }
14990
+ function isTrustedLocalDevelopmentRequest(request, env) {
14991
+ if (isHostedInstance(env)) return false;
14992
+ let hostname;
14993
+ try {
14994
+ hostname = new URL(request.url).hostname.toLowerCase();
14995
+ } catch {
14996
+ return false;
14997
+ }
14998
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname.endsWith(".localhost") || hostname.endsWith(".orb.local");
14999
+ }
15000
+ function isPublicApiRoute(routePath, request, env) {
15001
+ if (PUBLIC_ROUTES.has(routePath)) return true;
15002
+ if (routePath === "/api/auth/device/start" || routePath.startsWith("/api/auth/device/")) {
15003
+ return true;
15004
+ }
15005
+ if (isProviderIconRoute(routePath)) {
15006
+ return true;
15007
+ }
15008
+ return routePath === "/api/platform-auth/bootstrap" && isTrustedLocalDevelopmentRequest(request, env);
15009
+ }
15010
+ function requestObservedOrigins(request) {
15011
+ const origins = /* @__PURE__ */ new Set();
15012
+ let url = null;
15013
+ try {
15014
+ url = new URL(request.url);
15015
+ origins.add(url.origin);
15016
+ } catch {
15017
+ url = null;
15018
+ }
15019
+ const forwardedHost = request.headers.get("x-forwarded-host")?.split(",")[0]?.trim();
15020
+ const host = forwardedHost || request.headers.get("host")?.trim();
15021
+ if (!host) return origins;
15022
+ const forwardedProto = request.headers.get("x-forwarded-proto")?.split(",")[0]?.trim().toLowerCase();
15023
+ const schemes = /* @__PURE__ */ new Set();
15024
+ if (forwardedProto === "http" || forwardedProto === "https") schemes.add(forwardedProto);
15025
+ if (url) schemes.add(url.protocol.replace(":", ""));
15026
+ if (schemes.size === 0) {
15027
+ schemes.add(host.endsWith(".orb.local") ? "https" : "http");
15028
+ }
15029
+ for (const scheme of schemes) {
15030
+ try {
15031
+ origins.add(new URL(`${scheme}://${host}`).origin);
15032
+ } catch {
15033
+ }
15034
+ }
15035
+ return origins;
15036
+ }
15037
+ function isSameOriginBrowserRequest(request) {
15038
+ const fetchSite = request.headers.get("Sec-Fetch-Site")?.trim().toLowerCase();
15039
+ if (fetchSite === "same-origin" || fetchSite === "none") {
15040
+ return true;
15041
+ }
15042
+ if (fetchSite) {
15043
+ return false;
15044
+ }
15045
+ const origin = request.headers.get("Origin")?.trim();
15046
+ if (!origin) return true;
15047
+ if (origin === "null") return false;
15048
+ try {
15049
+ return requestObservedOrigins(request).has(new URL(origin).origin);
15050
+ } catch {
15051
+ return false;
15052
+ }
15053
+ }
15054
+ function isAdminOnlyApiRoute(routePath, method = "GET") {
15055
+ if (routePath === "/api/hooks") return true;
15056
+ if (routePath === "/api/skills" || routePath.startsWith("/api/skills/")) return true;
15057
+ if (routePath === "/api/pack" || routePath.startsWith("/api/pack/")) return true;
15058
+ if (routePath === "/api/unpack" || routePath.startsWith("/api/unpack/")) return true;
15059
+ if (routePath === "/api/prompts" || routePath.startsWith("/api/prompts/")) return true;
15060
+ if (routePath === "/api/tools" || routePath.startsWith("/api/tools/")) return true;
15061
+ if (routePath === "/api/models" || routePath.startsWith("/api/models/")) return true;
15062
+ if (routePath === "/api/providers") return true;
15063
+ if (routePath.startsWith("/api/providers/")) {
15064
+ return !isProviderIconRoute(routePath);
15065
+ }
15066
+ if (routePath.startsWith("/api/agents/")) {
15067
+ return !/^\/api\/agents\/[^/]+\/tenvs$/.test(routePath);
15068
+ }
15069
+ if (routePath === "/api/agents" && method.toUpperCase() !== "GET") return true;
15070
+ return false;
15071
+ }
15072
+
15073
+ // src/routing/cors.ts
15074
+ var RUNTIME_CORS_HEADERS = {
14839
15075
  "Access-Control-Allow-Origin": "*",
14840
15076
  "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
14841
15077
  "Access-Control-Allow-Headers": "Content-Type, Authorization, X-Requested-With",
14842
15078
  "Access-Control-Max-Age": "86400"
14843
15079
  };
15080
+ var CORS_RESPONSE_HEADERS = [
15081
+ "Access-Control-Allow-Origin",
15082
+ "Access-Control-Allow-Methods",
15083
+ "Access-Control-Allow-Headers",
15084
+ "Access-Control-Max-Age",
15085
+ "Access-Control-Allow-Credentials",
15086
+ "Access-Control-Expose-Headers"
15087
+ ];
15088
+ function mutateCorsHeaders(headers, exposeCors) {
15089
+ if (exposeCors) {
15090
+ for (const [key, value] of Object.entries(RUNTIME_CORS_HEADERS)) {
15091
+ if (!headers.has(key)) headers.set(key, value);
15092
+ }
15093
+ return;
15094
+ }
15095
+ for (const key of CORS_RESPONSE_HEADERS) {
15096
+ headers.delete(key);
15097
+ }
15098
+ }
15099
+ function exposesRuntimeCors(request, env) {
15100
+ return !isTrustedLocalDevelopmentRequest(request, env);
15101
+ }
15102
+ function runtimeCorsHeaders(request, env) {
15103
+ return exposesRuntimeCors(request, env) ? { ...RUNTIME_CORS_HEADERS } : {};
15104
+ }
15105
+ function applyRuntimeCors(response, request, env) {
15106
+ if (response.status === 101) return response;
15107
+ const exposeCors = exposesRuntimeCors(request, env);
15108
+ try {
15109
+ mutateCorsHeaders(response.headers, exposeCors);
15110
+ return response;
15111
+ } catch {
15112
+ }
15113
+ const headers = new Headers(response.headers);
15114
+ mutateCorsHeaders(headers, exposeCors);
15115
+ return new Response(response.body, {
15116
+ status: response.status,
15117
+ statusText: response.statusText,
15118
+ headers
15119
+ });
15120
+ }
15121
+ function runtimePreflightResponse(request, env) {
15122
+ if (!exposesRuntimeCors(request, env)) {
15123
+ return Response.json(
15124
+ { error: "Cross-origin access is disabled for local development" },
15125
+ { status: 403, headers: { "Cache-Control": "no-store" } }
15126
+ );
15127
+ }
15128
+ return new Response(null, {
15129
+ status: 204,
15130
+ headers: RUNTIME_CORS_HEADERS
15131
+ });
15132
+ }
15133
+
15134
+ // src/middleware/auth.ts
15135
+ function endpointCallerFromAuth(auth) {
15136
+ if (!auth) return null;
15137
+ const { id, username, role, permissions, email, display_name, avatar_url } = auth.user;
15138
+ return {
15139
+ id,
15140
+ username,
15141
+ role,
15142
+ permissions: permissions ?? null,
15143
+ email: email ?? null,
15144
+ display_name: display_name ?? null,
15145
+ avatar_url: avatar_url ?? null
15146
+ };
15147
+ }
14844
15148
  function parsePermissions(value) {
14845
15149
  if (Array.isArray(value)) return value.filter((p) => typeof p === "string");
14846
15150
  if (typeof value === "string" && value.trim()) {
@@ -14861,43 +15165,88 @@ function extractBearerToken(request) {
14861
15165
  if (cookieToken) {
14862
15166
  return cookieToken;
14863
15167
  }
14864
- const isWebSocket = request.headers.get("upgrade")?.toLowerCase() === "websocket";
14865
- if (isWebSocket) {
14866
- try {
14867
- const url = new URL(request.url);
14868
- const token = url.searchParams.get("token");
14869
- if (token) {
14870
- return token;
14871
- }
14872
- } catch (err) {
14873
- }
14874
- }
14875
15168
  return null;
14876
15169
  }
14877
15170
  function getAgentBuilder(env) {
14878
15171
  const agentBuilderId = env.AGENT_BUILDER.idFromName("singleton");
14879
15172
  return env.AGENT_BUILDER.get(agentBuilderId);
14880
15173
  }
15174
+ function parseWebSocketTicketAuth(authJson) {
15175
+ let parsed;
15176
+ try {
15177
+ parsed = JSON.parse(authJson);
15178
+ } catch {
15179
+ return null;
15180
+ }
15181
+ if (!parsed || typeof parsed !== "object") return null;
15182
+ const record = parsed;
15183
+ if (!record.user || typeof record.user !== "object") return null;
15184
+ const user = record.user;
15185
+ if (typeof user.id !== "string" || typeof user.username !== "string" || typeof user.role !== "string" || record.authType !== "super_admin" && record.authType !== "session" && record.authType !== "api_key") {
15186
+ return null;
15187
+ }
15188
+ const permissions = user.permissions;
15189
+ if (permissions !== void 0 && permissions !== null && (!Array.isArray(permissions) || !permissions.every((permission) => typeof permission === "string"))) {
15190
+ return null;
15191
+ }
15192
+ if (record.credentialId !== void 0 && typeof record.credentialId !== "string") return null;
15193
+ return {
15194
+ user: {
15195
+ id: user.id,
15196
+ username: user.username,
15197
+ role: user.role,
15198
+ permissions,
15199
+ platform_user_id: typeof user.platform_user_id === "string" ? user.platform_user_id : null,
15200
+ email: typeof user.email === "string" ? user.email : null,
15201
+ display_name: typeof user.display_name === "string" ? user.display_name : null,
15202
+ avatar_url: typeof user.avatar_url === "string" ? user.avatar_url : null
15203
+ },
15204
+ authType: record.authType,
15205
+ credentialId: record.credentialId
15206
+ };
15207
+ }
15208
+ async function consumeWebSocketTicket(request, env) {
15209
+ if (request.headers.get("upgrade")?.toLowerCase() !== "websocket") return null;
15210
+ let url;
15211
+ try {
15212
+ url = new URL(request.url);
15213
+ } catch {
15214
+ return null;
15215
+ }
15216
+ const ticket = url.searchParams.get("ticket");
15217
+ if (!ticket) return null;
15218
+ const authJson = await getAgentBuilder(env).consumeWebSocketTicket({
15219
+ ticket_hash: await hashToken(ticket),
15220
+ pathname: url.pathname
15221
+ });
15222
+ return typeof authJson === "string" ? parseWebSocketTicketAuth(authJson) : null;
15223
+ }
14881
15224
  async function authenticate(request, env) {
15225
+ const ticketAuth = await consumeWebSocketTicket(request, env);
15226
+ if (ticketAuth) return ticketAuth;
14882
15227
  const token = extractBearerToken(request);
14883
15228
  if (!token) {
14884
15229
  return null;
14885
15230
  }
14886
- if (env.SUPER_ADMIN_PASSWORD && env.ENCRYPTION_KEY) {
15231
+ if (typeof env.SUPER_ADMIN_PASSWORD === "string" && typeof env.ENCRYPTION_KEY === "string") {
14887
15232
  if (token.includes(".")) {
14888
15233
  const isValidSuperAdmin = await verifySuperAdminToken(
14889
15234
  token,
14890
15235
  env.ENCRYPTION_KEY
14891
15236
  );
14892
15237
  if (isValidSuperAdmin) {
14893
- return {
14894
- user: {
14895
- id: "super_admin",
14896
- username: "admin",
14897
- role: "admin"
14898
- },
14899
- authType: "super_admin"
14900
- };
15238
+ const tokenHash = await hashToken(token);
15239
+ const agentBuilder = getAgentBuilder(env);
15240
+ if (await agentBuilder.validateSuperAdminSession(tokenHash)) {
15241
+ return {
15242
+ user: {
15243
+ id: "super_admin",
15244
+ username: "admin",
15245
+ role: "admin"
15246
+ },
15247
+ authType: "super_admin"
15248
+ };
15249
+ }
14901
15250
  }
14902
15251
  }
14903
15252
  }
@@ -14942,7 +15291,8 @@ async function authenticate(request, env) {
14942
15291
  display_name: user.display_name ?? null,
14943
15292
  avatar_url: user.avatar_url ?? null
14944
15293
  },
14945
- authType: "api_key"
15294
+ authType: "api_key",
15295
+ credentialId: apiKey.id
14946
15296
  };
14947
15297
  }
14948
15298
  }
@@ -14954,7 +15304,10 @@ async function requireAuth(request, env) {
14954
15304
  if (!authContext) {
14955
15305
  return new Response(JSON.stringify({ error: "Unauthorized" }), {
14956
15306
  status: 401,
14957
- headers: { "Content-Type": "application/json", ...CORS_HEADERS }
15307
+ headers: {
15308
+ "Content-Type": "application/json",
15309
+ ...runtimeCorsHeaders(request, env)
15310
+ }
14958
15311
  });
14959
15312
  }
14960
15313
  return authContext;
@@ -14969,12 +15322,70 @@ async function requireAdmin(request, env) {
14969
15322
  JSON.stringify({ error: "Forbidden: Admin access required" }),
14970
15323
  {
14971
15324
  status: 403,
14972
- headers: { "Content-Type": "application/json", ...CORS_HEADERS }
15325
+ headers: {
15326
+ "Content-Type": "application/json",
15327
+ ...runtimeCorsHeaders(request, env)
15328
+ }
14973
15329
  }
14974
15330
  );
14975
15331
  }
14976
15332
  return result;
14977
15333
  }
15334
+ function defineController2(controller) {
15335
+ return controller;
15336
+ }
15337
+ function createThreadEndpointHandler(handler) {
15338
+ return (async (context) => {
15339
+ const { req, params, env } = context;
15340
+ const threadId = params.threadId ?? params.id;
15341
+ if (!threadId) {
15342
+ return Response.json({ error: "Thread ID required" }, { status: 400 });
15343
+ }
15344
+ const endpointAuth = {
15345
+ caller: endpointCallerFromAuth(context.auth),
15346
+ threadAccess: Boolean(
15347
+ context.threadAccess && context.threadAccess.threadId === threadId
15348
+ )
15349
+ };
15350
+ try {
15351
+ const agentBuilderId = env.AGENT_BUILDER.idFromName("singleton");
15352
+ const agentBuilder = env.AGENT_BUILDER.get(agentBuilderId);
15353
+ const metadata = await agentBuilder.getThread(threadId);
15354
+ if (!metadata) {
15355
+ return Response.json(
15356
+ { error: `Thread not found: ${threadId}` },
15357
+ { status: 404 }
15358
+ );
15359
+ }
15360
+ const meta = metadata;
15361
+ if (!meta.agent_id && meta.agent_name) {
15362
+ meta.agent_id = meta.agent_name;
15363
+ }
15364
+ const durableId = env.AGENT_BUILDER_THREAD.idFromName(threadId);
15365
+ const stub = env.AGENT_BUILDER_THREAD.get(durableId);
15366
+ const instance = stub;
15367
+ const threadState = ThreadStateImpl.fromThreadInstance(instance, metadata, {
15368
+ env
15369
+ });
15370
+ const endpointParams = {};
15371
+ for (const [key, value] of Object.entries(params)) {
15372
+ if (key === "threadId" || key === "id" && !params.threadId) {
15373
+ continue;
15374
+ }
15375
+ endpointParams[key === "_" ? "*" : key] = value;
15376
+ }
15377
+ const response = await handler(req, threadState, endpointParams, endpointAuth);
15378
+ await threadState.flushPendingEventDeliveries();
15379
+ return response;
15380
+ } catch (error) {
15381
+ console.error(`Error in thread endpoint for ${threadId}:`, error);
15382
+ return Response.json(
15383
+ { error: error.message || "Internal server error" },
15384
+ { status: 500 }
15385
+ );
15386
+ }
15387
+ });
15388
+ }
14978
15389
 
14979
15390
  // src/utils/permissions.ts
14980
15391
  function canAccessThread(auth, thread) {
@@ -14987,80 +15398,6 @@ function canAccessThread(auth, thread) {
14987
15398
  return thread.user_id === auth.user.id;
14988
15399
  }
14989
15400
 
14990
- // src/routing/access-policy.ts
14991
- var PUBLIC_ROUTES = /* @__PURE__ */ new Set([
14992
- "/api/auth/bootstrap",
14993
- "/api/auth/login",
14994
- "/api/auth/config",
14995
- "/api/auth/brolly-control",
14996
- "/api/auth/platform-cleanup",
14997
- "/api/auth/platform-replica",
14998
- "/api/auth/sa/start",
14999
- "/api/auth/sa/callback",
15000
- "/api/config",
15001
- "/api/auth/oauth/github",
15002
- "/api/auth/oauth/google",
15003
- "/api/auth/oauth/github/callback",
15004
- "/api/auth/oauth/google/callback",
15005
- "/api/hooks"
15006
- ]);
15007
- function isProviderIconRoute(routePath) {
15008
- return /^\/api\/providers\/[^/]+\/icon$/.test(routePath);
15009
- }
15010
- function isPublicThreadFileRoute(routePath, method) {
15011
- if (method !== "GET" && method !== "HEAD") return false;
15012
- return /^\/api\/threads\/[^/]+\/public\/.+/.test(routePath);
15013
- }
15014
- function isHostedInstance(env) {
15015
- const value = env?.STANDARD_AGENTS_HOSTED;
15016
- if (typeof value === "string") {
15017
- const normalized = value.trim().toLowerCase();
15018
- return normalized !== "" && normalized !== "0" && normalized !== "false";
15019
- }
15020
- return Boolean(value);
15021
- }
15022
- function isTrustedLocalDevelopmentRequest(request, env) {
15023
- if (isHostedInstance(env)) return false;
15024
- let hostname;
15025
- try {
15026
- hostname = new URL(request.url).hostname.toLowerCase();
15027
- } catch {
15028
- return false;
15029
- }
15030
- return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname.endsWith(".localhost") || hostname.endsWith(".orb.local");
15031
- }
15032
- function isPublicApiRoute(routePath, request, env) {
15033
- if (PUBLIC_ROUTES.has(routePath)) return true;
15034
- if (routePath === "/api/auth/device/start" || routePath.startsWith("/api/auth/device/")) {
15035
- return true;
15036
- }
15037
- if (isProviderIconRoute(routePath)) {
15038
- return true;
15039
- }
15040
- if (isPublicThreadFileRoute(routePath, request.method.toUpperCase())) {
15041
- return true;
15042
- }
15043
- if (!isTrustedLocalDevelopmentRequest(request, env)) return false;
15044
- return routePath === "/api/threads" || routePath.startsWith("/api/threads/") || routePath === "/api/platform" || routePath.startsWith("/api/platform/") || routePath === "/api/platform-session" || routePath.startsWith("/api/platform-session/") || routePath === "/api/platform-auth" || routePath.startsWith("/api/platform-auth/");
15045
- }
15046
- function isAdminOnlyApiRoute(routePath, method = "GET") {
15047
- if (routePath === "/api/skills" || routePath.startsWith("/api/skills/")) return true;
15048
- if (routePath === "/api/pack" || routePath.startsWith("/api/pack/")) return true;
15049
- if (routePath === "/api/unpack" || routePath.startsWith("/api/unpack/")) return true;
15050
- if (routePath === "/api/prompts" || routePath.startsWith("/api/prompts/")) return true;
15051
- if (routePath === "/api/tools" || routePath.startsWith("/api/tools/")) return true;
15052
- if (routePath === "/api/models" || routePath.startsWith("/api/models/")) return true;
15053
- if (routePath === "/api/providers") return true;
15054
- if (routePath.startsWith("/api/providers/")) {
15055
- return !isProviderIconRoute(routePath);
15056
- }
15057
- if (routePath.startsWith("/api/agents/")) {
15058
- return !/^\/api\/agents\/[^/]+\/tenvs$/.test(routePath);
15059
- }
15060
- if (routePath === "/api/agents" && method.toUpperCase() !== "GET") return true;
15061
- return false;
15062
- }
15063
-
15064
15401
  // src/runtime.ts
15065
15402
  init_client_forwarding();
15066
15403
 
@@ -15973,8 +16310,37 @@ var migration35 = {
15973
16310
  }
15974
16311
  };
15975
16312
 
16313
+ // src/durable-objects/migrations/036_add_tool_delivery_tokens.ts
16314
+ var migration36 = {
16315
+ version: 36,
16316
+ name: "add_tool_delivery_tokens",
16317
+ async up(sql) {
16318
+ sql.exec(`
16319
+ CREATE TABLE IF NOT EXISTS pending_client_calls (
16320
+ tool_call_id TEXT PRIMARY KEY,
16321
+ tool TEXT NOT NULL,
16322
+ args TEXT,
16323
+ request_permission TEXT,
16324
+ risk INTEGER,
16325
+ side TEXT NOT NULL DEFAULT 'a',
16326
+ status TEXT NOT NULL DEFAULT 'pending',
16327
+ created_at INTEGER NOT NULL,
16328
+ dispatched_client_id TEXT,
16329
+ dispatched_generation INTEGER NOT NULL DEFAULT 0
16330
+ )
16331
+ `);
16332
+ const columns = sql.exec(
16333
+ `PRAGMA table_info(pending_client_calls)`
16334
+ ).toArray();
16335
+ if (!columns.some((column) => column.name === "delivery_token_hash")) {
16336
+ sql.exec(`ALTER TABLE pending_client_calls ADD COLUMN delivery_token_hash TEXT`);
16337
+ }
16338
+ sql.exec(`UPDATE _metadata SET value = '36' WHERE key = 'schema_version'`);
16339
+ }
16340
+ };
16341
+
15976
16342
  // src/durable-objects/migrations/index.ts
15977
- var migrations = [migration, migration2, migration3, migration4, migration5, migration6, migration7, migration8, migration9, migration10, migration11, migration12, migration13, migration14, migration15, migration16, migration17, migration18, migration19, migration20, migration21, migration22, migration23, migration24, migration25, migration26, migration27, migration28, migration29, migration30, migration31, migration32, migration33, migration34, migration35];
16343
+ var migrations = [migration, migration2, migration3, migration4, migration5, migration6, migration7, migration8, migration9, migration10, migration11, migration12, migration13, migration14, migration15, migration16, migration17, migration18, migration19, migration20, migration21, migration22, migration23, migration24, migration25, migration26, migration27, migration28, migration29, migration30, migration31, migration32, migration33, migration34, migration35, migration36];
15978
16344
  var LATEST_SCHEMA_VERSION = migrations.length;
15979
16345
 
15980
16346
  // src/durable-objects/subagent-reconcile.ts
@@ -16810,7 +17176,7 @@ function eventSocketCanReceive(attachment, ownerUserId) {
16810
17176
  }
16811
17177
 
16812
17178
  // src/durable-objects/agentbuilder-migrations/0001_initial.ts
16813
- var migration36 = {
17179
+ var migration37 = {
16814
17180
  version: 1,
16815
17181
  async up(sql) {
16816
17182
  sql.exec(`
@@ -16909,7 +17275,7 @@ var migration36 = {
16909
17275
  };
16910
17276
 
16911
17277
  // src/durable-objects/agentbuilder-migrations/0002_add_tenvs_properties.ts
16912
- var migration37 = {
17278
+ var migration38 = {
16913
17279
  version: 2,
16914
17280
  async up(sql) {
16915
17281
  sql.exec(`ALTER TABLE threads ADD COLUMN tenvs TEXT`);
@@ -16921,7 +17287,7 @@ var migration37 = {
16921
17287
  };
16922
17288
 
16923
17289
  // src/durable-objects/agentbuilder-migrations/0003_add_parent_and_terminated.ts
16924
- var migration38 = {
17290
+ var migration39 = {
16925
17291
  version: 3,
16926
17292
  async up(sql) {
16927
17293
  sql.exec(`ALTER TABLE threads ADD COLUMN parent TEXT REFERENCES threads(id)`);
@@ -16934,7 +17300,7 @@ var migration38 = {
16934
17300
  };
16935
17301
 
16936
17302
  // src/durable-objects/agentbuilder-migrations/0004_add_env_tables.ts
16937
- var migration39 = {
17303
+ var migration40 = {
16938
17304
  version: 4,
16939
17305
  async up(sql) {
16940
17306
  sql.exec(`ALTER TABLE threads ADD COLUMN env TEXT`);
@@ -16958,7 +17324,7 @@ var migration39 = {
16958
17324
  };
16959
17325
 
16960
17326
  // src/durable-objects/agentbuilder-migrations/0005_add_pending_subagent_env_requests.ts
16961
- var migration40 = {
17327
+ var migration41 = {
16962
17328
  version: 5,
16963
17329
  async up(sql) {
16964
17330
  sql.exec(`
@@ -16984,7 +17350,7 @@ var migration40 = {
16984
17350
  };
16985
17351
 
16986
17352
  // src/durable-objects/agentbuilder-migrations/0006_add_env_value_types.ts
16987
- var migration41 = {
17353
+ var migration42 = {
16988
17354
  version: 6,
16989
17355
  async up(sql) {
16990
17356
  sql.exec(
@@ -16999,7 +17365,7 @@ var migration41 = {
16999
17365
  };
17000
17366
 
17001
17367
  // src/durable-objects/agentbuilder-migrations/0007_platform_identity_replica.ts
17002
- var migration42 = {
17368
+ var migration43 = {
17003
17369
  version: 7,
17004
17370
  async up(sql) {
17005
17371
  sql.exec(`ALTER TABLE users ADD COLUMN platform_user_id TEXT`);
@@ -17028,7 +17394,7 @@ var migration42 = {
17028
17394
  };
17029
17395
 
17030
17396
  // src/durable-objects/agentbuilder-migrations/0008_add_skills.ts
17031
- var migration43 = {
17397
+ var migration44 = {
17032
17398
  version: 8,
17033
17399
  async up(sql) {
17034
17400
  sql.exec(`
@@ -17059,7 +17425,7 @@ var migration43 = {
17059
17425
  };
17060
17426
 
17061
17427
  // src/durable-objects/agentbuilder-migrations/0009_add_user_permissions.ts
17062
- var migration44 = {
17428
+ var migration45 = {
17063
17429
  version: 9,
17064
17430
  async up(sql) {
17065
17431
  sql.exec(`ALTER TABLE users ADD COLUMN permissions TEXT`);
@@ -17070,7 +17436,7 @@ var migration44 = {
17070
17436
  };
17071
17437
 
17072
17438
  // src/durable-objects/agentbuilder-migrations/0010_add_device_logins.ts
17073
- var migration45 = {
17439
+ var migration46 = {
17074
17440
  version: 10,
17075
17441
  async up(sql) {
17076
17442
  sql.exec(`
@@ -17090,7 +17456,7 @@ var migration45 = {
17090
17456
  };
17091
17457
 
17092
17458
  // src/durable-objects/agentbuilder-migrations/0011_add_user_values.ts
17093
- var migration46 = {
17459
+ var migration47 = {
17094
17460
  version: 11,
17095
17461
  async up(sql) {
17096
17462
  sql.exec(`
@@ -17113,7 +17479,7 @@ var migration46 = {
17113
17479
  };
17114
17480
 
17115
17481
  // src/durable-objects/agentbuilder-migrations/0012_allow_api_instance_role.ts
17116
- var migration47 = {
17482
+ var migration48 = {
17117
17483
  version: 12,
17118
17484
  async up(sql) {
17119
17485
  sql.exec(`CREATE TABLE oauth_accounts_0012_backup AS SELECT * FROM oauth_accounts`);
@@ -17223,7 +17589,7 @@ var migration47 = {
17223
17589
  };
17224
17590
 
17225
17591
  // src/durable-objects/agentbuilder-migrations/0013_add_thread_activity.ts
17226
- var migration48 = {
17592
+ var migration49 = {
17227
17593
  version: 13,
17228
17594
  async up(sql) {
17229
17595
  sql.exec(`ALTER TABLE threads ADD COLUMN last_activity_at INTEGER`);
@@ -17239,7 +17605,7 @@ var migration48 = {
17239
17605
  };
17240
17606
 
17241
17607
  // src/durable-objects/agentbuilder-migrations/0014_thread_activity_scale.ts
17242
- var migration49 = {
17608
+ var migration50 = {
17243
17609
  version: 14,
17244
17610
  async up(sql) {
17245
17611
  sql.exec(`ALTER TABLE threads ADD COLUMN last_message_preview TEXT`);
@@ -17257,7 +17623,7 @@ var migration49 = {
17257
17623
  };
17258
17624
 
17259
17625
  // src/durable-objects/agentbuilder-migrations/0015_clear_envelope_previews.ts
17260
- var migration50 = {
17626
+ var migration51 = {
17261
17627
  version: 15,
17262
17628
  async up(sql) {
17263
17629
  sql.exec(
@@ -17271,7 +17637,7 @@ var migration50 = {
17271
17637
  };
17272
17638
 
17273
17639
  // src/durable-objects/agentbuilder-migrations/0016_add_origin_agent.ts
17274
- var migration51 = {
17640
+ var migration52 = {
17275
17641
  version: 16,
17276
17642
  async up(sql) {
17277
17643
  sql.exec(`ALTER TABLE threads ADD COLUMN origin_agent_name TEXT`);
@@ -17286,7 +17652,7 @@ var migration51 = {
17286
17652
  };
17287
17653
 
17288
17654
  // src/durable-objects/agentbuilder-migrations/0017_add_thread_message_recency.ts
17289
- var migration52 = {
17655
+ var migration53 = {
17290
17656
  version: 17,
17291
17657
  async up(sql) {
17292
17658
  sql.exec(`ALTER TABLE threads ADD COLUMN last_message_at INTEGER`);
@@ -17297,10 +17663,132 @@ var migration52 = {
17297
17663
  }
17298
17664
  };
17299
17665
 
17666
+ // src/durable-objects/agentbuilder-migrations/0018_auth_security.ts
17667
+ var migration54 = {
17668
+ version: 18,
17669
+ async up(sql) {
17670
+ sql.exec(`
17671
+ CREATE TABLE IF NOT EXISTS super_admin_sessions (
17672
+ token_hash TEXT PRIMARY KEY,
17673
+ created_at INTEGER NOT NULL,
17674
+ expires_at INTEGER NOT NULL
17675
+ )
17676
+ `);
17677
+ sql.exec(`
17678
+ CREATE INDEX IF NOT EXISTS idx_super_admin_sessions_expires
17679
+ ON super_admin_sessions(expires_at)
17680
+ `);
17681
+ sql.exec(`
17682
+ CREATE TABLE IF NOT EXISTS auth_rate_limits (
17683
+ bucket_key TEXT PRIMARY KEY,
17684
+ attempts INTEGER NOT NULL,
17685
+ reset_at INTEGER NOT NULL
17686
+ )
17687
+ `);
17688
+ sql.exec(`
17689
+ CREATE INDEX IF NOT EXISTS idx_auth_rate_limits_reset
17690
+ ON auth_rate_limits(reset_at)
17691
+ `);
17692
+ sql.exec(`
17693
+ INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '18')
17694
+ `);
17695
+ }
17696
+ };
17697
+
17698
+ // src/durable-objects/agentbuilder-migrations/0019_scope_skills.ts
17699
+ var migration55 = {
17700
+ version: 19,
17701
+ async up(sql) {
17702
+ sql.exec(`
17703
+ CREATE TABLE skills_0019 (
17704
+ owner_id TEXT NOT NULL,
17705
+ name TEXT NOT NULL,
17706
+ description TEXT NOT NULL,
17707
+ version TEXT,
17708
+ enabled INTEGER NOT NULL DEFAULT 1,
17709
+ shared INTEGER NOT NULL DEFAULT 0,
17710
+ created_at INTEGER DEFAULT (strftime('%s', 'now')),
17711
+ updated_at INTEGER DEFAULT (strftime('%s', 'now')),
17712
+ PRIMARY KEY (owner_id, name)
17713
+ )
17714
+ `);
17715
+ sql.exec(`
17716
+ INSERT INTO skills_0019 (
17717
+ owner_id, name, description, version, enabled, shared, created_at, updated_at
17718
+ )
17719
+ SELECT '__instance_shared__', name, description, version, enabled, 1, created_at, updated_at
17720
+ FROM skills
17721
+ `);
17722
+ sql.exec(`
17723
+ CREATE TABLE skill_files_0019 (
17724
+ owner_id TEXT NOT NULL,
17725
+ skill_name TEXT NOT NULL,
17726
+ path TEXT NOT NULL,
17727
+ content TEXT NOT NULL,
17728
+ size INTEGER NOT NULL,
17729
+ PRIMARY KEY (owner_id, skill_name, path),
17730
+ FOREIGN KEY (owner_id, skill_name) REFERENCES skills_0019(owner_id, name) ON DELETE CASCADE
17731
+ )
17732
+ `);
17733
+ sql.exec(`
17734
+ INSERT INTO skill_files_0019 (owner_id, skill_name, path, content, size)
17735
+ SELECT '__instance_shared__', skill_name, path, content, size FROM skill_files
17736
+ `);
17737
+ sql.exec(`DROP TABLE skill_files`);
17738
+ sql.exec(`DROP TABLE skills`);
17739
+ sql.exec(`ALTER TABLE skills_0019 RENAME TO skills`);
17740
+ sql.exec(`ALTER TABLE skill_files_0019 RENAME TO skill_files`);
17741
+ sql.exec(`CREATE INDEX idx_skills_owner_enabled ON skills(owner_id, enabled)`);
17742
+ sql.exec(`CREATE INDEX idx_skills_shared_enabled ON skills(shared, enabled)`);
17743
+ sql.exec(`CREATE INDEX idx_skill_files_skill ON skill_files(owner_id, skill_name)`);
17744
+ sql.exec(`INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '19')`);
17745
+ }
17746
+ };
17747
+
17748
+ // src/durable-objects/agentbuilder-migrations/0020_websocket_tickets.ts
17749
+ var migration56 = {
17750
+ version: 20,
17751
+ async up(sql) {
17752
+ sql.exec(`
17753
+ CREATE TABLE websocket_tickets (
17754
+ ticket_hash TEXT PRIMARY KEY,
17755
+ pathname TEXT NOT NULL,
17756
+ auth_json TEXT NOT NULL,
17757
+ created_at INTEGER NOT NULL,
17758
+ expires_at INTEGER NOT NULL
17759
+ )
17760
+ `);
17761
+ sql.exec(`
17762
+ CREATE INDEX idx_websocket_tickets_expires
17763
+ ON websocket_tickets(expires_at)
17764
+ `);
17765
+ sql.exec(`INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '20')`);
17766
+ }
17767
+ };
17768
+
17769
+ // src/durable-objects/agentbuilder-migrations/0021_platform_handoff_jtis.ts
17770
+ var migration57 = {
17771
+ version: 21,
17772
+ async up(sql) {
17773
+ sql.exec(`
17774
+ CREATE TABLE platform_handoff_jtis (
17775
+ jti TEXT PRIMARY KEY,
17776
+ consumed_at INTEGER NOT NULL,
17777
+ expires_at INTEGER NOT NULL
17778
+ )
17779
+ `);
17780
+ sql.exec(`
17781
+ CREATE INDEX idx_platform_handoff_jtis_expires
17782
+ ON platform_handoff_jtis(expires_at)
17783
+ `);
17784
+ sql.exec(`INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '21')`);
17785
+ }
17786
+ };
17787
+
17300
17788
  // src/durable-objects/agentbuilder-migrations/index.ts
17301
- var migrations2 = [migration36, migration37, migration38, migration39, migration40, migration41, migration42, migration43, migration44, migration45, migration46, migration47, migration48, migration49, migration50, migration51, migration52];
17789
+ var migrations2 = [migration37, migration38, migration39, migration40, migration41, migration42, migration43, migration44, migration45, migration46, migration47, migration48, migration49, migration50, migration51, migration52, migration53, migration54, migration55, migration56, migration57];
17302
17790
  var LATEST_SCHEMA_VERSION2 = migrations2.reduce(
17303
- (max, migration53) => Math.max(max, migration53.version),
17791
+ (max, migration58) => Math.max(max, migration58.version),
17304
17792
  0
17305
17793
  );
17306
17794
 
@@ -17805,6 +18293,7 @@ function validateSkillFiles(files) {
17805
18293
  }
17806
18294
 
17807
18295
  // src/durable-objects/DurableAgentBuilder.ts
18296
+ init_scope();
17808
18297
  var ENV_TYPES_PROPERTY = "__agentbuilder_env_types";
17809
18298
  function parseStoredPermissions(value) {
17810
18299
  if (typeof value !== "string" || !value.trim()) return null;
@@ -18397,9 +18886,9 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
18397
18886
  }
18398
18887
  }
18399
18888
  async runMigrations(fromVersion) {
18400
- for (const migration53 of migrations2) {
18401
- if (migration53.version > fromVersion) {
18402
- await migration53.up(this.ctx.storage.sql);
18889
+ for (const migration58 of migrations2) {
18890
+ if (migration58.version > fromVersion) {
18891
+ await migration58.up(this.ctx.storage.sql);
18403
18892
  }
18404
18893
  }
18405
18894
  }
@@ -20348,6 +20837,150 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
20348
20837
  tokenHash
20349
20838
  );
20350
20839
  }
20840
+ /** Register a short-lived super-admin token so it can be revoked alone. */
20841
+ async createSuperAdminSession(params) {
20842
+ await this.ensureMigrated();
20843
+ const now = Math.floor(Date.now() / 1e3);
20844
+ await this.ctx.storage.sql.exec(
20845
+ `DELETE FROM super_admin_sessions WHERE expires_at <= ?`,
20846
+ now
20847
+ );
20848
+ await this.ctx.storage.sql.exec(
20849
+ `INSERT INTO super_admin_sessions (token_hash, created_at, expires_at)
20850
+ VALUES (?, ?, ?)`,
20851
+ params.token_hash,
20852
+ now,
20853
+ params.expires_at
20854
+ );
20855
+ }
20856
+ /** A valid signature is insufficient: the session must still be registered. */
20857
+ async validateSuperAdminSession(tokenHash) {
20858
+ await this.ensureMigrated();
20859
+ const now = Math.floor(Date.now() / 1e3);
20860
+ const cursor = await this.ctx.storage.sql.exec(
20861
+ `SELECT 1 AS found FROM super_admin_sessions
20862
+ WHERE token_hash = ? AND expires_at > ?`,
20863
+ tokenHash,
20864
+ now
20865
+ );
20866
+ return cursor.toArray().length > 0;
20867
+ }
20868
+ /** Revoke only the presented super-admin session; no key rotation required. */
20869
+ async deleteSuperAdminSession(tokenHash) {
20870
+ await this.ensureMigrated();
20871
+ await this.ctx.storage.sql.exec(
20872
+ `DELETE FROM super_admin_sessions WHERE token_hash = ?`,
20873
+ tokenHash
20874
+ );
20875
+ }
20876
+ /** Register a path-bound bearer ticket for one WebSocket handshake. */
20877
+ async createWebSocketTicket(params) {
20878
+ await this.ensureMigrated();
20879
+ const now = Math.floor(Date.now() / 1e3);
20880
+ this.ctx.storage.sql.exec(
20881
+ `DELETE FROM websocket_tickets WHERE expires_at <= ?`,
20882
+ now
20883
+ );
20884
+ this.ctx.storage.sql.exec(
20885
+ `INSERT INTO websocket_tickets (ticket_hash, pathname, auth_json, created_at, expires_at)
20886
+ VALUES (?, ?, ?, ?, ?)`,
20887
+ params.ticket_hash,
20888
+ params.pathname,
20889
+ params.auth_json,
20890
+ now,
20891
+ params.expires_at
20892
+ );
20893
+ }
20894
+ /**
20895
+ * Atomically consume a WebSocket ticket. Deleting before validating its path
20896
+ * means even a mistaken or malicious first presentation burns the bearer.
20897
+ */
20898
+ async consumeWebSocketTicket(params) {
20899
+ await this.ensureMigrated();
20900
+ const cursor = this.ctx.storage.sql.exec(
20901
+ `DELETE FROM websocket_tickets
20902
+ WHERE ticket_hash = ?
20903
+ RETURNING pathname, auth_json, expires_at`,
20904
+ params.ticket_hash
20905
+ );
20906
+ const ticket = cursor.toArray()[0];
20907
+ const now = Math.floor(Date.now() / 1e3);
20908
+ if (!ticket || ticket.expires_at <= now || ticket.pathname !== params.pathname) {
20909
+ return null;
20910
+ }
20911
+ return ticket.auth_json;
20912
+ }
20913
+ /** Atomically accept an unexpired platform handoff identifier once. */
20914
+ async consumePlatformHandoffJti(params) {
20915
+ await this.ensureMigrated();
20916
+ const jti = params.jti.trim();
20917
+ const expiresAt = Math.floor(params.expires_at);
20918
+ const now = Math.floor(Date.now() / 1e3);
20919
+ if (!jti || jti.length > 200 || !Number.isSafeInteger(expiresAt) || expiresAt <= now) {
20920
+ return false;
20921
+ }
20922
+ this.ctx.storage.sql.exec(
20923
+ `DELETE FROM platform_handoff_jtis WHERE expires_at <= ?`,
20924
+ now
20925
+ );
20926
+ const cursor = this.ctx.storage.sql.exec(
20927
+ `INSERT OR IGNORE INTO platform_handoff_jtis (jti, consumed_at, expires_at)
20928
+ VALUES (?, ?, ?)`,
20929
+ jti,
20930
+ now,
20931
+ expiresAt
20932
+ );
20933
+ return cursor.rowsWritten > 0;
20934
+ }
20935
+ /**
20936
+ * Atomically consume one attempt from a fixed-window authentication bucket.
20937
+ * The singleton Durable Object serializes calls, preventing parallel bypass.
20938
+ */
20939
+ async consumeAuthRateLimit(params) {
20940
+ await this.ensureMigrated();
20941
+ const key = params.key.trim();
20942
+ const limit = Math.floor(params.limit);
20943
+ const windowSeconds = Math.floor(params.window_seconds);
20944
+ if (!key || key.length > 160 || limit < 1 || limit > 1e4 || windowSeconds < 1 || windowSeconds > 86400) {
20945
+ throw new Error("Invalid authentication rate-limit bucket");
20946
+ }
20947
+ const now = Math.floor(Date.now() / 1e3);
20948
+ await this.ctx.storage.sql.exec(
20949
+ `DELETE FROM auth_rate_limits WHERE reset_at <= ?`,
20950
+ now
20951
+ );
20952
+ const cursor = await this.ctx.storage.sql.exec(
20953
+ `SELECT attempts, reset_at FROM auth_rate_limits WHERE bucket_key = ?`,
20954
+ key
20955
+ );
20956
+ const current = cursor.toArray()[0];
20957
+ if (!current || current.reset_at <= now) {
20958
+ const resetAt = now + windowSeconds;
20959
+ await this.ctx.storage.sql.exec(
20960
+ `INSERT INTO auth_rate_limits (bucket_key, attempts, reset_at)
20961
+ VALUES (?, 1, ?)
20962
+ ON CONFLICT(bucket_key) DO UPDATE SET attempts = 1, reset_at = excluded.reset_at`,
20963
+ key,
20964
+ resetAt
20965
+ );
20966
+ return { allowed: true, retry_after: windowSeconds, remaining: limit - 1 };
20967
+ }
20968
+ const retryAfter = Math.max(1, current.reset_at - now);
20969
+ if (current.attempts >= limit) {
20970
+ return { allowed: false, retry_after: retryAfter, remaining: 0 };
20971
+ }
20972
+ const attempts = current.attempts + 1;
20973
+ await this.ctx.storage.sql.exec(
20974
+ `UPDATE auth_rate_limits SET attempts = ? WHERE bucket_key = ?`,
20975
+ attempts,
20976
+ key
20977
+ );
20978
+ return {
20979
+ allowed: true,
20980
+ retry_after: retryAfter,
20981
+ remaining: Math.max(0, limit - attempts)
20982
+ };
20983
+ }
20351
20984
  // ============================================================
20352
20985
  // API Key Methods
20353
20986
  // ============================================================
@@ -20665,40 +21298,61 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
20665
21298
  }
20666
21299
  }
20667
21300
  // ─────────────────────────────────────────────────────────────────────────
20668
- // Skills — the instance-global Agent Skills library (agentskills.io format).
20669
- // Installed once, visible to every thread; file contents live here so
20670
- // nothing about a skill is stored on a client machine.
21301
+ // Skills — owner-scoped Agent Skills plus explicitly shared admin skills.
20671
21302
  // ─────────────────────────────────────────────────────────────────────────
20672
21303
  /** Installed skills, metadata only (no file contents). */
20673
- async listSkills(options = {}) {
21304
+ async listSkills(scope, options = {}) {
20674
21305
  await this.ensureMigrated();
20675
- const where = options.includeDisabled ? "" : "WHERE enabled = 1";
20676
21306
  const cursor = await this.ctx.storage.sql.exec(
20677
21307
  `SELECT s.name, s.description, s.version, s.enabled, s.updated_at,
20678
- (SELECT json_group_array(f.path) FROM skill_files f WHERE f.skill_name = s.name) AS files
20679
- FROM skills s ${where} ORDER BY s.name`
21308
+ s.owner_id,
21309
+ (SELECT json_group_array(f.path) FROM skill_files f
21310
+ WHERE f.owner_id = s.owner_id AND f.skill_name = s.name) AS files
21311
+ FROM skills s
21312
+ WHERE (s.owner_id = ? OR (? = 1 AND s.shared = 1))
21313
+ AND (? = 1 OR s.enabled = 1)
21314
+ ORDER BY s.name, CASE WHEN s.owner_id = ? THEN 0 ELSE 1 END`,
21315
+ scope.ownerId,
21316
+ scope.includeShared ? 1 : 0,
21317
+ options.includeDisabled ? 1 : 0,
21318
+ scope.ownerId
20680
21319
  );
20681
- return cursor.toArray().map((row) => ({
20682
- name: row.name,
20683
- description: row.description,
20684
- version: row.version ?? void 0,
20685
- enabled: row.enabled === 1,
20686
- files: row.files ? JSON.parse(row.files).sort() : [],
20687
- updatedAt: row.updated_at
20688
- }));
21320
+ const skills = /* @__PURE__ */ new Map();
21321
+ for (const row of cursor.toArray()) {
21322
+ if (skills.has(row.name)) continue;
21323
+ const parsedFiles = row.files ? JSON.parse(row.files) : [];
21324
+ const files = Array.isArray(parsedFiles) ? parsedFiles.filter((path) => typeof path === "string").sort() : [];
21325
+ skills.set(row.name, {
21326
+ name: row.name,
21327
+ description: row.description,
21328
+ version: row.version ?? void 0,
21329
+ enabled: row.enabled === 1,
21330
+ files,
21331
+ updatedAt: row.updated_at
21332
+ });
21333
+ }
21334
+ return [...skills.values()];
20689
21335
  }
20690
21336
  /** One skill's metadata (null when not installed). */
20691
- async getSkill(name) {
20692
- const all = await this.listSkills({ includeDisabled: true });
21337
+ async getSkill(scope, name) {
21338
+ const all = await this.listSkills(scope, { includeDisabled: true });
20693
21339
  return all.find((s) => s.name === name) ?? null;
20694
21340
  }
20695
21341
  /** One bundled file's text (null when the skill or file is missing). */
20696
- async getSkillFile(name, path) {
21342
+ async getSkillFile(scope, name, path) {
20697
21343
  await this.ensureMigrated();
20698
21344
  const cursor = await this.ctx.storage.sql.exec(
20699
- `SELECT content FROM skill_files WHERE skill_name = ? AND path = ?`,
21345
+ `SELECT f.content FROM skill_files f
21346
+ JOIN skills s ON s.owner_id = f.owner_id AND s.name = f.skill_name
21347
+ WHERE f.skill_name = ? AND f.path = ?
21348
+ AND (f.owner_id = ? OR (? = 1 AND s.shared = 1))
21349
+ ORDER BY CASE WHEN f.owner_id = ? THEN 0 ELSE 1 END
21350
+ LIMIT 1`,
20700
21351
  name,
20701
- path.replace(/^\.\//, "")
21352
+ path.replace(/^\.\//, ""),
21353
+ scope.ownerId,
21354
+ scope.includeShared ? 1 : 0,
21355
+ scope.ownerId
20702
21356
  );
20703
21357
  const rows = cursor.toArray();
20704
21358
  return rows.length ? rows[0].content : null;
@@ -20709,44 +21363,56 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
20709
21363
  * that frontmatter. Throws SkillValidationError with an actionable message
20710
21364
  * on any standard violation.
20711
21365
  */
20712
- async upsertSkill(files) {
21366
+ async upsertSkill(scope, files) {
20713
21367
  await this.ensureMigrated();
20714
21368
  const frontmatter = validateSkillFiles(files);
20715
21369
  const name = frontmatter.name;
20716
- const existing = await this.getSkill(name);
20717
- await this.ctx.storage.sql.exec(`DELETE FROM skill_files WHERE skill_name = ?`, name);
21370
+ const ownScope = { ownerId: scope.ownerId };
21371
+ const existing = await this.getSkill(ownScope, name);
21372
+ await this.ctx.storage.sql.exec(
21373
+ `DELETE FROM skill_files WHERE owner_id = ? AND skill_name = ?`,
21374
+ scope.ownerId,
21375
+ name
21376
+ );
20718
21377
  await this.ctx.storage.sql.exec(
20719
- `INSERT INTO skills (name, description, version, enabled, updated_at)
20720
- VALUES (?, ?, ?, ?, strftime('%s', 'now'))
20721
- ON CONFLICT(name) DO UPDATE SET
21378
+ `INSERT INTO skills (owner_id, name, description, version, enabled, shared, updated_at)
21379
+ VALUES (?, ?, ?, ?, ?, ?, strftime('%s', 'now'))
21380
+ ON CONFLICT(owner_id, name) DO UPDATE SET
20722
21381
  description = excluded.description,
20723
21382
  version = excluded.version,
20724
21383
  updated_at = excluded.updated_at`,
21384
+ scope.ownerId,
20725
21385
  name,
20726
21386
  frontmatter.description,
20727
21387
  frontmatter.version ?? null,
20728
- existing ? existing.enabled ? 1 : 0 : 1
21388
+ existing ? existing.enabled ? 1 : 0 : 1,
21389
+ scope.ownerId === SHARED_SKILL_OWNER ? 1 : 0
20729
21390
  );
20730
21391
  for (const f of files) {
20731
21392
  const p = f.path.replace(/^\.\//, "");
20732
21393
  await this.ctx.storage.sql.exec(
20733
- `INSERT INTO skill_files (skill_name, path, content, size) VALUES (?, ?, ?, ?)`,
21394
+ `INSERT INTO skill_files (owner_id, skill_name, path, content, size) VALUES (?, ?, ?, ?, ?)`,
21395
+ scope.ownerId,
20734
21396
  name,
20735
21397
  p,
20736
21398
  f.content,
20737
21399
  f.content.length
20738
21400
  );
20739
21401
  }
20740
- return await this.getSkill(name);
21402
+ const installed = await this.getSkill(ownScope, name);
21403
+ if (!installed) throw new Error(`Installed skill was not found: ${name}`);
21404
+ return installed;
20741
21405
  }
20742
21406
  /** Enable/disable an installed skill. Returns false when not installed. */
20743
- async setSkillEnabled(name, enabled) {
21407
+ async setSkillEnabled(scope, name, enabled) {
20744
21408
  await this.ensureMigrated();
20745
- const existing = await this.getSkill(name);
21409
+ const existing = await this.getSkill({ ownerId: scope.ownerId }, name);
20746
21410
  if (!existing) return false;
20747
21411
  await this.ctx.storage.sql.exec(
20748
- `UPDATE skills SET enabled = ?, updated_at = strftime('%s', 'now') WHERE name = ?`,
21412
+ `UPDATE skills SET enabled = ?, updated_at = strftime('%s', 'now')
21413
+ WHERE owner_id = ? AND name = ?`,
20749
21414
  enabled ? 1 : 0,
21415
+ scope.ownerId,
20750
21416
  name
20751
21417
  );
20752
21418
  return true;
@@ -20821,12 +21487,20 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
20821
21487
  return true;
20822
21488
  }
20823
21489
  /** Uninstall a skill and its files. Returns false when not installed. */
20824
- async deleteSkill(name) {
21490
+ async deleteSkill(scope, name) {
20825
21491
  await this.ensureMigrated();
20826
- const existing = await this.getSkill(name);
21492
+ const existing = await this.getSkill({ ownerId: scope.ownerId }, name);
20827
21493
  if (!existing) return false;
20828
- await this.ctx.storage.sql.exec(`DELETE FROM skill_files WHERE skill_name = ?`, name);
20829
- await this.ctx.storage.sql.exec(`DELETE FROM skills WHERE name = ?`, name);
21494
+ await this.ctx.storage.sql.exec(
21495
+ `DELETE FROM skill_files WHERE owner_id = ? AND skill_name = ?`,
21496
+ scope.ownerId,
21497
+ name
21498
+ );
21499
+ await this.ctx.storage.sql.exec(
21500
+ `DELETE FROM skills WHERE owner_id = ? AND name = ?`,
21501
+ scope.ownerId,
21502
+ name
21503
+ );
20830
21504
  return true;
20831
21505
  }
20832
21506
  };
@@ -20918,8 +21592,38 @@ function selectBridgeSocket(candidates, ownerClientId) {
20918
21592
 
20919
21593
  // src/durable-objects/DurableThread.ts
20920
21594
  init_code_execution();
21595
+ init_remoteAttachment();
20921
21596
  var DEFAULT_CLIENT_TOOL_TIMEOUT_MS = 5 * 60 * 1e3;
20922
21597
  var BRIDGE_LIVENESS_TIMEOUT_MS = 20 * 1e3;
21598
+ var INTERNAL_TOOL_DELIVERY = Symbol("internal-tool-delivery");
21599
+ function generateToolDeliveryToken() {
21600
+ const bytes = crypto.getRandomValues(new Uint8Array(32));
21601
+ return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
21602
+ }
21603
+ async function hashToolDeliveryToken(token) {
21604
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(token));
21605
+ return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
21606
+ }
21607
+ async function createToolDeliveryCapability() {
21608
+ const token = generateToolDeliveryToken();
21609
+ return { token, tokenHash: await hashToolDeliveryToken(token) };
21610
+ }
21611
+ async function matchesToolDeliveryToken(expectedHash, token) {
21612
+ if (!expectedHash || !token || token.length > 256) return false;
21613
+ const actualHash = await hashToolDeliveryToken(token);
21614
+ if (expectedHash.length !== actualHash.length) return false;
21615
+ let difference = 0;
21616
+ for (let index = 0; index < actualHash.length; index++) {
21617
+ difference |= expectedHash.charCodeAt(index) ^ actualHash.charCodeAt(index);
21618
+ }
21619
+ return difference === 0;
21620
+ }
21621
+ function matchesToolDeliveryOwner(pending, proof) {
21622
+ return !pending.dispatched_client_id || pending.dispatched_client_id === proof.clientId && Number(pending.dispatched_generation ?? 0) === (proof.generation ?? -1);
21623
+ }
21624
+ async function authorizesToolDelivery(pending, proof) {
21625
+ return matchesToolDeliveryOwner(pending, proof) && await matchesToolDeliveryToken(pending.delivery_token_hash, proof.token);
21626
+ }
20923
21627
  var RUNTIME_STORAGE_PREFIX = "__agentbuilder_runtime_storage__:";
20924
21628
  function normalizeEndpointThreadPath(path) {
20925
21629
  if (!path || /^[A-Za-z][A-Za-z\d+\-.]*:/.test(path)) {
@@ -21070,6 +21774,30 @@ function createEndpointThreadStateBridge(state) {
21070
21774
  getFileThumbnail: (path) => state.getFileThumbnail(normalizeEndpointThreadPath(path))
21071
21775
  };
21072
21776
  }
21777
+ function routeBridgeClientFrame(socket, message, handlers) {
21778
+ const attachment = socket.deserializeAttachment();
21779
+ const attachedAsBridge = attachment !== null && typeof attachment === "object" && "type" in attachment && attachment.type === "bridge";
21780
+ if (!attachedAsBridge || !handlers.isTracked(socket)) return false;
21781
+ handlers.markSeen(socket);
21782
+ if (typeof message !== "string") return false;
21783
+ let parsed;
21784
+ try {
21785
+ const value = JSON.parse(message);
21786
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
21787
+ parsed = value;
21788
+ } catch {
21789
+ return false;
21790
+ }
21791
+ if (parsed.type === "tool_response") {
21792
+ handlers.resolveToolResponse(socket, parsed);
21793
+ return true;
21794
+ }
21795
+ if (typeof parsed.type === "string" && parsed.type.startsWith("stream_") && parsed.type !== "stream_ping" && parsed.type !== "stream_pong") {
21796
+ handlers.handleStreamFrame(socket, parsed);
21797
+ return true;
21798
+ }
21799
+ return false;
21800
+ }
21073
21801
  var DurableThread = class _DurableThread extends DurableObject {
21074
21802
  migratedToVersion = null;
21075
21803
  logSockets = /* @__PURE__ */ new Set();
@@ -22464,9 +23192,9 @@ var DurableThread = class _DurableThread extends DurableObject {
22464
23192
  * Each migration is run in order, starting from the current version + 1.
22465
23193
  */
22466
23194
  async runMigrations(fromVersion) {
22467
- for (const migration53 of migrations) {
22468
- if (migration53.version > fromVersion) {
22469
- await migration53.up(this.ctx.storage.sql);
23195
+ for (const migration58 of migrations) {
23196
+ if (migration58.version > fromVersion) {
23197
+ await migration58.up(this.ctx.storage.sql);
22470
23198
  }
22471
23199
  }
22472
23200
  }
@@ -23150,14 +23878,17 @@ var DurableThread = class _DurableThread extends DurableObject {
23150
23878
  return null;
23151
23879
  }
23152
23880
  const location = sourceFile.location;
23153
- if (!location || !/^https?:\/\//i.test(location)) {
23881
+ if (!location) {
23882
+ return null;
23883
+ }
23884
+ const response = await fetchRemoteAttachment(
23885
+ location,
23886
+ this.env.AGENT_BUILDER_ATTACHMENT_FETCH_HOSTS
23887
+ );
23888
+ if (!response) {
23154
23889
  return null;
23155
23890
  }
23156
23891
  try {
23157
- const response = await fetch(location);
23158
- if (!response.ok) {
23159
- return null;
23160
- }
23161
23892
  const buffer = await response.arrayBuffer();
23162
23893
  return this.arrayBufferToBase64(buffer);
23163
23894
  } catch {
@@ -24854,7 +25585,9 @@ ${resultContent}${attachmentSummary}`;
24854
25585
  void 0,
24855
25586
  "The client executing this operation was replaced before it finished. Retry if the work is still needed.",
24856
25587
  previous.client_id,
24857
- previous.generation
25588
+ previous.generation,
25589
+ void 0,
25590
+ INTERNAL_TOOL_DELIVERY
24858
25591
  );
24859
25592
  if (delivered) sides.add(delivered.side);
24860
25593
  }
@@ -24884,18 +25617,21 @@ ${resultContent}${attachmentSummary}`;
24884
25617
  data: { noClient: true }
24885
25618
  };
24886
25619
  }
25620
+ let deliveryToken;
24887
25621
  if (durability) {
24888
25622
  try {
24889
25623
  await this.ensureMigrated();
24890
25624
  await this.ensurePendingClientTable();
25625
+ const capability = await createToolDeliveryCapability();
25626
+ deliveryToken = capability.token;
24891
25627
  const ownerId = owner?.client_id ?? null;
24892
25628
  const socketClientId = this.bridgeClientInfo.get(socket)?.clientId ?? null;
24893
25629
  const fenced = owner && socketClientId === owner.client_id;
24894
25630
  await this.ctx.storage.sql.exec(
24895
25631
  `INSERT OR REPLACE INTO pending_client_calls
24896
25632
  (tool_call_id, tool, args, request_permission, risk, side, status, created_at,
24897
- dispatched_client_id, dispatched_generation)
24898
- VALUES (?, ?, ?, ?, ?, ?, 'pending', ?, ?, ?)`,
25633
+ dispatched_client_id, dispatched_generation, delivery_token_hash)
25634
+ VALUES (?, ?, ?, ?, ?, ?, 'pending', ?, ?, ?, ?)`,
24899
25635
  durability.toolCallId,
24900
25636
  request.tool,
24901
25637
  JSON.stringify(request.args ?? {}),
@@ -24904,7 +25640,8 @@ ${resultContent}${attachmentSummary}`;
24904
25640
  durability.side,
24905
25641
  Date.now(),
24906
25642
  fenced ? ownerId : null,
24907
- fenced ? owner.generation : 0
25643
+ fenced ? owner.generation : 0,
25644
+ capability.tokenHash
24908
25645
  );
24909
25646
  } catch (error) {
24910
25647
  console.error("[DurableThread] Failed to persist fast-path call (continuing):", error);
@@ -24940,7 +25677,8 @@ ${resultContent}${attachmentSummary}`;
24940
25677
  tool: request.tool,
24941
25678
  args: request.args ?? {},
24942
25679
  requestPermission: request.requestPermission ?? null,
24943
- risk: typeof request.risk === "number" ? request.risk : null
25680
+ risk: typeof request.risk === "number" ? request.risk : null,
25681
+ deliveryToken: deliveryToken ?? null
24944
25682
  });
24945
25683
  try {
24946
25684
  socket.send(frame);
@@ -25063,7 +25801,8 @@ ${resultContent}${attachmentSummary}`;
25063
25801
  status TEXT NOT NULL DEFAULT 'pending',
25064
25802
  created_at INTEGER NOT NULL,
25065
25803
  dispatched_client_id TEXT,
25066
- dispatched_generation INTEGER NOT NULL DEFAULT 0
25804
+ dispatched_generation INTEGER NOT NULL DEFAULT 0,
25805
+ delivery_token_hash TEXT
25067
25806
  )
25068
25807
  `);
25069
25808
  }
@@ -25104,13 +25843,15 @@ ${resultContent}${attachmentSummary}`;
25104
25843
  const socketClientId = this.bridgeClientInfo.get(socket)?.clientId ?? null;
25105
25844
  const fenced = owner && socketClientId === owner.client_id;
25106
25845
  const generation = fenced ? owner.generation : 0;
25846
+ const capability = await createToolDeliveryCapability();
25107
25847
  try {
25108
25848
  await this.ctx.storage.sql.exec(
25109
25849
  `UPDATE pending_client_calls
25110
- SET dispatched_client_id = ?, dispatched_generation = ?
25850
+ SET dispatched_client_id = ?, dispatched_generation = ?, delivery_token_hash = ?
25111
25851
  WHERE tool_call_id = ? AND status = 'pending'`,
25112
25852
  fenced ? owner.client_id : null,
25113
25853
  generation,
25854
+ capability.tokenHash,
25114
25855
  toolCallId
25115
25856
  );
25116
25857
  socket.send(
@@ -25122,7 +25863,8 @@ ${resultContent}${attachmentSummary}`;
25122
25863
  args: request.args ?? {},
25123
25864
  requestPermission: request.requestPermission ?? null,
25124
25865
  risk: typeof request.risk === "number" ? request.risk : null,
25125
- generation
25866
+ generation,
25867
+ deliveryToken: capability.token
25126
25868
  })
25127
25869
  );
25128
25870
  this.bridgeLastSeen.set(socket, Date.now());
@@ -25245,12 +25987,14 @@ ${resultContent}${attachmentSummary}`;
25245
25987
  const pending = await this.getPendingClientCalls();
25246
25988
  for (const p of pending) {
25247
25989
  if (socket.readyState !== WebSocket.OPEN) break;
25990
+ const capability = await createToolDeliveryCapability();
25248
25991
  await this.ctx.storage.sql.exec(
25249
25992
  `UPDATE pending_client_calls
25250
- SET dispatched_client_id = ?, dispatched_generation = ?
25993
+ SET dispatched_client_id = ?, dispatched_generation = ?, delivery_token_hash = ?
25251
25994
  WHERE tool_call_id = ? AND status = 'pending'`,
25252
25995
  fenced ? owner.client_id : null,
25253
25996
  generation,
25997
+ capability.tokenHash,
25254
25998
  p.toolCallId
25255
25999
  );
25256
26000
  socket.send(
@@ -25262,7 +26006,8 @@ ${resultContent}${attachmentSummary}`;
25262
26006
  args: p.args,
25263
26007
  requestPermission: p.requestPermission,
25264
26008
  risk: p.risk,
25265
- generation
26009
+ generation,
26010
+ deliveryToken: capability.token
25266
26011
  })
25267
26012
  );
25268
26013
  }
@@ -25275,20 +26020,25 @@ ${resultContent}${attachmentSummary}`;
25275
26020
  * call resolved. Returns the side to continue, or null if there is no pending
25276
26021
  * call (unknown id or already resolved — the delivery is idempotent).
25277
26022
  */
25278
- async deliverClientToolResult(toolCallId, ok, result, error, deliveredByClientId, deliveredGeneration) {
26023
+ async deliverClientToolResult(toolCallId, ok, result, error, deliveredByClientId, deliveredGeneration, deliveryToken, internalAuthorization) {
25279
26024
  await this.ensureMigrated();
25280
26025
  await this.ensurePendingClientTable();
25281
26026
  const pendingCursor = await this.ctx.storage.sql.exec(
25282
- `SELECT side, status, dispatched_client_id, dispatched_generation
26027
+ `SELECT side, status, dispatched_client_id, dispatched_generation, delivery_token_hash
25283
26028
  FROM pending_client_calls WHERE tool_call_id = ? LIMIT 1`,
25284
26029
  toolCallId
25285
26030
  );
25286
26031
  const pending = pendingCursor.toArray()[0];
25287
26032
  if (!pending || pending.status !== "pending") return null;
25288
- if (pending.dispatched_client_id) {
25289
- if (pending.dispatched_client_id !== deliveredByClientId || Number(pending.dispatched_generation ?? 0) !== (deliveredGeneration ?? -1)) {
25290
- return null;
25291
- }
26033
+ const proof = {
26034
+ token: deliveryToken,
26035
+ clientId: deliveredByClientId,
26036
+ generation: deliveredGeneration
26037
+ };
26038
+ if (internalAuthorization !== INTERNAL_TOOL_DELIVERY) {
26039
+ if (!await authorizesToolDelivery(pending, proof)) return null;
26040
+ } else if (!matchesToolDeliveryOwner(pending, proof)) {
26041
+ return null;
25292
26042
  }
25293
26043
  const side = pending.side === "b" ? "b" : "a";
25294
26044
  const content = ok ? result ?? "" : `Error: ${error ?? "tool failed"}`;
@@ -26005,29 +26755,17 @@ ${resultContent}${attachmentSummary}`;
26005
26755
  */
26006
26756
  async webSocketMessage(ws, message) {
26007
26757
  try {
26008
- if (this.bridgeSockets.has(ws)) {
26009
- this.bridgeLastSeen.set(ws, Date.now());
26010
- }
26758
+ const handledBridgeFrame = routeBridgeClientFrame(ws, message, {
26759
+ isTracked: (socket) => this.bridgeSockets.has(socket),
26760
+ markSeen: (socket) => this.bridgeLastSeen.set(socket, Date.now()),
26761
+ resolveToolResponse: (socket, payload) => this.resolveClientCall(socket, payload),
26762
+ handleStreamFrame: (socket, payload) => this.handleClientStreamFrame(socket, payload)
26763
+ });
26764
+ if (handledBridgeFrame) return;
26011
26765
  if (typeof message === "string" && message === "ping") {
26012
26766
  ws.send("pong");
26013
26767
  return;
26014
26768
  }
26015
- if (typeof message === "string") {
26016
- let parsed = null;
26017
- try {
26018
- parsed = JSON.parse(message);
26019
- } catch {
26020
- parsed = null;
26021
- }
26022
- if (parsed && parsed.type === "tool_response") {
26023
- this.resolveClientCall(ws, parsed);
26024
- return;
26025
- }
26026
- if (parsed && typeof parsed.type === "string" && parsed.type.startsWith("stream_") && parsed.type !== "stream_ping" && parsed.type !== "stream_pong") {
26027
- this.handleClientStreamFrame(ws, parsed);
26028
- return;
26029
- }
26030
- }
26031
26769
  } catch (error) {
26032
26770
  console.error("[DurableThread] Error handling WebSocket message:", error);
26033
26771
  }
@@ -29034,6 +29772,6 @@ init_context();
29034
29772
  init_ProviderRegistry();
29035
29773
  init_types2();
29036
29774
 
29037
- export { CLIENT_STREAM_CHUNK_BYTES, CodeExecutionBridge, DurableAgentBuilder, DurableThread, DurableUserHub, FlowStateSdk, NamespaceResolutionError, ProviderRegistry, ThreadWatchdog, authenticate, buildImageDescription, callClient, callClientStream, canAccessThread, cat, createNamespaceContext, createThreadEndpointHandler, decodeStreamChunk, defineController2 as defineController, emitThreadEvent, encodeStreamChunk, enhanceFlowState, exists, find, forceTurn, generateImageDescription, getFileStats, getMessages, getMessagesToSummarize, getShortName, getThumbnail, getUnsummarizedImageAttachments, getVisibleAgentNames, getVisibleModelNames, getVisiblePromptNames, getVisibleToolNames, grep, hasImageAttachments, head, injectMessage, isAdminOnlyApiRoute, isHostedInstance, isPublicApiRoute, isQualifiedName, isTrustedLocalDevelopmentRequest, linkFile, mkdir, move, optimizeImageContext, parseQualifiedName, qualifyName, queueTool, readFile, readdir, reloadHistory, rename, replaceImagesWithDescriptions, requireAdmin, requireAuth, resolveAgent, resolveHook, resolveModel, resolvePrompt, resolveTool, rmdir, stat, supportsClientForwarding, tail, unlink, updateThread, writeFile, writeImage };
29775
+ export { CLIENT_STREAM_CHUNK_BYTES, CodeExecutionBridge, DurableAgentBuilder, DurableThread, DurableUserHub, FlowStateSdk, NamespaceResolutionError, ProviderRegistry, ThreadWatchdog, applyRuntimeCors, authenticate, buildImageDescription, callClient, callClientStream, canAccessThread, cat, createNamespaceContext, createThreadEndpointHandler, decodeStreamChunk, defineController2 as defineController, emitThreadEvent, encodeStreamChunk, endpointCallerFromAuth, enhanceFlowState, exists, find, forceTurn, generateImageDescription, getFileStats, getMessages, getMessagesToSummarize, getShortName, getThumbnail, getUnsummarizedImageAttachments, getVisibleAgentNames, getVisibleModelNames, getVisiblePromptNames, getVisibleToolNames, grep, hasImageAttachments, head, injectMessage, isAdminOnlyApiRoute, isHostedInstance, isPublicApiRoute, isQualifiedName, isSameOriginBrowserRequest, isTrustedLocalDevelopmentRequest, linkFile, mkdir, move, optimizeImageContext, parseQualifiedName, qualifyName, queueTool, readFile, readdir, reloadHistory, rename, replaceImagesWithDescriptions, requireAdmin, requireAuth, resolveAgent, resolveHook, resolveModel, resolvePrompt, resolveTool, rmdir, runtimeCorsHeaders, runtimePreflightResponse, stat, supportsClientForwarding, tail, unlink, updateThread, writeFile, writeImage };
29038
29776
  //# sourceMappingURL=runtime.js.map
29039
29777
  //# sourceMappingURL=runtime.js.map