@standardagents/builder 0.38.0 → 0.38.2

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 +1243 -418
  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-BBu-Osn5.d.ts → index-CF-lgN47.d.ts} +74 -11
  41. package/dist/index.d.ts +2 -2
  42. package/dist/index.js +1064 -304
  43. package/dist/index.js.map +1 -1
  44. package/dist/plugin.js +106 -69
  45. package/dist/plugin.js.map +1 -1
  46. package/dist/runtime.d.ts +68 -11
  47. package/dist/runtime.js +950 -225
  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
@@ -49,6 +49,14 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
49
49
  mod
50
50
  ));
51
51
 
52
+ // src/skills/scope.ts
53
+ var SHARED_SKILL_OWNER;
54
+ var init_scope = __esm({
55
+ "src/skills/scope.ts"() {
56
+ SHARED_SKILL_OWNER = "__instance_shared__";
57
+ }
58
+ });
59
+
52
60
  // src/agents/client-forwarding.ts
53
61
  function createRuntimeStreams(openClientStream) {
54
62
  return {
@@ -3123,6 +3131,104 @@ var init_sessionTools = __esm({
3123
3131
  }
3124
3132
  });
3125
3133
 
3134
+ // src/utils/remoteAttachment.ts
3135
+ function parseAllowedHosts(value) {
3136
+ if (typeof value !== "string") {
3137
+ return /* @__PURE__ */ new Set();
3138
+ }
3139
+ return new Set(
3140
+ value.split(",").map((host) => host.trim().toLowerCase().replace(/\.$/, "")).filter(Boolean)
3141
+ );
3142
+ }
3143
+ function isForbiddenHostname(hostname) {
3144
+ const normalized = hostname.toLowerCase().replace(/\.$/, "");
3145
+ if (/^\d{1,3}(?:\.\d{1,3}){3}$/.test(normalized)) {
3146
+ return true;
3147
+ }
3148
+ if (normalized.startsWith("[") && normalized.endsWith("]")) {
3149
+ return true;
3150
+ }
3151
+ if (!normalized.includes(".")) {
3152
+ return true;
3153
+ }
3154
+ return FORBIDDEN_HOST_SUFFIXES.some(
3155
+ (suffix) => normalized === suffix.slice(1) || normalized.endsWith(suffix)
3156
+ );
3157
+ }
3158
+ function validateRemoteAttachmentUrl(value, allowedHostsValue) {
3159
+ let url;
3160
+ try {
3161
+ url = new URL(value);
3162
+ } catch {
3163
+ return null;
3164
+ }
3165
+ if (url.protocol !== "https:" || url.username !== "" || url.password !== "" || url.port !== "" && url.port !== "443") {
3166
+ return null;
3167
+ }
3168
+ const hostname = url.hostname.toLowerCase().replace(/\.$/, "");
3169
+ if (isForbiddenHostname(hostname)) {
3170
+ return null;
3171
+ }
3172
+ const allowedHosts = parseAllowedHosts(allowedHostsValue);
3173
+ if (!allowedHosts.has(hostname)) {
3174
+ return null;
3175
+ }
3176
+ return url;
3177
+ }
3178
+ async function fetchRemoteAttachment(location, allowedHostsValue, fetchImpl = fetch) {
3179
+ let url = validateRemoteAttachmentUrl(location, allowedHostsValue);
3180
+ if (!url) {
3181
+ return null;
3182
+ }
3183
+ for (let redirectCount = 0; redirectCount <= MAX_REDIRECTS; redirectCount++) {
3184
+ let response;
3185
+ try {
3186
+ response = await fetchImpl(url.toString(), {
3187
+ redirect: "manual"
3188
+ });
3189
+ } catch {
3190
+ return null;
3191
+ }
3192
+ if (!REDIRECT_STATUSES.has(response.status)) {
3193
+ return response.ok ? response : null;
3194
+ }
3195
+ await response.body?.cancel().catch(() => void 0);
3196
+ if (redirectCount === MAX_REDIRECTS) {
3197
+ return null;
3198
+ }
3199
+ const redirectLocation = response.headers.get("Location");
3200
+ if (!redirectLocation) {
3201
+ return null;
3202
+ }
3203
+ let resolvedLocation;
3204
+ try {
3205
+ resolvedLocation = new URL(redirectLocation, url).toString();
3206
+ } catch {
3207
+ return null;
3208
+ }
3209
+ url = validateRemoteAttachmentUrl(resolvedLocation, allowedHostsValue);
3210
+ if (!url) {
3211
+ return null;
3212
+ }
3213
+ }
3214
+ return null;
3215
+ }
3216
+ var REDIRECT_STATUSES, MAX_REDIRECTS, FORBIDDEN_HOST_SUFFIXES;
3217
+ var init_remoteAttachment = __esm({
3218
+ "src/utils/remoteAttachment.ts"() {
3219
+ REDIRECT_STATUSES = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
3220
+ MAX_REDIRECTS = 3;
3221
+ FORBIDDEN_HOST_SUFFIXES = [
3222
+ ".internal",
3223
+ ".lan",
3224
+ ".local",
3225
+ ".localhost",
3226
+ ".home",
3227
+ ".test"
3228
+ ];
3229
+ }
3230
+ });
3231
+
3126
3232
  // src/durable-objects/files.ts
3127
3233
  var files_exports = {};
3128
3234
  __export(files_exports, {
@@ -3860,6 +3966,7 @@ var init_ToolExecutor = __esm({
3860
3966
  init_types();
3861
3967
  init_ThreadStateImpl();
3862
3968
  init_sessionTools();
3969
+ init_remoteAttachment();
3863
3970
  SUBAGENT_CREATE_TOOL = "subagent_create";
3864
3971
  SUBAGENT_MESSAGE_TOOL = "subagent_message";
3865
3972
  SUBAGENTS_LIST_TOOL = "subagents";
@@ -4908,7 +5015,8 @@ Message one with subagent_message(referenceId, message).`
4908
5015
  stub,
4909
5016
  state.thread.instance,
4910
5017
  childResult.attachments,
4911
- `from-subagent-${childThreadId}`
5018
+ `from-subagent-${childThreadId}`,
5019
+ state.env.AGENT_BUILDER_ATTACHMENT_FETCH_HOSTS
4912
5020
  );
4913
5021
  if (resumable) {
4914
5022
  await this.settleResumableChildAfterRun(
@@ -4941,7 +5049,8 @@ Message one with subagent_message(referenceId, message).`
4941
5049
  stub,
4942
5050
  state.thread.instance,
4943
5051
  childResult.attachments,
4944
- `from-subagent-${childThreadId}`
5052
+ `from-subagent-${childThreadId}`,
5053
+ state.env.AGENT_BUILDER_ATTACHMENT_FETCH_HOSTS
4945
5054
  );
4946
5055
  const messageContent = this.formatSubagentTerminalMessage(
4947
5056
  childThreadId,
@@ -5355,7 +5464,8 @@ Message one with subagent_message(referenceId, message).`
5355
5464
  state.thread.instance,
5356
5465
  childThread,
5357
5466
  parentAttachments,
5358
- `from-parent-${state.threadId}`
5467
+ `from-parent-${state.threadId}`,
5468
+ state.env.AGENT_BUILDER_ATTACHMENT_FETCH_HOSTS
5359
5469
  );
5360
5470
  const targetRole = receivesMessages === "side_a" ? "user" : "assistant";
5361
5471
  const baseTimestamp = Date.now() * 1e3;
@@ -5615,7 +5725,7 @@ Reference: ${referenceId}` : `Reference: ${referenceId}`;
5615
5725
  const shortId = crypto.randomUUID().slice(0, 8);
5616
5726
  return `/attachments/${normalizedStem}-${shortId}.${finalExt}`;
5617
5727
  }
5618
- static async copyAttachmentsBetweenThreads(sourceThread, targetThread, attachments, _pathPrefix) {
5728
+ static async copyAttachmentsBetweenThreads(sourceThread, targetThread, attachments, _pathPrefix, allowedAttachmentFetchHosts) {
5619
5729
  if (!attachments || attachments.length === 0) {
5620
5730
  return [];
5621
5731
  }
@@ -5643,7 +5753,8 @@ Reference: ${referenceId}` : `Reference: ${referenceId}`;
5643
5753
  const copiedData = await this.readAttachmentDataFromSource(
5644
5754
  sourceThread,
5645
5755
  attachment.path,
5646
- sourceFile
5756
+ sourceFile,
5757
+ allowedAttachmentFetchHosts
5647
5758
  );
5648
5759
  if (!copiedData) {
5649
5760
  continue;
@@ -5700,7 +5811,7 @@ Reference: ${referenceId}` : `Reference: ${referenceId}`;
5700
5811
  }
5701
5812
  return copied;
5702
5813
  }
5703
- static async readAttachmentDataFromSource(sourceThread, path10, sourceFile) {
5814
+ static async readAttachmentDataFromSource(sourceThread, path10, sourceFile, allowedAttachmentFetchHosts) {
5704
5815
  const sourceData = await sourceThread.readFile(path10);
5705
5816
  if (sourceData.success && sourceData.data) {
5706
5817
  return sourceData.data;
@@ -5709,14 +5820,17 @@ Reference: ${referenceId}` : `Reference: ${referenceId}`;
5709
5820
  return null;
5710
5821
  }
5711
5822
  const location = sourceFile.location;
5712
- if (!location || !/^https?:\/\//i.test(location)) {
5823
+ if (!location) {
5824
+ return null;
5825
+ }
5826
+ const response = await fetchRemoteAttachment(
5827
+ location,
5828
+ allowedAttachmentFetchHosts
5829
+ );
5830
+ if (!response) {
5713
5831
  return null;
5714
5832
  }
5715
5833
  try {
5716
- const response = await fetch(location);
5717
- if (!response.ok) {
5718
- return null;
5719
- }
5720
5834
  const buffer = await response.arrayBuffer();
5721
5835
  return this.arrayBufferToBase64(buffer);
5722
5836
  } catch {
@@ -5970,7 +6084,8 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
5970
6084
  state.thread.instance,
5971
6085
  childStub,
5972
6086
  sourceAttachments,
5973
- `from-parent-${state.threadId}`
6087
+ `from-parent-${state.threadId}`,
6088
+ state.env.AGENT_BUILDER_ATTACHMENT_FETCH_HOSTS
5974
6089
  );
5975
6090
  await childStub.queueMessage(referenceId, {
5976
6091
  role: receivesMessages === "side_a" ? "user" : "assistant",
@@ -12570,6 +12685,32 @@ var ThreadStateImpl_exports = {};
12570
12685
  __export(ThreadStateImpl_exports, {
12571
12686
  ThreadStateImpl: () => ThreadStateImpl
12572
12687
  });
12688
+ function isRuntimeDurableNamespace(value) {
12689
+ return value !== null && typeof value === "object" && "idFromName" in value && typeof value.idFromName === "function" && "get" in value && typeof value.get === "function";
12690
+ }
12691
+ function isThreadStateRuntimeEnvironment(value) {
12692
+ 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));
12693
+ }
12694
+ function confinedUserEndpointUrl(userId, endpoint) {
12695
+ const base = new URL(
12696
+ `https://user-hub/users/${encodeURIComponent(userId)}/endpoints/`
12697
+ );
12698
+ const relative4 = endpoint.replace(/^\/+/, "");
12699
+ let decoded;
12700
+ try {
12701
+ decoded = decodeURIComponent(relative4);
12702
+ } catch {
12703
+ throw new Error("Invalid user endpoint path: malformed percent encoding.");
12704
+ }
12705
+ if (decoded.includes("\\") || decoded.includes("?") || decoded.includes("#") || /[\u0000-\u001f\u007f]/.test(decoded) || decoded.split("/").some((segment) => segment === "." || segment === "..")) {
12706
+ throw new Error("Invalid user endpoint path: traversal and URL syntax are not allowed.");
12707
+ }
12708
+ const resolved = new URL(relative4, base);
12709
+ if (resolved.origin !== base.origin || !resolved.pathname.startsWith(base.pathname)) {
12710
+ throw new Error("Invalid user endpoint path: endpoint escaped the user namespace.");
12711
+ }
12712
+ return resolved.toString();
12713
+ }
12573
12714
  function hasLocalCodeExecutionRuntime(threadInstance) {
12574
12715
  const candidate = threadInstance;
12575
12716
  return Boolean(candidate.env && candidate.ctx);
@@ -12750,6 +12891,7 @@ function runCodeViaThreadRpc(threadInstance, threadId, source, options) {
12750
12891
  var ThreadStateImpl, ExecutionStateImpl;
12751
12892
  var init_ThreadStateImpl = __esm({
12752
12893
  "src/agents/ThreadStateImpl.ts"() {
12894
+ init_scope();
12753
12895
  init_client_forwarding();
12754
12896
  init_types();
12755
12897
  init_utilities();
@@ -12983,6 +13125,14 @@ var init_ThreadStateImpl = __esm({
12983
13125
  }
12984
13126
  return value;
12985
13127
  }
13128
+ async instanceEnv(propertyName) {
13129
+ const agentBuilder = this._getAgentBuilderStub();
13130
+ if (typeof agentBuilder.getInstanceEnv !== "function") {
13131
+ throw new Error("instanceEnv is not supported by this runtime");
13132
+ }
13133
+ const values = await agentBuilder.getInstanceEnv();
13134
+ return values[propertyName];
13135
+ }
12986
13136
  async envType(propertyName) {
12987
13137
  const agentBuilder = this._getAgentBuilderStub();
12988
13138
  if (typeof agentBuilder.resolveThreadEnvType === "function") {
@@ -14013,12 +14163,11 @@ var init_ThreadStateImpl = __esm({
14013
14163
  * dispatcher recorded in the runtime context instead.
14014
14164
  */
14015
14165
  _getRuntimeEnv() {
14016
- const contextEnv = this._runtimeContext.env;
14017
- if (contextEnv?.AGENT_BUILDER) {
14018
- return contextEnv;
14166
+ if (isThreadStateRuntimeEnvironment(this._runtimeContext.env)) {
14167
+ return this._runtimeContext.env;
14019
14168
  }
14020
14169
  const instanceEnv = this._threadInstance.env;
14021
- if (!instanceEnv?.AGENT_BUILDER) {
14170
+ if (!isThreadStateRuntimeEnvironment(instanceEnv)) {
14022
14171
  throw new Error(
14023
14172
  "No runtime environment available on this ThreadState. Stub-backed states must be constructed with runtimeContext.env."
14024
14173
  );
@@ -14035,9 +14184,7 @@ var init_ThreadStateImpl = __esm({
14035
14184
  return namespace.get(namespace.idFromName(userId));
14036
14185
  }
14037
14186
  // ─────────────────────────────────────────────────────────────────────────
14038
- // Skills — instance-global Agent Skills library, backed by the
14039
- // DurableAgentBuilder singleton (skills are never stored per-thread or on
14040
- // a client machine).
14187
+ // Skills — owner-scoped library plus explicitly shared administrator skills.
14041
14188
  // ─────────────────────────────────────────────────────────────────────────
14042
14189
  /**
14043
14190
  * The profile of the user this thread belongs to (identity fields only —
@@ -14060,9 +14207,8 @@ var init_ThreadStateImpl = __esm({
14060
14207
  "Reentrant user-scope call: this ThreadState was raised from user-scope code; state.user().call() is unavailable here."
14061
14208
  );
14062
14209
  }
14063
- const path10 = endpoint.replace(/^\/+/, "");
14064
14210
  return hub().fetch(
14065
- new Request(`https://user-hub/users/${encodeURIComponent(userId)}/endpoints/${path10}`, {
14211
+ new Request(confinedUserEndpointUrl(userId, endpoint), {
14066
14212
  method: "POST",
14067
14213
  headers: {
14068
14214
  "Content-Type": "application/json",
@@ -14122,18 +14268,42 @@ var init_ThreadStateImpl = __esm({
14122
14268
  }
14123
14269
  get skills() {
14124
14270
  const stub = this._getAgentBuilderStub();
14271
+ const ownerId = this._metadata.user_id ?? SHARED_SKILL_OWNER;
14272
+ const scope = {
14273
+ ownerId,
14274
+ includeShared: ownerId !== SHARED_SKILL_OWNER
14275
+ };
14276
+ const requireMutationAdmin = async () => {
14277
+ const userId = this._metadata.user_id;
14278
+ if (!userId) {
14279
+ throw new Error("Skill mutations require an authenticated instance admin.");
14280
+ }
14281
+ const user = await stub.getUserById(userId);
14282
+ if (user?.role !== "admin") {
14283
+ throw new Error("Skill mutations require an instance admin.");
14284
+ }
14285
+ };
14125
14286
  return {
14126
- list: (options) => stub.listSkills(options),
14287
+ list: (options) => stub.listSkills(scope, options),
14127
14288
  get: async (name15) => {
14128
- const metadata = await stub.getSkill(name15);
14289
+ const metadata = await stub.getSkill(scope, name15);
14129
14290
  if (!metadata) return null;
14130
- const skillMd = await stub.getSkillFile(name15, "SKILL.md") ?? "";
14291
+ const skillMd = await stub.getSkillFile(scope, name15, "SKILL.md") ?? "";
14131
14292
  return { metadata, skillMd };
14132
14293
  },
14133
- readFile: (name15, path10) => stub.getSkillFile(name15, path10),
14134
- install: (files) => stub.upsertSkill(files),
14135
- setEnabled: (name15, enabled) => stub.setSkillEnabled(name15, enabled),
14136
- remove: (name15) => stub.deleteSkill(name15)
14294
+ readFile: (name15, path10) => stub.getSkillFile(scope, name15, path10),
14295
+ install: async (files) => {
14296
+ await requireMutationAdmin();
14297
+ return stub.upsertSkill(scope, files);
14298
+ },
14299
+ setEnabled: async (name15, enabled) => {
14300
+ await requireMutationAdmin();
14301
+ return stub.setSkillEnabled(scope, name15, enabled);
14302
+ },
14303
+ remove: async (name15) => {
14304
+ await requireMutationAdmin();
14305
+ return stub.deleteSkill(scope, name15);
14306
+ }
14137
14307
  };
14138
14308
  }
14139
14309
  // ─────────────────────────────────────────────────────────────────────────
@@ -67052,6 +67222,7 @@ function getParamRegexp(segment) {
67052
67222
  init_ThreadStateImpl();
67053
67223
 
67054
67224
  // src/utils/auth.ts
67225
+ var SUPER_ADMIN_TOKEN_TTL_SECONDS = 12 * 60 * 60;
67055
67226
  function generateSecureToken(length = 32) {
67056
67227
  const array44 = new Uint8Array(length);
67057
67228
  crypto.getRandomValues(array44);
@@ -67063,6 +67234,20 @@ function generateUserToken() {
67063
67234
  function generateApiKey() {
67064
67235
  return `agtbldr_${generateSecureToken(32)}`;
67065
67236
  }
67237
+ async function constantTimeEqual(left, right) {
67238
+ const encoder2 = new TextEncoder();
67239
+ const [leftDigest, rightDigest] = await Promise.all([
67240
+ crypto.subtle.digest("SHA-256", encoder2.encode(left)),
67241
+ crypto.subtle.digest("SHA-256", encoder2.encode(right))
67242
+ ]);
67243
+ const leftBytes = new Uint8Array(leftDigest);
67244
+ const rightBytes = new Uint8Array(rightDigest);
67245
+ let difference = 0;
67246
+ for (let index = 0; index < leftBytes.length; index++) {
67247
+ difference |= leftBytes[index] ^ rightBytes[index];
67248
+ }
67249
+ return difference === 0;
67250
+ }
67066
67251
  async function hashPassword(password) {
67067
67252
  const encoder2 = new TextEncoder();
67068
67253
  const data = encoder2.encode(password);
@@ -67174,9 +67359,9 @@ function isValidUserToken(token) {
67174
67359
  function isValidApiKey(key) {
67175
67360
  return key.startsWith("agtbldr_") && key.length > 10 || key.startsWith("sak_live_") && key.length > 10;
67176
67361
  }
67177
- async function signToken(payload, encryptionKey) {
67362
+ async function signToken(payload, encryptionKey2) {
67178
67363
  const encoder2 = new TextEncoder();
67179
- const keyData = encoder2.encode(encryptionKey);
67364
+ const keyData = encoder2.encode(encryptionKey2);
67180
67365
  const cryptoKey = await crypto.subtle.importKey(
67181
67366
  "raw",
67182
67367
  keyData,
@@ -67190,7 +67375,7 @@ async function signToken(payload, encryptionKey) {
67190
67375
  const signatureB64 = bufferToBase64(new Uint8Array(signature));
67191
67376
  return `${payloadB64}.${signatureB64}`;
67192
67377
  }
67193
- async function verifySignedToken(signedToken, encryptionKey) {
67378
+ async function verifySignedToken(signedToken, encryptionKey2) {
67194
67379
  try {
67195
67380
  const parts = signedToken.split(".");
67196
67381
  if (parts.length !== 2) {
@@ -67198,7 +67383,7 @@ async function verifySignedToken(signedToken, encryptionKey) {
67198
67383
  }
67199
67384
  const [payloadB64, signatureB64] = parts;
67200
67385
  const encoder2 = new TextEncoder();
67201
- const keyData = encoder2.encode(encryptionKey);
67386
+ const keyData = encoder2.encode(encryptionKey2);
67202
67387
  const cryptoKey = await crypto.subtle.importKey(
67203
67388
  "raw",
67204
67389
  keyData,
@@ -67219,14 +67404,14 @@ async function verifySignedToken(signedToken, encryptionKey) {
67219
67404
  return null;
67220
67405
  }
67221
67406
  }
67222
- async function generateSuperAdminToken(encryptionKey) {
67407
+ async function generateSuperAdminToken(encryptionKey2) {
67223
67408
  const timestamp = Date.now();
67224
67409
  const nonce = generateSecureToken(16);
67225
67410
  const payload = `super_admin:${timestamp}:${nonce}`;
67226
- return await signToken(payload, encryptionKey);
67411
+ return await signToken(payload, encryptionKey2);
67227
67412
  }
67228
- async function verifySuperAdminToken(token, encryptionKey) {
67229
- const payload = await verifySignedToken(token, encryptionKey);
67413
+ async function verifySuperAdminToken(token, encryptionKey2) {
67414
+ const payload = await verifySignedToken(token, encryptionKey2);
67230
67415
  if (!payload) {
67231
67416
  return false;
67232
67417
  }
@@ -67235,9 +67420,12 @@ async function verifySuperAdminToken(token, encryptionKey) {
67235
67420
  return false;
67236
67421
  }
67237
67422
  const timestamp = parseInt(parts[1], 10);
67423
+ if (!Number.isFinite(timestamp)) {
67424
+ return false;
67425
+ }
67238
67426
  const age = Date.now() - timestamp;
67239
- const maxAge = 30 * 24 * 60 * 60 * 1e3;
67240
- if (age > maxAge) {
67427
+ const maxAge = SUPER_ADMIN_TOKEN_TTL_SECONDS * 1e3;
67428
+ if (age < 0 || age > maxAge) {
67241
67429
  return false;
67242
67430
  }
67243
67431
  return true;
@@ -67258,17 +67446,99 @@ function base64ToBuffer(base64) {
67258
67446
  return bytes;
67259
67447
  }
67260
67448
 
67261
- // src/middleware/auth.ts
67262
- var CORS_HEADERS = {
67449
+ // src/routing/access-policy.ts
67450
+ function isHostedInstance(env2) {
67451
+ const value = env2 && typeof env2 === "object" ? Reflect.get(env2, "STANDARD_AGENTS_HOSTED") : void 0;
67452
+ if (typeof value === "string") {
67453
+ const normalized = value.trim().toLowerCase();
67454
+ return normalized !== "" && normalized !== "0" && normalized !== "false";
67455
+ }
67456
+ return Boolean(value);
67457
+ }
67458
+ function isTrustedLocalDevelopmentRequest(request, env2) {
67459
+ if (isHostedInstance(env2)) return false;
67460
+ let hostname;
67461
+ try {
67462
+ hostname = new URL(request.url).hostname.toLowerCase();
67463
+ } catch {
67464
+ return false;
67465
+ }
67466
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname.endsWith(".localhost") || hostname.endsWith(".orb.local");
67467
+ }
67468
+ function requestObservedOrigins(request) {
67469
+ const origins = /* @__PURE__ */ new Set();
67470
+ let url = null;
67471
+ try {
67472
+ url = new URL(request.url);
67473
+ origins.add(url.origin);
67474
+ } catch {
67475
+ url = null;
67476
+ }
67477
+ const forwardedHost = request.headers.get("x-forwarded-host")?.split(",")[0]?.trim();
67478
+ const host = forwardedHost || request.headers.get("host")?.trim();
67479
+ if (!host) return origins;
67480
+ const forwardedProto = request.headers.get("x-forwarded-proto")?.split(",")[0]?.trim().toLowerCase();
67481
+ const schemes = /* @__PURE__ */ new Set();
67482
+ if (forwardedProto === "http" || forwardedProto === "https") schemes.add(forwardedProto);
67483
+ if (url) schemes.add(url.protocol.replace(":", ""));
67484
+ if (schemes.size === 0) {
67485
+ schemes.add(host.endsWith(".orb.local") ? "https" : "http");
67486
+ }
67487
+ for (const scheme of schemes) {
67488
+ try {
67489
+ origins.add(new URL(`${scheme}://${host}`).origin);
67490
+ } catch {
67491
+ }
67492
+ }
67493
+ return origins;
67494
+ }
67495
+ function isSameOriginBrowserRequest(request) {
67496
+ const fetchSite = request.headers.get("Sec-Fetch-Site")?.trim().toLowerCase();
67497
+ if (fetchSite === "same-origin" || fetchSite === "none") {
67498
+ return true;
67499
+ }
67500
+ if (fetchSite) {
67501
+ return false;
67502
+ }
67503
+ const origin = request.headers.get("Origin")?.trim();
67504
+ if (!origin) return true;
67505
+ if (origin === "null") return false;
67506
+ try {
67507
+ return requestObservedOrigins(request).has(new URL(origin).origin);
67508
+ } catch {
67509
+ return false;
67510
+ }
67511
+ }
67512
+
67513
+ // src/routing/cors.ts
67514
+ var RUNTIME_CORS_HEADERS = {
67263
67515
  "Access-Control-Allow-Origin": "*",
67264
67516
  "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
67265
67517
  "Access-Control-Allow-Headers": "Content-Type, Authorization, X-Requested-With",
67266
67518
  "Access-Control-Max-Age": "86400"
67267
67519
  };
67268
- function hasPermission(user, permission) {
67520
+ function exposesRuntimeCors(request, env2) {
67521
+ return !isTrustedLocalDevelopmentRequest(request, env2);
67522
+ }
67523
+ function runtimeCorsHeaders(request, env2) {
67524
+ return exposesRuntimeCors(request, env2) ? { ...RUNTIME_CORS_HEADERS } : {};
67525
+ }
67526
+
67527
+ // src/middleware/auth.ts
67528
+ function isAuthenticationEnv(env2) {
67529
+ if (!env2 || typeof env2 !== "object") return false;
67530
+ const namespace = env2.AGENT_BUILDER;
67531
+ return !!namespace && typeof namespace === "object" && typeof namespace.idFromName === "function" && typeof namespace.get === "function";
67532
+ }
67533
+ function hasPermission(user, permission, options = {}) {
67269
67534
  if (user.role === "admin") return true;
67270
67535
  if (Array.isArray(user.permissions)) return user.permissions.includes(permission);
67271
- return user.role !== "api";
67536
+ return options.allowLegacyUserFallback !== false && user.role === "user";
67537
+ }
67538
+ function hasPermissionInEnvironment(user, permission, env2) {
67539
+ return hasPermission(user, permission, {
67540
+ allowLegacyUserFallback: !isHostedInstance(env2)
67541
+ });
67272
67542
  }
67273
67543
  function parsePermissions(value) {
67274
67544
  if (Array.isArray(value)) return value.filter((p) => typeof p === "string");
@@ -67290,43 +67560,96 @@ function extractBearerToken(request) {
67290
67560
  if (cookieToken) {
67291
67561
  return cookieToken;
67292
67562
  }
67293
- const isWebSocket = request.headers.get("upgrade")?.toLowerCase() === "websocket";
67294
- if (isWebSocket) {
67295
- try {
67296
- const url = new URL(request.url);
67297
- const token = url.searchParams.get("token");
67298
- if (token) {
67299
- return token;
67300
- }
67301
- } catch (err) {
67302
- }
67303
- }
67304
67563
  return null;
67305
67564
  }
67306
67565
  function getAgentBuilder(env2) {
67307
67566
  const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
67308
67567
  return env2.AGENT_BUILDER.get(agentBuilderId);
67309
67568
  }
67569
+ function parseWebSocketTicketAuth(authJson) {
67570
+ let parsed;
67571
+ try {
67572
+ parsed = JSON.parse(authJson);
67573
+ } catch {
67574
+ return null;
67575
+ }
67576
+ if (!parsed || typeof parsed !== "object") return null;
67577
+ const record24 = parsed;
67578
+ if (!record24.user || typeof record24.user !== "object") return null;
67579
+ const user = record24.user;
67580
+ if (typeof user.id !== "string" || typeof user.username !== "string" || typeof user.role !== "string" || record24.authType !== "super_admin" && record24.authType !== "session" && record24.authType !== "api_key") {
67581
+ return null;
67582
+ }
67583
+ const permissions = user.permissions;
67584
+ if (permissions !== void 0 && permissions !== null && (!Array.isArray(permissions) || !permissions.every((permission) => typeof permission === "string"))) {
67585
+ return null;
67586
+ }
67587
+ if (record24.credentialId !== void 0 && typeof record24.credentialId !== "string") return null;
67588
+ return {
67589
+ user: {
67590
+ id: user.id,
67591
+ username: user.username,
67592
+ role: user.role,
67593
+ permissions,
67594
+ platform_user_id: typeof user.platform_user_id === "string" ? user.platform_user_id : null,
67595
+ email: typeof user.email === "string" ? user.email : null,
67596
+ display_name: typeof user.display_name === "string" ? user.display_name : null,
67597
+ avatar_url: typeof user.avatar_url === "string" ? user.avatar_url : null
67598
+ },
67599
+ authType: record24.authType,
67600
+ credentialId: record24.credentialId
67601
+ };
67602
+ }
67603
+ async function consumeWebSocketTicket(request, env2) {
67604
+ if (request.headers.get("upgrade")?.toLowerCase() !== "websocket") return null;
67605
+ let url;
67606
+ try {
67607
+ url = new URL(request.url);
67608
+ } catch {
67609
+ return null;
67610
+ }
67611
+ const ticket = url.searchParams.get("ticket");
67612
+ if (!ticket) return null;
67613
+ const authJson = await getAgentBuilder(env2).consumeWebSocketTicket({
67614
+ ticket_hash: await hashToken(ticket),
67615
+ pathname: url.pathname
67616
+ });
67617
+ return typeof authJson === "string" ? parseWebSocketTicketAuth(authJson) : null;
67618
+ }
67619
+ function isWebSocketTicketPath(pathname) {
67620
+ if (!pathname.startsWith("/api/") || pathname.includes("?") || pathname.includes("#")) return false;
67621
+ try {
67622
+ return new URL(pathname, "https://runtime.invalid").pathname === pathname;
67623
+ } catch {
67624
+ return false;
67625
+ }
67626
+ }
67310
67627
  async function authenticate(request, env2) {
67628
+ const ticketAuth = await consumeWebSocketTicket(request, env2);
67629
+ if (ticketAuth) return ticketAuth;
67311
67630
  const token = extractBearerToken(request);
67312
67631
  if (!token) {
67313
67632
  return null;
67314
67633
  }
67315
- if (env2.SUPER_ADMIN_PASSWORD && env2.ENCRYPTION_KEY) {
67634
+ if (typeof env2.SUPER_ADMIN_PASSWORD === "string" && typeof env2.ENCRYPTION_KEY === "string") {
67316
67635
  if (token.includes(".")) {
67317
67636
  const isValidSuperAdmin = await verifySuperAdminToken(
67318
67637
  token,
67319
67638
  env2.ENCRYPTION_KEY
67320
67639
  );
67321
67640
  if (isValidSuperAdmin) {
67322
- return {
67323
- user: {
67324
- id: "super_admin",
67325
- username: "admin",
67326
- role: "admin"
67327
- },
67328
- authType: "super_admin"
67329
- };
67641
+ const tokenHash = await hashToken(token);
67642
+ const agentBuilder = getAgentBuilder(env2);
67643
+ if (await agentBuilder.validateSuperAdminSession(tokenHash)) {
67644
+ return {
67645
+ user: {
67646
+ id: "super_admin",
67647
+ username: "admin",
67648
+ role: "admin"
67649
+ },
67650
+ authType: "super_admin"
67651
+ };
67652
+ }
67330
67653
  }
67331
67654
  }
67332
67655
  }
@@ -67371,7 +67694,8 @@ async function authenticate(request, env2) {
67371
67694
  display_name: user.display_name ?? null,
67372
67695
  avatar_url: user.avatar_url ?? null
67373
67696
  },
67374
- authType: "api_key"
67697
+ authType: "api_key",
67698
+ credentialId: apiKey.id
67375
67699
  };
67376
67700
  }
67377
67701
  }
@@ -67383,7 +67707,10 @@ async function requireAuth(request, env2) {
67383
67707
  if (!authContext) {
67384
67708
  return new Response(JSON.stringify({ error: "Unauthorized" }), {
67385
67709
  status: 401,
67386
- headers: { "Content-Type": "application/json", ...CORS_HEADERS }
67710
+ headers: {
67711
+ "Content-Type": "application/json",
67712
+ ...runtimeCorsHeaders(request, env2)
67713
+ }
67387
67714
  });
67388
67715
  }
67389
67716
  return authContext;
@@ -67393,12 +67720,15 @@ async function requirePermission(request, env2, permission) {
67393
67720
  if (result instanceof Response) {
67394
67721
  return result;
67395
67722
  }
67396
- if (!hasPermission(result.user, permission)) {
67723
+ if (!hasPermissionInEnvironment(result.user, permission, env2)) {
67397
67724
  return new Response(
67398
67725
  JSON.stringify({ error: `Forbidden: '${permission}' access required` }),
67399
67726
  {
67400
67727
  status: 403,
67401
- headers: { "Content-Type": "application/json", ...CORS_HEADERS }
67728
+ headers: {
67729
+ "Content-Type": "application/json",
67730
+ ...runtimeCorsHeaders(request, env2)
67731
+ }
67402
67732
  }
67403
67733
  );
67404
67734
  }
@@ -67414,7 +67744,10 @@ async function requireAdmin(request, env2) {
67414
67744
  JSON.stringify({ error: "Forbidden: Admin access required" }),
67415
67745
  {
67416
67746
  status: 403,
67417
- headers: { "Content-Type": "application/json", ...CORS_HEADERS }
67747
+ headers: {
67748
+ "Content-Type": "application/json",
67749
+ ...runtimeCorsHeaders(request, env2)
67750
+ }
67418
67751
  }
67419
67752
  );
67420
67753
  }
@@ -67424,27 +67757,6 @@ function defineController2(controller) {
67424
67757
  return controller;
67425
67758
  }
67426
67759
 
67427
- // src/api/index.post.ts
67428
- var index_post_default = defineController2(() => {
67429
- const stream = new ReadableStream({
67430
- start(controller) {
67431
- const text2 = "hello world";
67432
- let index = 0;
67433
- const next = () => {
67434
- if (index < text2.length) {
67435
- controller.enqueue(new TextEncoder().encode(text2[index]));
67436
- index++;
67437
- setTimeout(next, 100);
67438
- } else {
67439
- controller.close();
67440
- }
67441
- };
67442
- next();
67443
- }
67444
- });
67445
- return new Response(stream);
67446
- });
67447
-
67448
67760
  // src/api/agents.get.ts
67449
67761
  var agents_get_default = defineController2(async ({ agents, agentNames, registry, auth }) => {
67450
67762
  const includeAuthoringDetails = auth?.user.role === "admin";
@@ -67526,29 +67838,16 @@ var agents_get_default = defineController2(async ({ agents, agentNames, registry
67526
67838
  return Response.json({ agents: validAgents });
67527
67839
  });
67528
67840
 
67529
- // src/routing/access-policy.ts
67530
- function isHostedInstance(env2) {
67531
- const value = env2?.STANDARD_AGENTS_HOSTED;
67532
- if (typeof value === "string") {
67533
- const normalized = value.trim().toLowerCase();
67534
- return normalized !== "" && normalized !== "0" && normalized !== "false";
67535
- }
67536
- return Boolean(value);
67537
- }
67538
- function isTrustedLocalDevelopmentRequest(request, env2) {
67539
- if (isHostedInstance(env2)) return false;
67540
- let hostname;
67541
- try {
67542
- hostname = new URL(request.url).hostname.toLowerCase();
67543
- } catch {
67544
- return false;
67545
- }
67546
- return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname.endsWith(".localhost") || hostname.endsWith(".orb.local");
67547
- }
67548
-
67549
67841
  // src/utils/platform-auth.ts
67550
67842
  var FALLBACK_PLATFORM_ORIGIN = "https://platform.standardagents.ai";
67551
67843
  var SESSION_TTL_SECONDS = 30 * 24 * 60 * 60;
67844
+ function agentBuilderAuthStub(env2) {
67845
+ const namespace = env2.AGENT_BUILDER;
67846
+ if (!namespace || typeof namespace.idFromName !== "function" || typeof namespace.get !== "function") {
67847
+ throw new Error("Server misconfigured: AGENT_BUILDER Durable Object binding is required for platform auth");
67848
+ }
67849
+ return namespace.get(namespace.idFromName("singleton"));
67850
+ }
67552
67851
  function platformApiKey(env2) {
67553
67852
  const value = env2.STANDARD_AGENTS_API_KEY;
67554
67853
  return typeof value === "string" && value.trim() ? value.trim() : null;
@@ -67707,11 +68006,7 @@ async function getPlatformIdentity(env2) {
67707
68006
  };
67708
68007
  }
67709
68008
  async function upsertReplicaUserForPlatformProfile(env2, profile) {
67710
- const namespace = env2.AGENT_BUILDER;
67711
- if (!namespace || typeof namespace.idFromName !== "function" || typeof namespace.get !== "function") {
67712
- throw new Error("Server misconfigured: AGENT_BUILDER Durable Object binding is required for platform auth");
67713
- }
67714
- const agentBuilder = namespace.get(namespace.idFromName("singleton"));
68009
+ const agentBuilder = agentBuilderAuthStub(env2);
67715
68010
  return await agentBuilder.upsertPlatformReplicaUser({
67716
68011
  platform_user_id: profile.platform_user_id,
67717
68012
  username: profile.username ?? null,
@@ -67723,11 +68018,7 @@ async function upsertReplicaUserForPlatformProfile(env2, profile) {
67723
68018
  });
67724
68019
  }
67725
68020
  async function mintLocalSessionForPlatformUser(env2, platformUser) {
67726
- const namespace = env2.AGENT_BUILDER;
67727
- if (!namespace || typeof namespace.idFromName !== "function" || typeof namespace.get !== "function") {
67728
- throw new Error("Server misconfigured: AGENT_BUILDER Durable Object binding is required for platform auth");
67729
- }
67730
- const agentBuilder = namespace.get(namespace.idFromName("singleton"));
68021
+ const agentBuilder = agentBuilderAuthStub(env2);
67731
68022
  const profile = typeof platformUser === "string" ? { platform_user_id: platformUser, username: `sa-${platformUser.slice(0, 12)}`, role: "admin" } : platformUser;
67732
68023
  const user = await upsertReplicaUserForPlatformProfile(env2, profile);
67733
68024
  if (user.role === "api") {
@@ -67739,6 +68030,12 @@ async function mintLocalSessionForPlatformUser(env2, platformUser) {
67739
68030
  await agentBuilder.createSession({ user_id: user.id, token_hash: tokenHash, expires_at: expiresAt });
67740
68031
  return { token, user: { id: user.id, username: user.username, role: user.role } };
67741
68032
  }
68033
+ async function consumePlatformHandoffJti(env2, jti, expiresAt) {
68034
+ return agentBuilderAuthStub(env2).consumePlatformHandoffJti({
68035
+ jti,
68036
+ expires_at: expiresAt
68037
+ });
68038
+ }
67742
68039
  async function createPlatformLocalSession(request, env2) {
67743
68040
  if (!isTrustedLocalDevelopmentRequest(request, env2)) {
67744
68041
  return null;
@@ -67752,12 +68049,7 @@ async function createPlatformLocalSession(request, env2) {
67752
68049
  if (!identity) {
67753
68050
  throw new Error("STANDARD_AGENTS_API_KEY could not be resolved to one active user-instance membership.");
67754
68051
  }
67755
- const agentBuilderNamespace = env2.AGENT_BUILDER;
67756
- if (!agentBuilderNamespace || typeof agentBuilderNamespace.idFromName !== "function" || typeof agentBuilderNamespace.get !== "function") {
67757
- throw new Error("Server misconfigured: AGENT_BUILDER Durable Object binding is required for platform auth");
67758
- }
67759
- const agentBuilderId = agentBuilderNamespace.idFromName("singleton");
67760
- const agentBuilder = agentBuilderNamespace.get(agentBuilderId);
68052
+ const agentBuilder = agentBuilderAuthStub(env2);
67761
68053
  const user = await agentBuilder.upsertPlatformReplicaUser({
67762
68054
  platform_user_id: identity.user.id,
67763
68055
  username: identity.user.username,
@@ -67813,7 +68105,7 @@ var index_get_default = defineController2(async ({ req, env: env2 }) => {
67813
68105
  });
67814
68106
 
67815
68107
  // src/api/api-keys/index.post.ts
67816
- var index_post_default2 = defineController2(async ({ req, env: env2 }) => {
68108
+ var index_post_default = defineController2(async ({ req, env: env2 }) => {
67817
68109
  try {
67818
68110
  if (isPlatformHosted(env2)) {
67819
68111
  return Response.json({ error: "API keys are managed in the Standard Agents platform." }, { status: 410 });
@@ -68031,7 +68323,7 @@ var config_get_default = defineController2(async ({ config, env: env2, req }) =>
68031
68323
  });
68032
68324
 
68033
68325
  // src/version.ts
68034
- var BUILDER_VERSION = "0.38.0".length > 0 ? "0.38.0" : "dev";
68326
+ var BUILDER_VERSION = "0.38.2".length > 0 ? "0.38.2" : "dev";
68035
68327
 
68036
68328
  // src/api/diagnostics.get.ts
68037
68329
  init_LLMRequest();
@@ -68150,6 +68442,35 @@ function resolveStreamTimeoutValue(env2, key, fallback) {
68150
68442
  return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
68151
68443
  }
68152
68444
 
68445
+ // src/middleware/request-auth.ts
68446
+ async function resolveRequestAuth(context) {
68447
+ if (context.auth) return context.auth;
68448
+ if (!isAuthenticationEnv(context.env)) {
68449
+ return new Response(JSON.stringify({ error: "Unauthorized" }), {
68450
+ status: 401,
68451
+ headers: { "Content-Type": "application/json" }
68452
+ });
68453
+ }
68454
+ return requireAuth(context.req, context.env);
68455
+ }
68456
+ async function resolveRequestPermission(context, permission) {
68457
+ if (context.auth) {
68458
+ if (!hasPermissionInEnvironment(context.auth.user, permission, context.env)) {
68459
+ return Response.json(
68460
+ { error: `Forbidden: '${permission}' access required` },
68461
+ { status: 403 }
68462
+ );
68463
+ }
68464
+ return context.auth;
68465
+ }
68466
+ return requirePermission(context.req, context.env, permission);
68467
+ }
68468
+ function routerAuthorizedThread(context, threadId) {
68469
+ const access = context.threadAccess;
68470
+ if (!access || !threadId) return null;
68471
+ return access.threadId === threadId ? access.thread : null;
68472
+ }
68473
+
68153
68474
  // src/events/access.ts
68154
68475
  var EVENT_STREAM_USER_ID_HEADER = "X-Standard-Agents-Event-User-Id";
68155
68476
  var EVENT_STREAM_SCOPE_HEADER = "X-Standard-Agents-Event-Scope";
@@ -68181,8 +68502,9 @@ function resolveEventStreamSubscription(request, auth) {
68181
68502
  }
68182
68503
 
68183
68504
  // src/api/events.get.ts
68184
- var events_get_default = defineController2(async ({ req, env: env2 }) => {
68185
- const authResult = await requireAuth(req, env2);
68505
+ var events_get_default = defineController2(async (context) => {
68506
+ const { req, env: env2 } = context;
68507
+ const authResult = await resolveRequestAuth(context);
68186
68508
  if (authResult instanceof Response) {
68187
68509
  return authResult;
68188
68510
  }
@@ -68211,9 +68533,10 @@ var events_get_default = defineController2(async ({ req, env: env2 }) => {
68211
68533
  const stub = env2.AGENT_BUILDER.get(agentBuilderId);
68212
68534
  return await stub.fetch(authenticatedUpgrade);
68213
68535
  } catch (error) {
68536
+ const message = error instanceof Error ? error.message : "Failed to establish WebSocket connection";
68214
68537
  console.error("Error establishing events WebSocket:", error);
68215
68538
  return Response.json(
68216
- { error: error.message || "Failed to establish WebSocket connection" },
68539
+ { error: message },
68217
68540
  { status: 500 }
68218
68541
  );
68219
68542
  }
@@ -68429,11 +68752,6 @@ var models_get_default = defineController2(async ({ models, modelNames }) => {
68429
68752
  const validModels = modelList.filter(Boolean);
68430
68753
  return Response.json({ models: validModels });
68431
68754
  });
68432
-
68433
- // src/api/new-test.get.ts
68434
- var new_test_get_default = defineController2(async ({ req, params, env: env2 }) => {
68435
- return { message: "Modified new test API route", timestamp: (/* @__PURE__ */ new Date()).toISOString() };
68436
- });
68437
68755
  function toJSONSchema(schema) {
68438
68756
  if (!schema) return null;
68439
68757
  if (schema && typeof schema === "object" && ("~standard" in schema || "_def" in schema)) {
@@ -68540,6 +68858,7 @@ var providers_get_default = defineController2(async ({ env: env2 }) => {
68540
68858
  });
68541
68859
 
68542
68860
  // src/api/skills/index.get.ts
68861
+ init_scope();
68543
68862
  var index_get_default2 = defineController2(async ({ req, env: env2 }) => {
68544
68863
  const authResult = await requireAdmin(req, env2);
68545
68864
  if (authResult instanceof Response) return authResult;
@@ -68547,7 +68866,10 @@ var index_get_default2 = defineController2(async ({ req, env: env2 }) => {
68547
68866
  const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
68548
68867
  const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
68549
68868
  try {
68550
- const skills = await agentBuilder.listSkills({ includeDisabled });
68869
+ const skills = await agentBuilder.listSkills(
68870
+ { ownerId: SHARED_SKILL_OWNER },
68871
+ { includeDisabled }
68872
+ );
68551
68873
  return Response.json({ skills });
68552
68874
  } catch (error) {
68553
68875
  return Response.json(
@@ -68562,7 +68884,8 @@ var SkillValidationError = class extends Error {
68562
68884
  };
68563
68885
 
68564
68886
  // src/api/skills/index.post.ts
68565
- var index_post_default3 = defineController2(async ({ req, env: env2 }) => {
68887
+ init_scope();
68888
+ var index_post_default2 = defineController2(async ({ req, env: env2 }) => {
68566
68889
  const authResult = await requireAdmin(req, env2);
68567
68890
  if (authResult instanceof Response) return authResult;
68568
68891
  let body;
@@ -68584,7 +68907,7 @@ var index_post_default3 = defineController2(async ({ req, env: env2 }) => {
68584
68907
  const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
68585
68908
  const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
68586
68909
  try {
68587
- const skill = await agentBuilder.upsertSkill(files);
68910
+ const skill = await agentBuilder.upsertSkill({ ownerId: SHARED_SKILL_OWNER }, files);
68588
68911
  return Response.json({ skill }, { status: 201 });
68589
68912
  } catch (error) {
68590
68913
  const message = error instanceof Error ? error.message : "Failed to install skill";
@@ -68646,7 +68969,7 @@ async function hasEnabledImmediatePromptTools(agentBuilder, threadId, agentId) {
68646
68969
  }
68647
68970
  return false;
68648
68971
  }
68649
- var index_post_default4 = defineController2(async ({ req, env: env2 }) => {
68972
+ var index_post_default3 = defineController2(async ({ req, env: env2 }) => {
68650
68973
  const authResult = await requireAuth(req, env2);
68651
68974
  if (authResult instanceof Response) {
68652
68975
  return authResult;
@@ -69243,15 +69566,138 @@ var migration17 = {
69243
69566
  }
69244
69567
  };
69245
69568
 
69569
+ // src/durable-objects/agentbuilder-migrations/0018_auth_security.ts
69570
+ var migration18 = {
69571
+ version: 18,
69572
+ async up(sql) {
69573
+ sql.exec(`
69574
+ CREATE TABLE IF NOT EXISTS super_admin_sessions (
69575
+ token_hash TEXT PRIMARY KEY,
69576
+ created_at INTEGER NOT NULL,
69577
+ expires_at INTEGER NOT NULL
69578
+ )
69579
+ `);
69580
+ sql.exec(`
69581
+ CREATE INDEX IF NOT EXISTS idx_super_admin_sessions_expires
69582
+ ON super_admin_sessions(expires_at)
69583
+ `);
69584
+ sql.exec(`
69585
+ CREATE TABLE IF NOT EXISTS auth_rate_limits (
69586
+ bucket_key TEXT PRIMARY KEY,
69587
+ attempts INTEGER NOT NULL,
69588
+ reset_at INTEGER NOT NULL
69589
+ )
69590
+ `);
69591
+ sql.exec(`
69592
+ CREATE INDEX IF NOT EXISTS idx_auth_rate_limits_reset
69593
+ ON auth_rate_limits(reset_at)
69594
+ `);
69595
+ sql.exec(`
69596
+ INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '18')
69597
+ `);
69598
+ }
69599
+ };
69600
+
69601
+ // src/durable-objects/agentbuilder-migrations/0019_scope_skills.ts
69602
+ var migration19 = {
69603
+ version: 19,
69604
+ async up(sql) {
69605
+ sql.exec(`
69606
+ CREATE TABLE skills_0019 (
69607
+ owner_id TEXT NOT NULL,
69608
+ name TEXT NOT NULL,
69609
+ description TEXT NOT NULL,
69610
+ version TEXT,
69611
+ enabled INTEGER NOT NULL DEFAULT 1,
69612
+ shared INTEGER NOT NULL DEFAULT 0,
69613
+ created_at INTEGER DEFAULT (strftime('%s', 'now')),
69614
+ updated_at INTEGER DEFAULT (strftime('%s', 'now')),
69615
+ PRIMARY KEY (owner_id, name)
69616
+ )
69617
+ `);
69618
+ sql.exec(`
69619
+ INSERT INTO skills_0019 (
69620
+ owner_id, name, description, version, enabled, shared, created_at, updated_at
69621
+ )
69622
+ SELECT '__instance_shared__', name, description, version, enabled, 1, created_at, updated_at
69623
+ FROM skills
69624
+ `);
69625
+ sql.exec(`
69626
+ CREATE TABLE skill_files_0019 (
69627
+ owner_id TEXT NOT NULL,
69628
+ skill_name TEXT NOT NULL,
69629
+ path TEXT NOT NULL,
69630
+ content TEXT NOT NULL,
69631
+ size INTEGER NOT NULL,
69632
+ PRIMARY KEY (owner_id, skill_name, path),
69633
+ FOREIGN KEY (owner_id, skill_name) REFERENCES skills_0019(owner_id, name) ON DELETE CASCADE
69634
+ )
69635
+ `);
69636
+ sql.exec(`
69637
+ INSERT INTO skill_files_0019 (owner_id, skill_name, path, content, size)
69638
+ SELECT '__instance_shared__', skill_name, path, content, size FROM skill_files
69639
+ `);
69640
+ sql.exec(`DROP TABLE skill_files`);
69641
+ sql.exec(`DROP TABLE skills`);
69642
+ sql.exec(`ALTER TABLE skills_0019 RENAME TO skills`);
69643
+ sql.exec(`ALTER TABLE skill_files_0019 RENAME TO skill_files`);
69644
+ sql.exec(`CREATE INDEX idx_skills_owner_enabled ON skills(owner_id, enabled)`);
69645
+ sql.exec(`CREATE INDEX idx_skills_shared_enabled ON skills(shared, enabled)`);
69646
+ sql.exec(`CREATE INDEX idx_skill_files_skill ON skill_files(owner_id, skill_name)`);
69647
+ sql.exec(`INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '19')`);
69648
+ }
69649
+ };
69650
+
69651
+ // src/durable-objects/agentbuilder-migrations/0020_websocket_tickets.ts
69652
+ var migration20 = {
69653
+ version: 20,
69654
+ async up(sql) {
69655
+ sql.exec(`
69656
+ CREATE TABLE websocket_tickets (
69657
+ ticket_hash TEXT PRIMARY KEY,
69658
+ pathname TEXT NOT NULL,
69659
+ auth_json TEXT NOT NULL,
69660
+ created_at INTEGER NOT NULL,
69661
+ expires_at INTEGER NOT NULL
69662
+ )
69663
+ `);
69664
+ sql.exec(`
69665
+ CREATE INDEX idx_websocket_tickets_expires
69666
+ ON websocket_tickets(expires_at)
69667
+ `);
69668
+ sql.exec(`INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '20')`);
69669
+ }
69670
+ };
69671
+
69672
+ // src/durable-objects/agentbuilder-migrations/0021_platform_handoff_jtis.ts
69673
+ var migration21 = {
69674
+ version: 21,
69675
+ async up(sql) {
69676
+ sql.exec(`
69677
+ CREATE TABLE platform_handoff_jtis (
69678
+ jti TEXT PRIMARY KEY,
69679
+ consumed_at INTEGER NOT NULL,
69680
+ expires_at INTEGER NOT NULL
69681
+ )
69682
+ `);
69683
+ sql.exec(`
69684
+ CREATE INDEX idx_platform_handoff_jtis_expires
69685
+ ON platform_handoff_jtis(expires_at)
69686
+ `);
69687
+ sql.exec(`INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '21')`);
69688
+ }
69689
+ };
69690
+
69246
69691
  // src/durable-objects/agentbuilder-migrations/index.ts
69247
- var migrations = [migration, migration2, migration3, migration4, migration5, migration6, migration7, migration8, migration9, migration10, migration11, migration12, migration13, migration14, migration15, migration16, migration17];
69692
+ var migrations = [migration, migration2, migration3, migration4, migration5, migration6, migration7, migration8, migration9, migration10, migration11, migration12, migration13, migration14, migration15, migration16, migration17, migration18, migration19, migration20, migration21];
69248
69693
  migrations.reduce(
69249
- (max, migration18) => Math.max(max, migration18.version),
69694
+ (max, migration22) => Math.max(max, migration22.version),
69250
69695
  0
69251
69696
  );
69252
69697
 
69253
69698
  // src/durable-objects/DurableAgentBuilder.ts
69254
69699
  init_types();
69700
+ init_scope();
69255
69701
  var THREAD_PREVIEW_MAX_CHARS = 140;
69256
69702
  var buildThreadPreview = (content) => {
69257
69703
  const collapsed = content.replace(/\s+/g, " ").trim();
@@ -69607,7 +70053,7 @@ var index_get_default3 = defineController2(async ({ req, env: env2 }) => {
69607
70053
 
69608
70054
  // src/api/users/index.post.ts
69609
70055
  var VALID_ROLES = ["admin", "user"];
69610
- var index_post_default5 = defineController2(async ({ req, env: env2 }) => {
70056
+ var index_post_default4 = defineController2(async ({ req, env: env2 }) => {
69611
70057
  try {
69612
70058
  if (isPlatformHosted(env2)) {
69613
70059
  return Response.json({ error: "Users are managed in the Standard Agents platform." }, { status: 410 });
@@ -69871,7 +70317,7 @@ var name_get_default2 = defineController2(async ({ params, models }) => {
69871
70317
  });
69872
70318
 
69873
70319
  // src/api/pack/[agentName]/index.post.ts
69874
- var index_post_default6 = defineController2(async ({ req, env: env2 }) => {
70320
+ var index_post_default5 = defineController2(async ({ req, env: env2 }) => {
69875
70321
  const auth = await requireAdmin(req, env2);
69876
70322
  if (auth instanceof Response) return auth;
69877
70323
  const url = new URL(req.url);
@@ -70051,6 +70497,7 @@ var name_get_default3 = defineController2(async ({ params, prompts, models, mode
70051
70497
  });
70052
70498
 
70053
70499
  // src/api/skills/[name].delete.ts
70500
+ init_scope();
70054
70501
  var name_delete_default = defineController2(async ({ req, env: env2, params }) => {
70055
70502
  const authResult = await requireAdmin(req, env2);
70056
70503
  if (authResult instanceof Response) return authResult;
@@ -70059,7 +70506,7 @@ var name_delete_default = defineController2(async ({ req, env: env2, params }) =
70059
70506
  const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
70060
70507
  const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
70061
70508
  try {
70062
- const found = await agentBuilder.deleteSkill(name15);
70509
+ const found = await agentBuilder.deleteSkill({ ownerId: SHARED_SKILL_OWNER }, name15);
70063
70510
  if (!found) return Response.json({ error: `Skill not found: ${name15}` }, { status: 404 });
70064
70511
  return Response.json({ ok: true, removed: name15 });
70065
70512
  } catch (error) {
@@ -70071,6 +70518,7 @@ var name_delete_default = defineController2(async ({ req, env: env2, params }) =
70071
70518
  });
70072
70519
 
70073
70520
  // src/api/skills/[name].get.ts
70521
+ init_scope();
70074
70522
  var name_get_default4 = defineController2(async ({ req, env: env2, params }) => {
70075
70523
  const authResult = await requireAdmin(req, env2);
70076
70524
  if (authResult instanceof Response) return authResult;
@@ -70079,9 +70527,10 @@ var name_get_default4 = defineController2(async ({ req, env: env2, params }) =>
70079
70527
  const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
70080
70528
  const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
70081
70529
  try {
70082
- const skill = await agentBuilder.getSkill(name15);
70530
+ const scope = { ownerId: SHARED_SKILL_OWNER };
70531
+ const skill = await agentBuilder.getSkill(scope, name15);
70083
70532
  if (!skill) return Response.json({ error: `Skill not found: ${name15}` }, { status: 404 });
70084
- const skillMd = await agentBuilder.getSkillFile(name15, "SKILL.md") ?? "";
70533
+ const skillMd = await agentBuilder.getSkillFile(scope, name15, "SKILL.md") ?? "";
70085
70534
  return Response.json({ skill, skillMd });
70086
70535
  } catch (error) {
70087
70536
  return Response.json(
@@ -70092,6 +70541,7 @@ var name_get_default4 = defineController2(async ({ req, env: env2, params }) =>
70092
70541
  });
70093
70542
 
70094
70543
  // src/api/skills/[name].patch.ts
70544
+ init_scope();
70095
70545
  var name_patch_default = defineController2(async ({ req, env: env2, params }) => {
70096
70546
  const authResult = await requireAdmin(req, env2);
70097
70547
  if (authResult instanceof Response) return authResult;
@@ -70109,9 +70559,10 @@ var name_patch_default = defineController2(async ({ req, env: env2, params }) =>
70109
70559
  const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
70110
70560
  const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
70111
70561
  try {
70112
- const found = await agentBuilder.setSkillEnabled(name15, body.enabled);
70562
+ const scope = { ownerId: SHARED_SKILL_OWNER };
70563
+ const found = await agentBuilder.setSkillEnabled(scope, name15, body.enabled);
70113
70564
  if (!found) return Response.json({ error: `Skill not found: ${name15}` }, { status: 404 });
70114
- const skill = await agentBuilder.getSkill(name15);
70565
+ const skill = await agentBuilder.getSkill(scope, name15);
70115
70566
  return Response.json({ skill });
70116
70567
  } catch (error) {
70117
70568
  return Response.json(
@@ -70408,7 +70859,6 @@ var id_patch_default = defineController2(async ({ req, env: env2, params }) => {
70408
70859
  // Map agent_name back to agent_id for API compat
70409
70860
  user_id: updatedThread.user_id,
70410
70861
  tags: updatedThread.tags || [],
70411
- env: updatedThread.env ?? updatedThread.tenvs ?? null,
70412
70862
  env_types: updatedThread.env_types ?? null,
70413
70863
  created_at: updatedThread.created_at
70414
70864
  });
@@ -70421,33 +70871,6 @@ var id_patch_default = defineController2(async ({ req, env: env2, params }) => {
70421
70871
  }
70422
70872
  });
70423
70873
 
70424
- // src/middleware/request-auth.ts
70425
- async function resolveRequestAuth(context) {
70426
- if (context.auth) return context.auth;
70427
- return requireAuth(context.req, context.env);
70428
- }
70429
- async function resolveOptionalRequestAuth(context) {
70430
- if (context.auth) return context.auth;
70431
- return authenticate(context.req, context.env);
70432
- }
70433
- async function resolveRequestPermission(context, permission) {
70434
- if (context.auth) {
70435
- if (!hasPermission(context.auth.user, permission)) {
70436
- return Response.json(
70437
- { error: `Forbidden: '${permission}' access required` },
70438
- { status: 403 }
70439
- );
70440
- }
70441
- return context.auth;
70442
- }
70443
- return requirePermission(context.req, context.env, permission);
70444
- }
70445
- function routerAuthorizedThread(context, threadId) {
70446
- const access = context.threadAccess;
70447
- if (!access || !threadId) return null;
70448
- return access.threadId === threadId ? access.thread : null;
70449
- }
70450
-
70451
70874
  // src/api/threads/[id].ts
70452
70875
  function resolveIconUrl2(icon, origin) {
70453
70876
  if (!icon) return void 0;
@@ -70578,7 +71001,7 @@ var name_get_default5 = defineController2(async ({ params, tools }) => {
70578
71001
 
70579
71002
  // src/api/unpack/[packageId]/index.post.ts
70580
71003
  init_package_identifier();
70581
- var index_post_default7 = defineController2(async ({ req, env: env2 }) => {
71004
+ var index_post_default6 = defineController2(async ({ req, env: env2 }) => {
70582
71005
  const auth = await requireAdmin(req, env2);
70583
71006
  if (auth instanceof Response) return auth;
70584
71007
  const url = new URL(req.url);
@@ -70723,17 +71146,43 @@ var id_put_default = defineController2(async ({ req, env: env2, params }) => {
70723
71146
  });
70724
71147
 
70725
71148
  // src/api/auth/bootstrap.post.ts
71149
+ var SESSION_TTL_SECONDS2 = 30 * 24 * 60 * 60;
70726
71150
  var bootstrap_post_default = defineController2(async ({ req, env: env2 }) => {
70727
71151
  try {
71152
+ if (!isTrustedLocalDevelopmentRequest(req, env2) || !isSameOriginBrowserRequest(req)) {
71153
+ return Response.json(
71154
+ { error: "Local bootstrap is available only to same-origin clients" },
71155
+ { status: 403, headers: { "Cache-Control": "no-store" } }
71156
+ );
71157
+ }
70728
71158
  const platformTokenSession = await createPlatformLocalSession(req, env2);
70729
71159
  if (!platformTokenSession) {
70730
- return new Response(null, { status: 204 });
71160
+ return new Response(null, {
71161
+ status: 204,
71162
+ headers: { "Cache-Control": "no-store" }
71163
+ });
70731
71164
  }
70732
- return Response.json(platformTokenSession);
71165
+ return Response.json({
71166
+ user: platformTokenSession.user,
71167
+ authType: platformTokenSession.authType,
71168
+ platform: platformTokenSession.platform
71169
+ }, {
71170
+ headers: {
71171
+ "Cache-Control": "no-store",
71172
+ "Set-Cookie": buildSessionCookie(
71173
+ req,
71174
+ platformTokenSession.token,
71175
+ SESSION_TTL_SECONDS2
71176
+ )
71177
+ }
71178
+ });
70733
71179
  } catch (error) {
70734
71180
  const message = error instanceof Error ? error.message : "Failed to bootstrap platform auth";
70735
71181
  const status = message.includes("STANDARD_AGENTS_API_KEY") ? 401 : 502;
70736
- return Response.json({ error: message }, { status });
71182
+ return Response.json(
71183
+ { error: message },
71184
+ { status, headers: { "Cache-Control": "no-store" } }
71185
+ );
70737
71186
  }
70738
71187
  });
70739
71188
 
@@ -70862,7 +71311,51 @@ var config_get_default2 = defineController2(async ({ req, env: env2 }) => {
70862
71311
  };
70863
71312
  });
70864
71313
 
71314
+ // src/api/auth/current-key.delete.ts
71315
+ var current_key_delete_default = defineController2(async ({ req, env: env2 }) => {
71316
+ try {
71317
+ const auth = await authenticate(req, env2);
71318
+ if (!auth) return Response.json({ error: "Unauthorized" }, { status: 401 });
71319
+ if (auth.authType !== "api_key" || !auth.credentialId) {
71320
+ return Response.json({ error: "An API key must authenticate this request." }, { status: 403 });
71321
+ }
71322
+ const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
71323
+ const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
71324
+ await agentBuilder.deleteApiKey(auth.credentialId, auth.user.id);
71325
+ return Response.json({ revoked: true });
71326
+ } catch (error) {
71327
+ console.error("Current API key revocation error:", error);
71328
+ return Response.json(
71329
+ { error: error instanceof Error ? error.message : "Failed to revoke API key" },
71330
+ { status: 500 }
71331
+ );
71332
+ }
71333
+ });
71334
+
71335
+ // src/utils/auth-rate-limit.ts
71336
+ function authClientAddress(request) {
71337
+ return request.headers.get("CF-Connecting-IP")?.trim() || request.headers.get("X-Real-IP")?.trim() || request.headers.get("X-Forwarded-For")?.split(",")[0]?.trim() || "unknown";
71338
+ }
71339
+ async function authRateLimitKey(scope, identifier) {
71340
+ return `${scope}:${await hashToken(identifier.trim().toLowerCase())}`;
71341
+ }
71342
+ function authRateLimitedResponse(result) {
71343
+ return Response.json(
71344
+ { error: "Too many authentication attempts. Try again later." },
71345
+ {
71346
+ status: 429,
71347
+ headers: {
71348
+ "Cache-Control": "no-store",
71349
+ "Retry-After": String(Math.max(1, Math.ceil(result.retry_after)))
71350
+ }
71351
+ }
71352
+ );
71353
+ }
71354
+
70865
71355
  // src/api/auth/login.post.ts
71356
+ var LOGIN_ADDRESS_LIMIT = { limit: 10, window_seconds: 5 * 60 };
71357
+ var LOGIN_IDENTITY_LIMIT = { limit: 25, window_seconds: 15 * 60 };
71358
+ var LOGIN_INSTANCE_LIMIT = { limit: 500, window_seconds: 5 * 60 };
70866
71359
  var login_post_default = defineController2(async ({ req, env: env2 }) => {
70867
71360
  try {
70868
71361
  if (isPlatformHosted(env2)) {
@@ -70879,7 +71372,31 @@ var login_post_default = defineController2(async ({ req, env: env2 }) => {
70879
71372
  { status: 400 }
70880
71373
  );
70881
71374
  }
70882
- if (env2.SUPER_ADMIN_PASSWORD && username === "admin" && password === env2.SUPER_ADMIN_PASSWORD) {
71375
+ if (!env2.AGENT_BUILDER || typeof env2.AGENT_BUILDER.idFromName !== "function") {
71376
+ return Response.json(
71377
+ { error: "Server misconfigured: AGENT_BUILDER Durable Object binding is required for local login" },
71378
+ { status: 500 }
71379
+ );
71380
+ }
71381
+ const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
71382
+ const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
71383
+ const instanceLimit = await agentBuilder.consumeAuthRateLimit({
71384
+ key: "login-instance",
71385
+ ...LOGIN_INSTANCE_LIMIT
71386
+ });
71387
+ if (!instanceLimit.allowed) return authRateLimitedResponse(instanceLimit);
71388
+ const addressLimit = await agentBuilder.consumeAuthRateLimit({
71389
+ key: await authRateLimitKey("login-address", authClientAddress(req)),
71390
+ ...LOGIN_ADDRESS_LIMIT
71391
+ });
71392
+ if (!addressLimit.allowed) return authRateLimitedResponse(addressLimit);
71393
+ const identityLimit = await agentBuilder.consumeAuthRateLimit({
71394
+ key: await authRateLimitKey("login-identity", username || "<missing>"),
71395
+ ...LOGIN_IDENTITY_LIMIT
71396
+ });
71397
+ if (!identityLimit.allowed) return authRateLimitedResponse(identityLimit);
71398
+ const isSuperAdminPassword = env2.SUPER_ADMIN_PASSWORD && username === "admin" ? await constantTimeEqual(password, env2.SUPER_ADMIN_PASSWORD) : false;
71399
+ if (isSuperAdminPassword) {
70883
71400
  if (!env2.ENCRYPTION_KEY) {
70884
71401
  return Response.json(
70885
71402
  { error: "Server misconfigured: ENCRYPTION_KEY required for super admin" },
@@ -70887,6 +71404,12 @@ var login_post_default = defineController2(async ({ req, env: env2 }) => {
70887
71404
  );
70888
71405
  }
70889
71406
  const token2 = await generateSuperAdminToken(env2.ENCRYPTION_KEY);
71407
+ const tokenHash2 = await hashToken(token2);
71408
+ const expiresAt2 = Math.floor(Date.now() / 1e3) + SUPER_ADMIN_TOKEN_TTL_SECONDS;
71409
+ await agentBuilder.createSuperAdminSession({
71410
+ token_hash: tokenHash2,
71411
+ expires_at: expiresAt2
71412
+ });
70890
71413
  return Response.json({
70891
71414
  token: token2,
70892
71415
  user: {
@@ -70894,6 +71417,11 @@ var login_post_default = defineController2(async ({ req, env: env2 }) => {
70894
71417
  username: "admin",
70895
71418
  role: "admin"
70896
71419
  }
71420
+ }, {
71421
+ headers: {
71422
+ "Cache-Control": "no-store",
71423
+ "Set-Cookie": buildSessionCookie(req, token2, SUPER_ADMIN_TOKEN_TTL_SECONDS)
71424
+ }
70897
71425
  });
70898
71426
  }
70899
71427
  if (!username) {
@@ -70902,14 +71430,6 @@ var login_post_default = defineController2(async ({ req, env: env2 }) => {
70902
71430
  { status: 400 }
70903
71431
  );
70904
71432
  }
70905
- if (!env2.AGENT_BUILDER || typeof env2.AGENT_BUILDER.idFromName !== "function") {
70906
- return Response.json(
70907
- { error: "Server misconfigured: AGENT_BUILDER Durable Object binding is required for local login" },
70908
- { status: 500 }
70909
- );
70910
- }
70911
- const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
70912
- const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
70913
71433
  const user = await agentBuilder.getUserByUsername(username);
70914
71434
  if (!user) {
70915
71435
  return Response.json(
@@ -70939,11 +71459,16 @@ var login_post_default = defineController2(async ({ req, env: env2 }) => {
70939
71459
  username: user.username,
70940
71460
  role: user.role
70941
71461
  }
71462
+ }, {
71463
+ headers: {
71464
+ "Cache-Control": "no-store",
71465
+ "Set-Cookie": buildSessionCookie(req, token, 30 * 24 * 60 * 60)
71466
+ }
70942
71467
  });
70943
71468
  } catch (error) {
70944
71469
  console.error("Login error:", error);
70945
71470
  return Response.json(
70946
- { error: error.message || "Login failed" },
71471
+ { error: error instanceof Error ? error.message : "Login failed" },
70947
71472
  { status: 500 }
70948
71473
  );
70949
71474
  }
@@ -70956,38 +71481,23 @@ async function deleteLocalSession(req, env2) {
70956
71481
  if (!token) {
70957
71482
  return;
70958
71483
  }
70959
- if (!token.includes(".") && isValidUserToken(token)) {
70960
- const tokenHash = await hashToken(token);
70961
- const agentBuilder = env2.AGENT_BUILDER.get(env2.AGENT_BUILDER.idFromName("singleton"));
71484
+ const tokenHash = await hashToken(token);
71485
+ const agentBuilder = env2.AGENT_BUILDER.get(env2.AGENT_BUILDER.idFromName("singleton"));
71486
+ if (token.includes(".")) {
71487
+ await agentBuilder.deleteSuperAdminSession(tokenHash);
71488
+ } else if (isValidUserToken(token)) {
70962
71489
  await agentBuilder.deleteSession(tokenHash);
70963
71490
  }
70964
71491
  }
70965
71492
  function clearedSessionCookie(req) {
70966
71493
  return buildSessionCookie(req, "", 0);
70967
71494
  }
70968
-
70969
- // src/api/auth/logout.get.ts
70970
- function safeReturnTo(value) {
70971
- if (!value || !value.startsWith("/") || value.startsWith("//")) return "/";
70972
- return value;
71495
+ function isSameOriginLogout(req) {
71496
+ const origin = req.headers.get("Origin");
71497
+ if (origin && origin !== new URL(req.url).origin) return false;
71498
+ const fetchSite = req.headers.get("Sec-Fetch-Site");
71499
+ return !fetchSite || fetchSite === "same-origin" || fetchSite === "none";
70973
71500
  }
70974
- var logout_get_default = defineController2(async ({ req, env: env2 }) => {
70975
- try {
70976
- await deleteLocalSession(req, env2);
70977
- } catch (error) {
70978
- console.error("Logout error:", error);
70979
- }
70980
- const url = new URL(req.url);
70981
- const returnTo = safeReturnTo(url.searchParams.get("return_to"));
70982
- const location = isPlatformHosted(env2) ? platformLogoutUrl(req, env2, returnTo) : `${url.origin}/login`;
70983
- return new Response(null, {
70984
- status: 302,
70985
- headers: {
70986
- Location: location,
70987
- "Set-Cookie": clearedSessionCookie(req)
70988
- }
70989
- });
70990
- });
70991
71501
 
70992
71502
  // src/api/auth/logout.post.ts
70993
71503
  var logout_post_default = defineController2(async ({ req, env: env2 }) => {
@@ -70998,9 +71508,15 @@ var logout_post_default = defineController2(async ({ req, env: env2 }) => {
70998
71508
  "Set-Cookie": clearedSessionCookie(req)
70999
71509
  }
71000
71510
  });
71511
+ if (!isSameOriginLogout(req)) {
71512
+ return Response.json({ error: "Cross-origin logout refused" }, { status: 403 });
71513
+ }
71001
71514
  try {
71002
71515
  await deleteLocalSession(req, env2);
71003
- return clearCookie({ success: true });
71516
+ const url = new URL(req.url);
71517
+ const returnTo = url.searchParams.get("return_to");
71518
+ const redirect = isPlatformHosted(env2) ? platformLogoutUrl(req, env2, returnTo) : void 0;
71519
+ return clearCookie({ success: true, ...redirect ? { redirect } : {} });
71004
71520
  } catch (error) {
71005
71521
  console.error("Logout error:", error);
71006
71522
  return clearCookie({ error: error.message || "Logout failed" }, 500);
@@ -71103,6 +71619,37 @@ var platform_replica_post_default = defineController2(async ({ req, env: env2 })
71103
71619
  return Response.json({ ok: true, ...result });
71104
71620
  });
71105
71621
 
71622
+ // src/api/auth/websocket-ticket.post.ts
71623
+ var WEBSOCKET_TICKET_TTL_SECONDS = 30;
71624
+ var websocket_ticket_post_default = defineController2(async ({ req, env: env2 }) => {
71625
+ const auth = await authenticate(req, env2);
71626
+ if (!auth) return Response.json({ error: "Unauthorized" }, { status: 401 });
71627
+ let body;
71628
+ try {
71629
+ body = await req.json();
71630
+ } catch {
71631
+ return Response.json({ error: "A JSON body is required." }, { status: 400 });
71632
+ }
71633
+ const pathname = body && typeof body === "object" ? body.pathname : void 0;
71634
+ if (typeof pathname !== "string" || !isWebSocketTicketPath(pathname)) {
71635
+ return Response.json({ error: "pathname must be a normalized /api/* path." }, { status: 400 });
71636
+ }
71637
+ const ticket = generateSecureToken(32);
71638
+ const now = Math.floor(Date.now() / 1e3);
71639
+ const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
71640
+ const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
71641
+ await agentBuilder.createWebSocketTicket({
71642
+ ticket_hash: await hashToken(ticket),
71643
+ pathname,
71644
+ auth_json: JSON.stringify(auth),
71645
+ expires_at: now + WEBSOCKET_TICKET_TTL_SECONDS
71646
+ });
71647
+ return Response.json(
71648
+ { ticket, expires_in: WEBSOCKET_TICKET_TTL_SECONDS },
71649
+ { headers: { "Cache-Control": "no-store" } }
71650
+ );
71651
+ });
71652
+
71106
71653
  // src/api/envs/_shared.ts
71107
71654
  function buildScopedEnvResponse(entries) {
71108
71655
  const variables = [...entries].sort((a, b) => a.name.localeCompare(b.name)).map((entry) => ({
@@ -71579,6 +72126,7 @@ var path_default = defineController2(async ({ req, params, env: env2, url }) =>
71579
72126
  });
71580
72127
 
71581
72128
  // src/api/platform-auth/bootstrap.post.ts
72129
+ var AGENT_BUILDER_SESSION_TTL_SECONDS = 30 * 24 * 60 * 60;
71582
72130
  function uniqueEndpoints(preferred, candidates) {
71583
72131
  return Array.from(new Set([preferred, ...candidates].filter((value) => Boolean(value))));
71584
72132
  }
@@ -71593,10 +72141,10 @@ function buildSessionCookieHeader(req, sessionCookie) {
71593
72141
  return `${sessionCookie}; ${attributes.join("; ")}`;
71594
72142
  }
71595
72143
  var bootstrap_post_default2 = defineController2(async ({ req, env: env2 }) => {
71596
- if (!isTrustedLocalDevelopmentRequest(req, env2)) {
72144
+ if (!isTrustedLocalDevelopmentRequest(req, env2) || !isSameOriginBrowserRequest(req)) {
71597
72145
  return Response.json(
71598
- { error: "Platform auth bridge is disabled outside local development" },
71599
- { status: 410 }
72146
+ { error: "Platform auth bridge is restricted to same-origin local development" },
72147
+ { status: 403 }
71600
72148
  );
71601
72149
  }
71602
72150
  const requestCookie = req.headers.get("Cookie")?.trim() || "";
@@ -71623,10 +72171,16 @@ var bootstrap_post_default2 = defineController2(async ({ req, env: env2 }) => {
71623
72171
  account: localSession.platform.account,
71624
72172
  authType: localSession.authType,
71625
72173
  verified: localSession.platform.verified,
71626
- token: localSession.token,
71627
72174
  localUser: localSession.user
71628
72175
  }, {
71629
- headers: { "Cache-Control": "no-store" }
72176
+ headers: {
72177
+ "Cache-Control": "no-store",
72178
+ "Set-Cookie": buildSessionCookie(
72179
+ req,
72180
+ localSession.token,
72181
+ AGENT_BUILDER_SESSION_TTL_SECONDS
72182
+ )
72183
+ }
71630
72184
  });
71631
72185
  }
71632
72186
  }
@@ -73280,12 +73834,11 @@ var tools_get_default2 = defineController2(async ({ params, url }) => {
73280
73834
  });
73281
73835
 
73282
73836
  // src/api/threads/[id]/bridge.ts
73283
- var bridge_default = defineController2(async ({ req, params, env: env2 }) => {
73284
- if (isPlatformHosted(env2)) {
73285
- const authResult = await requireAuth(req, env2);
73286
- if (authResult instanceof Response) {
73287
- return authResult;
73288
- }
73837
+ var bridge_default = defineController2(async (context) => {
73838
+ const { req, params, env: env2 } = context;
73839
+ const authResult = await resolveRequestAuth(context);
73840
+ if (authResult instanceof Response) {
73841
+ return authResult;
73289
73842
  }
73290
73843
  const threadId = params.id;
73291
73844
  if (!threadId) {
@@ -73298,22 +73851,31 @@ var bridge_default = defineController2(async ({ req, params, env: env2 }) => {
73298
73851
  );
73299
73852
  }
73300
73853
  try {
73301
- const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
73302
- const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
73303
- const thread = await agentBuilder.getThread(threadId);
73304
- if (!thread) {
73305
- return Response.json(
73306
- { error: `Thread not found: ${threadId}` },
73307
- { status: 404 }
73308
- );
73854
+ if (!routerAuthorizedThread(context, threadId)) {
73855
+ const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
73856
+ const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
73857
+ const thread = await agentBuilder.getThread(threadId);
73858
+ if (!thread) {
73859
+ return Response.json(
73860
+ { error: `Thread not found: ${threadId}` },
73861
+ { status: 404 }
73862
+ );
73863
+ }
73864
+ if (!canAccessThread(authResult, thread)) {
73865
+ return Response.json(
73866
+ { error: "Forbidden: You don't have access to this thread" },
73867
+ { status: 403 }
73868
+ );
73869
+ }
73309
73870
  }
73310
73871
  const durableId = env2.AGENT_BUILDER_THREAD.idFromName(threadId);
73311
73872
  const stub = env2.AGENT_BUILDER_THREAD.get(durableId);
73312
73873
  return await stub.fetch(req);
73313
73874
  } catch (error) {
73875
+ const message = error instanceof Error ? error.message : "Failed to establish client bridge";
73314
73876
  console.error(`Error establishing bridge for thread ${threadId}:`, error);
73315
73877
  return Response.json(
73316
- { error: error.message || "Failed to establish client bridge" },
73878
+ { error: message },
73317
73879
  { status: 500 }
73318
73880
  );
73319
73881
  }
@@ -73538,19 +74100,9 @@ var cost_get_default = defineController2(async ({ req, params, env: env2 }) => {
73538
74100
 
73539
74101
  // src/api/threads/[id]/diagnostics.get.ts
73540
74102
  var diagnostics_get_default2 = defineController2(async ({ req, params, env: env2 }) => {
73541
- if (isPlatformHosted(env2)) {
73542
- const authResult = await requirePermission(req, env2, "logs");
73543
- if (authResult instanceof Response) {
73544
- return authResult;
73545
- }
73546
- } else {
73547
- const authContext = await authenticate(req, env2);
73548
- if (authContext && !hasPermission(authContext.user, "logs")) {
73549
- return Response.json(
73550
- { error: "Forbidden: 'logs' access required" },
73551
- { status: 403 }
73552
- );
73553
- }
74103
+ const authResult = await requirePermission(req, env2, "logs");
74104
+ if (authResult instanceof Response) {
74105
+ return authResult;
73554
74106
  }
73555
74107
  const threadId = params.id;
73556
74108
  if (!threadId) {
@@ -73718,6 +74270,33 @@ var fs_default = defineController2(async ({ req, params, env: env2 }) => {
73718
74270
  }
73719
74271
  });
73720
74272
 
74273
+ // src/utils/kv-key-policy.ts
74274
+ var THREAD_CLIENT_RESERVED_KEYS = /* @__PURE__ */ new Set([
74275
+ "execution_owner",
74276
+ "approvals",
74277
+ "bg_processes",
74278
+ "lines_capacity_cache"
74279
+ ]);
74280
+ var THREAD_CLIENT_RESERVED_PREFIXES = [
74281
+ "approval_"
74282
+ ];
74283
+ var USER_CLIENT_RESERVED_KEYS = /* @__PURE__ */ new Set([
74284
+ "standardcode.lines.leases",
74285
+ "standardcode.lines.capacity_cache"
74286
+ ]);
74287
+ function isThreadKvKeyClientWritable(key) {
74288
+ return !THREAD_CLIENT_RESERVED_KEYS.has(key) && !THREAD_CLIENT_RESERVED_PREFIXES.some((prefix) => key.startsWith(prefix));
74289
+ }
74290
+ function isUserKvKeyClientWritable(key) {
74291
+ return !USER_CLIENT_RESERVED_KEYS.has(key);
74292
+ }
74293
+ function reservedKvKeyResponse() {
74294
+ return Response.json(
74295
+ { error: "Key is reserved for server-managed state" },
74296
+ { status: 403 }
74297
+ );
74298
+ }
74299
+
73721
74300
  // src/api/threads/[id]/kv/index.ts
73722
74301
  async function getAuthorizedThreadStub2(context, threadId) {
73723
74302
  const { env: env2 } = context;
@@ -73811,11 +74390,14 @@ async function handlePost(stub, req) {
73811
74390
  if (body instanceof Response) return body;
73812
74391
  const key = readKey(body.key);
73813
74392
  if (key instanceof Response) return key;
74393
+ const previousKey = typeof body.previousKey === "string" && body.previousKey !== key ? body.previousKey : null;
74394
+ if (!isThreadKvKeyClientWritable(key) || previousKey?.trim() && !isThreadKvKeyClientWritable(previousKey)) {
74395
+ return reservedKvKeyResponse();
74396
+ }
73814
74397
  if (!Object.prototype.hasOwnProperty.call(body, "value")) {
73815
74398
  return Response.json({ error: "Value is required" }, { status: 400 });
73816
74399
  }
73817
74400
  await stub.setValue(key, body.value);
73818
- const previousKey = typeof body.previousKey === "string" && body.previousKey !== key ? body.previousKey : null;
73819
74401
  if (previousKey?.trim()) {
73820
74402
  await stub.setValue(previousKey, null);
73821
74403
  }
@@ -73841,6 +74423,7 @@ async function handleDelete(stub, req, url) {
73841
74423
  }
73842
74424
  const key = readKey(keyValue);
73843
74425
  if (key instanceof Response) return key;
74426
+ if (!isThreadKvKeyClientWritable(key)) return reservedKvKeyResponse();
73844
74427
  await stub.setValue(key, null);
73845
74428
  return new Response(null, { status: 204 });
73846
74429
  }
@@ -73878,19 +74461,9 @@ var kv_default = defineController2(async (context) => {
73878
74461
  // src/api/threads/[id]/logs.get.ts
73879
74462
  var logs_get_default = defineController2(async (context) => {
73880
74463
  const { req, params, env: env2 } = context;
73881
- if (isPlatformHosted(env2)) {
73882
- const authResult = await resolveRequestPermission(context, "logs");
73883
- if (authResult instanceof Response) {
73884
- return authResult;
73885
- }
73886
- } else {
73887
- const authContext = await resolveOptionalRequestAuth(context);
73888
- if (authContext && !hasPermission(authContext.user, "logs")) {
73889
- return Response.json(
73890
- { error: "Forbidden: 'logs' access required" },
73891
- { status: 403 }
73892
- );
73893
- }
74464
+ const authResult = await resolveRequestPermission(context, "logs");
74465
+ if (authResult instanceof Response) {
74466
+ return authResult;
73894
74467
  }
73895
74468
  const url = new URL(req.url);
73896
74469
  const threadId = params.id;
@@ -74123,19 +74696,9 @@ var pending_tool_calls_get_default = defineController2(async ({ params, env: env
74123
74696
 
74124
74697
  // src/api/threads/[id]/revive.post.ts
74125
74698
  var revive_post_default = defineController2(async ({ req, params, env: env2 }) => {
74126
- if (isPlatformHosted(env2)) {
74127
- const authResult = await requirePermission(req, env2, "logs");
74128
- if (authResult instanceof Response) {
74129
- return authResult;
74130
- }
74131
- } else {
74132
- const authContext = await authenticate(req, env2);
74133
- if (authContext && !hasPermission(authContext.user, "logs")) {
74134
- return Response.json(
74135
- { error: "Forbidden: 'logs' access required" },
74136
- { status: 403 }
74137
- );
74138
- }
74699
+ const authResult = await requirePermission(req, env2, "logs");
74700
+ if (authResult instanceof Response) {
74701
+ return authResult;
74139
74702
  }
74140
74703
  const threadId = params.id;
74141
74704
  if (!threadId) {
@@ -74169,20 +74732,15 @@ var revive_post_default = defineController2(async ({ req, params, env: env2 }) =
74169
74732
  });
74170
74733
 
74171
74734
  // src/api/threads/[id]/rpc.post.ts
74735
+ function unsafeLocalRpcEnabled(env2) {
74736
+ const value = Reflect.get(env2, "AGENT_BUILDER_ENABLE_UNSAFE_RPC");
74737
+ if (typeof value !== "string") return value === true;
74738
+ const normalized = value.trim().toLowerCase();
74739
+ return normalized === "1" || normalized === "true";
74740
+ }
74172
74741
  var rpc_post_default = defineController2(async ({ req, params, env: env2 }) => {
74173
- if (isPlatformHosted(env2)) {
74174
- const authResult = await requirePermission(req, env2, "logs");
74175
- if (authResult instanceof Response) {
74176
- return authResult;
74177
- }
74178
- } else {
74179
- const authContext = await authenticate(req, env2);
74180
- if (authContext && !hasPermission(authContext.user, "logs")) {
74181
- return Response.json(
74182
- { error: "Forbidden: 'logs' access required" },
74183
- { status: 403 }
74184
- );
74185
- }
74742
+ if (!isTrustedLocalDevelopmentRequest(req, env2) || !unsafeLocalRpcEnabled(env2)) {
74743
+ return Response.json({ error: "Not found" }, { status: 404 });
74186
74744
  }
74187
74745
  const threadId = params.id;
74188
74746
  if (!threadId) {
@@ -74196,7 +74754,11 @@ var rpc_post_default = defineController2(async ({ req, params, env: env2 }) => {
74196
74754
  try {
74197
74755
  const id = env2.AGENT_BUILDER_THREAD.idFromName(threadId);
74198
74756
  const thread = env2.AGENT_BUILDER_THREAD.get(id);
74199
- const result = await thread[method](...args);
74757
+ const rpcMethod = Reflect.get(thread, method);
74758
+ if (typeof rpcMethod !== "function") {
74759
+ return Response.json({ error: "Method not found" }, { status: 404 });
74760
+ }
74761
+ const result = await Reflect.apply(rpcMethod, thread, args);
74200
74762
  if (result instanceof Response) {
74201
74763
  return result;
74202
74764
  }
@@ -74239,12 +74801,11 @@ var stop_post_default = defineController2(async ({ params, env: env2 }) => {
74239
74801
  });
74240
74802
 
74241
74803
  // src/api/threads/[id]/stream.ts
74242
- var stream_default = defineController2(async ({ req, params, env: env2 }) => {
74243
- if (isPlatformHosted(env2)) {
74244
- const authResult = await requireAuth(req, env2);
74245
- if (authResult instanceof Response) {
74246
- return authResult;
74247
- }
74804
+ var stream_default = defineController2(async (context) => {
74805
+ const { req, params, env: env2 } = context;
74806
+ const authResult = await resolveRequestAuth(context);
74807
+ if (authResult instanceof Response) {
74808
+ return authResult;
74248
74809
  }
74249
74810
  const threadId = params.id;
74250
74811
  if (!threadId) {
@@ -74257,22 +74818,31 @@ var stream_default = defineController2(async ({ req, params, env: env2 }) => {
74257
74818
  );
74258
74819
  }
74259
74820
  try {
74260
- const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
74261
- const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
74262
- const thread = await agentBuilder.getThread(threadId);
74263
- if (!thread) {
74264
- return Response.json(
74265
- { error: `Thread not found: ${threadId}` },
74266
- { status: 404 }
74267
- );
74821
+ if (!routerAuthorizedThread(context, threadId)) {
74822
+ const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
74823
+ const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
74824
+ const thread = await agentBuilder.getThread(threadId);
74825
+ if (!thread) {
74826
+ return Response.json(
74827
+ { error: `Thread not found: ${threadId}` },
74828
+ { status: 404 }
74829
+ );
74830
+ }
74831
+ if (!canAccessThread(authResult, thread)) {
74832
+ return Response.json(
74833
+ { error: "Forbidden: You don't have access to this thread" },
74834
+ { status: 403 }
74835
+ );
74836
+ }
74268
74837
  }
74269
74838
  const durableId = env2.AGENT_BUILDER_THREAD.idFromName(threadId);
74270
74839
  const stub = env2.AGENT_BUILDER_THREAD.get(durableId);
74271
74840
  return await stub.fetch(req);
74272
74841
  } catch (error) {
74842
+ const message = error instanceof Error ? error.message : "Failed to establish message stream";
74273
74843
  console.error(`Error handling message stream for thread ${threadId}:`, error);
74274
74844
  return Response.json(
74275
- { error: error.message || "Failed to establish message stream" },
74845
+ { error: message },
74276
74846
  { status: 500 }
74277
74847
  );
74278
74848
  }
@@ -74321,6 +74891,9 @@ var tool_result_post_default = defineController2(async ({ req, params, env: env2
74321
74891
  if (!toolCallId || typeof toolCallId !== "string") {
74322
74892
  return Response.json({ error: "tool_call_id required" }, { status: 400 });
74323
74893
  }
74894
+ if (!body.delivery_token || typeof body.delivery_token !== "string") {
74895
+ return Response.json({ error: "delivery_token required" }, { status: 400 });
74896
+ }
74324
74897
  try {
74325
74898
  const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
74326
74899
  const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
@@ -74336,7 +74909,8 @@ var tool_result_post_default = defineController2(async ({ req, params, env: env2
74336
74909
  typeof body.result === "string" ? body.result : void 0,
74337
74910
  typeof body.error === "string" ? body.error : void 0,
74338
74911
  typeof body.client_id === "string" ? body.client_id : void 0,
74339
- typeof body.generation === "number" ? body.generation : void 0
74912
+ typeof body.generation === "number" ? body.generation : void 0,
74913
+ body.delivery_token
74340
74914
  );
74341
74915
  if (!delivered) {
74342
74916
  return Response.json({ status: "ignored" }, { status: 200 });
@@ -74504,8 +75078,9 @@ var env_patch_default = defineController2(async ({ req, env: env2, params }) =>
74504
75078
  });
74505
75079
 
74506
75080
  // src/api/users/[id]/stream.ts
74507
- var stream_default2 = defineController2(async ({ req, env: env2, params }) => {
74508
- const authResult = await requireAuth(req, env2);
75081
+ var stream_default2 = defineController2(async (context) => {
75082
+ const { req, env: env2, params } = context;
75083
+ const authResult = await resolveRequestAuth(context);
74509
75084
  if (authResult instanceof Response) return authResult;
74510
75085
  const isAdmin = authResult.authType === "super_admin" || authResult.user?.role === "admin";
74511
75086
  if (!isAdmin) {
@@ -74647,11 +75222,23 @@ var name_get_default8 = defineController2(async ({ params, url, prompts, agents,
74647
75222
  });
74648
75223
 
74649
75224
  // src/api/auth/device/start.post.ts
75225
+ var DEVICE_ADDRESS_LIMIT = { limit: 20, window_seconds: 10 * 60 };
75226
+ var DEVICE_INSTANCE_LIMIT = { limit: 300, window_seconds: 10 * 60 };
74650
75227
  var start_post_default = defineController2(async ({ req, env: env2 }) => {
74651
- const bytes = crypto.getRandomValues(new Uint8Array(24));
74652
- const code = [...bytes].map((b) => "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789"[b % 57]).join("");
74653
75228
  const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
74654
75229
  const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
75230
+ const addressLimit = await agentBuilder.consumeAuthRateLimit({
75231
+ key: await authRateLimitKey("device-start-address", authClientAddress(req)),
75232
+ ...DEVICE_ADDRESS_LIMIT
75233
+ });
75234
+ if (!addressLimit.allowed) return authRateLimitedResponse(addressLimit);
75235
+ const instanceLimit = await agentBuilder.consumeAuthRateLimit({
75236
+ key: "device-start-instance",
75237
+ ...DEVICE_INSTANCE_LIMIT
75238
+ });
75239
+ if (!instanceLimit.allowed) return authRateLimitedResponse(instanceLimit);
75240
+ const bytes = crypto.getRandomValues(new Uint8Array(24));
75241
+ const code = [...bytes].map((b) => "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789"[b % 57]).join("");
74655
75242
  await agentBuilder.startDeviceLogin(await hashToken(code));
74656
75243
  const origin = new URL(req.url).origin;
74657
75244
  const verifyBaseRaw = env2.STANDARD_AGENTS_DEVICE_VERIFY_URL_BASE;
@@ -74665,8 +75252,74 @@ var start_post_default = defineController2(async ({ req, env: env2 }) => {
74665
75252
  });
74666
75253
  });
74667
75254
 
75255
+ // src/api/auth/sa/_login-state.ts
75256
+ var LOGIN_STATE_COOKIE = "agtbldr_sa_login_state";
75257
+ var LOGIN_STATE_PARAM = "__sa_login_state";
75258
+ var LOGIN_STATE_TTL_SECONDS = 10 * 60;
75259
+ function safeReturnTo(value) {
75260
+ if (!value || !value.startsWith("/") || value.startsWith("//")) return "/";
75261
+ return value;
75262
+ }
75263
+ function stateCookie(req, value, maxAge) {
75264
+ const attributes = ["Path=/api/auth/sa", "HttpOnly", "SameSite=Lax"];
75265
+ if (new URL(req.url).protocol === "https:") attributes.push("Secure");
75266
+ return `${LOGIN_STATE_COOKIE}=${encodeURIComponent(value)}; ${attributes.join("; ")}; Max-Age=${maxAge}`;
75267
+ }
75268
+ function readStateCookie(req) {
75269
+ const raw = req.headers.get("Cookie");
75270
+ if (!raw) return null;
75271
+ for (const part of raw.split(";")) {
75272
+ const separator = part.indexOf("=");
75273
+ if (separator < 0 || part.slice(0, separator).trim() !== LOGIN_STATE_COOKIE) continue;
75274
+ try {
75275
+ return decodeURIComponent(part.slice(separator + 1).trim()) || null;
75276
+ } catch {
75277
+ return null;
75278
+ }
75279
+ }
75280
+ return null;
75281
+ }
75282
+ function statefulReturnTo(returnTo, state) {
75283
+ const target = new URL(safeReturnTo(returnTo), "https://instance.invalid");
75284
+ target.searchParams.set(LOGIN_STATE_PARAM, state);
75285
+ return `${target.pathname}${target.search}${target.hash}`;
75286
+ }
75287
+ function beginPlatformLogin(req, env2, returnTo, error) {
75288
+ const state = crypto.randomUUID();
75289
+ const location = new URL(platformLoginUrl(req, env2, statefulReturnTo(returnTo, state)));
75290
+ if (error) location.searchParams.set("error", error);
75291
+ return new Response(null, {
75292
+ status: 302,
75293
+ headers: {
75294
+ Location: location.toString(),
75295
+ "Set-Cookie": stateCookie(req, state, LOGIN_STATE_TTL_SECONDS),
75296
+ "Cache-Control": "no-store"
75297
+ }
75298
+ });
75299
+ }
75300
+ function consumePlatformLoginState(req, rawReturnTo) {
75301
+ const target = new URL(safeReturnTo(rawReturnTo), "https://instance.invalid");
75302
+ const state = target.searchParams.get(LOGIN_STATE_PARAM);
75303
+ target.searchParams.delete(LOGIN_STATE_PARAM);
75304
+ const returnTo = `${target.pathname}${target.search}${target.hash}`;
75305
+ return {
75306
+ returnTo,
75307
+ valid: Boolean(state && state === readStateCookie(req)),
75308
+ present: Boolean(state)
75309
+ };
75310
+ }
75311
+ function withClearedPlatformLoginState(req, response) {
75312
+ const headers = new Headers(response.headers);
75313
+ headers.append("Set-Cookie", stateCookie(req, "", 0));
75314
+ return new Response(response.body, {
75315
+ status: response.status,
75316
+ statusText: response.statusText,
75317
+ headers
75318
+ });
75319
+ }
75320
+
74668
75321
  // src/api/auth/sa/callback.get.ts
74669
- var SESSION_TTL_SECONDS2 = 30 * 24 * 60 * 60;
75322
+ var SESSION_TTL_SECONDS3 = 30 * 24 * 60 * 60;
74670
75323
  function safeReturnTo2(value) {
74671
75324
  if (!value || !value.startsWith("/") || value.startsWith("//")) return "/";
74672
75325
  return value;
@@ -74674,15 +75327,12 @@ function safeReturnTo2(value) {
74674
75327
  function failRedirect(req, env2, reason, returnTo) {
74675
75328
  const origin = new URL(req.url).origin;
74676
75329
  if (isPlatformHosted(env2)) {
74677
- const target = new URL(platformLoginUrl(req, env2, returnTo ?? "/"));
74678
- target.searchParams.set(
74679
- "error",
75330
+ return beginPlatformLogin(
75331
+ req,
75332
+ env2,
75333
+ returnTo ?? "/",
74680
75334
  reason === "api_only_account" ? "api_only_account" : "instance_login_failed"
74681
75335
  );
74682
- return new Response(null, {
74683
- status: 302,
74684
- headers: { Location: target.toString() }
74685
- });
74686
75336
  }
74687
75337
  return new Response(null, {
74688
75338
  status: 302,
@@ -74694,7 +75344,9 @@ var callback_get_default = defineController2(async ({ req, env: env2 }) => {
74694
75344
  return failRedirect(req, env2, "not_hosted");
74695
75345
  }
74696
75346
  const url = new URL(req.url);
74697
- const failReturnTo = safeReturnTo2(url.searchParams.get("return_to"));
75347
+ const loginState = consumePlatformLoginState(req, url.searchParams.get("return_to"));
75348
+ const finishLoginState = (response) => loginState.present ? withClearedPlatformLoginState(req, response) : response;
75349
+ const failReturnTo = safeReturnTo2(loginState.returnTo);
74698
75350
  const handoff = url.searchParams.get("handoff") || url.searchParams.get("token");
74699
75351
  if (!handoff) {
74700
75352
  return failRedirect(req, env2, "invalid_response", failReturnTo);
@@ -74708,29 +75360,37 @@ var callback_get_default = defineController2(async ({ req, env: env2 }) => {
74708
75360
  projectId: configuredProjectId
74709
75361
  }
74710
75362
  );
74711
- if (!payload || !payload.platform_user_id || !payload.role) {
75363
+ if (!payload || !payload.platform_user_id || !payload.role || !payload.jti) {
74712
75364
  return failRedirect(req, env2, "invalid_handoff", failReturnTo);
74713
75365
  }
74714
75366
  if (!configuredProjectId && payload.project_id !== hostedInstanceRedirectId(req, env2)) {
74715
75367
  return failRedirect(req, env2, "invalid_handoff", failReturnTo);
74716
75368
  }
74717
75369
  if (payload.role === "api") {
74718
- const apiReturnTo = safeReturnTo2(url.searchParams.get("return_to"));
75370
+ const apiReturnTo = failReturnTo;
74719
75371
  if (/^\/api\/auth\/device\/[A-Za-z0-9]{8,64}\/approve$/.test(apiReturnTo)) {
74720
- return new Response(null, {
75372
+ return finishLoginState(new Response(null, {
74721
75373
  status: 302,
74722
75374
  headers: {
74723
75375
  Location: `${url.origin}${apiReturnTo}?handoff=${encodeURIComponent(handoff)}`
74724
75376
  }
74725
- });
75377
+ }));
74726
75378
  }
74727
75379
  const landingRaw = env2.STANDARD_AGENTS_API_ROLE_LANDING_URL;
74728
75380
  const landing = typeof landingRaw === "string" ? landingRaw.trim() : "";
74729
75381
  if (landing && /^https:\/\//.test(landing)) {
74730
- return new Response(null, { status: 302, headers: { Location: landing } });
75382
+ return finishLoginState(
75383
+ new Response(null, { status: 302, headers: { Location: landing } })
75384
+ );
74731
75385
  }
74732
75386
  return failRedirect(req, env2, "api_only_account");
74733
75387
  }
75388
+ if (!loginState.valid) {
75389
+ return beginPlatformLogin(req, env2, failReturnTo);
75390
+ }
75391
+ if (!await consumePlatformHandoffJti(env2, payload.jti, payload.exp)) {
75392
+ return failRedirect(req, env2, "invalid_handoff", failReturnTo);
75393
+ }
74734
75394
  const session = await mintLocalSessionForPlatformUser(env2, {
74735
75395
  platform_user_id: payload.platform_user_id,
74736
75396
  username: payload.username ?? null,
@@ -74740,14 +75400,15 @@ var callback_get_default = defineController2(async ({ req, env: env2 }) => {
74740
75400
  role: payload.role,
74741
75401
  permissions: payload.permissions ?? null
74742
75402
  });
74743
- const returnTo = safeReturnTo2(url.searchParams.get("return_to"));
74744
- return new Response(null, {
74745
- status: 302,
74746
- headers: {
74747
- Location: `${url.origin}${returnTo}`,
74748
- "Set-Cookie": buildSessionCookie(req, session.token, SESSION_TTL_SECONDS2)
74749
- }
75403
+ const returnTo = failReturnTo;
75404
+ const headers = new Headers({
75405
+ Location: `${url.origin}${returnTo}`
74750
75406
  });
75407
+ headers.append("Set-Cookie", buildSessionCookie(req, session.token, SESSION_TTL_SECONDS3));
75408
+ return withClearedPlatformLoginState(req, new Response(null, {
75409
+ status: 302,
75410
+ headers
75411
+ }));
74751
75412
  });
74752
75413
 
74753
75414
  // src/api/auth/sa/start.get.ts
@@ -74757,12 +75418,7 @@ var start_get_default = defineController2(async ({ req, env: env2 }) => {
74757
75418
  }
74758
75419
  const url = new URL(req.url);
74759
75420
  const returnTo = url.searchParams.get("return_to") || "/";
74760
- return new Response(null, {
74761
- status: 302,
74762
- headers: {
74763
- Location: platformLoginUrl(req, env2, returnTo)
74764
- }
74765
- });
75421
+ return beginPlatformLogin(req, env2, returnTo);
74766
75422
  });
74767
75423
 
74768
75424
  // src/api/users/me/[...rest].ts
@@ -74852,8 +75508,9 @@ var env_patch_default2 = defineController2(async ({ req, env: env2 }) => {
74852
75508
  });
74853
75509
 
74854
75510
  // src/api/users/me/hub.ts
74855
- var hub_default = defineController2(async ({ req, env: env2 }) => {
74856
- const authResult = await requireAuth(req, env2);
75511
+ var hub_default = defineController2(async (context) => {
75512
+ const { req, env: env2 } = context;
75513
+ const authResult = await resolveRequestAuth(context);
74857
75514
  if (authResult instanceof Response) {
74858
75515
  return authResult;
74859
75516
  }
@@ -74881,9 +75538,10 @@ var hub_default = defineController2(async ({ req, env: env2 }) => {
74881
75538
  const stub = namespace.get(hubId);
74882
75539
  return await stub.fetch(req);
74883
75540
  } catch (error) {
75541
+ const message = error instanceof Error ? error.message : "Failed to establish hub connection";
74884
75542
  console.error("Error establishing user hub socket:", error);
74885
75543
  return Response.json(
74886
- { error: error.message || "Failed to establish hub connection" },
75544
+ { error: message },
74887
75545
  { status: 500 }
74888
75546
  );
74889
75547
  }
@@ -74907,6 +75565,7 @@ var index_delete_default = defineController2(async ({ req, env: env2 }) => {
74907
75565
  if (!key) {
74908
75566
  return Response.json({ error: "key query parameter required" }, { status: 400 });
74909
75567
  }
75568
+ if (!isUserKvKeyClientWritable(key)) return reservedKvKeyResponse();
74910
75569
  const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
74911
75570
  const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
74912
75571
  if (typeof agentBuilder.setUserValue !== "function") {
@@ -74977,7 +75636,7 @@ var index_get_default4 = defineController2(async ({ req, env: env2 }) => {
74977
75636
  });
74978
75637
 
74979
75638
  // src/api/users/me/kv/index.post.ts
74980
- var index_post_default8 = defineController2(async ({ req, env: env2 }) => {
75639
+ var index_post_default7 = defineController2(async ({ req, env: env2 }) => {
74981
75640
  try {
74982
75641
  const authResult = await requireAuth(req, env2);
74983
75642
  if (authResult instanceof Response) {
@@ -75003,6 +75662,7 @@ var index_post_default8 = defineController2(async ({ req, env: env2 }) => {
75003
75662
  if (typeof key !== "string" || key.length === 0) {
75004
75663
  return Response.json({ error: "key required" }, { status: 400 });
75005
75664
  }
75665
+ if (!isUserKvKeyClientWritable(key)) return reservedKvKeyResponse();
75006
75666
  const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
75007
75667
  const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
75008
75668
  if (typeof agentBuilder.setUserValue !== "function") {
@@ -75023,8 +75683,9 @@ var index_post_default8 = defineController2(async ({ req, env: env2 }) => {
75023
75683
  });
75024
75684
 
75025
75685
  // src/api/users/me/stream.ts
75026
- var stream_default3 = defineController2(async ({ req, env: env2 }) => {
75027
- const authResult = await requireAuth(req, env2);
75686
+ var stream_default3 = defineController2(async (context) => {
75687
+ const { req, env: env2 } = context;
75688
+ const authResult = await resolveRequestAuth(context);
75028
75689
  if (authResult instanceof Response) return authResult;
75029
75690
  if (authResult.authType === "super_admin") {
75030
75691
  return Response.json(
@@ -75041,13 +75702,39 @@ var stream_default3 = defineController2(async ({ req, env: env2 }) => {
75041
75702
  return forwardToUserScope(req, env2, authResult.user.id, "stream");
75042
75703
  });
75043
75704
 
75044
- // src/api/auth/device/[code]/approve.get.ts
75705
+ // src/api/auth/device/[code]/_approval.ts
75706
+ var CONFIRMATION_TTL_SECONDS = 5 * 60;
75707
+ function escapeHtml(value) {
75708
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
75709
+ }
75045
75710
  function page(title, body, ok) {
75046
75711
  return new Response(
75047
- `<!doctype html><html><head><meta charset="utf-8"><title>${title}</title><style>body{background:#0a0c10;color:#e8ecf3;font:16px/1.6 ui-sans-serif,system-ui;display:flex;min-height:100vh;align-items:center;justify-content:center}div{max-width:420px;text-align:center;padding:40px;border:1px solid #232936;border-radius:14px;background:#11141b}h1{font-size:22px;color:${ok ? "#2dd4bf" : "#f87171"}}p{color:#8b93a7;margin-top:10px}</style></head><body><div><h1>${title}</h1><p>${body}</p></div></body></html>`,
75048
- { headers: { "Content-Type": "text/html; charset=utf-8" } }
75712
+ `<!doctype html><html><head><meta charset="utf-8"><meta name="referrer" content="no-referrer"><title>${escapeHtml(title)}</title><style>body{background:#0a0c10;color:#e8ecf3;font:16px/1.6 ui-sans-serif,system-ui;display:flex;min-height:100vh;align-items:center;justify-content:center}main{max-width:420px;text-align:center;padding:40px;border:1px solid #232936;border-radius:14px;background:#11141b}h1{font-size:22px;color:${ok ? "#2dd4bf" : "#f87171"}}p{color:#8b93a7;margin-top:10px}</style></head><body><main><h1>${escapeHtml(title)}</h1><p>${escapeHtml(body)}</p></main></body></html>`,
75713
+ {
75714
+ headers: {
75715
+ "Content-Type": "text/html; charset=utf-8",
75716
+ "Cache-Control": "no-store",
75717
+ "Referrer-Policy": "no-referrer",
75718
+ "X-Content-Type-Options": "nosniff"
75719
+ }
75720
+ }
75049
75721
  );
75050
75722
  }
75723
+ function confirmationPage(req, code, confirmationToken, handoff) {
75724
+ const url = new URL(req.url);
75725
+ const handoffInput = handoff ? `<input type="hidden" name="handoff" value="${escapeHtml(handoff)}">` : "";
75726
+ const shortCode = code.length > 12 ? `${code.slice(0, 6)}\u2026${code.slice(-6)}` : code;
75727
+ const html = `<!doctype html><html><head><meta charset="utf-8"><meta name="referrer" content="no-referrer"><title>Approve device sign-in</title><style>body{background:#0a0c10;color:#e8ecf3;font:16px/1.6 ui-sans-serif,system-ui;display:flex;min-height:100vh;align-items:center;justify-content:center}main{max-width:440px;text-align:center;padding:40px;border:1px solid #232936;border-radius:14px;background:#11141b}h1{font-size:22px}p{color:#a5adbd}.code{font:600 18px ui-monospace,monospace;letter-spacing:.08em;color:#e8ecf3}form{display:flex;gap:12px;justify-content:center;margin-top:26px}button,a{border-radius:9px;padding:10px 18px;font:inherit;text-decoration:none}button{border:0;background:#2dd4bf;color:#07110f;font-weight:700;cursor:pointer}a{border:1px solid #394150;color:#cbd5e1}</style></head><body><main><h1>Approve device sign-in?</h1><p>A command-line client is requesting a personal instance API key for your account.</p><p class="code">${escapeHtml(shortCode)}</p><form method="post" action="${escapeHtml(url.pathname)}"><input type="hidden" name="confirmation_token" value="${escapeHtml(confirmationToken)}">${handoffInput}<button type="submit">Approve device</button><a href="/">Cancel</a></form></main></body></html>`;
75728
+ return new Response(html, {
75729
+ headers: {
75730
+ "Content-Type": "text/html; charset=utf-8",
75731
+ "Cache-Control": "no-store",
75732
+ "Referrer-Policy": "no-referrer",
75733
+ "X-Content-Type-Options": "nosniff",
75734
+ "Content-Security-Policy": "default-src 'none'; style-src 'unsafe-inline'; form-action 'self'; base-uri 'none'; frame-ancestors 'none'"
75735
+ }
75736
+ });
75737
+ }
75051
75738
  function outcome(env2, ok, reason, fallback) {
75052
75739
  const baseRaw = env2.STANDARD_AGENTS_DEVICE_OUTCOME_URL_BASE;
75053
75740
  const base = typeof baseRaw === "string" ? baseRaw.trim().replace(/\/+$/, "") : "";
@@ -75057,12 +75744,9 @@ function outcome(env2, ok, reason, fallback) {
75057
75744
  }
75058
75745
  return page(fallback.title, fallback.body, ok);
75059
75746
  }
75060
- var approve_get_default = defineController2(async ({ req, env: env2, params }) => {
75061
- const code = params.code;
75062
- if (!code) return outcome(env2, false, "invalid_link", { title: "Missing code", body: "This device link is malformed." });
75747
+ async function resolveDeviceApprovalIdentity(req, env2) {
75063
75748
  const url = new URL(req.url);
75064
- let approvingUserId = null;
75065
- const handoff = url.searchParams.get("handoff");
75749
+ const handoff = url.searchParams.get("handoff") ?? (req.method === "POST" ? (await req.clone().formData().catch(() => null))?.get("handoff")?.toString() ?? null : null);
75066
75750
  if (handoff && isPlatformHosted(env2)) {
75067
75751
  const configuredProjectId = typeof env2.STANDARD_AGENTS_PROJECT_ID === "string" ? env2.STANDARD_AGENTS_PROJECT_ID : void 0;
75068
75752
  const payload = await verifyPlatformSignedPayload(
@@ -75071,10 +75755,13 @@ var approve_get_default = defineController2(async ({ req, env: env2, params }) =
75071
75755
  { kind: "instance_handoff", projectId: configuredProjectId }
75072
75756
  );
75073
75757
  const projectOk = payload && (configuredProjectId || payload.project_id === hostedInstanceRedirectId(req, env2));
75074
- if (!payload || !payload.platform_user_id || !payload.role || !projectOk) {
75075
- return outcome(env2, false, "signin_failed", { title: "Sign in failed", body: "This sign-in link is invalid or has expired. Start again from your terminal." });
75758
+ if (!payload || !payload.platform_user_id || !payload.role || !payload.jti || !projectOk) {
75759
+ return outcome(env2, false, "signin_failed", {
75760
+ title: "Sign in failed",
75761
+ body: "This sign-in link is invalid or has expired. Start again from your terminal."
75762
+ });
75076
75763
  }
75077
- const user = await upsertReplicaUserForPlatformProfile(env2, {
75764
+ const platformProfile = {
75078
75765
  platform_user_id: payload.platform_user_id,
75079
75766
  username: payload.username ?? null,
75080
75767
  display_name: payload.display_name ?? null,
@@ -75082,27 +75769,163 @@ var approve_get_default = defineController2(async ({ req, env: env2, params }) =
75082
75769
  avatar_url: payload.avatar_url ?? null,
75083
75770
  role: payload.role,
75084
75771
  permissions: payload.permissions ?? null
75085
- });
75086
- approvingUserId = user.id;
75087
- } else {
75088
- const authResult = await requireAuth(req, env2);
75089
- if (authResult instanceof Response) {
75090
- const returnTo = encodeURIComponent(url.pathname);
75091
- const hosted = Boolean(env2.STANDARD_AGENTS_HOSTED && String(env2.STANDARD_AGENTS_HOSTED) !== "0" && String(env2.STANDARD_AGENTS_HOSTED).toLowerCase() !== "false");
75092
- const target = hosted ? `/api/auth/sa/start?return_to=${returnTo}` : `/login?return_to=${returnTo}`;
75093
- return new Response(null, { status: 302, headers: { Location: `${url.origin}${target}` } });
75094
- }
75095
- if (authResult.authType === "api_key") {
75096
- return outcome(env2, false, "session_required", { title: "Sign in required", body: "Device logins must be approved from a signed-in browser session." });
75772
+ };
75773
+ return {
75774
+ confirmationSubject: `platform:${payload.platform_user_id}`,
75775
+ handoff,
75776
+ platformProfile,
75777
+ platformHandoff: {
75778
+ jti: payload.jti,
75779
+ expires_at: payload.exp
75780
+ }
75781
+ };
75782
+ }
75783
+ const authResult = await requireAuth(req, env2);
75784
+ if (authResult instanceof Response) {
75785
+ if (req.method !== "GET") {
75786
+ return outcome(env2, false, "session_required", {
75787
+ title: "Sign in required",
75788
+ body: "Return to the device link and sign in again before approving."
75789
+ });
75097
75790
  }
75098
- approvingUserId = authResult.user.id;
75791
+ const returnTo = encodeURIComponent(url.pathname);
75792
+ const hosted = Boolean(
75793
+ env2.STANDARD_AGENTS_HOSTED && String(env2.STANDARD_AGENTS_HOSTED) !== "0" && String(env2.STANDARD_AGENTS_HOSTED).toLowerCase() !== "false"
75794
+ );
75795
+ const target = hosted ? `/api/auth/sa/start?return_to=${returnTo}` : `/login?return_to=${returnTo}`;
75796
+ return new Response(null, { status: 302, headers: { Location: `${url.origin}${target}` } });
75797
+ }
75798
+ if (authResult.authType === "api_key") {
75799
+ return outcome(env2, false, "session_required", {
75800
+ title: "Sign in required",
75801
+ body: "Device logins must be approved from a signed-in browser session."
75802
+ });
75803
+ }
75804
+ return {
75805
+ confirmationSubject: `user:${authResult.user.id}`,
75806
+ handoff: null,
75807
+ localUserId: authResult.user.id
75808
+ };
75809
+ }
75810
+ async function consumeDeviceApprovalHandoff(env2, identity) {
75811
+ if (!identity.platformHandoff) return true;
75812
+ return consumePlatformHandoffJti(
75813
+ env2,
75814
+ identity.platformHandoff.jti,
75815
+ identity.platformHandoff.expires_at
75816
+ );
75817
+ }
75818
+ async function materializeDeviceApprovalUser(env2, identity) {
75819
+ if (identity.localUserId) return identity.localUserId;
75820
+ if (!identity.platformProfile) {
75821
+ throw new Error("Device approval identity is missing a local user and platform profile");
75822
+ }
75823
+ const user = await upsertReplicaUserForPlatformProfile(
75824
+ env2,
75825
+ identity.platformProfile
75826
+ );
75827
+ return user.id;
75828
+ }
75829
+ function encryptionKey(env2) {
75830
+ const value = env2.ENCRYPTION_KEY;
75831
+ return typeof value === "string" && value.length > 0 ? value : null;
75832
+ }
75833
+ async function createDeviceConfirmationToken(env2, code, confirmationSubject) {
75834
+ const key = encryptionKey(env2);
75835
+ if (!key) return null;
75836
+ return signToken(JSON.stringify({
75837
+ kind: "device_approval_confirmation",
75838
+ code_hash: await hashToken(code),
75839
+ subject: confirmationSubject,
75840
+ exp: Math.floor(Date.now() / 1e3) + CONFIRMATION_TTL_SECONDS,
75841
+ nonce: crypto.randomUUID()
75842
+ }), key);
75843
+ }
75844
+ async function verifyDeviceConfirmationToken(env2, code, confirmationSubject, token) {
75845
+ const key = encryptionKey(env2);
75846
+ if (!key) return false;
75847
+ const raw = await verifySignedToken(token, key);
75848
+ if (!raw) return false;
75849
+ try {
75850
+ const payload = JSON.parse(raw);
75851
+ return payload.kind === "device_approval_confirmation" && payload.code_hash === await hashToken(code) && payload.subject === confirmationSubject && typeof payload.exp === "number" && payload.exp >= Math.floor(Date.now() / 1e3);
75852
+ } catch {
75853
+ return false;
75854
+ }
75855
+ }
75856
+ function isSameOriginApprovalPost(req) {
75857
+ const url = new URL(req.url);
75858
+ const origin = req.headers.get("Origin");
75859
+ if (origin && origin !== url.origin) return false;
75860
+ const fetchSite = req.headers.get("Sec-Fetch-Site");
75861
+ return !fetchSite || fetchSite === "same-origin" || fetchSite === "none";
75862
+ }
75863
+
75864
+ // src/api/auth/device/[code]/approve.get.ts
75865
+ var approve_get_default = defineController2(async ({ req, env: env2, params }) => {
75866
+ const code = params.code;
75867
+ if (!code) {
75868
+ return outcome(env2, false, "invalid_link", {
75869
+ title: "Missing code",
75870
+ body: "This device link is malformed."
75871
+ });
75872
+ }
75873
+ const identity = await resolveDeviceApprovalIdentity(req, env2);
75874
+ if (identity instanceof Response) return identity;
75875
+ const confirmationToken = await createDeviceConfirmationToken(
75876
+ env2,
75877
+ code,
75878
+ identity.confirmationSubject
75879
+ );
75880
+ if (!confirmationToken) {
75881
+ return outcome(env2, false, "signin_failed", {
75882
+ title: "Sign in failed",
75883
+ body: "This instance cannot create a secure device confirmation. Contact its administrator."
75884
+ });
75099
75885
  }
75886
+ return confirmationPage(req, code, confirmationToken, identity.handoff);
75887
+ });
75888
+
75889
+ // src/api/auth/device/[code]/approve.post.ts
75890
+ var approve_post_default = defineController2(async ({ req, env: env2, params }) => {
75891
+ const code = params.code;
75892
+ if (!code) {
75893
+ return outcome(env2, false, "invalid_link", {
75894
+ title: "Missing code",
75895
+ body: "This device link is malformed."
75896
+ });
75897
+ }
75898
+ if (!isSameOriginApprovalPost(req)) {
75899
+ return Response.json({ error: "Cross-origin device approval refused" }, { status: 403 });
75900
+ }
75901
+ const form = await req.clone().formData().catch(() => null);
75902
+ const confirmationToken = form?.get("confirmation_token")?.toString() ?? "";
75903
+ if (!confirmationToken) {
75904
+ return Response.json({ error: "Device confirmation token required" }, { status: 403 });
75905
+ }
75906
+ const identity = await resolveDeviceApprovalIdentity(req, env2);
75907
+ if (identity instanceof Response) return identity;
75908
+ if (!await verifyDeviceConfirmationToken(
75909
+ env2,
75910
+ code,
75911
+ identity.confirmationSubject,
75912
+ confirmationToken
75913
+ )) {
75914
+ return Response.json({ error: "Invalid or expired device confirmation" }, { status: 403 });
75915
+ }
75916
+ if (!await consumeDeviceApprovalHandoff(env2, identity)) {
75917
+ return outcome(env2, false, "signin_failed", {
75918
+ title: "Sign in failed",
75919
+ body: "This sign-in link was already used or has expired. Start again from your terminal."
75920
+ });
75921
+ }
75922
+ const userId = await materializeDeviceApprovalUser(env2, identity);
75100
75923
  const token = generateApiKey();
75101
75924
  const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
75102
75925
  const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
75103
75926
  const ok = await agentBuilder.approveDeviceLogin({
75104
75927
  code_hash: await hashToken(code),
75105
- user_id: approvingUserId,
75928
+ user_id: userId,
75106
75929
  token,
75107
75930
  token_hash: await hashToken(token),
75108
75931
  key_prefix: token.slice(0, 12),
@@ -75110,12 +75933,19 @@ var approve_get_default = defineController2(async ({ req, env: env2, params }) =
75110
75933
  key_name: "CLI device login"
75111
75934
  });
75112
75935
  if (!ok) {
75113
- return outcome(env2, false, "expired", { title: "Link expired", body: "This device login has expired or was already used. Start again from your terminal." });
75936
+ return outcome(env2, false, "expired", {
75937
+ title: "Link expired",
75938
+ body: "This device login has expired or was already used. Start again from your terminal."
75939
+ });
75114
75940
  }
75115
- return outcome(env2, true, "done", { title: "You're connected", body: "Standard Code is now signed in. Return to your terminal \u2014 it will pick up automatically." });
75941
+ return outcome(env2, true, "done", {
75942
+ title: "You're connected",
75943
+ body: "Standard Code is now signed in. Return to your terminal \u2014 it will pick up automatically."
75944
+ });
75116
75945
  });
75117
75946
 
75118
75947
  // src/api/skills/[name]/files/[...path].get.ts
75948
+ init_scope();
75119
75949
  var path_get_default = defineController2(async ({ req, env: env2, params }) => {
75120
75950
  const authResult = await requireAdmin(req, env2);
75121
75951
  if (authResult instanceof Response) return authResult;
@@ -75128,7 +75958,11 @@ var path_get_default = defineController2(async ({ req, env: env2, params }) => {
75128
75958
  const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
75129
75959
  const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
75130
75960
  try {
75131
- const content = await agentBuilder.getSkillFile(name15, filePath);
75961
+ const content = await agentBuilder.getSkillFile(
75962
+ { ownerId: SHARED_SKILL_OWNER },
75963
+ name15,
75964
+ filePath
75965
+ );
75132
75966
  if (content === null) {
75133
75967
  return Response.json({ error: `File not found: ${filePath} in skill ${name15}` }, { status: 404 });
75134
75968
  }
@@ -75143,6 +75977,24 @@ var path_get_default = defineController2(async ({ req, env: env2, params }) => {
75143
75977
 
75144
75978
  // src/api/threads/[id]/fs/[...path].ts
75145
75979
  var CHUNK_SIZE2 = 1.75 * 1024 * 1024;
75980
+ var EXECUTABLE_DOCUMENT_MIME_TYPES = /* @__PURE__ */ new Set([
75981
+ "text/html",
75982
+ "image/svg+xml",
75983
+ "application/xhtml+xml"
75984
+ ]);
75985
+ function safeDownloadName(name15) {
75986
+ return name15.replace(/["\\\r\n]/g, "_");
75987
+ }
75988
+ function fileResponseHeaders(file) {
75989
+ const mimeType = file.mimeType.split(";", 1)[0]?.trim().toLowerCase();
75990
+ const disposition = mimeType && EXECUTABLE_DOCUMENT_MIME_TYPES.has(mimeType) ? "attachment" : "inline";
75991
+ return {
75992
+ "Content-Type": file.mimeType,
75993
+ "Content-Length": String(file.size),
75994
+ "Content-Disposition": `${disposition}; filename="${safeDownloadName(file.name)}"`,
75995
+ "X-Content-Type-Options": "nosniff"
75996
+ };
75997
+ }
75146
75998
  function toAttachmentRef(file) {
75147
75999
  const metadata = file.metadata;
75148
76000
  const width = file.width ?? metadata?.width;
@@ -75233,8 +76085,9 @@ async function handleGet2(stub, path10, url) {
75233
76085
  headers: {
75234
76086
  "Content-Type": "image/jpeg",
75235
76087
  // Thumbnails are typically JPEG
75236
- "Cache-Control": "public, max-age=31536000"
76088
+ "Cache-Control": "public, max-age=31536000",
75237
76089
  // Cache for 1 year
76090
+ "X-Content-Type-Options": "nosniff"
75238
76091
  }
75239
76092
  });
75240
76093
  }
@@ -75274,11 +76127,7 @@ async function handleGet2(stub, path10, url) {
75274
76127
  bytes[i] = binary.charCodeAt(i);
75275
76128
  }
75276
76129
  return new Response(bytes, {
75277
- headers: {
75278
- "Content-Type": file.mimeType,
75279
- "Content-Length": String(file.size),
75280
- "Content-Disposition": `inline; filename="${file.name}"`
75281
- }
76130
+ headers: fileResponseHeaders(file)
75282
76131
  });
75283
76132
  }
75284
76133
  function streamChunkedFile(stub, path10, file) {
@@ -75306,12 +76155,8 @@ function streamChunkedFile(stub, path10, file) {
75306
76155
  }
75307
76156
  })();
75308
76157
  return new Response(readable, {
75309
- headers: {
75310
- "Content-Type": file.mimeType,
75311
- "Content-Length": String(file.size),
75312
- "Content-Disposition": `inline; filename="${file.name}"`
75313
- // Note: We set Content-Length, but also use chunked transfer internally
75314
- }
76158
+ // Note: We set Content-Length, but also use chunked transfer internally.
76159
+ headers: fileResponseHeaders(file)
75315
76160
  });
75316
76161
  }
75317
76162
  async function handlePost2(stub, path10, req) {
@@ -75508,19 +76353,9 @@ async function handleChunkedUploadComplete(stub, filePath, req) {
75508
76353
 
75509
76354
  // src/api/threads/[id]/logs/[logId].get.ts
75510
76355
  var logId_get_default = defineController2(async ({ req, params, env: env2 }) => {
75511
- if (isPlatformHosted(env2)) {
75512
- const authResult = await requirePermission(req, env2, "logs");
75513
- if (authResult instanceof Response) {
75514
- return authResult;
75515
- }
75516
- } else {
75517
- const authContext = await authenticate(req, env2);
75518
- if (authContext && !hasPermission(authContext.user, "logs")) {
75519
- return Response.json(
75520
- { error: "Forbidden: 'logs' access required" },
75521
- { status: 403 }
75522
- );
75523
- }
76356
+ const authResult = await requirePermission(req, env2, "logs");
76357
+ if (authResult instanceof Response) {
76358
+ return authResult;
75524
76359
  }
75525
76360
  const threadId = params.id;
75526
76361
  const logId = params.logId;
@@ -75932,19 +76767,9 @@ function createInspectProviderConfig(providerName, factory, env2) {
75932
76767
  };
75933
76768
  }
75934
76769
  var provider_get_default = defineController2(async ({ req, params, env: env2 }) => {
75935
- if (isPlatformHosted(env2)) {
75936
- const authResult = await requirePermission(req, env2, "logs");
75937
- if (authResult instanceof Response) {
75938
- return authResult;
75939
- }
75940
- } else {
75941
- const authContext = await authenticate(req, env2);
75942
- if (authContext && !hasPermission(authContext.user, "logs")) {
75943
- return Response.json(
75944
- { error: "Forbidden: 'logs' access required" },
75945
- { status: 403 }
75946
- );
75947
- }
76770
+ const authResult = await requirePermission(req, env2, "logs");
76771
+ if (authResult instanceof Response) {
76772
+ return authResult;
75948
76773
  }
75949
76774
  const threadId = params.id;
75950
76775
  const logId = params.logId;
@@ -76164,30 +76989,28 @@ var provider_get_default = defineController2(async ({ req, params, env: env2 })
76164
76989
 
76165
76990
  // src/built-in-routes.ts
76166
76991
  var routeHandlers = {
76167
- "POST:/": index_post_default,
76168
76992
  "GET:/agents": agents_get_default,
76169
76993
  "GET:/api-keys": index_get_default,
76170
- "POST:/api-keys": index_post_default2,
76994
+ "POST:/api-keys": index_post_default,
76171
76995
  "GET:/config": config_get_default,
76172
76996
  "GET:/diagnostics": diagnostics_get_default,
76173
76997
  "GET:/events": events_get_default,
76174
76998
  "GET:/hooks": hooks_get_default,
76175
76999
  "GET:/models": models_get_default,
76176
- "GET:/new-test": new_test_get_default,
76177
77000
  "GET:/platform-endpoint": platform_endpoint_default,
76178
77001
  "GET:/prompts": prompts_get_default,
76179
77002
  "GET:/providers": providers_get_default,
76180
77003
  "GET:/skills": index_get_default2,
76181
- "POST:/skills": index_post_default3,
76182
- "POST:/threads": index_post_default4,
77004
+ "POST:/skills": index_post_default2,
77005
+ "POST:/threads": index_post_default3,
76183
77006
  "GET:/threads": threads_default,
76184
77007
  "GET:/tools": tools_get_default,
76185
77008
  "GET:/users": index_get_default3,
76186
- "POST:/users": index_post_default5,
77009
+ "POST:/users": index_post_default4,
76187
77010
  "GET:/agents/:name": name_get_default,
76188
77011
  "DELETE:/api-keys/:id": id_delete_default,
76189
77012
  "GET:/models/:name": name_get_default2,
76190
- "POST:/pack/:agentName": index_post_default6,
77013
+ "POST:/pack/:agentName": index_post_default5,
76191
77014
  "GET:/prompts/:name": name_get_default3,
76192
77015
  "DELETE:/skills/:name": name_delete_default,
76193
77016
  "GET:/skills/:name": name_get_default4,
@@ -76196,18 +77019,19 @@ var routeHandlers = {
76196
77019
  "PATCH:/threads/:id": id_patch_default,
76197
77020
  "GET:/threads/:id": id_default,
76198
77021
  "GET:/tools/:name": name_get_default5,
76199
- "POST:/unpack/:packageId": index_post_default7,
77022
+ "POST:/unpack/:packageId": index_post_default6,
76200
77023
  "DELETE:/users/:id": id_delete_default3,
76201
77024
  "PUT:/users/:id": id_put_default,
76202
77025
  "POST:/auth/bootstrap": bootstrap_post_default,
76203
77026
  "POST:/auth/brolly-control": brolly_control_post_default,
76204
77027
  "GET:/auth/config": config_get_default2,
77028
+ "DELETE:/auth/current-key": current_key_delete_default,
76205
77029
  "POST:/auth/login": login_post_default,
76206
- "GET:/auth/logout": logout_get_default,
76207
77030
  "POST:/auth/logout": logout_post_default,
76208
77031
  "GET:/auth/me": me_get_default,
76209
77032
  "POST:/auth/platform-cleanup": platform_cleanup_post_default,
76210
77033
  "POST:/auth/platform-replica": platform_replica_post_default,
77034
+ "POST:/auth/websocket-ticket": websocket_ticket_post_default,
76211
77035
  "GET:/envs/instance": instance_get_default,
76212
77036
  "PATCH:/envs/instance": instance_patch_default,
76213
77037
  "POST:/models/available": available_post_default,
@@ -76283,9 +77107,10 @@ var routeHandlers = {
76283
77107
  "GET:/users/me/hub": hub_default,
76284
77108
  "DELETE:/users/me/kv": index_delete_default,
76285
77109
  "GET:/users/me/kv": index_get_default4,
76286
- "POST:/users/me/kv": index_post_default8,
77110
+ "POST:/users/me/kv": index_post_default7,
76287
77111
  "GET:/users/me/stream": stream_default3,
76288
77112
  "GET:/auth/device/:code/approve": approve_get_default,
77113
+ "POST:/auth/device/:code/approve": approve_post_default,
76289
77114
  "GET:/skills/:name/files/**": path_get_default,
76290
77115
  "DELETE:/threads/:id/fs/**": path_default3,
76291
77116
  "POST:/threads/:id/fs/**": path_default3,