@desplega.ai/agent-swarm 1.113.0 → 1.115.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. package/README.md +2 -2
  2. package/dist/{actions-q4n7cz6e.js → actions-17ya5wde.js} +8 -9
  3. package/dist/{app-es4nzc71.js → app-4f718yk0.js} +6 -6
  4. package/dist/{assistant-0x8ey0vs.js → assistant-ah7csnjz.js} +19 -18
  5. package/dist/{boot-reembed-69agkhv6.js → boot-reembed-4jpxhb25.js} +6 -6
  6. package/dist/{boot-reembed-p0ny05t8.js → boot-reembed-qata34xa.js} +5 -5
  7. package/dist/{boot-scrub-logs-yybrkmvr.js → boot-scrub-logs-tv48nn3z.js} +4 -4
  8. package/dist/{claude-adapter-mma9a1yr.js → claude-adapter-3csn5bks.js} +3 -3
  9. package/dist/{claude-managed-adapter-pv2nbqy4.js → claude-managed-adapter-kysmkz89.js} +2 -2
  10. package/dist/cli-0pygq9r1.js +429 -0
  11. package/dist/{cli-hsetkkd3.js → cli-12vb0xg6.js} +33 -9
  12. package/dist/{cli-ncfgsqbd.js → cli-15y7qhzp.js} +262 -33
  13. package/dist/{cli-7ga43spr.js → cli-1yjrbg1f.js} +1 -1
  14. package/dist/{cli-36ymmpmp.js → cli-3samkyap.js} +2 -2
  15. package/dist/cli-52fg7s7x.js +269 -0
  16. package/dist/{cli-hjhvekf4.js → cli-65ekje01.js} +11 -8
  17. package/dist/{cli-h3k622d0.js → cli-6aq8xhvm.js} +90 -3
  18. package/dist/{cli-f3qa069v.js → cli-7xje5amz.js} +1 -1
  19. package/dist/cli-86fdnerz.js +405 -0
  20. package/dist/{cli-9cy6nc5m.js → cli-8fjwym53.js} +1 -1
  21. package/dist/{cli-c3frqf65.js → cli-b23axgz4.js} +3 -3
  22. package/dist/{cli-a0t7vapf.js → cli-bc89my0z.js} +1 -1
  23. package/dist/{cli-bnzbn8j8.js → cli-beea4a3y.js} +73 -24
  24. package/dist/{cli-q21d49ac.js → cli-bw80ck94.js} +1 -1
  25. package/dist/{cli-pwc5e83c.js → cli-cchksf5q.js} +12 -7
  26. package/dist/{cli-30bbaveh.js → cli-cmw9rzy5.js} +2 -2
  27. package/dist/{cli-y6mrptcn.js → cli-cnwssmsr.js} +1 -1
  28. package/dist/{cli-r5g6ngtn.js → cli-dcca6j8m.js} +46 -5
  29. package/dist/{cli-tkmm5nf9.js → cli-e61fs2hp.js} +59 -2
  30. package/dist/{cli-6wscj9z0.js → cli-e7306swv.js} +14 -14
  31. package/dist/{cli-dcfyyh3t.js → cli-ftyf58xj.js} +2 -2
  32. package/dist/{cli-4xyj9jtq.js → cli-hrhye2rm.js} +1062 -478
  33. package/dist/cli-jkhwmmgg.js +136 -0
  34. package/dist/{cli-y4ycrnjc.js → cli-k3y4z3h5.js} +1 -1
  35. package/dist/{cli-6xd0bvf8.js → cli-ma04faw5.js} +1 -1
  36. package/dist/{cli-2fc97xm5.js → cli-mnbgv3w7.js} +4 -4
  37. package/dist/{cli-trmapn9k.js → cli-pet3aev6.js} +95 -10
  38. package/dist/{cli-z46pnksz.js → cli-q1rx7g0d.js} +175 -28
  39. package/dist/{cli-qwgtacd6.js → cli-r9f6ngrf.js} +1244 -336
  40. package/dist/{cli-gtkqc144.js → cli-shvaxw27.js} +13 -7
  41. package/dist/{cli-ywez6yps.js → cli-sq7f72d6.js} +1 -1
  42. package/dist/cli-t0vqevnm.js +393 -0
  43. package/dist/{cli-tbw13sx8.js → cli-tece6zgm.js} +1 -1
  44. package/dist/{cli-bgvskydy.js → cli-tffvrh6j.js} +1 -1
  45. package/dist/cli-wh91zby6.js +187222 -0
  46. package/dist/{cli-anrj584m.js → cli-xz9aq0rf.js} +1 -1
  47. package/dist/{cli-mq580e06.js → cli-zsrdty74.js} +18 -18
  48. package/dist/cli.js +32 -15
  49. package/dist/{codex-adapter-5cmy858w.js → codex-adapter-de3w0nch.js} +10 -10
  50. package/dist/{codex-session-runner-46q8mrba.js → codex-session-runner-bnw8cysk.js} +10 -10
  51. package/dist/{commands-z5dbwta3.js → commands-hch4e2k1.js} +4 -4
  52. package/dist/{db-bbgahh4z.js → db-p3h2j06x.js} +14 -4
  53. package/dist/{handlers-6b44317z.js → handlers-9er4zx9q.js} +17 -31
  54. package/dist/{hook-sbp5fmps.js → hook-4axs756z.js} +7 -7
  55. package/dist/{http-2xz43gz3.js → http-809fvtz2.js} +1934 -886
  56. package/dist/{index-wz1ea15p.js → index-2dgfj9av.js} +3 -3
  57. package/dist/{index-nhat35em.js → index-47em3d2n.js} +14 -11
  58. package/dist/{index-5ghxn8s6.js → index-9fqaktjr.js} +12 -9
  59. package/dist/{index-0gzmqj4k.js → index-9ht8gr56.js} +13 -10
  60. package/dist/{index-vfedgdw6.js → index-rm85cfdx.js} +368 -40
  61. package/dist/{index-ykvs43z4.js → index-t2snazez.js} +4 -4
  62. package/dist/{keepalive-62gfj91d.js → keepalive-3cps6e3e.js} +7 -6
  63. package/dist/{lead-ag0akn0v.js → lead-fkk95wgq.js} +36 -32
  64. package/dist/{maintenance-xxrrzk5x.js → maintenance-fweqh0yj.js} +6 -6
  65. package/dist/{mistral-conversations-26yrnbf1.js → mistral-conversations-7gqnz2wt.js} +7 -7
  66. package/dist/oauth-refresh-sweep-sycqtbph.js +114 -0
  67. package/dist/{onboard-64zyfcbs.js → onboard-cy0rkgvx.js} +2 -2
  68. package/dist/{opencode-adapter-mqsxr170.js → opencode-adapter-p6bxnbm4.js} +2 -2
  69. package/dist/{otel-zjd9zaxs.js → otel-g299k41b.js} +2 -2
  70. package/dist/{otel-impl-b86qseaa.js → otel-impl-wbns8hf7.js} +1 -1
  71. package/dist/{pi-mono-adapter-kxjxpfgy.js → pi-mono-adapter-05sck1ds.js} +17 -109
  72. package/dist/{pricing-refresh-cgtkffpa.js → pricing-refresh-0hvazmkf.js} +6 -6
  73. package/dist/rbac-roles-kfesjy9y.js +28 -0
  74. package/dist/rbac-roles-mwcwhbxc.js +283 -0
  75. package/dist/{seed-pricing-1r1y1xkq.js → seed-pricing-h2kdq4cq.js} +5 -5
  76. package/dist/{setup-mkcgkjf7.js → setup-8506q97w.js} +2 -2
  77. package/dist/{worker-351sze63.js → worker-4r5c6zyb.js} +36 -32
  78. package/openapi.json +1327 -20
  79. package/package.json +3 -3
  80. package/src/agentmail/handlers.ts +16 -6
  81. package/src/agentmail/templates.ts +25 -5
  82. package/src/be/audit-user.ts +28 -4
  83. package/src/be/db-queries/oauth.ts +42 -0
  84. package/src/be/db.ts +314 -28
  85. package/src/be/identity.ts +79 -0
  86. package/src/be/mcp-proxy.ts +132 -0
  87. package/src/be/migrations/109_rbac_roles.sql +79 -0
  88. package/src/be/migrations/111_oauth_credential_bindings.sql +6 -0
  89. package/src/be/migrations/112_script_connections_graphql.sql +96 -0
  90. package/src/be/migrations/113_task_routing_affinity.sql +9 -0
  91. package/src/be/migrations/114_backfill_gpt_5_6_pricing.sql +15 -0
  92. package/src/be/modelsdev-cache.json +135 -0
  93. package/src/be/oauth-credential-bindings.ts +35 -0
  94. package/src/be/oauth-refresh-sweep.ts +159 -0
  95. package/src/be/rbac-audit.ts +52 -13
  96. package/src/be/rbac-roles.ts +424 -0
  97. package/src/be/script-connections.ts +459 -40
  98. package/src/be/script-credential-broker.ts +29 -3
  99. package/src/be/scripts/typecheck.ts +51 -7
  100. package/src/be/seed-scripts/catalog/catalog-report.inline.ts +325 -24
  101. package/src/be/seed-scripts/catalog/catalog-report.ts +325 -24
  102. package/src/be/users.ts +24 -0
  103. package/src/cli.tsx +17 -0
  104. package/src/github/handlers.ts +10 -9
  105. package/src/github/templates.ts +45 -9
  106. package/src/gitlab/handlers.ts +20 -8
  107. package/src/gitlab/templates.ts +18 -6
  108. package/src/heartbeat/heartbeat.ts +143 -21
  109. package/src/http/all-routes.ts +3 -0
  110. package/src/http/approval-requests.ts +12 -0
  111. package/src/http/config.ts +44 -10
  112. package/src/http/core.ts +30 -3
  113. package/src/http/favorites.ts +1 -5
  114. package/src/http/index.ts +25 -1
  115. package/src/http/mcp-oauth.ts +2 -0
  116. package/src/http/mcp-servers.ts +44 -3
  117. package/src/http/oauth-generic.ts +105 -0
  118. package/src/http/poll.ts +20 -3
  119. package/src/http/script-connection-proxy.ts +116 -0
  120. package/src/http/script-connections.ts +1657 -0
  121. package/src/http/scripts.ts +27 -7
  122. package/src/http/skills.ts +11 -0
  123. package/src/http/tasks.ts +27 -13
  124. package/src/http/x.ts +6 -2
  125. package/src/integrations/kapso/inbound.ts +23 -6
  126. package/src/jira/sync.ts +111 -17
  127. package/src/jira/templates.ts +10 -2
  128. package/src/mcp-client/http-client.ts +194 -0
  129. package/src/oauth/app-validation.ts +16 -0
  130. package/src/oauth/ensure-token.ts +69 -14
  131. package/src/oauth/mcp-wrapper.ts +15 -0
  132. package/src/oauth/wrapper.ts +48 -20
  133. package/src/prompts/session-templates.ts +3 -3
  134. package/src/providers/codex-models.ts +100 -30
  135. package/src/providers/modelsdev-reasoning.json +51 -0
  136. package/src/providers/pi-mono-mcp-client.ts +2 -133
  137. package/src/providers/reasoning-effort.ts +11 -5
  138. package/src/rbac/admission.ts +68 -0
  139. package/src/rbac/index.ts +2 -0
  140. package/src/rbac/legacy-policy.ts +12 -0
  141. package/src/rbac/permissions.ts +48 -0
  142. package/src/scheduler/scheduler.ts +9 -1
  143. package/src/scripts-runtime/api-client.ts +151 -19
  144. package/src/scripts-runtime/api-types.ts +38 -6
  145. package/src/scripts-runtime/credential-broker/default-bindings.ts +1 -0
  146. package/src/scripts-runtime/credential-broker/types.ts +8 -0
  147. package/src/scripts-runtime/ctx.ts +11 -1
  148. package/src/scripts-runtime/eval-harness.ts +5 -1
  149. package/src/scripts-runtime/executors/types.ts +2 -1
  150. package/src/scripts-runtime/loader.ts +3 -1
  151. package/src/scripts-runtime/mcp-client.ts +87 -0
  152. package/src/scripts-runtime/sdk-allowlist.ts +1 -0
  153. package/src/scripts-runtime/types/stdlib.d.ts +6 -0
  154. package/src/scripts-runtime/types/swarm-sdk.d.ts +19 -0
  155. package/src/server-user.ts +114 -59
  156. package/src/server.ts +12 -0
  157. package/src/slack/assistant.ts +14 -6
  158. package/src/slack/enrich.ts +17 -0
  159. package/src/slack/handlers.ts +8 -22
  160. package/src/slack/responses.ts +91 -1
  161. package/src/slack/thread-buffer.ts +6 -3
  162. package/src/slack/watcher.ts +9 -6
  163. package/src/tasks/worker-follow-up.ts +100 -3
  164. package/src/tests/agentmail-handlers.test.ts +32 -3
  165. package/src/tests/approval-requests.test.ts +61 -0
  166. package/src/tests/audit-user.test.ts +47 -1
  167. package/src/tests/codex-adapter.test.ts +46 -23
  168. package/src/tests/credential-broker.test.ts +5 -2
  169. package/src/tests/delete-page-tool.test.ts +197 -0
  170. package/src/tests/github-handlers.test.ts +28 -2
  171. package/src/tests/gitlab-handlers.test.ts +125 -0
  172. package/src/tests/heartbeat.test.ts +48 -3
  173. package/src/tests/identity.test.ts +117 -0
  174. package/src/tests/jira-sync.test.ts +99 -1
  175. package/src/tests/kapso-inbound.test.ts +7 -0
  176. package/src/tests/mcp-tools-user.test.ts +60 -6
  177. package/src/tests/model-control.test.ts +8 -2
  178. package/src/tests/oauth-credential-bindings.test.ts +490 -0
  179. package/src/tests/oauth-refresh-sweep.test.ts +160 -0
  180. package/src/tests/pool-affinity.test.ts +401 -0
  181. package/src/tests/prompt-template-remaining.test.ts +2 -2
  182. package/src/tests/rbac-admission-e2e.test.ts +386 -0
  183. package/src/tests/rbac-admission.test.ts +492 -0
  184. package/src/tests/rbac-e2e-helpers.ts +3 -0
  185. package/src/tests/rbac-engine.test.ts +50 -0
  186. package/src/tests/rbac-mcp-admission-e2e.test.ts +362 -0
  187. package/src/tests/rbac-roles.test.ts +328 -0
  188. package/src/tests/reasoning-effort.test.ts +15 -2
  189. package/src/tests/schedule-target-type.test.ts +50 -0
  190. package/src/tests/scheduled-tasks.test.ts +40 -0
  191. package/src/tests/script-apis-mcp.test.ts +37 -13
  192. package/src/tests/script-connections-http.test.ts +1158 -0
  193. package/src/tests/script-connections-mcp.test.ts +894 -0
  194. package/src/tests/script-connections.test.ts +754 -6
  195. package/src/tests/scripts-external-api.test.ts +49 -1
  196. package/src/tests/scripts-runtime-secret-egress.test.ts +1 -0
  197. package/src/tests/scripts-runtime.test.ts +291 -0
  198. package/src/tests/seed-scripts.test.ts +36 -0
  199. package/src/tests/slack-identity-resolution.test.ts +37 -3
  200. package/src/tests/slack-inline-output.test.ts +65 -0
  201. package/src/tests/slack-thread-buffer.test.ts +28 -0
  202. package/src/tests/tool-annotations.test.ts +1 -0
  203. package/src/tests/user-token-rest-auth.test.ts +140 -1
  204. package/src/tests/workflow-swarm-script.test.ts +44 -0
  205. package/src/tests/workflow-triggers-v2.test.ts +300 -1
  206. package/src/tools/credential-bindings/tool.ts +277 -18
  207. package/src/tools/delete-page.ts +145 -0
  208. package/src/tools/mcp-servers/mcp-server-delete.ts +7 -4
  209. package/src/tools/request-human-input.ts +2 -0
  210. package/src/tools/resolve-user.ts +77 -27
  211. package/src/tools/script-apis.ts +62 -3
  212. package/src/tools/script-connections/tool.ts +275 -31
  213. package/src/tools/send-task.ts +15 -1
  214. package/src/tools/task-action.ts +34 -2
  215. package/src/tools/templates.ts +53 -1
  216. package/src/tools/tool-config.ts +2 -1
  217. package/src/tools/utils.ts +2 -0
  218. package/src/tools/workflows/create-workflow.ts +8 -1
  219. package/src/tools/workflows/update-workflow.ts +11 -2
  220. package/src/types.ts +90 -11
  221. package/src/utils/scoped-resource.ts +25 -0
  222. package/src/workflows/executors/human-in-the-loop.ts +1 -0
  223. package/src/workflows/executors/swarm-script.ts +7 -1
  224. package/src/workflows/triggers.ts +146 -18
  225. package/templates/skills/swarm-scripts/content.md +33 -8
  226. package/dist/cli-bgef578c.js +0 -771
  227. package/dist/cli-c9mtm09b.js +0 -43
  228. package/dist/cli-ceewqajw.js +0 -1698
  229. package/dist/{anthropic-messages-8nc6r2dw.js → anthropic-messages-kq3pjhbp.js} +6 -6
  230. package/dist/{azure-openai-responses-6yfzw3qw.js → azure-openai-responses-gm1ejtmn.js} +7 -7
  231. package/dist/{cli-vbraamxr.js → cli-6b4wwmm8.js} +9 -9
  232. package/dist/{cli-2r7s8jhp.js → cli-sznberje.js} +3 -3
  233. package/dist/{google-generative-ai-vfq6ymz9.js → google-generative-ai-hyrra2jm.js} +3 -3
  234. package/dist/{google-vertex-mj5y5rfj.js → google-vertex-8bs2pzgn.js} +3 -3
  235. package/dist/{openai-codex-responses-8h6kvfv8.js → openai-codex-responses-3nkvje79.js} +4 -4
  236. package/dist/{openai-completions-x6kjnqjd.js → openai-completions-0asftkwg.js} +11 -11
  237. package/dist/{openai-responses-7jwyz3ds.js → openai-responses-pmatek45.js} +10 -10
@@ -17,7 +17,7 @@ import {
17
17
 
18
18
  describe("REASONING_EFFORT_LEVELS", () => {
19
19
  test("is the closed normalized enum", () => {
20
- expect(REASONING_EFFORT_LEVELS).toEqual(["off", "low", "medium", "high", "xhigh"]);
20
+ expect(REASONING_EFFORT_LEVELS).toEqual(["off", "low", "medium", "high", "xhigh", "max"]);
21
21
  });
22
22
  });
23
23
 
@@ -25,10 +25,16 @@ describe("reasoningCapability — cache-sourced levels", () => {
25
25
  test("claude claude-opus-4-8: levels come from reasoning_options.effort, not the fallback", () => {
26
26
  const cap = reasoningCapability("claude", "claude-opus-4-8");
27
27
  expect(cap.supported).toBe(true);
28
- // Cache lists [low, medium, high, xhigh, max]; "max" is dropped (out of scope).
28
+ // Cache lists [low, medium, high, xhigh, max]; "max" is not a Claude CLI effort.
29
29
  expect(cap.levels).toEqual(["low", "medium", "high", "xhigh"]);
30
30
  });
31
31
 
32
+ test("codex gpt-5.6-sol supports the new max effort", () => {
33
+ const cap = reasoningCapability("codex", "gpt-5.6-sol");
34
+ expect(cap.supported).toBe(true);
35
+ expect(cap.levels).toEqual(["off", "low", "medium", "high", "xhigh", "max"]);
36
+ });
37
+
32
38
  test("codex gpt-5.1-codex-max: cache already includes xhigh", () => {
33
39
  const cap = reasoningCapability("codex", "gpt-5.1-codex-max");
34
40
  expect(cap.supported).toBe(true);
@@ -156,6 +162,13 @@ describe("applyReasoningEffort — codex-config shape", () => {
156
162
  config: { model_reasoning_effort: "xhigh" },
157
163
  });
158
164
  });
165
+
166
+ test("max on gpt-5.6-sol is applied", () => {
167
+ expect(applyReasoningEffort("codex", "gpt-5.6-sol", "max")).toEqual({
168
+ kind: "codex-config",
169
+ config: { model_reasoning_effort: "max" },
170
+ });
171
+ });
159
172
  });
160
173
 
161
174
  describe("applyReasoningEffort — pi-session shape", () => {
@@ -288,6 +288,56 @@ describe("dispatchScheduleTarget — script target", () => {
288
288
  expect(result.task).toBeUndefined();
289
289
  }, 15_000);
290
290
 
291
+ test("scheduled script runs receive ctx.api and ctx.mcp connections", async () => {
292
+ const { upsertScriptConnection } = await import("../be/script-connections");
293
+ await upsertScriptConnection({
294
+ slug: "schedgql",
295
+ kind: "graphql",
296
+ scope: "global",
297
+ baseUrl: "https://gql.vendor.test/graphql",
298
+ allowedHosts: ["gql.vendor.test"],
299
+ agentId,
300
+ });
301
+ const mcpRow = {
302
+ id: crypto.randomUUID(),
303
+ serverId: crypto.randomUUID(),
304
+ runtime: { slug: "schedmcp", kind: "mcp", connectionId: "", tools: [{ name: "ping" }] },
305
+ };
306
+ mcpRow.runtime.connectionId = mcpRow.id;
307
+ getDb()
308
+ .prepare(
309
+ `INSERT INTO mcp_servers (id, name, transport, scope, url, createdAt, lastUpdatedAt)
310
+ VALUES (?, ?, 'http', 'global', 'http://mcp.invalid.test/mcp', datetime('now'), datetime('now'))`,
311
+ )
312
+ .run(mcpRow.serverId, `sched-mcp-${mcpRow.serverId.slice(0, 8)}`);
313
+ getDb()
314
+ .prepare(
315
+ `INSERT INTO script_connections
316
+ (id, slug, kind, scope, allowed_hosts_json, mcp_server_id, generated_runtime_json)
317
+ VALUES (?, 'schedmcp', 'mcp', 'global', '[]', ?, ?)`,
318
+ )
319
+ .run(mcpRow.id, mcpRow.serverId, JSON.stringify(mcpRow.runtime));
320
+
321
+ await saveGlobalScript(
322
+ "schedule-target-type-ctx-connections",
323
+ `export default async (args, ctx) => {
324
+ if (!ctx.api || !("schedgql" in ctx.api)) throw new Error("ctx.api missing schedgql");
325
+ if (!ctx.mcp || !("schedmcp" in ctx.mcp)) throw new Error("ctx.mcp missing schedmcp");
326
+ return { ok: true };
327
+ };`,
328
+ );
329
+ const schedule = createScheduledTask({
330
+ name: `dispatch-script-ctx-${crypto.randomUUID()}`,
331
+ intervalMs: 60_000,
332
+ targetType: "script",
333
+ scriptName: "schedule-target-type-ctx-connections",
334
+ createdByAgentId: agentId,
335
+ });
336
+
337
+ const result = await dispatchScheduleTarget(schedule);
338
+ expect(result.triggeredWorkflows).toBe(false);
339
+ }, 15_000);
340
+
291
341
  test("throws a clear error when the script does not exist", async () => {
292
342
  const schedule = createScheduledTask({
293
343
  name: `dispatch-script-missing-${crypto.randomUUID()}`,
@@ -4,11 +4,13 @@ import {
4
4
  closeDb,
5
5
  createAgent,
6
6
  createScheduledTask,
7
+ createUser,
7
8
  deleteScheduledTask,
8
9
  getDb,
9
10
  getScheduledTaskById,
10
11
  getScheduledTaskByName,
11
12
  getScheduledTasks,
13
+ getTaskById,
12
14
  initDb,
13
15
  updateScheduledTask,
14
16
  } from "../be/db";
@@ -315,6 +317,44 @@ describe("Scheduled Tasks Integration", () => {
315
317
  expect(afterRun?.lastRunAt).toBeDefined();
316
318
  });
317
319
 
320
+ test("fired task inherits the schedule's createdBy as requestedByUserId", async () => {
321
+ const requester = createUser({ name: "Schedule Requester", email: "sched-req@example.com" });
322
+ const schedule = createScheduledTask({
323
+ name: "test-manual-run-createdby",
324
+ cronExpression: "0 9 * * *",
325
+ taskTemplate: "Task carrying schedule provenance",
326
+ createdByAgentId: testAgent.id,
327
+ createdBy: requester.id,
328
+ enabled: true,
329
+ });
330
+
331
+ await runScheduleNow(schedule.id);
332
+
333
+ const createdTask = getDb()
334
+ .query("SELECT id FROM agent_tasks WHERE task = ? ORDER BY createdAt DESC LIMIT 1")
335
+ .get(schedule.taskTemplate) as { id: string };
336
+ const task = getTaskById(createdTask.id);
337
+ expect(task?.requestedByUserId).toBe(requester.id);
338
+ });
339
+
340
+ test("fired task has no requestedByUserId when the schedule's createdBy is unset (first-ever schedules)", async () => {
341
+ const schedule = createScheduledTask({
342
+ name: "test-manual-run-no-createdby",
343
+ cronExpression: "0 9 * * *",
344
+ taskTemplate: "Task with no schedule provenance",
345
+ createdByAgentId: testAgent.id,
346
+ enabled: true,
347
+ });
348
+
349
+ await runScheduleNow(schedule.id);
350
+
351
+ const createdTask = getDb()
352
+ .query("SELECT id FROM agent_tasks WHERE task = ? ORDER BY createdAt DESC LIMIT 1")
353
+ .get(schedule.taskTemplate) as { id: string };
354
+ const task = getTaskById(createdTask.id);
355
+ expect(task?.requestedByUserId ?? null).toBeNull();
356
+ });
357
+
318
358
  test("should throw error for non-existent schedule", async () => {
319
359
  await expect(runScheduleNow("non-existent-schedule-id")).rejects.toThrow(
320
360
  "Schedule not found",
@@ -114,6 +114,7 @@ async function dispatchScriptsApi(url: string, init: RequestInit = {}): Promise<
114
114
  }
115
115
 
116
116
  let workerId: string;
117
+ let leadId: string;
117
118
  let scriptId: string;
118
119
  let savedEnv: NodeJS.ProcessEnv;
119
120
  let savedFetch: typeof globalThis.fetch;
@@ -136,6 +137,7 @@ beforeAll(async () => {
136
137
  refreshSecretScrubberCache();
137
138
  setScriptEmbeddingProviderForTests(noOpEmbeddingProvider);
138
139
  workerId = createAgent({ name: "script-apis-mcp-worker", isLead: false, status: "idle" }).id;
140
+ leadId = createAgent({ name: "script-apis-mcp-lead", isLead: true, status: "idle" }).id;
139
141
  process.env.MCP_BASE_URL = "http://script-apis-mcp.test";
140
142
  globalThis.fetch = (async (input, init) => {
141
143
  const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
@@ -185,18 +187,43 @@ describe("script-apis MCP tool", () => {
185
187
  const tools = buildToolServer();
186
188
  const result = (await tools.scriptApis.handler(
187
189
  { action: "create", scriptId, authMode: "bearer", label: "demo" },
188
- meta(workerId),
190
+ meta(leadId),
189
191
  )) as StructuredResult<{ id: string; token: string; authMode: string }>;
190
192
  expect(result.structuredContent.success).toBe(true);
191
193
  expect(result.structuredContent.data?.token).toMatch(/^xsk_/);
192
194
  expect(result.structuredContent.data?.authMode).toBe("bearer");
193
195
  });
194
196
 
195
- test("list masks bearer tokens by default and reveals with includeSecrets", async () => {
197
+ test("non-lead agents cannot mint or reveal bearer tokens", async () => {
196
198
  const tools = buildToolServer();
199
+ const createDenied = (await tools.scriptApis.handler(
200
+ { action: "create", scriptId, authMode: "bearer", label: "demo" },
201
+ meta(workerId),
202
+ )) as StructuredResult<unknown>;
203
+ expect(createDenied.structuredContent.success).toBe(false);
204
+ expect(createDenied.structuredContent.status).toBe(403);
205
+ expect(createDenied.structuredContent.error).toContain("Only lead agents can create");
206
+
197
207
  const created = (await tools.scriptApis.handler(
198
208
  { action: "create", scriptId, authMode: "bearer" },
209
+ meta(leadId),
210
+ )) as StructuredResult<{ id: string; token: string }>;
211
+ expect(created.structuredContent.success).toBe(true);
212
+
213
+ const revealDenied = (await tools.scriptApis.handler(
214
+ { action: "list", scriptId, includeSecrets: true },
199
215
  meta(workerId),
216
+ )) as StructuredResult<unknown>;
217
+ expect(revealDenied.structuredContent.success).toBe(false);
218
+ expect(revealDenied.structuredContent.status).toBe(403);
219
+ expect(revealDenied.structuredContent.error).toContain("Only lead agents can reveal");
220
+ });
221
+
222
+ test("list masks bearer tokens by default and reveals with includeSecrets", async () => {
223
+ const tools = buildToolServer();
224
+ const created = (await tools.scriptApis.handler(
225
+ { action: "create", scriptId, authMode: "bearer" },
226
+ meta(leadId),
200
227
  )) as StructuredResult<{ id: string; token: string }>;
201
228
  const realToken = created.structuredContent.data?.token;
202
229
  expect(realToken).toBeTruthy();
@@ -209,17 +236,14 @@ describe("script-apis MCP tool", () => {
209
236
 
210
237
  const revealed = (await tools.scriptApis.handler(
211
238
  { action: "list", scriptId, includeSecrets: true },
212
- meta(workerId),
239
+ meta(leadId),
213
240
  )) as StructuredResult<{ apis: Array<{ id: string; token: string | null }> }>;
214
241
  expect(revealed.structuredContent.data?.apis[0]?.token).toBe(realToken);
215
242
  });
216
243
 
217
244
  test("list returns a null token for authMode 'none'", async () => {
218
245
  const tools = buildToolServer();
219
- await tools.scriptApis.handler(
220
- { action: "create", scriptId, authMode: "none" },
221
- meta(workerId),
222
- );
246
+ await tools.scriptApis.handler({ action: "create", scriptId, authMode: "none" }, meta(leadId));
223
247
  const listed = (await tools.scriptApis.handler(
224
248
  { action: "list", scriptId },
225
249
  meta(workerId),
@@ -231,14 +255,14 @@ describe("script-apis MCP tool", () => {
231
255
  const tools = buildToolServer();
232
256
  const created = (await tools.scriptApis.handler(
233
257
  { action: "create", scriptId, authMode: "bearer" },
234
- meta(workerId),
258
+ meta(leadId),
235
259
  )) as StructuredResult<{ id: string; token: string }>;
236
260
  const endpointId = created.structuredContent.data?.id as string;
237
261
  const oldToken = created.structuredContent.data?.token;
238
262
 
239
263
  const rotated = (await tools.scriptApis.handler(
240
264
  { action: "rotate", scriptId, endpointId },
241
- meta(workerId),
265
+ meta(leadId),
242
266
  )) as StructuredResult<{ token: string }>;
243
267
  expect(rotated.structuredContent.success).toBe(true);
244
268
  expect(rotated.structuredContent.data?.token).toBeTruthy();
@@ -249,13 +273,13 @@ describe("script-apis MCP tool", () => {
249
273
  const tools = buildToolServer();
250
274
  const created = (await tools.scriptApis.handler(
251
275
  { action: "create", scriptId, authMode: "none" },
252
- meta(workerId),
276
+ meta(leadId),
253
277
  )) as StructuredResult<{ id: string }>;
254
278
  const endpointId = created.structuredContent.data?.id as string;
255
279
 
256
280
  const updated = (await tools.scriptApis.handler(
257
281
  { action: "update", scriptId, endpointId, enabled: false, label: "renamed" },
258
- meta(workerId),
282
+ meta(leadId),
259
283
  )) as StructuredResult<{ enabled: boolean; label: string | null }>;
260
284
  expect(updated.structuredContent.success).toBe(true);
261
285
  expect(updated.structuredContent.data?.enabled).toBe(false);
@@ -266,13 +290,13 @@ describe("script-apis MCP tool", () => {
266
290
  const tools = buildToolServer();
267
291
  const created = (await tools.scriptApis.handler(
268
292
  { action: "create", scriptId, authMode: "none" },
269
- meta(workerId),
293
+ meta(leadId),
270
294
  )) as StructuredResult<{ id: string }>;
271
295
  const endpointId = created.structuredContent.data?.id as string;
272
296
 
273
297
  const deleted = (await tools.scriptApis.handler(
274
298
  { action: "delete", scriptId, endpointId },
275
- meta(workerId),
299
+ meta(leadId),
276
300
  )) as StructuredResult<{ deleted: boolean }>;
277
301
  expect(deleted.structuredContent.success).toBe(true);
278
302
  expect(deleted.structuredContent.data?.deleted).toBe(true);