@standardagents/builder 0.38.0 → 0.38.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/built-in-routes.js +1165 -366
  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 +1060 -303
  43. package/dist/index.js.map +1 -1
  44. package/dist/plugin.js +102 -68
  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.1".length > 0 ? "0.38.1" : "dev";
68035
68327
 
68036
68328
  // src/api/diagnostics.get.ts
68037
68329
  init_LLMRequest();
@@ -68429,11 +68721,6 @@ var models_get_default = defineController2(async ({ models, modelNames }) => {
68429
68721
  const validModels = modelList.filter(Boolean);
68430
68722
  return Response.json({ models: validModels });
68431
68723
  });
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
68724
  function toJSONSchema(schema) {
68438
68725
  if (!schema) return null;
68439
68726
  if (schema && typeof schema === "object" && ("~standard" in schema || "_def" in schema)) {
@@ -68540,6 +68827,7 @@ var providers_get_default = defineController2(async ({ env: env2 }) => {
68540
68827
  });
68541
68828
 
68542
68829
  // src/api/skills/index.get.ts
68830
+ init_scope();
68543
68831
  var index_get_default2 = defineController2(async ({ req, env: env2 }) => {
68544
68832
  const authResult = await requireAdmin(req, env2);
68545
68833
  if (authResult instanceof Response) return authResult;
@@ -68547,7 +68835,10 @@ var index_get_default2 = defineController2(async ({ req, env: env2 }) => {
68547
68835
  const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
68548
68836
  const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
68549
68837
  try {
68550
- const skills = await agentBuilder.listSkills({ includeDisabled });
68838
+ const skills = await agentBuilder.listSkills(
68839
+ { ownerId: SHARED_SKILL_OWNER },
68840
+ { includeDisabled }
68841
+ );
68551
68842
  return Response.json({ skills });
68552
68843
  } catch (error) {
68553
68844
  return Response.json(
@@ -68562,7 +68853,8 @@ var SkillValidationError = class extends Error {
68562
68853
  };
68563
68854
 
68564
68855
  // src/api/skills/index.post.ts
68565
- var index_post_default3 = defineController2(async ({ req, env: env2 }) => {
68856
+ init_scope();
68857
+ var index_post_default2 = defineController2(async ({ req, env: env2 }) => {
68566
68858
  const authResult = await requireAdmin(req, env2);
68567
68859
  if (authResult instanceof Response) return authResult;
68568
68860
  let body;
@@ -68584,7 +68876,7 @@ var index_post_default3 = defineController2(async ({ req, env: env2 }) => {
68584
68876
  const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
68585
68877
  const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
68586
68878
  try {
68587
- const skill = await agentBuilder.upsertSkill(files);
68879
+ const skill = await agentBuilder.upsertSkill({ ownerId: SHARED_SKILL_OWNER }, files);
68588
68880
  return Response.json({ skill }, { status: 201 });
68589
68881
  } catch (error) {
68590
68882
  const message = error instanceof Error ? error.message : "Failed to install skill";
@@ -68646,7 +68938,7 @@ async function hasEnabledImmediatePromptTools(agentBuilder, threadId, agentId) {
68646
68938
  }
68647
68939
  return false;
68648
68940
  }
68649
- var index_post_default4 = defineController2(async ({ req, env: env2 }) => {
68941
+ var index_post_default3 = defineController2(async ({ req, env: env2 }) => {
68650
68942
  const authResult = await requireAuth(req, env2);
68651
68943
  if (authResult instanceof Response) {
68652
68944
  return authResult;
@@ -69243,15 +69535,138 @@ var migration17 = {
69243
69535
  }
69244
69536
  };
69245
69537
 
69538
+ // src/durable-objects/agentbuilder-migrations/0018_auth_security.ts
69539
+ var migration18 = {
69540
+ version: 18,
69541
+ async up(sql) {
69542
+ sql.exec(`
69543
+ CREATE TABLE IF NOT EXISTS super_admin_sessions (
69544
+ token_hash TEXT PRIMARY KEY,
69545
+ created_at INTEGER NOT NULL,
69546
+ expires_at INTEGER NOT NULL
69547
+ )
69548
+ `);
69549
+ sql.exec(`
69550
+ CREATE INDEX IF NOT EXISTS idx_super_admin_sessions_expires
69551
+ ON super_admin_sessions(expires_at)
69552
+ `);
69553
+ sql.exec(`
69554
+ CREATE TABLE IF NOT EXISTS auth_rate_limits (
69555
+ bucket_key TEXT PRIMARY KEY,
69556
+ attempts INTEGER NOT NULL,
69557
+ reset_at INTEGER NOT NULL
69558
+ )
69559
+ `);
69560
+ sql.exec(`
69561
+ CREATE INDEX IF NOT EXISTS idx_auth_rate_limits_reset
69562
+ ON auth_rate_limits(reset_at)
69563
+ `);
69564
+ sql.exec(`
69565
+ INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '18')
69566
+ `);
69567
+ }
69568
+ };
69569
+
69570
+ // src/durable-objects/agentbuilder-migrations/0019_scope_skills.ts
69571
+ var migration19 = {
69572
+ version: 19,
69573
+ async up(sql) {
69574
+ sql.exec(`
69575
+ CREATE TABLE skills_0019 (
69576
+ owner_id TEXT NOT NULL,
69577
+ name TEXT NOT NULL,
69578
+ description TEXT NOT NULL,
69579
+ version TEXT,
69580
+ enabled INTEGER NOT NULL DEFAULT 1,
69581
+ shared INTEGER NOT NULL DEFAULT 0,
69582
+ created_at INTEGER DEFAULT (strftime('%s', 'now')),
69583
+ updated_at INTEGER DEFAULT (strftime('%s', 'now')),
69584
+ PRIMARY KEY (owner_id, name)
69585
+ )
69586
+ `);
69587
+ sql.exec(`
69588
+ INSERT INTO skills_0019 (
69589
+ owner_id, name, description, version, enabled, shared, created_at, updated_at
69590
+ )
69591
+ SELECT '__instance_shared__', name, description, version, enabled, 1, created_at, updated_at
69592
+ FROM skills
69593
+ `);
69594
+ sql.exec(`
69595
+ CREATE TABLE skill_files_0019 (
69596
+ owner_id TEXT NOT NULL,
69597
+ skill_name TEXT NOT NULL,
69598
+ path TEXT NOT NULL,
69599
+ content TEXT NOT NULL,
69600
+ size INTEGER NOT NULL,
69601
+ PRIMARY KEY (owner_id, skill_name, path),
69602
+ FOREIGN KEY (owner_id, skill_name) REFERENCES skills_0019(owner_id, name) ON DELETE CASCADE
69603
+ )
69604
+ `);
69605
+ sql.exec(`
69606
+ INSERT INTO skill_files_0019 (owner_id, skill_name, path, content, size)
69607
+ SELECT '__instance_shared__', skill_name, path, content, size FROM skill_files
69608
+ `);
69609
+ sql.exec(`DROP TABLE skill_files`);
69610
+ sql.exec(`DROP TABLE skills`);
69611
+ sql.exec(`ALTER TABLE skills_0019 RENAME TO skills`);
69612
+ sql.exec(`ALTER TABLE skill_files_0019 RENAME TO skill_files`);
69613
+ sql.exec(`CREATE INDEX idx_skills_owner_enabled ON skills(owner_id, enabled)`);
69614
+ sql.exec(`CREATE INDEX idx_skills_shared_enabled ON skills(shared, enabled)`);
69615
+ sql.exec(`CREATE INDEX idx_skill_files_skill ON skill_files(owner_id, skill_name)`);
69616
+ sql.exec(`INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '19')`);
69617
+ }
69618
+ };
69619
+
69620
+ // src/durable-objects/agentbuilder-migrations/0020_websocket_tickets.ts
69621
+ var migration20 = {
69622
+ version: 20,
69623
+ async up(sql) {
69624
+ sql.exec(`
69625
+ CREATE TABLE websocket_tickets (
69626
+ ticket_hash TEXT PRIMARY KEY,
69627
+ pathname TEXT NOT NULL,
69628
+ auth_json TEXT NOT NULL,
69629
+ created_at INTEGER NOT NULL,
69630
+ expires_at INTEGER NOT NULL
69631
+ )
69632
+ `);
69633
+ sql.exec(`
69634
+ CREATE INDEX idx_websocket_tickets_expires
69635
+ ON websocket_tickets(expires_at)
69636
+ `);
69637
+ sql.exec(`INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '20')`);
69638
+ }
69639
+ };
69640
+
69641
+ // src/durable-objects/agentbuilder-migrations/0021_platform_handoff_jtis.ts
69642
+ var migration21 = {
69643
+ version: 21,
69644
+ async up(sql) {
69645
+ sql.exec(`
69646
+ CREATE TABLE platform_handoff_jtis (
69647
+ jti TEXT PRIMARY KEY,
69648
+ consumed_at INTEGER NOT NULL,
69649
+ expires_at INTEGER NOT NULL
69650
+ )
69651
+ `);
69652
+ sql.exec(`
69653
+ CREATE INDEX idx_platform_handoff_jtis_expires
69654
+ ON platform_handoff_jtis(expires_at)
69655
+ `);
69656
+ sql.exec(`INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '21')`);
69657
+ }
69658
+ };
69659
+
69246
69660
  // 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];
69661
+ var migrations = [migration, migration2, migration3, migration4, migration5, migration6, migration7, migration8, migration9, migration10, migration11, migration12, migration13, migration14, migration15, migration16, migration17, migration18, migration19, migration20, migration21];
69248
69662
  migrations.reduce(
69249
- (max, migration18) => Math.max(max, migration18.version),
69663
+ (max, migration22) => Math.max(max, migration22.version),
69250
69664
  0
69251
69665
  );
69252
69666
 
69253
69667
  // src/durable-objects/DurableAgentBuilder.ts
69254
69668
  init_types();
69669
+ init_scope();
69255
69670
  var THREAD_PREVIEW_MAX_CHARS = 140;
69256
69671
  var buildThreadPreview = (content) => {
69257
69672
  const collapsed = content.replace(/\s+/g, " ").trim();
@@ -69607,7 +70022,7 @@ var index_get_default3 = defineController2(async ({ req, env: env2 }) => {
69607
70022
 
69608
70023
  // src/api/users/index.post.ts
69609
70024
  var VALID_ROLES = ["admin", "user"];
69610
- var index_post_default5 = defineController2(async ({ req, env: env2 }) => {
70025
+ var index_post_default4 = defineController2(async ({ req, env: env2 }) => {
69611
70026
  try {
69612
70027
  if (isPlatformHosted(env2)) {
69613
70028
  return Response.json({ error: "Users are managed in the Standard Agents platform." }, { status: 410 });
@@ -69871,7 +70286,7 @@ var name_get_default2 = defineController2(async ({ params, models }) => {
69871
70286
  });
69872
70287
 
69873
70288
  // src/api/pack/[agentName]/index.post.ts
69874
- var index_post_default6 = defineController2(async ({ req, env: env2 }) => {
70289
+ var index_post_default5 = defineController2(async ({ req, env: env2 }) => {
69875
70290
  const auth = await requireAdmin(req, env2);
69876
70291
  if (auth instanceof Response) return auth;
69877
70292
  const url = new URL(req.url);
@@ -70051,6 +70466,7 @@ var name_get_default3 = defineController2(async ({ params, prompts, models, mode
70051
70466
  });
70052
70467
 
70053
70468
  // src/api/skills/[name].delete.ts
70469
+ init_scope();
70054
70470
  var name_delete_default = defineController2(async ({ req, env: env2, params }) => {
70055
70471
  const authResult = await requireAdmin(req, env2);
70056
70472
  if (authResult instanceof Response) return authResult;
@@ -70059,7 +70475,7 @@ var name_delete_default = defineController2(async ({ req, env: env2, params }) =
70059
70475
  const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
70060
70476
  const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
70061
70477
  try {
70062
- const found = await agentBuilder.deleteSkill(name15);
70478
+ const found = await agentBuilder.deleteSkill({ ownerId: SHARED_SKILL_OWNER }, name15);
70063
70479
  if (!found) return Response.json({ error: `Skill not found: ${name15}` }, { status: 404 });
70064
70480
  return Response.json({ ok: true, removed: name15 });
70065
70481
  } catch (error) {
@@ -70071,6 +70487,7 @@ var name_delete_default = defineController2(async ({ req, env: env2, params }) =
70071
70487
  });
70072
70488
 
70073
70489
  // src/api/skills/[name].get.ts
70490
+ init_scope();
70074
70491
  var name_get_default4 = defineController2(async ({ req, env: env2, params }) => {
70075
70492
  const authResult = await requireAdmin(req, env2);
70076
70493
  if (authResult instanceof Response) return authResult;
@@ -70079,9 +70496,10 @@ var name_get_default4 = defineController2(async ({ req, env: env2, params }) =>
70079
70496
  const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
70080
70497
  const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
70081
70498
  try {
70082
- const skill = await agentBuilder.getSkill(name15);
70499
+ const scope = { ownerId: SHARED_SKILL_OWNER };
70500
+ const skill = await agentBuilder.getSkill(scope, name15);
70083
70501
  if (!skill) return Response.json({ error: `Skill not found: ${name15}` }, { status: 404 });
70084
- const skillMd = await agentBuilder.getSkillFile(name15, "SKILL.md") ?? "";
70502
+ const skillMd = await agentBuilder.getSkillFile(scope, name15, "SKILL.md") ?? "";
70085
70503
  return Response.json({ skill, skillMd });
70086
70504
  } catch (error) {
70087
70505
  return Response.json(
@@ -70092,6 +70510,7 @@ var name_get_default4 = defineController2(async ({ req, env: env2, params }) =>
70092
70510
  });
70093
70511
 
70094
70512
  // src/api/skills/[name].patch.ts
70513
+ init_scope();
70095
70514
  var name_patch_default = defineController2(async ({ req, env: env2, params }) => {
70096
70515
  const authResult = await requireAdmin(req, env2);
70097
70516
  if (authResult instanceof Response) return authResult;
@@ -70109,9 +70528,10 @@ var name_patch_default = defineController2(async ({ req, env: env2, params }) =>
70109
70528
  const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
70110
70529
  const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
70111
70530
  try {
70112
- const found = await agentBuilder.setSkillEnabled(name15, body.enabled);
70531
+ const scope = { ownerId: SHARED_SKILL_OWNER };
70532
+ const found = await agentBuilder.setSkillEnabled(scope, name15, body.enabled);
70113
70533
  if (!found) return Response.json({ error: `Skill not found: ${name15}` }, { status: 404 });
70114
- const skill = await agentBuilder.getSkill(name15);
70534
+ const skill = await agentBuilder.getSkill(scope, name15);
70115
70535
  return Response.json({ skill });
70116
70536
  } catch (error) {
70117
70537
  return Response.json(
@@ -70408,7 +70828,6 @@ var id_patch_default = defineController2(async ({ req, env: env2, params }) => {
70408
70828
  // Map agent_name back to agent_id for API compat
70409
70829
  user_id: updatedThread.user_id,
70410
70830
  tags: updatedThread.tags || [],
70411
- env: updatedThread.env ?? updatedThread.tenvs ?? null,
70412
70831
  env_types: updatedThread.env_types ?? null,
70413
70832
  created_at: updatedThread.created_at
70414
70833
  });
@@ -70424,15 +70843,17 @@ var id_patch_default = defineController2(async ({ req, env: env2, params }) => {
70424
70843
  // src/middleware/request-auth.ts
70425
70844
  async function resolveRequestAuth(context) {
70426
70845
  if (context.auth) return context.auth;
70846
+ if (!isAuthenticationEnv(context.env)) {
70847
+ return new Response(JSON.stringify({ error: "Unauthorized" }), {
70848
+ status: 401,
70849
+ headers: { "Content-Type": "application/json" }
70850
+ });
70851
+ }
70427
70852
  return requireAuth(context.req, context.env);
70428
70853
  }
70429
- async function resolveOptionalRequestAuth(context) {
70430
- if (context.auth) return context.auth;
70431
- return authenticate(context.req, context.env);
70432
- }
70433
70854
  async function resolveRequestPermission(context, permission) {
70434
70855
  if (context.auth) {
70435
- if (!hasPermission(context.auth.user, permission)) {
70856
+ if (!hasPermissionInEnvironment(context.auth.user, permission, context.env)) {
70436
70857
  return Response.json(
70437
70858
  { error: `Forbidden: '${permission}' access required` },
70438
70859
  { status: 403 }
@@ -70578,7 +70999,7 @@ var name_get_default5 = defineController2(async ({ params, tools }) => {
70578
70999
 
70579
71000
  // src/api/unpack/[packageId]/index.post.ts
70580
71001
  init_package_identifier();
70581
- var index_post_default7 = defineController2(async ({ req, env: env2 }) => {
71002
+ var index_post_default6 = defineController2(async ({ req, env: env2 }) => {
70582
71003
  const auth = await requireAdmin(req, env2);
70583
71004
  if (auth instanceof Response) return auth;
70584
71005
  const url = new URL(req.url);
@@ -70723,17 +71144,43 @@ var id_put_default = defineController2(async ({ req, env: env2, params }) => {
70723
71144
  });
70724
71145
 
70725
71146
  // src/api/auth/bootstrap.post.ts
71147
+ var SESSION_TTL_SECONDS2 = 30 * 24 * 60 * 60;
70726
71148
  var bootstrap_post_default = defineController2(async ({ req, env: env2 }) => {
70727
71149
  try {
71150
+ if (!isTrustedLocalDevelopmentRequest(req, env2) || !isSameOriginBrowserRequest(req)) {
71151
+ return Response.json(
71152
+ { error: "Local bootstrap is available only to same-origin clients" },
71153
+ { status: 403, headers: { "Cache-Control": "no-store" } }
71154
+ );
71155
+ }
70728
71156
  const platformTokenSession = await createPlatformLocalSession(req, env2);
70729
71157
  if (!platformTokenSession) {
70730
- return new Response(null, { status: 204 });
71158
+ return new Response(null, {
71159
+ status: 204,
71160
+ headers: { "Cache-Control": "no-store" }
71161
+ });
70731
71162
  }
70732
- return Response.json(platformTokenSession);
71163
+ return Response.json({
71164
+ user: platformTokenSession.user,
71165
+ authType: platformTokenSession.authType,
71166
+ platform: platformTokenSession.platform
71167
+ }, {
71168
+ headers: {
71169
+ "Cache-Control": "no-store",
71170
+ "Set-Cookie": buildSessionCookie(
71171
+ req,
71172
+ platformTokenSession.token,
71173
+ SESSION_TTL_SECONDS2
71174
+ )
71175
+ }
71176
+ });
70733
71177
  } catch (error) {
70734
71178
  const message = error instanceof Error ? error.message : "Failed to bootstrap platform auth";
70735
71179
  const status = message.includes("STANDARD_AGENTS_API_KEY") ? 401 : 502;
70736
- return Response.json({ error: message }, { status });
71180
+ return Response.json(
71181
+ { error: message },
71182
+ { status, headers: { "Cache-Control": "no-store" } }
71183
+ );
70737
71184
  }
70738
71185
  });
70739
71186
 
@@ -70862,7 +71309,51 @@ var config_get_default2 = defineController2(async ({ req, env: env2 }) => {
70862
71309
  };
70863
71310
  });
70864
71311
 
71312
+ // src/api/auth/current-key.delete.ts
71313
+ var current_key_delete_default = defineController2(async ({ req, env: env2 }) => {
71314
+ try {
71315
+ const auth = await authenticate(req, env2);
71316
+ if (!auth) return Response.json({ error: "Unauthorized" }, { status: 401 });
71317
+ if (auth.authType !== "api_key" || !auth.credentialId) {
71318
+ return Response.json({ error: "An API key must authenticate this request." }, { status: 403 });
71319
+ }
71320
+ const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
71321
+ const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
71322
+ await agentBuilder.deleteApiKey(auth.credentialId, auth.user.id);
71323
+ return Response.json({ revoked: true });
71324
+ } catch (error) {
71325
+ console.error("Current API key revocation error:", error);
71326
+ return Response.json(
71327
+ { error: error instanceof Error ? error.message : "Failed to revoke API key" },
71328
+ { status: 500 }
71329
+ );
71330
+ }
71331
+ });
71332
+
71333
+ // src/utils/auth-rate-limit.ts
71334
+ function authClientAddress(request) {
71335
+ 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";
71336
+ }
71337
+ async function authRateLimitKey(scope, identifier) {
71338
+ return `${scope}:${await hashToken(identifier.trim().toLowerCase())}`;
71339
+ }
71340
+ function authRateLimitedResponse(result) {
71341
+ return Response.json(
71342
+ { error: "Too many authentication attempts. Try again later." },
71343
+ {
71344
+ status: 429,
71345
+ headers: {
71346
+ "Cache-Control": "no-store",
71347
+ "Retry-After": String(Math.max(1, Math.ceil(result.retry_after)))
71348
+ }
71349
+ }
71350
+ );
71351
+ }
71352
+
70865
71353
  // src/api/auth/login.post.ts
71354
+ var LOGIN_ADDRESS_LIMIT = { limit: 10, window_seconds: 5 * 60 };
71355
+ var LOGIN_IDENTITY_LIMIT = { limit: 25, window_seconds: 15 * 60 };
71356
+ var LOGIN_INSTANCE_LIMIT = { limit: 500, window_seconds: 5 * 60 };
70866
71357
  var login_post_default = defineController2(async ({ req, env: env2 }) => {
70867
71358
  try {
70868
71359
  if (isPlatformHosted(env2)) {
@@ -70879,7 +71370,31 @@ var login_post_default = defineController2(async ({ req, env: env2 }) => {
70879
71370
  { status: 400 }
70880
71371
  );
70881
71372
  }
70882
- if (env2.SUPER_ADMIN_PASSWORD && username === "admin" && password === env2.SUPER_ADMIN_PASSWORD) {
71373
+ if (!env2.AGENT_BUILDER || typeof env2.AGENT_BUILDER.idFromName !== "function") {
71374
+ return Response.json(
71375
+ { error: "Server misconfigured: AGENT_BUILDER Durable Object binding is required for local login" },
71376
+ { status: 500 }
71377
+ );
71378
+ }
71379
+ const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
71380
+ const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
71381
+ const instanceLimit = await agentBuilder.consumeAuthRateLimit({
71382
+ key: "login-instance",
71383
+ ...LOGIN_INSTANCE_LIMIT
71384
+ });
71385
+ if (!instanceLimit.allowed) return authRateLimitedResponse(instanceLimit);
71386
+ const addressLimit = await agentBuilder.consumeAuthRateLimit({
71387
+ key: await authRateLimitKey("login-address", authClientAddress(req)),
71388
+ ...LOGIN_ADDRESS_LIMIT
71389
+ });
71390
+ if (!addressLimit.allowed) return authRateLimitedResponse(addressLimit);
71391
+ const identityLimit = await agentBuilder.consumeAuthRateLimit({
71392
+ key: await authRateLimitKey("login-identity", username || "<missing>"),
71393
+ ...LOGIN_IDENTITY_LIMIT
71394
+ });
71395
+ if (!identityLimit.allowed) return authRateLimitedResponse(identityLimit);
71396
+ const isSuperAdminPassword = env2.SUPER_ADMIN_PASSWORD && username === "admin" ? await constantTimeEqual(password, env2.SUPER_ADMIN_PASSWORD) : false;
71397
+ if (isSuperAdminPassword) {
70883
71398
  if (!env2.ENCRYPTION_KEY) {
70884
71399
  return Response.json(
70885
71400
  { error: "Server misconfigured: ENCRYPTION_KEY required for super admin" },
@@ -70887,6 +71402,12 @@ var login_post_default = defineController2(async ({ req, env: env2 }) => {
70887
71402
  );
70888
71403
  }
70889
71404
  const token2 = await generateSuperAdminToken(env2.ENCRYPTION_KEY);
71405
+ const tokenHash2 = await hashToken(token2);
71406
+ const expiresAt2 = Math.floor(Date.now() / 1e3) + SUPER_ADMIN_TOKEN_TTL_SECONDS;
71407
+ await agentBuilder.createSuperAdminSession({
71408
+ token_hash: tokenHash2,
71409
+ expires_at: expiresAt2
71410
+ });
70890
71411
  return Response.json({
70891
71412
  token: token2,
70892
71413
  user: {
@@ -70894,6 +71415,11 @@ var login_post_default = defineController2(async ({ req, env: env2 }) => {
70894
71415
  username: "admin",
70895
71416
  role: "admin"
70896
71417
  }
71418
+ }, {
71419
+ headers: {
71420
+ "Cache-Control": "no-store",
71421
+ "Set-Cookie": buildSessionCookie(req, token2, SUPER_ADMIN_TOKEN_TTL_SECONDS)
71422
+ }
70897
71423
  });
70898
71424
  }
70899
71425
  if (!username) {
@@ -70902,14 +71428,6 @@ var login_post_default = defineController2(async ({ req, env: env2 }) => {
70902
71428
  { status: 400 }
70903
71429
  );
70904
71430
  }
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
71431
  const user = await agentBuilder.getUserByUsername(username);
70914
71432
  if (!user) {
70915
71433
  return Response.json(
@@ -70939,11 +71457,16 @@ var login_post_default = defineController2(async ({ req, env: env2 }) => {
70939
71457
  username: user.username,
70940
71458
  role: user.role
70941
71459
  }
71460
+ }, {
71461
+ headers: {
71462
+ "Cache-Control": "no-store",
71463
+ "Set-Cookie": buildSessionCookie(req, token, 30 * 24 * 60 * 60)
71464
+ }
70942
71465
  });
70943
71466
  } catch (error) {
70944
71467
  console.error("Login error:", error);
70945
71468
  return Response.json(
70946
- { error: error.message || "Login failed" },
71469
+ { error: error instanceof Error ? error.message : "Login failed" },
70947
71470
  { status: 500 }
70948
71471
  );
70949
71472
  }
@@ -70956,38 +71479,23 @@ async function deleteLocalSession(req, env2) {
70956
71479
  if (!token) {
70957
71480
  return;
70958
71481
  }
70959
- if (!token.includes(".") && isValidUserToken(token)) {
70960
- const tokenHash = await hashToken(token);
70961
- const agentBuilder = env2.AGENT_BUILDER.get(env2.AGENT_BUILDER.idFromName("singleton"));
71482
+ const tokenHash = await hashToken(token);
71483
+ const agentBuilder = env2.AGENT_BUILDER.get(env2.AGENT_BUILDER.idFromName("singleton"));
71484
+ if (token.includes(".")) {
71485
+ await agentBuilder.deleteSuperAdminSession(tokenHash);
71486
+ } else if (isValidUserToken(token)) {
70962
71487
  await agentBuilder.deleteSession(tokenHash);
70963
71488
  }
70964
71489
  }
70965
71490
  function clearedSessionCookie(req) {
70966
71491
  return buildSessionCookie(req, "", 0);
70967
71492
  }
70968
-
70969
- // src/api/auth/logout.get.ts
70970
- function safeReturnTo(value) {
70971
- if (!value || !value.startsWith("/") || value.startsWith("//")) return "/";
70972
- return value;
71493
+ function isSameOriginLogout(req) {
71494
+ const origin = req.headers.get("Origin");
71495
+ if (origin && origin !== new URL(req.url).origin) return false;
71496
+ const fetchSite = req.headers.get("Sec-Fetch-Site");
71497
+ return !fetchSite || fetchSite === "same-origin" || fetchSite === "none";
70973
71498
  }
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
71499
 
70992
71500
  // src/api/auth/logout.post.ts
70993
71501
  var logout_post_default = defineController2(async ({ req, env: env2 }) => {
@@ -70998,9 +71506,15 @@ var logout_post_default = defineController2(async ({ req, env: env2 }) => {
70998
71506
  "Set-Cookie": clearedSessionCookie(req)
70999
71507
  }
71000
71508
  });
71509
+ if (!isSameOriginLogout(req)) {
71510
+ return Response.json({ error: "Cross-origin logout refused" }, { status: 403 });
71511
+ }
71001
71512
  try {
71002
71513
  await deleteLocalSession(req, env2);
71003
- return clearCookie({ success: true });
71514
+ const url = new URL(req.url);
71515
+ const returnTo = url.searchParams.get("return_to");
71516
+ const redirect = isPlatformHosted(env2) ? platformLogoutUrl(req, env2, returnTo) : void 0;
71517
+ return clearCookie({ success: true, ...redirect ? { redirect } : {} });
71004
71518
  } catch (error) {
71005
71519
  console.error("Logout error:", error);
71006
71520
  return clearCookie({ error: error.message || "Logout failed" }, 500);
@@ -71103,6 +71617,37 @@ var platform_replica_post_default = defineController2(async ({ req, env: env2 })
71103
71617
  return Response.json({ ok: true, ...result });
71104
71618
  });
71105
71619
 
71620
+ // src/api/auth/websocket-ticket.post.ts
71621
+ var WEBSOCKET_TICKET_TTL_SECONDS = 30;
71622
+ var websocket_ticket_post_default = defineController2(async ({ req, env: env2 }) => {
71623
+ const auth = await authenticate(req, env2);
71624
+ if (!auth) return Response.json({ error: "Unauthorized" }, { status: 401 });
71625
+ let body;
71626
+ try {
71627
+ body = await req.json();
71628
+ } catch {
71629
+ return Response.json({ error: "A JSON body is required." }, { status: 400 });
71630
+ }
71631
+ const pathname = body && typeof body === "object" ? body.pathname : void 0;
71632
+ if (typeof pathname !== "string" || !isWebSocketTicketPath(pathname)) {
71633
+ return Response.json({ error: "pathname must be a normalized /api/* path." }, { status: 400 });
71634
+ }
71635
+ const ticket = generateSecureToken(32);
71636
+ const now = Math.floor(Date.now() / 1e3);
71637
+ const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
71638
+ const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
71639
+ await agentBuilder.createWebSocketTicket({
71640
+ ticket_hash: await hashToken(ticket),
71641
+ pathname,
71642
+ auth_json: JSON.stringify(auth),
71643
+ expires_at: now + WEBSOCKET_TICKET_TTL_SECONDS
71644
+ });
71645
+ return Response.json(
71646
+ { ticket, expires_in: WEBSOCKET_TICKET_TTL_SECONDS },
71647
+ { headers: { "Cache-Control": "no-store" } }
71648
+ );
71649
+ });
71650
+
71106
71651
  // src/api/envs/_shared.ts
71107
71652
  function buildScopedEnvResponse(entries) {
71108
71653
  const variables = [...entries].sort((a, b) => a.name.localeCompare(b.name)).map((entry) => ({
@@ -71579,6 +72124,7 @@ var path_default = defineController2(async ({ req, params, env: env2, url }) =>
71579
72124
  });
71580
72125
 
71581
72126
  // src/api/platform-auth/bootstrap.post.ts
72127
+ var AGENT_BUILDER_SESSION_TTL_SECONDS = 30 * 24 * 60 * 60;
71582
72128
  function uniqueEndpoints(preferred, candidates) {
71583
72129
  return Array.from(new Set([preferred, ...candidates].filter((value) => Boolean(value))));
71584
72130
  }
@@ -71593,10 +72139,10 @@ function buildSessionCookieHeader(req, sessionCookie) {
71593
72139
  return `${sessionCookie}; ${attributes.join("; ")}`;
71594
72140
  }
71595
72141
  var bootstrap_post_default2 = defineController2(async ({ req, env: env2 }) => {
71596
- if (!isTrustedLocalDevelopmentRequest(req, env2)) {
72142
+ if (!isTrustedLocalDevelopmentRequest(req, env2) || !isSameOriginBrowserRequest(req)) {
71597
72143
  return Response.json(
71598
- { error: "Platform auth bridge is disabled outside local development" },
71599
- { status: 410 }
72144
+ { error: "Platform auth bridge is restricted to same-origin local development" },
72145
+ { status: 403 }
71600
72146
  );
71601
72147
  }
71602
72148
  const requestCookie = req.headers.get("Cookie")?.trim() || "";
@@ -71623,10 +72169,16 @@ var bootstrap_post_default2 = defineController2(async ({ req, env: env2 }) => {
71623
72169
  account: localSession.platform.account,
71624
72170
  authType: localSession.authType,
71625
72171
  verified: localSession.platform.verified,
71626
- token: localSession.token,
71627
72172
  localUser: localSession.user
71628
72173
  }, {
71629
- headers: { "Cache-Control": "no-store" }
72174
+ headers: {
72175
+ "Cache-Control": "no-store",
72176
+ "Set-Cookie": buildSessionCookie(
72177
+ req,
72178
+ localSession.token,
72179
+ AGENT_BUILDER_SESSION_TTL_SECONDS
72180
+ )
72181
+ }
71630
72182
  });
71631
72183
  }
71632
72184
  }
@@ -73281,11 +73833,9 @@ var tools_get_default2 = defineController2(async ({ params, url }) => {
73281
73833
 
73282
73834
  // src/api/threads/[id]/bridge.ts
73283
73835
  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
- }
73836
+ const authResult = await requireAuth(req, env2);
73837
+ if (authResult instanceof Response) {
73838
+ return authResult;
73289
73839
  }
73290
73840
  const threadId = params.id;
73291
73841
  if (!threadId) {
@@ -73538,19 +74088,9 @@ var cost_get_default = defineController2(async ({ req, params, env: env2 }) => {
73538
74088
 
73539
74089
  // src/api/threads/[id]/diagnostics.get.ts
73540
74090
  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
- }
74091
+ const authResult = await requirePermission(req, env2, "logs");
74092
+ if (authResult instanceof Response) {
74093
+ return authResult;
73554
74094
  }
73555
74095
  const threadId = params.id;
73556
74096
  if (!threadId) {
@@ -73718,6 +74258,33 @@ var fs_default = defineController2(async ({ req, params, env: env2 }) => {
73718
74258
  }
73719
74259
  });
73720
74260
 
74261
+ // src/utils/kv-key-policy.ts
74262
+ var THREAD_CLIENT_RESERVED_KEYS = /* @__PURE__ */ new Set([
74263
+ "execution_owner",
74264
+ "approvals",
74265
+ "bg_processes",
74266
+ "lines_capacity_cache"
74267
+ ]);
74268
+ var THREAD_CLIENT_RESERVED_PREFIXES = [
74269
+ "approval_"
74270
+ ];
74271
+ var USER_CLIENT_RESERVED_KEYS = /* @__PURE__ */ new Set([
74272
+ "standardcode.lines.leases",
74273
+ "standardcode.lines.capacity_cache"
74274
+ ]);
74275
+ function isThreadKvKeyClientWritable(key) {
74276
+ return !THREAD_CLIENT_RESERVED_KEYS.has(key) && !THREAD_CLIENT_RESERVED_PREFIXES.some((prefix) => key.startsWith(prefix));
74277
+ }
74278
+ function isUserKvKeyClientWritable(key) {
74279
+ return !USER_CLIENT_RESERVED_KEYS.has(key);
74280
+ }
74281
+ function reservedKvKeyResponse() {
74282
+ return Response.json(
74283
+ { error: "Key is reserved for server-managed state" },
74284
+ { status: 403 }
74285
+ );
74286
+ }
74287
+
73721
74288
  // src/api/threads/[id]/kv/index.ts
73722
74289
  async function getAuthorizedThreadStub2(context, threadId) {
73723
74290
  const { env: env2 } = context;
@@ -73811,11 +74378,14 @@ async function handlePost(stub, req) {
73811
74378
  if (body instanceof Response) return body;
73812
74379
  const key = readKey(body.key);
73813
74380
  if (key instanceof Response) return key;
74381
+ const previousKey = typeof body.previousKey === "string" && body.previousKey !== key ? body.previousKey : null;
74382
+ if (!isThreadKvKeyClientWritable(key) || previousKey?.trim() && !isThreadKvKeyClientWritable(previousKey)) {
74383
+ return reservedKvKeyResponse();
74384
+ }
73814
74385
  if (!Object.prototype.hasOwnProperty.call(body, "value")) {
73815
74386
  return Response.json({ error: "Value is required" }, { status: 400 });
73816
74387
  }
73817
74388
  await stub.setValue(key, body.value);
73818
- const previousKey = typeof body.previousKey === "string" && body.previousKey !== key ? body.previousKey : null;
73819
74389
  if (previousKey?.trim()) {
73820
74390
  await stub.setValue(previousKey, null);
73821
74391
  }
@@ -73841,6 +74411,7 @@ async function handleDelete(stub, req, url) {
73841
74411
  }
73842
74412
  const key = readKey(keyValue);
73843
74413
  if (key instanceof Response) return key;
74414
+ if (!isThreadKvKeyClientWritable(key)) return reservedKvKeyResponse();
73844
74415
  await stub.setValue(key, null);
73845
74416
  return new Response(null, { status: 204 });
73846
74417
  }
@@ -73878,19 +74449,9 @@ var kv_default = defineController2(async (context) => {
73878
74449
  // src/api/threads/[id]/logs.get.ts
73879
74450
  var logs_get_default = defineController2(async (context) => {
73880
74451
  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
- }
74452
+ const authResult = await resolveRequestPermission(context, "logs");
74453
+ if (authResult instanceof Response) {
74454
+ return authResult;
73894
74455
  }
73895
74456
  const url = new URL(req.url);
73896
74457
  const threadId = params.id;
@@ -74123,19 +74684,9 @@ var pending_tool_calls_get_default = defineController2(async ({ params, env: env
74123
74684
 
74124
74685
  // src/api/threads/[id]/revive.post.ts
74125
74686
  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
- }
74687
+ const authResult = await requirePermission(req, env2, "logs");
74688
+ if (authResult instanceof Response) {
74689
+ return authResult;
74139
74690
  }
74140
74691
  const threadId = params.id;
74141
74692
  if (!threadId) {
@@ -74169,20 +74720,15 @@ var revive_post_default = defineController2(async ({ req, params, env: env2 }) =
74169
74720
  });
74170
74721
 
74171
74722
  // src/api/threads/[id]/rpc.post.ts
74723
+ function unsafeLocalRpcEnabled(env2) {
74724
+ const value = Reflect.get(env2, "AGENT_BUILDER_ENABLE_UNSAFE_RPC");
74725
+ if (typeof value !== "string") return value === true;
74726
+ const normalized = value.trim().toLowerCase();
74727
+ return normalized === "1" || normalized === "true";
74728
+ }
74172
74729
  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
- }
74730
+ if (!isTrustedLocalDevelopmentRequest(req, env2) || !unsafeLocalRpcEnabled(env2)) {
74731
+ return Response.json({ error: "Not found" }, { status: 404 });
74186
74732
  }
74187
74733
  const threadId = params.id;
74188
74734
  if (!threadId) {
@@ -74196,7 +74742,11 @@ var rpc_post_default = defineController2(async ({ req, params, env: env2 }) => {
74196
74742
  try {
74197
74743
  const id = env2.AGENT_BUILDER_THREAD.idFromName(threadId);
74198
74744
  const thread = env2.AGENT_BUILDER_THREAD.get(id);
74199
- const result = await thread[method](...args);
74745
+ const rpcMethod = Reflect.get(thread, method);
74746
+ if (typeof rpcMethod !== "function") {
74747
+ return Response.json({ error: "Method not found" }, { status: 404 });
74748
+ }
74749
+ const result = await Reflect.apply(rpcMethod, thread, args);
74200
74750
  if (result instanceof Response) {
74201
74751
  return result;
74202
74752
  }
@@ -74240,11 +74790,9 @@ var stop_post_default = defineController2(async ({ params, env: env2 }) => {
74240
74790
 
74241
74791
  // src/api/threads/[id]/stream.ts
74242
74792
  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
- }
74793
+ const authResult = await requireAuth(req, env2);
74794
+ if (authResult instanceof Response) {
74795
+ return authResult;
74248
74796
  }
74249
74797
  const threadId = params.id;
74250
74798
  if (!threadId) {
@@ -74321,6 +74869,9 @@ var tool_result_post_default = defineController2(async ({ req, params, env: env2
74321
74869
  if (!toolCallId || typeof toolCallId !== "string") {
74322
74870
  return Response.json({ error: "tool_call_id required" }, { status: 400 });
74323
74871
  }
74872
+ if (!body.delivery_token || typeof body.delivery_token !== "string") {
74873
+ return Response.json({ error: "delivery_token required" }, { status: 400 });
74874
+ }
74324
74875
  try {
74325
74876
  const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
74326
74877
  const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
@@ -74336,7 +74887,8 @@ var tool_result_post_default = defineController2(async ({ req, params, env: env2
74336
74887
  typeof body.result === "string" ? body.result : void 0,
74337
74888
  typeof body.error === "string" ? body.error : void 0,
74338
74889
  typeof body.client_id === "string" ? body.client_id : void 0,
74339
- typeof body.generation === "number" ? body.generation : void 0
74890
+ typeof body.generation === "number" ? body.generation : void 0,
74891
+ body.delivery_token
74340
74892
  );
74341
74893
  if (!delivered) {
74342
74894
  return Response.json({ status: "ignored" }, { status: 200 });
@@ -74647,11 +75199,23 @@ var name_get_default8 = defineController2(async ({ params, url, prompts, agents,
74647
75199
  });
74648
75200
 
74649
75201
  // src/api/auth/device/start.post.ts
75202
+ var DEVICE_ADDRESS_LIMIT = { limit: 20, window_seconds: 10 * 60 };
75203
+ var DEVICE_INSTANCE_LIMIT = { limit: 300, window_seconds: 10 * 60 };
74650
75204
  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
75205
  const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
74654
75206
  const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
75207
+ const addressLimit = await agentBuilder.consumeAuthRateLimit({
75208
+ key: await authRateLimitKey("device-start-address", authClientAddress(req)),
75209
+ ...DEVICE_ADDRESS_LIMIT
75210
+ });
75211
+ if (!addressLimit.allowed) return authRateLimitedResponse(addressLimit);
75212
+ const instanceLimit = await agentBuilder.consumeAuthRateLimit({
75213
+ key: "device-start-instance",
75214
+ ...DEVICE_INSTANCE_LIMIT
75215
+ });
75216
+ if (!instanceLimit.allowed) return authRateLimitedResponse(instanceLimit);
75217
+ const bytes = crypto.getRandomValues(new Uint8Array(24));
75218
+ const code = [...bytes].map((b) => "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789"[b % 57]).join("");
74655
75219
  await agentBuilder.startDeviceLogin(await hashToken(code));
74656
75220
  const origin = new URL(req.url).origin;
74657
75221
  const verifyBaseRaw = env2.STANDARD_AGENTS_DEVICE_VERIFY_URL_BASE;
@@ -74665,8 +75229,74 @@ var start_post_default = defineController2(async ({ req, env: env2 }) => {
74665
75229
  });
74666
75230
  });
74667
75231
 
75232
+ // src/api/auth/sa/_login-state.ts
75233
+ var LOGIN_STATE_COOKIE = "agtbldr_sa_login_state";
75234
+ var LOGIN_STATE_PARAM = "__sa_login_state";
75235
+ var LOGIN_STATE_TTL_SECONDS = 10 * 60;
75236
+ function safeReturnTo(value) {
75237
+ if (!value || !value.startsWith("/") || value.startsWith("//")) return "/";
75238
+ return value;
75239
+ }
75240
+ function stateCookie(req, value, maxAge) {
75241
+ const attributes = ["Path=/api/auth/sa", "HttpOnly", "SameSite=Lax"];
75242
+ if (new URL(req.url).protocol === "https:") attributes.push("Secure");
75243
+ return `${LOGIN_STATE_COOKIE}=${encodeURIComponent(value)}; ${attributes.join("; ")}; Max-Age=${maxAge}`;
75244
+ }
75245
+ function readStateCookie(req) {
75246
+ const raw = req.headers.get("Cookie");
75247
+ if (!raw) return null;
75248
+ for (const part of raw.split(";")) {
75249
+ const separator = part.indexOf("=");
75250
+ if (separator < 0 || part.slice(0, separator).trim() !== LOGIN_STATE_COOKIE) continue;
75251
+ try {
75252
+ return decodeURIComponent(part.slice(separator + 1).trim()) || null;
75253
+ } catch {
75254
+ return null;
75255
+ }
75256
+ }
75257
+ return null;
75258
+ }
75259
+ function statefulReturnTo(returnTo, state) {
75260
+ const target = new URL(safeReturnTo(returnTo), "https://instance.invalid");
75261
+ target.searchParams.set(LOGIN_STATE_PARAM, state);
75262
+ return `${target.pathname}${target.search}${target.hash}`;
75263
+ }
75264
+ function beginPlatformLogin(req, env2, returnTo, error) {
75265
+ const state = crypto.randomUUID();
75266
+ const location = new URL(platformLoginUrl(req, env2, statefulReturnTo(returnTo, state)));
75267
+ if (error) location.searchParams.set("error", error);
75268
+ return new Response(null, {
75269
+ status: 302,
75270
+ headers: {
75271
+ Location: location.toString(),
75272
+ "Set-Cookie": stateCookie(req, state, LOGIN_STATE_TTL_SECONDS),
75273
+ "Cache-Control": "no-store"
75274
+ }
75275
+ });
75276
+ }
75277
+ function consumePlatformLoginState(req, rawReturnTo) {
75278
+ const target = new URL(safeReturnTo(rawReturnTo), "https://instance.invalid");
75279
+ const state = target.searchParams.get(LOGIN_STATE_PARAM);
75280
+ target.searchParams.delete(LOGIN_STATE_PARAM);
75281
+ const returnTo = `${target.pathname}${target.search}${target.hash}`;
75282
+ return {
75283
+ returnTo,
75284
+ valid: Boolean(state && state === readStateCookie(req)),
75285
+ present: Boolean(state)
75286
+ };
75287
+ }
75288
+ function withClearedPlatformLoginState(req, response) {
75289
+ const headers = new Headers(response.headers);
75290
+ headers.append("Set-Cookie", stateCookie(req, "", 0));
75291
+ return new Response(response.body, {
75292
+ status: response.status,
75293
+ statusText: response.statusText,
75294
+ headers
75295
+ });
75296
+ }
75297
+
74668
75298
  // src/api/auth/sa/callback.get.ts
74669
- var SESSION_TTL_SECONDS2 = 30 * 24 * 60 * 60;
75299
+ var SESSION_TTL_SECONDS3 = 30 * 24 * 60 * 60;
74670
75300
  function safeReturnTo2(value) {
74671
75301
  if (!value || !value.startsWith("/") || value.startsWith("//")) return "/";
74672
75302
  return value;
@@ -74674,15 +75304,12 @@ function safeReturnTo2(value) {
74674
75304
  function failRedirect(req, env2, reason, returnTo) {
74675
75305
  const origin = new URL(req.url).origin;
74676
75306
  if (isPlatformHosted(env2)) {
74677
- const target = new URL(platformLoginUrl(req, env2, returnTo ?? "/"));
74678
- target.searchParams.set(
74679
- "error",
75307
+ return beginPlatformLogin(
75308
+ req,
75309
+ env2,
75310
+ returnTo ?? "/",
74680
75311
  reason === "api_only_account" ? "api_only_account" : "instance_login_failed"
74681
75312
  );
74682
- return new Response(null, {
74683
- status: 302,
74684
- headers: { Location: target.toString() }
74685
- });
74686
75313
  }
74687
75314
  return new Response(null, {
74688
75315
  status: 302,
@@ -74694,7 +75321,9 @@ var callback_get_default = defineController2(async ({ req, env: env2 }) => {
74694
75321
  return failRedirect(req, env2, "not_hosted");
74695
75322
  }
74696
75323
  const url = new URL(req.url);
74697
- const failReturnTo = safeReturnTo2(url.searchParams.get("return_to"));
75324
+ const loginState = consumePlatformLoginState(req, url.searchParams.get("return_to"));
75325
+ const finishLoginState = (response) => loginState.present ? withClearedPlatformLoginState(req, response) : response;
75326
+ const failReturnTo = safeReturnTo2(loginState.returnTo);
74698
75327
  const handoff = url.searchParams.get("handoff") || url.searchParams.get("token");
74699
75328
  if (!handoff) {
74700
75329
  return failRedirect(req, env2, "invalid_response", failReturnTo);
@@ -74708,29 +75337,37 @@ var callback_get_default = defineController2(async ({ req, env: env2 }) => {
74708
75337
  projectId: configuredProjectId
74709
75338
  }
74710
75339
  );
74711
- if (!payload || !payload.platform_user_id || !payload.role) {
75340
+ if (!payload || !payload.platform_user_id || !payload.role || !payload.jti) {
74712
75341
  return failRedirect(req, env2, "invalid_handoff", failReturnTo);
74713
75342
  }
74714
75343
  if (!configuredProjectId && payload.project_id !== hostedInstanceRedirectId(req, env2)) {
74715
75344
  return failRedirect(req, env2, "invalid_handoff", failReturnTo);
74716
75345
  }
74717
75346
  if (payload.role === "api") {
74718
- const apiReturnTo = safeReturnTo2(url.searchParams.get("return_to"));
75347
+ const apiReturnTo = failReturnTo;
74719
75348
  if (/^\/api\/auth\/device\/[A-Za-z0-9]{8,64}\/approve$/.test(apiReturnTo)) {
74720
- return new Response(null, {
75349
+ return finishLoginState(new Response(null, {
74721
75350
  status: 302,
74722
75351
  headers: {
74723
75352
  Location: `${url.origin}${apiReturnTo}?handoff=${encodeURIComponent(handoff)}`
74724
75353
  }
74725
- });
75354
+ }));
74726
75355
  }
74727
75356
  const landingRaw = env2.STANDARD_AGENTS_API_ROLE_LANDING_URL;
74728
75357
  const landing = typeof landingRaw === "string" ? landingRaw.trim() : "";
74729
75358
  if (landing && /^https:\/\//.test(landing)) {
74730
- return new Response(null, { status: 302, headers: { Location: landing } });
75359
+ return finishLoginState(
75360
+ new Response(null, { status: 302, headers: { Location: landing } })
75361
+ );
74731
75362
  }
74732
75363
  return failRedirect(req, env2, "api_only_account");
74733
75364
  }
75365
+ if (!loginState.valid) {
75366
+ return beginPlatformLogin(req, env2, failReturnTo);
75367
+ }
75368
+ if (!await consumePlatformHandoffJti(env2, payload.jti, payload.exp)) {
75369
+ return failRedirect(req, env2, "invalid_handoff", failReturnTo);
75370
+ }
74734
75371
  const session = await mintLocalSessionForPlatformUser(env2, {
74735
75372
  platform_user_id: payload.platform_user_id,
74736
75373
  username: payload.username ?? null,
@@ -74740,14 +75377,15 @@ var callback_get_default = defineController2(async ({ req, env: env2 }) => {
74740
75377
  role: payload.role,
74741
75378
  permissions: payload.permissions ?? null
74742
75379
  });
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
- }
75380
+ const returnTo = failReturnTo;
75381
+ const headers = new Headers({
75382
+ Location: `${url.origin}${returnTo}`
74750
75383
  });
75384
+ headers.append("Set-Cookie", buildSessionCookie(req, session.token, SESSION_TTL_SECONDS3));
75385
+ return withClearedPlatformLoginState(req, new Response(null, {
75386
+ status: 302,
75387
+ headers
75388
+ }));
74751
75389
  });
74752
75390
 
74753
75391
  // src/api/auth/sa/start.get.ts
@@ -74757,12 +75395,7 @@ var start_get_default = defineController2(async ({ req, env: env2 }) => {
74757
75395
  }
74758
75396
  const url = new URL(req.url);
74759
75397
  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
- });
75398
+ return beginPlatformLogin(req, env2, returnTo);
74766
75399
  });
74767
75400
 
74768
75401
  // src/api/users/me/[...rest].ts
@@ -74907,6 +75540,7 @@ var index_delete_default = defineController2(async ({ req, env: env2 }) => {
74907
75540
  if (!key) {
74908
75541
  return Response.json({ error: "key query parameter required" }, { status: 400 });
74909
75542
  }
75543
+ if (!isUserKvKeyClientWritable(key)) return reservedKvKeyResponse();
74910
75544
  const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
74911
75545
  const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
74912
75546
  if (typeof agentBuilder.setUserValue !== "function") {
@@ -74977,7 +75611,7 @@ var index_get_default4 = defineController2(async ({ req, env: env2 }) => {
74977
75611
  });
74978
75612
 
74979
75613
  // src/api/users/me/kv/index.post.ts
74980
- var index_post_default8 = defineController2(async ({ req, env: env2 }) => {
75614
+ var index_post_default7 = defineController2(async ({ req, env: env2 }) => {
74981
75615
  try {
74982
75616
  const authResult = await requireAuth(req, env2);
74983
75617
  if (authResult instanceof Response) {
@@ -75003,6 +75637,7 @@ var index_post_default8 = defineController2(async ({ req, env: env2 }) => {
75003
75637
  if (typeof key !== "string" || key.length === 0) {
75004
75638
  return Response.json({ error: "key required" }, { status: 400 });
75005
75639
  }
75640
+ if (!isUserKvKeyClientWritable(key)) return reservedKvKeyResponse();
75006
75641
  const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
75007
75642
  const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
75008
75643
  if (typeof agentBuilder.setUserValue !== "function") {
@@ -75041,13 +75676,39 @@ var stream_default3 = defineController2(async ({ req, env: env2 }) => {
75041
75676
  return forwardToUserScope(req, env2, authResult.user.id, "stream");
75042
75677
  });
75043
75678
 
75044
- // src/api/auth/device/[code]/approve.get.ts
75679
+ // src/api/auth/device/[code]/_approval.ts
75680
+ var CONFIRMATION_TTL_SECONDS = 5 * 60;
75681
+ function escapeHtml(value) {
75682
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
75683
+ }
75045
75684
  function page(title, body, ok) {
75046
75685
  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" } }
75686
+ `<!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>`,
75687
+ {
75688
+ headers: {
75689
+ "Content-Type": "text/html; charset=utf-8",
75690
+ "Cache-Control": "no-store",
75691
+ "Referrer-Policy": "no-referrer",
75692
+ "X-Content-Type-Options": "nosniff"
75693
+ }
75694
+ }
75049
75695
  );
75050
75696
  }
75697
+ function confirmationPage(req, code, confirmationToken, handoff) {
75698
+ const url = new URL(req.url);
75699
+ const handoffInput = handoff ? `<input type="hidden" name="handoff" value="${escapeHtml(handoff)}">` : "";
75700
+ const shortCode = code.length > 12 ? `${code.slice(0, 6)}\u2026${code.slice(-6)}` : code;
75701
+ 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>`;
75702
+ return new Response(html, {
75703
+ headers: {
75704
+ "Content-Type": "text/html; charset=utf-8",
75705
+ "Cache-Control": "no-store",
75706
+ "Referrer-Policy": "no-referrer",
75707
+ "X-Content-Type-Options": "nosniff",
75708
+ "Content-Security-Policy": "default-src 'none'; style-src 'unsafe-inline'; form-action 'self'; base-uri 'none'; frame-ancestors 'none'"
75709
+ }
75710
+ });
75711
+ }
75051
75712
  function outcome(env2, ok, reason, fallback) {
75052
75713
  const baseRaw = env2.STANDARD_AGENTS_DEVICE_OUTCOME_URL_BASE;
75053
75714
  const base = typeof baseRaw === "string" ? baseRaw.trim().replace(/\/+$/, "") : "";
@@ -75057,12 +75718,9 @@ function outcome(env2, ok, reason, fallback) {
75057
75718
  }
75058
75719
  return page(fallback.title, fallback.body, ok);
75059
75720
  }
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." });
75721
+ async function resolveDeviceApprovalIdentity(req, env2) {
75063
75722
  const url = new URL(req.url);
75064
- let approvingUserId = null;
75065
- const handoff = url.searchParams.get("handoff");
75723
+ const handoff = url.searchParams.get("handoff") ?? (req.method === "POST" ? (await req.clone().formData().catch(() => null))?.get("handoff")?.toString() ?? null : null);
75066
75724
  if (handoff && isPlatformHosted(env2)) {
75067
75725
  const configuredProjectId = typeof env2.STANDARD_AGENTS_PROJECT_ID === "string" ? env2.STANDARD_AGENTS_PROJECT_ID : void 0;
75068
75726
  const payload = await verifyPlatformSignedPayload(
@@ -75071,10 +75729,13 @@ var approve_get_default = defineController2(async ({ req, env: env2, params }) =
75071
75729
  { kind: "instance_handoff", projectId: configuredProjectId }
75072
75730
  );
75073
75731
  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." });
75732
+ if (!payload || !payload.platform_user_id || !payload.role || !payload.jti || !projectOk) {
75733
+ return outcome(env2, false, "signin_failed", {
75734
+ title: "Sign in failed",
75735
+ body: "This sign-in link is invalid or has expired. Start again from your terminal."
75736
+ });
75076
75737
  }
75077
- const user = await upsertReplicaUserForPlatformProfile(env2, {
75738
+ const platformProfile = {
75078
75739
  platform_user_id: payload.platform_user_id,
75079
75740
  username: payload.username ?? null,
75080
75741
  display_name: payload.display_name ?? null,
@@ -75082,27 +75743,163 @@ var approve_get_default = defineController2(async ({ req, env: env2, params }) =
75082
75743
  avatar_url: payload.avatar_url ?? null,
75083
75744
  role: payload.role,
75084
75745
  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." });
75746
+ };
75747
+ return {
75748
+ confirmationSubject: `platform:${payload.platform_user_id}`,
75749
+ handoff,
75750
+ platformProfile,
75751
+ platformHandoff: {
75752
+ jti: payload.jti,
75753
+ expires_at: payload.exp
75754
+ }
75755
+ };
75756
+ }
75757
+ const authResult = await requireAuth(req, env2);
75758
+ if (authResult instanceof Response) {
75759
+ if (req.method !== "GET") {
75760
+ return outcome(env2, false, "session_required", {
75761
+ title: "Sign in required",
75762
+ body: "Return to the device link and sign in again before approving."
75763
+ });
75097
75764
  }
75098
- approvingUserId = authResult.user.id;
75765
+ const returnTo = encodeURIComponent(url.pathname);
75766
+ const hosted = Boolean(
75767
+ env2.STANDARD_AGENTS_HOSTED && String(env2.STANDARD_AGENTS_HOSTED) !== "0" && String(env2.STANDARD_AGENTS_HOSTED).toLowerCase() !== "false"
75768
+ );
75769
+ const target = hosted ? `/api/auth/sa/start?return_to=${returnTo}` : `/login?return_to=${returnTo}`;
75770
+ return new Response(null, { status: 302, headers: { Location: `${url.origin}${target}` } });
75771
+ }
75772
+ if (authResult.authType === "api_key") {
75773
+ return outcome(env2, false, "session_required", {
75774
+ title: "Sign in required",
75775
+ body: "Device logins must be approved from a signed-in browser session."
75776
+ });
75777
+ }
75778
+ return {
75779
+ confirmationSubject: `user:${authResult.user.id}`,
75780
+ handoff: null,
75781
+ localUserId: authResult.user.id
75782
+ };
75783
+ }
75784
+ async function consumeDeviceApprovalHandoff(env2, identity) {
75785
+ if (!identity.platformHandoff) return true;
75786
+ return consumePlatformHandoffJti(
75787
+ env2,
75788
+ identity.platformHandoff.jti,
75789
+ identity.platformHandoff.expires_at
75790
+ );
75791
+ }
75792
+ async function materializeDeviceApprovalUser(env2, identity) {
75793
+ if (identity.localUserId) return identity.localUserId;
75794
+ if (!identity.platformProfile) {
75795
+ throw new Error("Device approval identity is missing a local user and platform profile");
75796
+ }
75797
+ const user = await upsertReplicaUserForPlatformProfile(
75798
+ env2,
75799
+ identity.platformProfile
75800
+ );
75801
+ return user.id;
75802
+ }
75803
+ function encryptionKey(env2) {
75804
+ const value = env2.ENCRYPTION_KEY;
75805
+ return typeof value === "string" && value.length > 0 ? value : null;
75806
+ }
75807
+ async function createDeviceConfirmationToken(env2, code, confirmationSubject) {
75808
+ const key = encryptionKey(env2);
75809
+ if (!key) return null;
75810
+ return signToken(JSON.stringify({
75811
+ kind: "device_approval_confirmation",
75812
+ code_hash: await hashToken(code),
75813
+ subject: confirmationSubject,
75814
+ exp: Math.floor(Date.now() / 1e3) + CONFIRMATION_TTL_SECONDS,
75815
+ nonce: crypto.randomUUID()
75816
+ }), key);
75817
+ }
75818
+ async function verifyDeviceConfirmationToken(env2, code, confirmationSubject, token) {
75819
+ const key = encryptionKey(env2);
75820
+ if (!key) return false;
75821
+ const raw = await verifySignedToken(token, key);
75822
+ if (!raw) return false;
75823
+ try {
75824
+ const payload = JSON.parse(raw);
75825
+ 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);
75826
+ } catch {
75827
+ return false;
75828
+ }
75829
+ }
75830
+ function isSameOriginApprovalPost(req) {
75831
+ const url = new URL(req.url);
75832
+ const origin = req.headers.get("Origin");
75833
+ if (origin && origin !== url.origin) return false;
75834
+ const fetchSite = req.headers.get("Sec-Fetch-Site");
75835
+ return !fetchSite || fetchSite === "same-origin" || fetchSite === "none";
75836
+ }
75837
+
75838
+ // src/api/auth/device/[code]/approve.get.ts
75839
+ var approve_get_default = defineController2(async ({ req, env: env2, params }) => {
75840
+ const code = params.code;
75841
+ if (!code) {
75842
+ return outcome(env2, false, "invalid_link", {
75843
+ title: "Missing code",
75844
+ body: "This device link is malformed."
75845
+ });
75846
+ }
75847
+ const identity = await resolveDeviceApprovalIdentity(req, env2);
75848
+ if (identity instanceof Response) return identity;
75849
+ const confirmationToken = await createDeviceConfirmationToken(
75850
+ env2,
75851
+ code,
75852
+ identity.confirmationSubject
75853
+ );
75854
+ if (!confirmationToken) {
75855
+ return outcome(env2, false, "signin_failed", {
75856
+ title: "Sign in failed",
75857
+ body: "This instance cannot create a secure device confirmation. Contact its administrator."
75858
+ });
75099
75859
  }
75860
+ return confirmationPage(req, code, confirmationToken, identity.handoff);
75861
+ });
75862
+
75863
+ // src/api/auth/device/[code]/approve.post.ts
75864
+ var approve_post_default = defineController2(async ({ req, env: env2, params }) => {
75865
+ const code = params.code;
75866
+ if (!code) {
75867
+ return outcome(env2, false, "invalid_link", {
75868
+ title: "Missing code",
75869
+ body: "This device link is malformed."
75870
+ });
75871
+ }
75872
+ if (!isSameOriginApprovalPost(req)) {
75873
+ return Response.json({ error: "Cross-origin device approval refused" }, { status: 403 });
75874
+ }
75875
+ const form = await req.clone().formData().catch(() => null);
75876
+ const confirmationToken = form?.get("confirmation_token")?.toString() ?? "";
75877
+ if (!confirmationToken) {
75878
+ return Response.json({ error: "Device confirmation token required" }, { status: 403 });
75879
+ }
75880
+ const identity = await resolveDeviceApprovalIdentity(req, env2);
75881
+ if (identity instanceof Response) return identity;
75882
+ if (!await verifyDeviceConfirmationToken(
75883
+ env2,
75884
+ code,
75885
+ identity.confirmationSubject,
75886
+ confirmationToken
75887
+ )) {
75888
+ return Response.json({ error: "Invalid or expired device confirmation" }, { status: 403 });
75889
+ }
75890
+ if (!await consumeDeviceApprovalHandoff(env2, identity)) {
75891
+ return outcome(env2, false, "signin_failed", {
75892
+ title: "Sign in failed",
75893
+ body: "This sign-in link was already used or has expired. Start again from your terminal."
75894
+ });
75895
+ }
75896
+ const userId = await materializeDeviceApprovalUser(env2, identity);
75100
75897
  const token = generateApiKey();
75101
75898
  const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
75102
75899
  const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
75103
75900
  const ok = await agentBuilder.approveDeviceLogin({
75104
75901
  code_hash: await hashToken(code),
75105
- user_id: approvingUserId,
75902
+ user_id: userId,
75106
75903
  token,
75107
75904
  token_hash: await hashToken(token),
75108
75905
  key_prefix: token.slice(0, 12),
@@ -75110,12 +75907,19 @@ var approve_get_default = defineController2(async ({ req, env: env2, params }) =
75110
75907
  key_name: "CLI device login"
75111
75908
  });
75112
75909
  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." });
75910
+ return outcome(env2, false, "expired", {
75911
+ title: "Link expired",
75912
+ body: "This device login has expired or was already used. Start again from your terminal."
75913
+ });
75114
75914
  }
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." });
75915
+ return outcome(env2, true, "done", {
75916
+ title: "You're connected",
75917
+ body: "Standard Code is now signed in. Return to your terminal \u2014 it will pick up automatically."
75918
+ });
75116
75919
  });
75117
75920
 
75118
75921
  // src/api/skills/[name]/files/[...path].get.ts
75922
+ init_scope();
75119
75923
  var path_get_default = defineController2(async ({ req, env: env2, params }) => {
75120
75924
  const authResult = await requireAdmin(req, env2);
75121
75925
  if (authResult instanceof Response) return authResult;
@@ -75128,7 +75932,11 @@ var path_get_default = defineController2(async ({ req, env: env2, params }) => {
75128
75932
  const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
75129
75933
  const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
75130
75934
  try {
75131
- const content = await agentBuilder.getSkillFile(name15, filePath);
75935
+ const content = await agentBuilder.getSkillFile(
75936
+ { ownerId: SHARED_SKILL_OWNER },
75937
+ name15,
75938
+ filePath
75939
+ );
75132
75940
  if (content === null) {
75133
75941
  return Response.json({ error: `File not found: ${filePath} in skill ${name15}` }, { status: 404 });
75134
75942
  }
@@ -75143,6 +75951,24 @@ var path_get_default = defineController2(async ({ req, env: env2, params }) => {
75143
75951
 
75144
75952
  // src/api/threads/[id]/fs/[...path].ts
75145
75953
  var CHUNK_SIZE2 = 1.75 * 1024 * 1024;
75954
+ var EXECUTABLE_DOCUMENT_MIME_TYPES = /* @__PURE__ */ new Set([
75955
+ "text/html",
75956
+ "image/svg+xml",
75957
+ "application/xhtml+xml"
75958
+ ]);
75959
+ function safeDownloadName(name15) {
75960
+ return name15.replace(/["\\\r\n]/g, "_");
75961
+ }
75962
+ function fileResponseHeaders(file) {
75963
+ const mimeType = file.mimeType.split(";", 1)[0]?.trim().toLowerCase();
75964
+ const disposition = mimeType && EXECUTABLE_DOCUMENT_MIME_TYPES.has(mimeType) ? "attachment" : "inline";
75965
+ return {
75966
+ "Content-Type": file.mimeType,
75967
+ "Content-Length": String(file.size),
75968
+ "Content-Disposition": `${disposition}; filename="${safeDownloadName(file.name)}"`,
75969
+ "X-Content-Type-Options": "nosniff"
75970
+ };
75971
+ }
75146
75972
  function toAttachmentRef(file) {
75147
75973
  const metadata = file.metadata;
75148
75974
  const width = file.width ?? metadata?.width;
@@ -75233,8 +76059,9 @@ async function handleGet2(stub, path10, url) {
75233
76059
  headers: {
75234
76060
  "Content-Type": "image/jpeg",
75235
76061
  // Thumbnails are typically JPEG
75236
- "Cache-Control": "public, max-age=31536000"
76062
+ "Cache-Control": "public, max-age=31536000",
75237
76063
  // Cache for 1 year
76064
+ "X-Content-Type-Options": "nosniff"
75238
76065
  }
75239
76066
  });
75240
76067
  }
@@ -75274,11 +76101,7 @@ async function handleGet2(stub, path10, url) {
75274
76101
  bytes[i] = binary.charCodeAt(i);
75275
76102
  }
75276
76103
  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
- }
76104
+ headers: fileResponseHeaders(file)
75282
76105
  });
75283
76106
  }
75284
76107
  function streamChunkedFile(stub, path10, file) {
@@ -75306,12 +76129,8 @@ function streamChunkedFile(stub, path10, file) {
75306
76129
  }
75307
76130
  })();
75308
76131
  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
- }
76132
+ // Note: We set Content-Length, but also use chunked transfer internally.
76133
+ headers: fileResponseHeaders(file)
75315
76134
  });
75316
76135
  }
75317
76136
  async function handlePost2(stub, path10, req) {
@@ -75508,19 +76327,9 @@ async function handleChunkedUploadComplete(stub, filePath, req) {
75508
76327
 
75509
76328
  // src/api/threads/[id]/logs/[logId].get.ts
75510
76329
  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
- }
76330
+ const authResult = await requirePermission(req, env2, "logs");
76331
+ if (authResult instanceof Response) {
76332
+ return authResult;
75524
76333
  }
75525
76334
  const threadId = params.id;
75526
76335
  const logId = params.logId;
@@ -75932,19 +76741,9 @@ function createInspectProviderConfig(providerName, factory, env2) {
75932
76741
  };
75933
76742
  }
75934
76743
  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
- }
76744
+ const authResult = await requirePermission(req, env2, "logs");
76745
+ if (authResult instanceof Response) {
76746
+ return authResult;
75948
76747
  }
75949
76748
  const threadId = params.id;
75950
76749
  const logId = params.logId;
@@ -76164,30 +76963,28 @@ var provider_get_default = defineController2(async ({ req, params, env: env2 })
76164
76963
 
76165
76964
  // src/built-in-routes.ts
76166
76965
  var routeHandlers = {
76167
- "POST:/": index_post_default,
76168
76966
  "GET:/agents": agents_get_default,
76169
76967
  "GET:/api-keys": index_get_default,
76170
- "POST:/api-keys": index_post_default2,
76968
+ "POST:/api-keys": index_post_default,
76171
76969
  "GET:/config": config_get_default,
76172
76970
  "GET:/diagnostics": diagnostics_get_default,
76173
76971
  "GET:/events": events_get_default,
76174
76972
  "GET:/hooks": hooks_get_default,
76175
76973
  "GET:/models": models_get_default,
76176
- "GET:/new-test": new_test_get_default,
76177
76974
  "GET:/platform-endpoint": platform_endpoint_default,
76178
76975
  "GET:/prompts": prompts_get_default,
76179
76976
  "GET:/providers": providers_get_default,
76180
76977
  "GET:/skills": index_get_default2,
76181
- "POST:/skills": index_post_default3,
76182
- "POST:/threads": index_post_default4,
76978
+ "POST:/skills": index_post_default2,
76979
+ "POST:/threads": index_post_default3,
76183
76980
  "GET:/threads": threads_default,
76184
76981
  "GET:/tools": tools_get_default,
76185
76982
  "GET:/users": index_get_default3,
76186
- "POST:/users": index_post_default5,
76983
+ "POST:/users": index_post_default4,
76187
76984
  "GET:/agents/:name": name_get_default,
76188
76985
  "DELETE:/api-keys/:id": id_delete_default,
76189
76986
  "GET:/models/:name": name_get_default2,
76190
- "POST:/pack/:agentName": index_post_default6,
76987
+ "POST:/pack/:agentName": index_post_default5,
76191
76988
  "GET:/prompts/:name": name_get_default3,
76192
76989
  "DELETE:/skills/:name": name_delete_default,
76193
76990
  "GET:/skills/:name": name_get_default4,
@@ -76196,18 +76993,19 @@ var routeHandlers = {
76196
76993
  "PATCH:/threads/:id": id_patch_default,
76197
76994
  "GET:/threads/:id": id_default,
76198
76995
  "GET:/tools/:name": name_get_default5,
76199
- "POST:/unpack/:packageId": index_post_default7,
76996
+ "POST:/unpack/:packageId": index_post_default6,
76200
76997
  "DELETE:/users/:id": id_delete_default3,
76201
76998
  "PUT:/users/:id": id_put_default,
76202
76999
  "POST:/auth/bootstrap": bootstrap_post_default,
76203
77000
  "POST:/auth/brolly-control": brolly_control_post_default,
76204
77001
  "GET:/auth/config": config_get_default2,
77002
+ "DELETE:/auth/current-key": current_key_delete_default,
76205
77003
  "POST:/auth/login": login_post_default,
76206
- "GET:/auth/logout": logout_get_default,
76207
77004
  "POST:/auth/logout": logout_post_default,
76208
77005
  "GET:/auth/me": me_get_default,
76209
77006
  "POST:/auth/platform-cleanup": platform_cleanup_post_default,
76210
77007
  "POST:/auth/platform-replica": platform_replica_post_default,
77008
+ "POST:/auth/websocket-ticket": websocket_ticket_post_default,
76211
77009
  "GET:/envs/instance": instance_get_default,
76212
77010
  "PATCH:/envs/instance": instance_patch_default,
76213
77011
  "POST:/models/available": available_post_default,
@@ -76283,9 +77081,10 @@ var routeHandlers = {
76283
77081
  "GET:/users/me/hub": hub_default,
76284
77082
  "DELETE:/users/me/kv": index_delete_default,
76285
77083
  "GET:/users/me/kv": index_get_default4,
76286
- "POST:/users/me/kv": index_post_default8,
77084
+ "POST:/users/me/kv": index_post_default7,
76287
77085
  "GET:/users/me/stream": stream_default3,
76288
77086
  "GET:/auth/device/:code/approve": approve_get_default,
77087
+ "POST:/auth/device/:code/approve": approve_post_default,
76289
77088
  "GET:/skills/:name/files/**": path_get_default,
76290
77089
  "DELETE:/threads/:id/fs/**": path_default3,
76291
77090
  "POST:/threads/:id/fs/**": path_default3,