@desplega.ai/agent-swarm 1.130.0 → 1.131.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 (138) hide show
  1. package/README.md +1 -1
  2. package/dist/{actions-73ee723n.js → actions-jgcy477k.js} +7 -7
  3. package/dist/{anthropic-messages-3xgme8my.js → anthropic-messages-rjmd2e7n.js} +5 -5
  4. package/dist/{app-hz5c2xay.js → app-5da78bd4.js} +4 -4
  5. package/dist/{assistant-08mr16yp.js → assistant-f2vkk1hj.js} +9 -9
  6. package/dist/{azure-openai-responses-xa4sh9f6.js → azure-openai-responses-c0yjmbfd.js} +8 -5
  7. package/dist/{boot-reembed-5bgan3cd.js → boot-reembed-q7mp8hcr.js} +4 -4
  8. package/dist/{boot-reembed-z7ykyeq8.js → boot-reembed-xmw8wcx6.js} +5 -5
  9. package/dist/{boot-scrub-logs-b2rjk8mv.js → boot-scrub-logs-dsqrv79n.js} +3 -3
  10. package/dist/{claude-adapter-qr0k17km.js → claude-adapter-q6x7yspx.js} +6 -6
  11. package/dist/{claude-managed-adapter-gv19tdew.js → claude-managed-adapter-wsg37bt8.js} +1 -1
  12. package/dist/{cli-vr2f46zj.js → cli-114fvtmj.js} +1 -1
  13. package/dist/{cli-mhr8tq3v.js → cli-2f07m0xr.js} +1 -1
  14. package/dist/{cli-mgerp0vs.js → cli-2rk0cx7s.js} +2 -2
  15. package/dist/{cli-tfw3c0y1.js → cli-3ty0t082.js} +2 -2
  16. package/dist/{cli-fs153s3x.js → cli-4rkfg1wx.js} +3 -3
  17. package/dist/{cli-s18zzkfp.js → cli-50vbt00w.js} +44 -7
  18. package/dist/{cli-vgexpfsr.js → cli-57khth04.js} +6 -3
  19. package/dist/{cli-n9yxcnvk.js → cli-5n6e5yfn.js} +29 -7
  20. package/dist/{cli-q8zwffx0.js → cli-6dp3ydxa.js} +158 -39
  21. package/dist/{cli-r62me33w.js → cli-6dpytdr2.js} +2 -2
  22. package/dist/{cli-ch7t5v7n.js → cli-6map995v.js} +9 -4
  23. package/dist/{cli-326083vb.js → cli-6pcz9h15.js} +14 -14
  24. package/dist/{cli-h3e66g5b.js → cli-793jvbsh.js} +1 -1
  25. package/dist/{cli-yj7z99vp.js → cli-7dm2zqy2.js} +2 -2
  26. package/dist/{cli-t5tsq67k.js → cli-9dr5qzc0.js} +39 -4
  27. package/dist/{cli-m8fs5ttq.js → cli-aca5pz9f.js} +1 -1
  28. package/dist/{cli-w10fkrf1.js → cli-akdvwc9v.js} +354 -115
  29. package/dist/{cli-hntzdchd.js → cli-bqw0mt1n.js} +1 -1
  30. package/dist/{cli-7v5dhmc8.js → cli-ccf9vy7q.js} +6 -6
  31. package/dist/{cli-6nh2qxvh.js → cli-darcwax3.js} +1 -1
  32. package/dist/{cli-xg2eezyv.js → cli-f1am9szv.js} +1 -1
  33. package/dist/{cli-1px8pn1s.js → cli-g1re8r05.js} +2 -2
  34. package/dist/{cli-z9q7pb38.js → cli-g74vpjp2.js} +4 -4
  35. package/dist/{cli-brebbsen.js → cli-gecy3z2s.js} +20 -8
  36. package/dist/{cli-epsvdckx.js → cli-gm9cbhbq.js} +20 -10
  37. package/dist/{cli-xdrmxvd4.js → cli-jbcva0xj.js} +55 -11
  38. package/dist/{cli-haanc4qd.js → cli-jevgrb4w.js} +22 -22
  39. package/dist/{cli-e5p3es48.js → cli-kd1ck4rx.js} +1 -1
  40. package/dist/{cli-1mj7yz1r.js → cli-nrkkknat.js} +1 -1
  41. package/dist/{cli-2pa5w9b6.js → cli-pxfxkx5m.js} +1 -1
  42. package/dist/{cli-qcsjdppx.js → cli-rgrfzg8z.js} +7 -7
  43. package/dist/{cli-mf009e0j.js → cli-se2p6rkb.js} +1 -1
  44. package/dist/{cli-tp519bjz.js → cli-sy7jk3tx.js} +2 -2
  45. package/dist/{cli-qbb2ten8.js → cli-t8d349cy.js} +2 -2
  46. package/dist/{cli-s21zzx4b.js → cli-w2wxd56v.js} +448 -184
  47. package/dist/{cli-qmdnhpej.js → cli-wz9115x0.js} +17 -7
  48. package/dist/{cli-4n4mdv21.js → cli-xpxde7rm.js} +7 -3
  49. package/dist/{cli-dakxgk70.js → cli-ycrnynpw.js} +4 -4
  50. package/dist/{cli-73bvdyxr.js → cli-ygrbmjja.js} +6 -6
  51. package/dist/cli.js +22 -22
  52. package/dist/{codex-adapter-6pc50x9q.js → codex-adapter-htjkn22b.js} +4 -4
  53. package/dist/{codex-hook-amndawyb.js → codex-hook-hq54t9ft.js} +2 -2
  54. package/dist/{codex-session-runner-tpg8hyj7.js → codex-session-runner-wjfc7smz.js} +4 -4
  55. package/dist/{commands-yfpwt11g.js → commands-xhrfwqsk.js} +3 -3
  56. package/dist/{db-jn499nmp.js → db-anvwb22f.js} +3 -3
  57. package/dist/{google-generative-ai-txgg2q58.js → google-generative-ai-hysrd8qx.js} +7 -5
  58. package/dist/{google-vertex-t663s1c3.js → google-vertex-p5arrga0.js} +7 -5
  59. package/dist/{handlers-eev4nng0.js → handlers-b9kbc621.js} +9 -9
  60. package/dist/{hook-9fkwkf3m.js → hook-cgk4wxmx.js} +5 -5
  61. package/dist/{hook-sn7pj75w.js → hook-xkvn6c5h.js} +4 -4
  62. package/dist/{http-xdbbtmhx.js → http-9xh6ta4r.js} +324 -82
  63. package/dist/{index-9vnkprkh.js → index-25wjsdv2.js} +10 -10
  64. package/dist/{index-5fdsemf0.js → index-eaxyqcmj.js} +155 -37
  65. package/dist/{index-fnack5vh.js → index-vg4snay4.js} +11 -11
  66. package/dist/{index-c0qedjh6.js → index-ywds4812.js} +9 -9
  67. package/dist/{keepalive-fc7a2hw9.js → keepalive-vc7s2aa6.js} +5 -5
  68. package/dist/{lead-1fva34y9.js → lead-r2yqfmx7.js} +29 -29
  69. package/dist/{maintenance-0rdb12vf.js → maintenance-mw70k468.js} +5 -5
  70. package/dist/{mistral-conversations-4swenhmf.js → mistral-conversations-81fwav0j.js} +2 -2
  71. package/dist/{oauth-refresh-sweep-f9rptqbt.js → oauth-refresh-sweep-yecgjpk4.js} +4 -4
  72. package/dist/{onboard-9zn0fx2m.js → onboard-xj0f73f9.js} +2 -2
  73. package/dist/{openai-codex-responses-xetseqvt.js → openai-codex-responses-5g27pd6f.js} +43 -23
  74. package/dist/{openai-completions-5r2g5axa.js → openai-completions-3vkmfz2a.js} +55 -11
  75. package/dist/{openai-responses-ce67hd23.js → openai-responses-dg4t7j7a.js} +8 -5
  76. package/dist/{openrouter-images-pzfp22jb.js → openrouter-images-g3gm8931.js} +1 -1
  77. package/dist/{otel-impl-thnttmbn.js → otel-impl-nxg0n0b3.js} +1 -1
  78. package/dist/{pi-mono-adapter-bpfrn6d7.js → pi-mono-adapter-4g5kby31.js} +17114 -7514
  79. package/dist/{pricing-refresh-n0ve0kh6.js → pricing-refresh-2cn6effr.js} +5 -5
  80. package/dist/{rbac-roles-w45bhahg.js → rbac-roles-1hcxdtg7.js} +4 -4
  81. package/dist/{rbac-roles-bbwb4wrm.js → rbac-roles-vynday5g.js} +5 -5
  82. package/dist/{render-v2-d675cp9r.js → render-v2-tc0bh8kk.js} +4 -4
  83. package/dist/{seed-pricing-47q77nw4.js → seed-pricing-8kceb43f.js} +4 -4
  84. package/dist/{setup-3vnv2m43.js → setup-46xtgq3c.js} +2 -2
  85. package/dist/{worker-7shsr8hk.js → worker-qrbz6hmv.js} +29 -29
  86. package/openapi.json +342 -10
  87. package/package.json +6 -6
  88. package/src/apps/store.ts +13 -4
  89. package/src/assets/key.ts +8 -1
  90. package/src/be/asset-key-audit.ts +3 -1
  91. package/src/be/db-queries/mcp-oauth.ts +26 -0
  92. package/src/be/db.ts +42 -1
  93. package/src/be/migrations/129_asset_keys_apps_scripts.sql +161 -0
  94. package/src/be/migrations/130_scratch_script_retention_grace.sql +6 -0
  95. package/src/be/scripts/db.ts +48 -3
  96. package/src/be/scripts/retention.ts +164 -0
  97. package/src/be/scripts/run-saved.ts +16 -1
  98. package/src/be/seed-skills/index.ts +7 -0
  99. package/src/http/assets.ts +120 -29
  100. package/src/http/index.ts +5 -0
  101. package/src/http/mcp-oauth.ts +69 -1
  102. package/src/http/scripts.ts +21 -3
  103. package/src/http/users.ts +45 -0
  104. package/src/http/x.ts +14 -0
  105. package/src/oauth/ensure-mcp-token.ts +7 -1
  106. package/src/oauth/mcp-wrapper.ts +247 -24
  107. package/src/providers/claude-managed-models.ts +34 -10
  108. package/src/providers/pi-mono-adapter.ts +1 -1
  109. package/src/tests/asset-key-api.test.ts +48 -2
  110. package/src/tests/asset-key-invariants.test.ts +92 -0
  111. package/src/tests/asset-key-migration.test.ts +174 -8
  112. package/src/tests/asset-key-normalization.test.ts +2 -0
  113. package/src/tests/claude-managed-adapter.test.ts +38 -7
  114. package/src/tests/mcp-oauth-dcr-reuse.test.ts +95 -0
  115. package/src/tests/mcp-oauth-ensure-token.test.ts +23 -0
  116. package/src/tests/mcp-oauth-manual-client.test.ts +135 -2
  117. package/src/tests/mcp-oauth-wrapper.test.ts +380 -0
  118. package/src/tests/rbac-admission-e2e.test.ts +68 -0
  119. package/src/tests/sandboxed-process.test.ts +45 -0
  120. package/src/tests/script-connections.test.ts +4 -0
  121. package/src/tests/scripts-external-api.test.ts +8 -3
  122. package/src/tests/scripts-http.test.ts +12 -0
  123. package/src/tests/scripts-retention.test.ts +384 -0
  124. package/src/tests/whoami-route.test.ts +127 -0
  125. package/src/tests/workflow-engine-v2.test.ts +80 -7
  126. package/src/tests/workflow-swarm-script.test.ts +118 -5
  127. package/src/tools/workflows/create-workflow.ts +4 -1
  128. package/src/types.ts +18 -5
  129. package/src/utils/sandboxed-process.ts +40 -14
  130. package/src/utils/template.ts +8 -1
  131. package/src/workflows/engine.ts +27 -4
  132. package/src/workflows/executors/swarm-script.ts +52 -1
  133. package/src/workflows/retry-poller.ts +19 -2
  134. package/templates/schedules/daily-compounding-reflection/content.md +12 -3
  135. package/templates/skills/asset-namespaces/content.md +2 -2
  136. package/templates/skills/scheduled-task-resilience/SKILL.md +102 -0
  137. package/templates/skills/scheduled-task-resilience/config.json +14 -0
  138. package/templates/skills/scheduled-task-resilience/content.md +97 -0
@@ -152,7 +152,7 @@ import {
152
152
  withRemoteContext,
153
153
  withSpanContext,
154
154
  writeSkillsToFilesystem
155
- } from "./cli-haanc4qd.js";
155
+ } from "./cli-jevgrb4w.js";
156
156
  import"./cli-1z7048dy.js";
157
157
  import {
158
158
  Anthropic
@@ -173,34 +173,34 @@ import {
173
173
  checkHeartbeatChecklist,
174
174
  runHeartbeatSweep,
175
175
  stopHeartbeat
176
- } from "./cli-z9q7pb38.js";
176
+ } from "./cli-g74vpjp2.js";
177
177
  import {
178
178
  createResumeFollowUp,
179
179
  createWorkerTaskFollowUp
180
- } from "./cli-tfw3c0y1.js";
180
+ } from "./cli-3ty0t082.js";
181
181
  import {
182
182
  calculateNextRun,
183
183
  dispatchScheduleTarget,
184
184
  ensure,
185
185
  initialize,
186
186
  require_dist
187
- } from "./cli-dakxgk70.js";
187
+ } from "./cli-ycrnynpw.js";
188
188
  import {
189
189
  getModelsCatalog
190
- } from "./cli-qbb2ten8.js";
190
+ } from "./cli-t8d349cy.js";
191
191
  import {
192
192
  normalizeModelKey
193
- } from "./cli-vr2f46zj.js";
193
+ } from "./cli-114fvtmj.js";
194
194
  import {
195
195
  getUserGrant
196
- } from "./cli-tp519bjz.js";
196
+ } from "./cli-sy7jk3tx.js";
197
197
  import {
198
198
  can,
199
199
  clearAuditSink,
200
200
  decideToolAdmission,
201
201
  isRbacEnabled,
202
202
  setAuditSink
203
- } from "./cli-hntzdchd.js";
203
+ } from "./cli-bqw0mt1n.js";
204
204
  import {
205
205
  ensureAgentFsCredentialsForAgent,
206
206
  extractArgsJsonSchema,
@@ -210,11 +210,12 @@ import {
210
210
  scriptSdkTypesWithGeneratedApis,
211
211
  scriptStdlibTypesWithGeneratedApis,
212
212
  typecheckScript
213
- } from "./cli-fs153s3x.js";
213
+ } from "./cli-4rkfg1wx.js";
214
214
  import {
215
215
  collectScriptReferences,
216
+ getApp,
216
217
  listAppRecords
217
- } from "./cli-brebbsen.js";
218
+ } from "./cli-gecy3z2s.js";
218
219
  import {
219
220
  RawLlmConfigSchema,
220
221
  RawLlmOutputSchema,
@@ -234,7 +235,7 @@ import {
234
235
  validateDefinition,
235
236
  validateJsonSchema,
236
237
  verifyHmacSignature
237
- } from "./cli-xdrmxvd4.js";
238
+ } from "./cli-jbcva0xj.js";
238
239
  import"./cli-3dpj7atn.js";
239
240
  import {
240
241
  ConnectionAuthInputSchema,
@@ -242,6 +243,7 @@ import {
242
243
  MIN_SCRIPT_WALL_CLOCK_MS,
243
244
  applyMcpOAuthRefresh,
244
245
  assertUrlSafe,
246
+ authMethodForStoredClient,
245
247
  buildAuthorizeUrl,
246
248
  buildSandboxedCommand,
247
249
  buildScriptCredentialBindingsWithFailures,
@@ -281,6 +283,7 @@ import {
281
283
  listScripts,
282
284
  listVendoredOpenapiEntries,
283
285
  markMcpOAuthTokenStatus,
286
+ normalizeTokenEndpointAuthMethod,
284
287
  oauthAppToProviderConfig,
285
288
  publicEndpointSsrfOptions,
286
289
  readStreamCapped,
@@ -289,25 +292,29 @@ import {
289
292
  refreshScriptConnection,
290
293
  registerClient,
291
294
  require_typescript,
295
+ resolveAdvertisedTokenEndpointAuthMethod,
296
+ restoreScratchScriptLastUsedIfUnchanged,
292
297
  revokeMcpToken,
293
298
  rotateScriptApiSecret,
294
299
  runScript,
295
300
  sandboxSpawnEnv,
301
+ selectDcrTokenEndpointAuthMethod,
296
302
  setMcpServerAuthMethod,
297
303
  setScriptConnectionEnabled,
304
+ touchScratchScriptLastUsed,
298
305
  updateScriptApi,
299
306
  upsertCredentialBinding,
300
307
  upsertMcpOAuthToken,
301
308
  upsertScriptByName,
302
309
  upsertScriptConnection
303
- } from "./cli-q8zwffx0.js";
310
+ } from "./cli-6dp3ydxa.js";
304
311
  import"./cli-epan13p1.js";
305
312
  import {
306
313
  getApiKey
307
314
  } from "./cli-f14fvzag.js";
308
315
  import {
309
316
  searchScripts
310
- } from "./cli-1px8pn1s.js";
317
+ } from "./cli-g1re8r05.js";
311
318
  import {
312
319
  CANDIDATE_SET_MULTIPLIER,
313
320
  getEmbeddingProvider,
@@ -315,7 +322,7 @@ import {
315
322
  init_constants as init_constants2,
316
323
  init_reranker,
317
324
  rerank
318
- } from "./cli-mf009e0j.js";
325
+ } from "./cli-se2p6rkb.js";
319
326
  import"./cli-0258203w.js";
320
327
  import {
321
328
  acquireOAuthRefreshLock,
@@ -340,14 +347,14 @@ import {
340
347
  updateAuthorizationIdentity,
341
348
  updateOAuthAppById,
342
349
  upsertAuthorization
343
- } from "./cli-1mj7yz1r.js";
350
+ } from "./cli-nrkkknat.js";
344
351
  import"./cli-fad8m16k.js";
345
352
  import {
346
353
  FilesError,
347
354
  getFileStorageProvider,
348
355
  normalizeFilesError
349
356
  } from "./cli-bnx54kxv.js";
350
- import"./cli-r62me33w.js";
357
+ import"./cli-6dpytdr2.js";
351
358
  import {
352
359
  findUserById,
353
360
  fingerprintApiKey,
@@ -363,15 +370,15 @@ import {
363
370
  resolveUserByToken,
364
371
  revokeToken,
365
372
  unlinkIdentity
366
- } from "./cli-m8fs5ttq.js";
373
+ } from "./cli-aca5pz9f.js";
367
374
  import {
368
375
  createTaskWithSiblingAwareness
369
- } from "./cli-mhr8tq3v.js";
376
+ } from "./cli-2f07m0xr.js";
370
377
  import {
371
378
  getSlackApp,
372
379
  startSlackApp,
373
380
  stopSlackApp
374
- } from "./cli-qcsjdppx.js";
381
+ } from "./cli-rgrfzg8z.js";
375
382
  import"./cli-k9qkf1hw.js";
376
383
  import"./cli-b0p7rfnd.js";
377
384
  import"./cli-t930g0xh.js";
@@ -749,7 +756,7 @@ import {
749
756
  upsertSwarmConfig,
750
757
  validateConfigValue,
751
758
  withFavoriteFlags
752
- } from "./cli-s18zzkfp.js";
759
+ } from "./cli-50vbt00w.js";
753
760
  import {
754
761
  getAllTemplateDefinitions,
755
762
  getTemplateDefinition,
@@ -762,7 +769,7 @@ import {
762
769
  isEnvFlagEnabled,
763
770
  isSteeringEnabled,
764
771
  resolveTemplate
765
- } from "./cli-4n4mdv21.js";
772
+ } from "./cli-xpxde7rm.js";
766
773
  import {
767
774
  MAX_PROFILE_FILE_LENGTH,
768
775
  getAppUrl,
@@ -784,7 +791,7 @@ import {
784
791
  import {
785
792
  init_package,
786
793
  package_default
787
- } from "./cli-7v5dhmc8.js";
794
+ } from "./cli-ccf9vy7q.js";
788
795
  import {
789
796
  array,
790
797
  exports_external,
@@ -799,20 +806,20 @@ import {
799
806
  } from "./cli-9aryf9tv.js";
800
807
  import {
801
808
  bedrockProviderModule
802
- } from "./cli-t5tsq67k.js";
809
+ } from "./cli-9dr5qzc0.js";
803
810
  import"./cli-cwy5hqbq.js";
804
811
  import {
805
812
  setBedrockProviderModule
806
- } from "./cli-6nh2qxvh.js";
813
+ } from "./cli-darcwax3.js";
807
814
  import"./cli-jvv0g8bh.js";
808
815
  import"./cli-r6kqqbj0.js";
809
- import"./cli-ch7t5v7n.js";
816
+ import"./cli-6map995v.js";
810
817
  import"./cli-4wgngmwv.js";
811
818
  import"./cli-vapyk8sw.js";
812
819
  import"./cli-zyxs93y9.js";
813
- import"./cli-vgexpfsr.js";
820
+ import"./cli-57khth04.js";
814
821
  import"./cli-y2apkfe2.js";
815
- import"./cli-w10fkrf1.js";
822
+ import"./cli-akdvwc9v.js";
816
823
  import"./cli-8wrympd7.js";
817
824
  import"./cli-7sgrxg9a.js";
818
825
  import"./cli-3fj60etf.js";
@@ -836,6 +843,107 @@ import {
836
843
  } from "node:http";
837
844
  init_db();
838
845
 
846
+ // src/be/scripts/retention.ts
847
+ init_db();
848
+ var SCRATCH_RETENTION_DAYS = 14;
849
+ var SCRATCH_GC_INTERVAL_MS = 24 * 60 * 60 * 1000;
850
+ var scratchGcTimer = null;
851
+ function appReferencedScriptIds(candidateIds) {
852
+ const ids = new Set;
853
+ for (const app of listAppRecords()) {
854
+ for (const scriptId of collectScriptReferences(app.definition).keys()) {
855
+ ids.add(scriptId);
856
+ }
857
+ if (app.definitionError === undefined)
858
+ continue;
859
+ const serialized = JSON.stringify(app.definition ?? null);
860
+ for (const id of candidateIds) {
861
+ if (!ids.has(id) && serialized.includes(id))
862
+ ids.add(id);
863
+ }
864
+ }
865
+ return ids;
866
+ }
867
+ function scriptApiReferencedScriptIds() {
868
+ const rows = getDb().prepare("SELECT DISTINCT scriptId FROM script_apis").all();
869
+ return new Set(rows.map((row) => row.scriptId));
870
+ }
871
+ function workflowReferencedAgentScriptKeys() {
872
+ const keys = new Set;
873
+ for (const workflow of listWorkflows()) {
874
+ if (!workflow.createdByAgentId)
875
+ continue;
876
+ for (const node of workflow.definition.nodes) {
877
+ if (node.type !== "swarm-script")
878
+ continue;
879
+ const scriptName = node.config.scriptName;
880
+ const scope = node.config.scope;
881
+ if (typeof scriptName !== "string" || scope === "global")
882
+ continue;
883
+ keys.add(`${scriptName}::${workflow.createdByAgentId}`);
884
+ }
885
+ }
886
+ return keys;
887
+ }
888
+ function workflowReferencedScratchNamesForOwnerlessWorkflows() {
889
+ const names = new Set;
890
+ for (const workflow of listWorkflows()) {
891
+ if (workflow.createdByAgentId)
892
+ continue;
893
+ for (const node of workflow.definition.nodes) {
894
+ if (node.type !== "swarm-script")
895
+ continue;
896
+ const scriptName = node.config.scriptName;
897
+ const scope = node.config.scope;
898
+ if (typeof scriptName !== "string" || scope === "global")
899
+ continue;
900
+ names.add(scriptName);
901
+ }
902
+ }
903
+ return names;
904
+ }
905
+ function purgeExpiredScratchScripts(now = new Date) {
906
+ const cutoff = new Date(now.getTime() - SCRATCH_RETENTION_DAYS * 24 * 60 * 60 * 1000).toISOString();
907
+ const candidates = getDb().prepare(`SELECT id, name, scopeId FROM scripts
908
+ WHERE scope = 'agent'
909
+ AND isScratch = 1
910
+ AND name GLOB 'scratch-*'
911
+ AND updatedAt < ?`).all(cutoff);
912
+ if (candidates.length === 0)
913
+ return 0;
914
+ const referenced = appReferencedScriptIds(candidates.map((row) => row.id));
915
+ const apiReferenced = scriptApiReferencedScriptIds();
916
+ const workflowReferenced = workflowReferencedAgentScriptKeys();
917
+ const ownerlessWorkflowReferencedNames = workflowReferencedScratchNamesForOwnerlessWorkflows();
918
+ const idsToDelete = candidates.filter((row) => !referenced.has(row.id)).filter((row) => !apiReferenced.has(row.id)).filter((row) => !(row.scopeId && workflowReferenced.has(`${row.name}::${row.scopeId}`))).filter((row) => !ownerlessWorkflowReferencedNames.has(row.name)).map((row) => row.id);
919
+ if (idsToDelete.length === 0)
920
+ return 0;
921
+ const placeholders = idsToDelete.map(() => "?").join(",");
922
+ return getDb().prepare(`DELETE FROM scripts WHERE id IN (${placeholders}) RETURNING id`).all(...idsToDelete).length;
923
+ }
924
+ function runScratchScriptGc(label) {
925
+ try {
926
+ const purged = purgeExpiredScratchScripts();
927
+ console.log(`[scratch-script-gc] ${label} purge removed ${purged} scratch script row(s)`);
928
+ } catch (err) {
929
+ console.error(`[scratch-script-gc] ${label} purge failed:`, err.message);
930
+ }
931
+ }
932
+ function startScratchScriptGc(intervalMs = SCRATCH_GC_INTERVAL_MS) {
933
+ if (scratchGcTimer)
934
+ return;
935
+ runScratchScriptGc("Initial");
936
+ scratchGcTimer = setInterval(() => runScratchScriptGc("Periodic"), intervalMs);
937
+ if (typeof scratchGcTimer.unref === "function")
938
+ scratchGcTimer.unref();
939
+ }
940
+ function stopScratchScriptGc() {
941
+ if (!scratchGcTimer)
942
+ return;
943
+ clearInterval(scratchGcTimer);
944
+ scratchGcTimer = null;
945
+ }
946
+
839
947
  // src/be/seed-capabilities.ts
840
948
  init_db();
841
949
  var LEGACY_ALWAYS_ON_DEFAULTS = [
@@ -2619,7 +2727,7 @@ var listAssetsRoute = route({
2619
2727
  tags: ["Assets"],
2620
2728
  query: exports_external.object({
2621
2729
  keyPrefix: AssetKeySchema.optional(),
2622
- types: exports_external.string().optional().describe("Comma-separated task,workflow,schedule,page,file list"),
2730
+ types: exports_external.string().optional().describe("Comma-separated task,workflow,schedule,page,app,script,file list"),
2623
2731
  limit: exports_external.coerce.number().int().min(1).max(1000).optional()
2624
2732
  }),
2625
2733
  responses: {
@@ -2659,6 +2767,41 @@ function ensureOperator(req, res) {
2659
2767
  jsonError(res, "Operator access required", 403);
2660
2768
  return false;
2661
2769
  }
2770
+ var moveAssetBodySchema = exports_external.object({ key: AssetKeySchema });
2771
+ var moveAssetSuccessSchema = exports_external.object({
2772
+ entityType: AssetEntityTypeSchema,
2773
+ id: exports_external.string(),
2774
+ key: AssetKeySchema
2775
+ });
2776
+ var moveAssetResponses = {
2777
+ 200: { description: "Asset namespace updated", schema: moveAssetSuccessSchema },
2778
+ 400: { description: "Invalid namespace" },
2779
+ 403: { description: "Move not authorized" },
2780
+ 404: { description: "Asset not found" },
2781
+ 409: { description: "Moves blocked until audit warnings are repaired" }
2782
+ };
2783
+ var moveAppAssetRoute = route({
2784
+ method: "patch",
2785
+ path: "/api/assets/app/{id}/key",
2786
+ pattern: ["api", "assets", "app", null, "key"],
2787
+ summary: "Move an app to another logical namespace",
2788
+ tags: ["Assets"],
2789
+ params: exports_external.object({ id: exports_external.string().min(1) }),
2790
+ body: moveAssetBodySchema,
2791
+ responses: moveAssetResponses,
2792
+ rbac: { permission: "app.manage" }
2793
+ });
2794
+ var moveScriptAssetRoute = route({
2795
+ method: "patch",
2796
+ path: "/api/assets/script/{id}/key",
2797
+ pattern: ["api", "assets", "script", null, "key"],
2798
+ summary: "Move a script to another logical namespace",
2799
+ tags: ["Assets"],
2800
+ params: exports_external.object({ id: exports_external.string().min(1) }),
2801
+ body: moveAssetBodySchema,
2802
+ responses: moveAssetResponses,
2803
+ rbac: { permission: "script.global.write" }
2804
+ });
2662
2805
  var moveAssetRoute = route({
2663
2806
  method: "patch",
2664
2807
  path: "/api/assets/{entityType}/{id}/key",
@@ -2667,25 +2810,23 @@ var moveAssetRoute = route({
2667
2810
  description: "Updates namespace metadata only. Provider-backed files keep the same provider key, org, and drive; no remote move occurs. Personal keys are labels, not a privacy guarantee.",
2668
2811
  tags: ["Assets"],
2669
2812
  params: exports_external.object({ entityType: AssetEntityTypeSchema, id: exports_external.string().min(1) }),
2670
- body: exports_external.object({ key: AssetKeySchema }),
2671
- responses: {
2672
- 200: {
2673
- description: "Asset namespace updated",
2674
- schema: exports_external.object({
2675
- entityType: AssetEntityTypeSchema,
2676
- id: exports_external.string(),
2677
- key: AssetKeySchema
2678
- })
2679
- },
2680
- 400: { description: "Invalid namespace" },
2681
- 403: { description: "Move not authorized" },
2682
- 404: { description: "Asset not found" },
2683
- 409: { description: "Moves blocked until audit warnings are repaired" }
2684
- },
2813
+ body: moveAssetBodySchema,
2814
+ responses: moveAssetResponses,
2685
2815
  rbac: {
2686
- ungated: "preserves each entity's current mutation posture; task moves additionally use task.fs.mutate, file moves require operator authentication, and personal destinations require a matching trusted user"
2816
+ ungated: "preserves each entity's current mutation posture; task moves use task.fs.mutate, app moves use app.manage, scripts preserve agent ownership/global-lead rules, file moves require operator authentication, and personal destinations require a matching trusted user"
2687
2817
  }
2688
2818
  });
2819
+ function assetMovePrincipal(req, myAgentId) {
2820
+ const auth = getRequestAuth(req);
2821
+ if (auth?.kind === "operator")
2822
+ return { kind: "operator" };
2823
+ if (auth?.kind === "user")
2824
+ return { kind: "user", userId: auth.userId };
2825
+ if (!myAgentId)
2826
+ return null;
2827
+ const agent = getAgentById(myAgentId);
2828
+ return { kind: "agent", agentId: myAgentId, isLead: agent?.isLead ?? false };
2829
+ }
2689
2830
  function canMutateTaskNamespace(task, myAgentId, req) {
2690
2831
  const resource = {
2691
2832
  kind: "task",
@@ -2693,20 +2834,32 @@ function canMutateTaskNamespace(task, myAgentId, req) {
2693
2834
  agentId: task.agentId,
2694
2835
  creatorAgentId: task.creatorAgentId
2695
2836
  };
2696
- const auth = getRequestAuth(req);
2697
- let principal;
2698
- if (auth?.kind === "operator") {
2699
- principal = { kind: "operator" };
2700
- } else if (auth?.kind === "user") {
2701
- principal = { kind: "user", userId: auth.userId };
2702
- } else {
2703
- if (!myAgentId)
2704
- return false;
2705
- const agent = getAgentById(myAgentId);
2706
- principal = { kind: "agent", agentId: myAgentId, isLead: agent?.isLead ?? false };
2707
- }
2837
+ const principal = assetMovePrincipal(req, myAgentId);
2838
+ if (!principal)
2839
+ return false;
2708
2840
  return can({ principal, verb: "task.fs.mutate", resource, source: "http" }).allow;
2709
2841
  }
2842
+ function canManageAppNamespace(id, req, myAgentId) {
2843
+ const principal = assetMovePrincipal(req, myAgentId);
2844
+ return !!principal && can({ principal, verb: "app.manage", resource: { kind: "app", appId: id }, source: "http" }).allow;
2845
+ }
2846
+ function canManageScriptNamespace(script, req, myAgentId) {
2847
+ const principal = assetMovePrincipal(req, myAgentId);
2848
+ if (!principal)
2849
+ return false;
2850
+ if (principal.kind === "operator")
2851
+ return true;
2852
+ if (principal.kind !== "agent")
2853
+ return false;
2854
+ if (script.scope === "agent")
2855
+ return script.scopeId === principal.agentId;
2856
+ return can({
2857
+ principal,
2858
+ verb: "script.global.write",
2859
+ resource: { kind: "owned", scope: "global" },
2860
+ source: "http"
2861
+ }).allow;
2862
+ }
2710
2863
  async function handleAssets(req, res, pathSegments, queryParams, myAgentId) {
2711
2864
  if (keyAuditRoute.match(req.method, pathSegments)) {
2712
2865
  const parsed = await keyAuditRoute.parse(req, res, pathSegments, queryParams);
@@ -2768,7 +2921,7 @@ async function handleAssets(req, res, pathSegments, queryParams, myAgentId) {
2768
2921
  }
2769
2922
  return true;
2770
2923
  }
2771
- if (moveAssetRoute.match(req.method, pathSegments)) {
2924
+ if (moveAppAssetRoute.match(req.method, pathSegments) || moveScriptAssetRoute.match(req.method, pathSegments) || moveAssetRoute.match(req.method, pathSegments)) {
2772
2925
  const parsed = await moveAssetRoute.parse(req, res, pathSegments, queryParams);
2773
2926
  if (!parsed)
2774
2927
  return true;
@@ -2785,6 +2938,27 @@ async function handleAssets(req, res, pathSegments, queryParams, myAgentId) {
2785
2938
  return true;
2786
2939
  }
2787
2940
  }
2941
+ if (parsed.params.entityType === "app") {
2942
+ if (!getApp(parsed.params.id)) {
2943
+ jsonError(res, "Asset not found", 404);
2944
+ return true;
2945
+ }
2946
+ if (!canManageAppNamespace(parsed.params.id, req, myAgentId)) {
2947
+ jsonError(res, "Not authorized to move this app namespace", 403);
2948
+ return true;
2949
+ }
2950
+ }
2951
+ if (parsed.params.entityType === "script") {
2952
+ const script = getScriptById(parsed.params.id);
2953
+ if (!script) {
2954
+ jsonError(res, "Asset not found", 404);
2955
+ return true;
2956
+ }
2957
+ if (!canManageScriptNamespace(script, req, myAgentId)) {
2958
+ jsonError(res, "Not authorized to move this script namespace", 403);
2959
+ return true;
2960
+ }
2961
+ }
2788
2962
  try {
2789
2963
  const actor = resolveHttpAuditUserId(req, myAgentId);
2790
2964
  const key = authorizeAssetKeyWrite(parsed.body.key, actor);
@@ -6589,7 +6763,8 @@ var DiscoveryResultSchema = exports_external.object({
6589
6763
  scopes: exports_external.array(exports_external.string()),
6590
6764
  requiresOAuth: exports_external.literal(true),
6591
6765
  dcrSupported: exports_external.boolean(),
6592
- bearerMethodsSupported: exports_external.array(exports_external.string()).nullable()
6766
+ bearerMethodsSupported: exports_external.array(exports_external.string()).nullable(),
6767
+ tokenEndpointAuthMethodsSupported: exports_external.array(exports_external.string()).nullable()
6593
6768
  });
6594
6769
  var MetadataResponseSchema = exports_external.union([
6595
6770
  exports_external.object({ requiresOAuth: exports_external.literal(false) }),
@@ -6645,7 +6820,8 @@ function manualClientFromToken(token) {
6645
6820
  authorizeUrl: token.authorizeUrl,
6646
6821
  tokenUrl: token.tokenUrl,
6647
6822
  revocationUrl: token.revocationUrl,
6648
- scopes: splitScopes(token.scope)
6823
+ scopes: splitScopes(token.scope),
6824
+ tokenEndpointAuthMethod: authMethodForStoredClient(token.tokenEndpointAuthMethod)
6649
6825
  };
6650
6826
  }
6651
6827
  async function discoverForMcp(resourceUrl) {
@@ -6667,7 +6843,8 @@ async function discoverForMcp(resourceUrl) {
6667
6843
  scopes: prmd.scopes_supported ?? as.scopes_supported ?? [],
6668
6844
  requiresOAuth: true,
6669
6845
  dcrSupported: !!as.registration_endpoint,
6670
- bearerMethodsSupported: prmd.bearer_methods_supported ?? null
6846
+ bearerMethodsSupported: prmd.bearer_methods_supported ?? null,
6847
+ tokenEndpointAuthMethodsSupported: as.token_endpoint_auth_methods_supported ?? null
6671
6848
  };
6672
6849
  }
6673
6850
  function getMcpOrError(res, mcpServerId) {
@@ -6824,7 +7001,8 @@ var manualClientRoute = route({
6824
7001
  authorizeUrl: exports_external.string().url().optional(),
6825
7002
  tokenUrl: exports_external.string().url().optional(),
6826
7003
  revocationUrl: exports_external.string().url().optional(),
6827
- scopes: exports_external.array(exports_external.string()).optional()
7004
+ scopes: exports_external.array(exports_external.string()).optional(),
7005
+ tokenEndpointAuthMethod: exports_external.enum(["client_secret_basic", "client_secret_post", "none"]).optional()
6828
7006
  }),
6829
7007
  responses: {
6830
7008
  200: {
@@ -6886,7 +7064,8 @@ async function runAuthorizeFlow(res, mcpServerId, server, q, userId) {
6886
7064
  authorizeUrl: discovery.authorizeUrl,
6887
7065
  tokenUrl: discovery.tokenUrl,
6888
7066
  revocationUrl: discovery.revocationUrl,
6889
- scopes: reusable.scopes.length > 0 ? reusable.scopes : discovery.scopes
7067
+ scopes: reusable.scopes.length > 0 ? reusable.scopes : discovery.scopes,
7068
+ tokenEndpointAuthMethod: authMethodForStoredClient(reusable.tokenEndpointAuthMethod)
6890
7069
  };
6891
7070
  }
6892
7071
  }
@@ -6900,15 +7079,17 @@ async function runAuthorizeFlow(res, mcpServerId, server, q, userId) {
6900
7079
  return null;
6901
7080
  }
6902
7081
  const scopes2 = q.scopes ? splitScopes(q.scopes) : discovery.scopes;
7082
+ const requestedAuthMethod = selectDcrTokenEndpointAuthMethod(discovery.tokenEndpointAuthMethodsSupported ?? undefined);
6903
7083
  const dcr = await registerClient(discovery.registrationEndpoint, {
6904
7084
  client_name: `agent-swarm (${server.name})`,
6905
7085
  redirect_uris: [callbackRedirectUri()],
6906
7086
  grant_types: ["authorization_code", "refresh_token"],
6907
7087
  response_types: ["code"],
6908
- token_endpoint_auth_method: "client_secret_basic",
7088
+ token_endpoint_auth_method: requestedAuthMethod,
6909
7089
  application_type: "web",
6910
7090
  scope: scopes2.join(" ") || undefined
6911
7091
  });
7092
+ const registeredAuthMethod = dcr.token_endpoint_auth_method ? resolveAdvertisedTokenEndpointAuthMethod(dcr.token_endpoint_auth_method, "returned") : requestedAuthMethod;
6912
7093
  registeredScopes = scopes2;
6913
7094
  client = {
6914
7095
  clientId: dcr.client_id,
@@ -6918,7 +7099,8 @@ async function runAuthorizeFlow(res, mcpServerId, server, q, userId) {
6918
7099
  authorizeUrl: discovery.authorizeUrl,
6919
7100
  tokenUrl: discovery.tokenUrl,
6920
7101
  revocationUrl: discovery.revocationUrl,
6921
- scopes: scopes2
7102
+ scopes: scopes2,
7103
+ tokenEndpointAuthMethod: registeredAuthMethod
6922
7104
  };
6923
7105
  }
6924
7106
  const scopes = q.scopes ? splitScopes(q.scopes) : client.scopes;
@@ -6957,6 +7139,7 @@ async function runAuthorizeFlow(res, mcpServerId, server, q, userId) {
6957
7139
  registeredScopes,
6958
7140
  dcrClientId: client.clientId,
6959
7141
  dcrClientSecret: client.clientSecret,
7142
+ tokenEndpointAuthMethod: client.tokenEndpointAuthMethod,
6960
7143
  redirectUri: callbackRedirectUri(),
6961
7144
  finalRedirect: q.redirect ?? null
6962
7145
  });
@@ -6996,6 +7179,7 @@ async function completeMcpOAuthCallback(res, query) {
6996
7179
  tokenUrl: pending.tokenUrl,
6997
7180
  clientId: pending.dcrClientId ?? "",
6998
7181
  clientSecret: pending.dcrClientSecret ?? undefined,
7182
+ tokenEndpointAuthMethod: authMethodForStoredClient(pending.tokenEndpointAuthMethod),
6999
7183
  redirectUri: pending.redirectUri,
7000
7184
  code: query.code,
7001
7185
  codeVerifier: pending.codeVerifier,
@@ -7021,6 +7205,7 @@ async function completeMcpOAuthCallback(res, query) {
7021
7205
  registeredScopes: pending.registeredScopes ?? undefined,
7022
7206
  dcrClientId: pending.dcrClientId,
7023
7207
  dcrClientSecret: pending.dcrClientSecret,
7208
+ tokenEndpointAuthMethod: authMethodForStoredClient(pending.tokenEndpointAuthMethod),
7024
7209
  clientSource,
7025
7210
  lastRefreshedAt: new Date().toISOString()
7026
7211
  });
@@ -7166,6 +7351,7 @@ async function handleMcpOAuth(req, res, pathSegments, queryParams) {
7166
7351
  tokenUrl: existing.tokenUrl,
7167
7352
  clientId: existing.dcrClientId ?? "",
7168
7353
  clientSecret: existing.dcrClientSecret ?? undefined,
7354
+ tokenEndpointAuthMethod: authMethodForStoredClient(existing.tokenEndpointAuthMethod),
7169
7355
  refreshToken: existing.refreshToken,
7170
7356
  resource: existing.resourceUrl
7171
7357
  });
@@ -7208,7 +7394,8 @@ async function handleMcpOAuth(req, res, pathSegments, queryParams) {
7208
7394
  token: token.accessToken,
7209
7395
  tokenTypeHint: "access_token",
7210
7396
  clientId: token.dcrClientId ?? "",
7211
- clientSecret: token.dcrClientSecret ?? undefined
7397
+ clientSecret: token.dcrClientSecret ?? undefined,
7398
+ tokenEndpointAuthMethod: authMethodForStoredClient(token.tokenEndpointAuthMethod)
7212
7399
  });
7213
7400
  } catch (err) {
7214
7401
  console.warn("[mcp-oauth] revocation call failed (continuing with local delete):", err instanceof Error ? err.message : err);
@@ -7234,6 +7421,8 @@ async function handleMcpOAuth(req, res, pathSegments, queryParams) {
7234
7421
  let authorizationServerIssuer = overrides.authorizationServerIssuer ?? null;
7235
7422
  let resourceUrl = server.url;
7236
7423
  let scopes = overrides.scopes ?? [];
7424
+ let tokenEndpointAuthMethod = overrides.tokenEndpointAuthMethod ? normalizeTokenEndpointAuthMethod(overrides.tokenEndpointAuthMethod) : undefined;
7425
+ let advertisedAuthMethods = null;
7237
7426
  if (!authorizeUrl || !tokenUrl) {
7238
7427
  const discovery = await discoverForMcp(server.url);
7239
7428
  if (!discovery) {
@@ -7247,6 +7436,11 @@ async function handleMcpOAuth(req, res, pathSegments, queryParams) {
7247
7436
  resourceUrl = discovery.resourceUrl;
7248
7437
  if (scopes.length === 0)
7249
7438
  scopes = discovery.scopes;
7439
+ advertisedAuthMethods = discovery.tokenEndpointAuthMethodsSupported;
7440
+ }
7441
+ if (!tokenEndpointAuthMethod) {
7442
+ const postUnsupported = advertisedAuthMethods !== null && advertisedAuthMethods.length > 0 && !advertisedAuthMethods.includes("client_secret_post");
7443
+ tokenEndpointAuthMethod = postUnsupported ? selectDcrTokenEndpointAuthMethod(advertisedAuthMethods ?? undefined) : authMethodForStoredClient(undefined);
7250
7444
  }
7251
7445
  if (!authorizationServerIssuer) {
7252
7446
  jsonError(res, "authorizationServerIssuer is required when endpoints are provided manually.", 400);
@@ -7265,6 +7459,7 @@ async function handleMcpOAuth(req, res, pathSegments, queryParams) {
7265
7459
  revocationUrl,
7266
7460
  dcrClientId: overrides.clientId,
7267
7461
  dcrClientSecret: overrides.clientSecret ?? null,
7462
+ tokenEndpointAuthMethod,
7268
7463
  clientSource: "manual",
7269
7464
  status: "error",
7270
7465
  lastErrorMessage: "Manual client pre-registered; awaiting authorize flow."
@@ -16464,12 +16659,14 @@ async function handleScripts(req, res, pathSegments, queryParams, agentId) {
16464
16659
  return true;
16465
16660
  let source = parsed.body.source;
16466
16661
  let fsMode = parsed.body.fsMode;
16662
+ let namedScript = null;
16467
16663
  if (parsed.body.name) {
16468
16664
  const script = resolveScript(parsed.body.name, agent.id, parsed.body.scope);
16469
16665
  if (!script) {
16470
16666
  jsonError(res, "Script not found", 404);
16471
16667
  return true;
16472
16668
  }
16669
+ namedScript = script;
16473
16670
  source = script.source;
16474
16671
  fsMode = script.fsMode;
16475
16672
  }
@@ -16478,6 +16675,7 @@ async function handleScripts(req, res, pathSegments, queryParams, agentId) {
16478
16675
  return true;
16479
16676
  }
16480
16677
  const startedAt = new Date().toISOString();
16678
+ const runStartTouch = namedScript?.isScratch ? touchScratchScriptLastUsed(namedScript.id) : null;
16481
16679
  const credentials = await buildScriptCredentialBindingsWithFailures({ agentId: agent.id });
16482
16680
  const output = await runScript({
16483
16681
  source,
@@ -16489,8 +16687,14 @@ async function handleScripts(req, res, pathSegments, queryParams, agentId) {
16489
16687
  apiConnections: getScriptApiConnectionDescriptors({ agentId: agent.id }),
16490
16688
  mcpConnections: getScriptMcpConnectionDescriptors({ agentId: agent.id })
16491
16689
  });
16690
+ const ok = output.exitCode === 0 && !output.error && !output.runtimeError;
16691
+ if (namedScript?.isScratch && ok) {
16692
+ touchScratchScriptLastUsed(namedScript.id);
16693
+ } else if (namedScript?.isScratch && runStartTouch) {
16694
+ restoreScratchScriptLastUsedIfUnchanged(namedScript.id, namedScript.updatedAt, runStartTouch);
16695
+ }
16492
16696
  let kvSaved;
16493
- if (parsed.body.idempotencyKey && !output.error && output.exitCode === 0) {
16697
+ if (parsed.body.idempotencyKey && ok) {
16494
16698
  const kvNamespace = `script:executions`;
16495
16699
  const kvKey = parsed.body.idempotencyKey;
16496
16700
  const kvValue = {
@@ -16509,7 +16713,7 @@ async function handleScripts(req, res, pathSegments, queryParams, agentId) {
16509
16713
  kvSaved = { namespace: kvNamespace, key: kvKey };
16510
16714
  }
16511
16715
  let autoSaved;
16512
- if (parsed.body.source && !output.error && output.exitCode === 0) {
16716
+ if (parsed.body.source && ok) {
16513
16717
  const slug = scratchSlug(parsed.body.intent, parsed.body.source);
16514
16718
  await upsertScriptByName({
16515
16719
  name: slug,
@@ -16527,7 +16731,6 @@ async function handleScripts(req, res, pathSegments, queryParams, agentId) {
16527
16731
  });
16528
16732
  autoSaved = { slug, reason: "successful_inline_run" };
16529
16733
  }
16530
- const ok = output.exitCode === 0 && !output.error && !output.runtimeError;
16531
16734
  const runError = ok ? undefined : scrubSecrets([
16532
16735
  output.error,
16533
16736
  output.runtimeError ? `${output.runtimeError.name}: ${output.runtimeError.message}` : undefined
@@ -20766,6 +20969,7 @@ async function handleTrackers(req, res, pathSegments) {
20766
20969
  init_zod();
20767
20970
  init_db();
20768
20971
  init_types();
20972
+ init_request_auth_context();
20769
20973
 
20770
20974
  // src/http/operator-actor.ts
20771
20975
  init_request_auth_context();
@@ -20852,6 +21056,24 @@ var UnmappedIdentitySchema = exports_external.object({
20852
21056
  sampleEventType: exports_external.string().nullable(),
20853
21057
  sampleContext: exports_external.unknown().nullable()
20854
21058
  });
21059
+ var whoamiRoute = route({
21060
+ method: "get",
21061
+ path: "/api/whoami",
21062
+ pattern: ["api", "whoami"],
21063
+ summary: "Resolve the authenticated principal behind the presented bearer",
21064
+ tags: ["Users"],
21065
+ responses: {
21066
+ 200: {
21067
+ description: "Authenticated principal",
21068
+ schema: exports_external.object({
21069
+ kind: exports_external.enum(["operator", "user"]),
21070
+ user: exports_external.union([UserSchema, exports_external.null()])
21071
+ })
21072
+ },
21073
+ 401: { description: "Unauthorized" }
21074
+ },
21075
+ auth: { apiKey: true }
21076
+ });
20855
21077
  var listUsers = route({
20856
21078
  method: "get",
20857
21079
  path: "/api/users",
@@ -21165,6 +21387,18 @@ function collectUnmappedForKind(kind, limit) {
21165
21387
  return Array.from(byId.values());
21166
21388
  }
21167
21389
  async function handleUsers(req, res, pathSegments, queryParams) {
21390
+ if (whoamiRoute.match(req.method, pathSegments)) {
21391
+ const parsed = await whoamiRoute.parse(req, res, pathSegments, queryParams);
21392
+ if (!parsed)
21393
+ return true;
21394
+ const auth = getRequestAuth(req);
21395
+ if (auth?.kind === "user") {
21396
+ whoamiRoute.respond(res, 200, { kind: "user", user: auth.user });
21397
+ } else {
21398
+ whoamiRoute.respond(res, 200, { kind: "operator", user: null });
21399
+ }
21400
+ return true;
21401
+ }
21168
21402
  if (listUsers.match(req.method, pathSegments)) {
21169
21403
  const parsed = await listUsers.parse(req, res, pathSegments, queryParams);
21170
21404
  if (!parsed)
@@ -23077,6 +23311,7 @@ async function handleX(req, res, pathSegments) {
23077
23311
  }
23078
23312
  const timeoutMs = resolveTimeoutMs(req);
23079
23313
  const startedAt = new Date().toISOString();
23314
+ const runStartTouch = script.isScratch ? touchScratchScriptLastUsed(script.id) : null;
23080
23315
  const credentials = await buildScriptCredentialBindingsWithFailures({
23081
23316
  agentId: endpoint.agentId
23082
23317
  });
@@ -23093,6 +23328,11 @@ async function handleX(req, res, pathSegments) {
23093
23328
  });
23094
23329
  const ok = output.exitCode === 0 && !output.error && !output.runtimeError;
23095
23330
  const error = ok ? null : buildExecutionError(output);
23331
+ if (ok) {
23332
+ touchScratchScriptLastUsed(script.id);
23333
+ } else if (script.isScratch && runStartTouch) {
23334
+ restoreScratchScriptLastUsedIfUnchanged(script.id, script.updatedAt, runStartTouch);
23335
+ }
23096
23336
  try {
23097
23337
  recordScriptApiUsage(endpointId);
23098
23338
  } catch {}
@@ -23345,18 +23585,19 @@ async function shutdown() {
23345
23585
  sessionsProcessed: getServerSessionsProcessed()
23346
23586
  });
23347
23587
  if (hasCapability("scheduling")) {
23348
- const { stopScheduler } = await import("./index-9vnkprkh.js");
23588
+ const { stopScheduler } = await import("./index-25wjsdv2.js");
23349
23589
  stopScheduler();
23350
23590
  }
23351
23591
  stopHeartbeat();
23352
23592
  stopScriptRunSupervisor();
23353
23593
  await stopSlackApp();
23354
23594
  if (process.env.OAUTH_KEEPALIVE_DISABLE !== "true") {
23355
- const { stopOAuthKeepalive } = await import("./keepalive-fc7a2hw9.js");
23595
+ const { stopOAuthKeepalive } = await import("./keepalive-vc7s2aa6.js");
23356
23596
  await stopOAuthKeepalive();
23357
23597
  }
23358
23598
  stopOAuthPendingGc();
23359
23599
  stopMemoryGc();
23600
+ stopScratchScriptGc();
23360
23601
  stopAuditGc();
23361
23602
  stopAuditWriter();
23362
23603
  flushAuditBuffer();
@@ -23414,15 +23655,15 @@ try {
23414
23655
  console.warn("[startup] CAPABILITIES upgrade seed failed (non-fatal):", err);
23415
23656
  }
23416
23657
  try {
23417
- const { seedPricingFromModelsDev } = await import("./seed-pricing-47q77nw4.js");
23658
+ const { seedPricingFromModelsDev } = await import("./seed-pricing-8kceb43f.js");
23418
23659
  seedPricingFromModelsDev();
23419
- const { startPricingRefreshLoop } = await import("./pricing-refresh-n0ve0kh6.js");
23660
+ const { startPricingRefreshLoop } = await import("./pricing-refresh-2cn6effr.js");
23420
23661
  startPricingRefreshLoop();
23421
23662
  } catch (err) {
23422
23663
  console.error("[startup] Failed to seed pricing rows:", err);
23423
23664
  }
23424
23665
  try {
23425
- const { ensureRbacSeedsSynced } = await import("./rbac-roles-bbwb4wrm.js");
23666
+ const { ensureRbacSeedsSynced } = await import("./rbac-roles-vynday5g.js");
23426
23667
  ensureRbacSeedsSynced();
23427
23668
  } catch (err) {
23428
23669
  console.error("[startup] Failed to sync RBAC seed rows:", err);
@@ -23430,7 +23671,7 @@ try {
23430
23671
  throw err;
23431
23672
  }
23432
23673
  try {
23433
- const { runAllSeeders } = await import("./index-5fdsemf0.js");
23674
+ const { runAllSeeders } = await import("./index-eaxyqcmj.js");
23434
23675
  await runAllSeeders({ scriptEmbeddingMode: "skip" });
23435
23676
  } catch (err) {
23436
23677
  console.error("[startup] Failed to seed built-in entities:", err);
@@ -23438,6 +23679,7 @@ try {
23438
23679
  setAuditSink(enqueueAuditRow);
23439
23680
  startAuditWriter();
23440
23681
  startAuditGc();
23682
+ startScratchScriptGc();
23441
23683
  initialize();
23442
23684
  await initOtel("api");
23443
23685
  httpServer.listen(port, async () => {
@@ -23466,35 +23708,35 @@ httpServer.listen(port, async () => {
23466
23708
  initWorkflows();
23467
23709
  startScriptRunSupervisor(getMcpBaseUrl());
23468
23710
  if (hasCapability("scheduling")) {
23469
- const { startScheduler } = await import("./index-9vnkprkh.js");
23470
- const { getExecutorRegistry: getExecutorRegistry2 } = await import("./index-c0qedjh6.js");
23711
+ const { startScheduler } = await import("./index-25wjsdv2.js");
23712
+ const { getExecutorRegistry: getExecutorRegistry2 } = await import("./index-ywds4812.js");
23471
23713
  const intervalMs = Number(process.env.SCHEDULER_INTERVAL_MS) || 1e4;
23472
23714
  startScheduler(getExecutorRegistry2(), intervalMs, {
23473
23715
  runId: globalState.__runId
23474
23716
  });
23475
23717
  }
23476
23718
  if (!isEnvFlagEnabled("HEARTBEAT_DISABLE", false)) {
23477
- const { startHeartbeat } = await import("./index-fnack5vh.js");
23719
+ const { startHeartbeat } = await import("./index-vg4snay4.js");
23478
23720
  const heartbeatMs = Number(process.env.HEARTBEAT_INTERVAL_MS) || 90000;
23479
23721
  startHeartbeat(heartbeatMs);
23480
23722
  }
23481
23723
  if (process.env.OAUTH_KEEPALIVE_DISABLE !== "true") {
23482
- const { startOAuthKeepalive } = await import("./keepalive-fc7a2hw9.js");
23724
+ const { startOAuthKeepalive } = await import("./keepalive-vc7s2aa6.js");
23483
23725
  startOAuthKeepalive();
23484
23726
  }
23485
23727
  if (process.env.OAUTH_REFRESH_SWEEP_DISABLE !== "true") {
23486
- const { startOAuthRefreshSweep } = await import("./oauth-refresh-sweep-f9rptqbt.js");
23728
+ const { startOAuthRefreshSweep } = await import("./oauth-refresh-sweep-yecgjpk4.js");
23487
23729
  startOAuthRefreshSweep();
23488
23730
  }
23489
23731
  startOAuthPendingGc();
23490
23732
  startMemoryGc();
23491
- import("./boot-reembed-5bgan3cd.js").then(({ runBootReembed }) => runBootReembed()).catch((err) => {
23733
+ import("./boot-reembed-q7mp8hcr.js").then(({ runBootReembed }) => runBootReembed()).catch((err) => {
23492
23734
  console.error("[boot-reembed] startup backfill failed (non-fatal):", err);
23493
23735
  });
23494
- import("./boot-reembed-z7ykyeq8.js").then(({ runBootReembedScripts }) => runBootReembedScripts()).catch((err) => {
23736
+ import("./boot-reembed-xmw8wcx6.js").then(({ runBootReembedScripts }) => runBootReembedScripts()).catch((err) => {
23495
23737
  console.error("[boot-reembed-scripts] startup backfill failed (non-fatal):", err);
23496
23738
  });
23497
- import("./boot-scrub-logs-b2rjk8mv.js").then(({ runBootScrubLogs }) => runBootScrubLogs()).catch((err) => {
23739
+ import("./boot-scrub-logs-dsqrv79n.js").then(({ runBootScrubLogs }) => runBootScrubLogs()).catch((err) => {
23498
23740
  console.error("[boot-scrub-logs] startup scrub failed (non-fatal):", err);
23499
23741
  });
23500
23742
  }).on("error", (err) => {