@desplega.ai/agent-swarm 2.0.0 → 2.1.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 (238) hide show
  1. package/dist/{acp-adapter-sj2g0m84.js → acp-adapter-kr4abrsk.js} +10 -10
  2. package/dist/{actions-fegk44gr.js → actions-eh4g6w2r.js} +27 -27
  3. package/dist/{amp-adapter-ad8ft89x.js → amp-adapter-7h7wdt8q.js} +5 -5
  4. package/dist/{app-kfvgbvzy.js → app-7k8s9w5p.js} +9 -9
  5. package/dist/{assistant-z7zdy9ja.js → assistant-wswt2y7p.js} +28 -28
  6. package/dist/{boot-reembed-j8xa181e.js → boot-reembed-9apm9qw6.js} +7 -7
  7. package/dist/{boot-reembed-5c2z0mbe.js → boot-reembed-whzbdthj.js} +7 -7
  8. package/dist/{boot-scrub-sweep-hnkn3rgt.js → boot-scrub-sweep-ndzmvmgt.js} +8 -8
  9. package/dist/{boot-seeds-kwjq4d2z.js → boot-seeds-e9sta0yt.js} +13 -11
  10. package/dist/{claude-adapter-9vmvpvgd.js → claude-adapter-4mqd3r6r.js} +11 -11
  11. package/dist/{claude-managed-adapter-k9qae05j.js → claude-managed-adapter-9wswb52m.js} +3 -3
  12. package/dist/{claude-managed-setup-q54zx5q2.js → claude-managed-setup-tpe1effx.js} +4 -4
  13. package/dist/{claude-sdk-session-9jye6cxr.js → claude-sdk-session-q49ghmh9.js} +10340 -10197
  14. package/dist/{cli-ke896fcf.js → cli-01xvj1rq.js} +10 -10
  15. package/dist/{cli-tr4sxp68.js → cli-035vekcw.js} +2 -2
  16. package/dist/{cli-z5jjpaxw.js → cli-0pd8hzhc.js} +3 -3
  17. package/dist/{cli-n5vwg842.js → cli-0veb92a3.js} +6 -1
  18. package/dist/{cli-wayw93nt.js → cli-10jesr25.js} +3 -3
  19. package/dist/{cli-se8w6wny.js → cli-1y7yywmj.js} +1 -1
  20. package/dist/{cli-v1yb0rqb.js → cli-20krdhz5.js} +47 -19
  21. package/dist/{cli-d5f9ed5c.js → cli-29rja109.js} +39 -20
  22. package/dist/{cli-apx87jne.js → cli-2pwa0q4d.js} +15 -4
  23. package/dist/{cli-ee9gtz70.js → cli-4mengw46.js} +348 -25
  24. package/dist/{cli-03gedhng.js → cli-5twdke5x.js} +6 -6
  25. package/dist/{cli-6razj6t4.js → cli-711vwbxn.js} +2 -2
  26. package/dist/{cli-s2sp957d.js → cli-78vv7446.js} +1 -1
  27. package/dist/{cli-3fbmmawm.js → cli-7n6s5man.js} +4 -4
  28. package/dist/{cli-hnhtk6cj.js → cli-8wwxw8a3.js} +1 -1
  29. package/dist/{cli-hnmhtzvh.js → cli-9jzdj90z.js} +2 -0
  30. package/dist/{cli-skxvvpap.js → cli-cvv9k0hh.js} +28 -25
  31. package/dist/{cli-74ahz61x.js → cli-d9286m9h.js} +2 -2
  32. package/dist/{cli-4jxm2qtz.js → cli-gxzkg1de.js} +28 -16
  33. package/dist/{cli-zkpks3r3.js → cli-h1arqn31.js} +1 -1
  34. package/dist/{cli-r62a35ea.js → cli-hqr88ew6.js} +1 -1
  35. package/dist/{cli-ra0xzggj.js → cli-j67mh2vg.js} +2 -2
  36. package/dist/{cli-29jc5q7w.js → cli-ksphgh48.js} +2 -2
  37. package/dist/{cli-eejsgvm4.js → cli-mgz4nbgg.js} +4 -4
  38. package/dist/{cli-vatyhshy.js → cli-mz5as5ha.js} +4 -4
  39. package/dist/{cli-f7kr7svz.js → cli-n8ndw8j7.js} +2 -2
  40. package/dist/{cli-1e4b6bzh.js → cli-naqzr17j.js} +7 -7
  41. package/dist/{cli-q0ke1yww.js → cli-nm1p8jfr.js} +6 -6
  42. package/dist/{cli-m50t4309.js → cli-p3zbc1rw.js} +5 -5
  43. package/dist/{cli-cgtksawx.js → cli-pe05f8yk.js} +23 -3
  44. package/dist/{cli-vdach5a9.js → cli-qmzsbkvy.js} +4 -4
  45. package/dist/{cli-n3hzg89b.js → cli-qnm4e9e4.js} +57 -579
  46. package/dist/{cli-s4setq91.js → cli-qqy4q48b.js} +3 -3
  47. package/dist/{cli-k382tshb.js → cli-r35rw5de.js} +8 -7
  48. package/dist/{cli-k3dp5tpf.js → cli-r610p9bs.js} +2 -2
  49. package/dist/{cli-e6mpjhed.js → cli-rc6pf5kv.js} +1 -1
  50. package/dist/{cli-3dj8k4vy.js → cli-rdxm77k3.js} +1 -1
  51. package/dist/{cli-tqaazemc.js → cli-s69nfyeh.js} +1 -1
  52. package/dist/{cli-6jyj59nb.js → cli-sawv63e8.js} +2 -2
  53. package/dist/{cli-q50942rs.js → cli-sg19gbf0.js} +1 -1
  54. package/dist/{cli-p1x85d2t.js → cli-shk7r5cs.js} +12 -9
  55. package/dist/{cli-przpmdgr.js → cli-sksetb2q.js} +11 -1
  56. package/dist/{cli-d2qr7aj4.js → cli-t02z59f2.js} +1 -1
  57. package/dist/{cli-j0pfptdp.js → cli-v7g551te.js} +39 -43
  58. package/dist/{cli-bzszjf2p.js → cli-vgqzp4em.js} +519 -126
  59. package/dist/cli-wfnemr1t.js +86 -0
  60. package/dist/{cli-ne236afy.js → cli-wms6x9db.js} +8 -8
  61. package/dist/{cli-5hkm7whv.js → cli-x1r545x8.js} +1 -1
  62. package/dist/{cli-kt6zqg0e.js → cli-xczsgshn.js} +2 -2
  63. package/dist/{cli-ef1hg1wv.js → cli-y3167wt1.js} +430 -238
  64. package/dist/{cli-tsde1hdr.js → cli-yjmqpxt3.js} +12 -11
  65. package/dist/{cli-9nfx44eh.js → cli-z780ec7c.js} +1 -1
  66. package/dist/{cli-r2pnvj8g.js → cli-zm9t2vde.js} +2 -2
  67. package/dist/cli.js +24 -23
  68. package/dist/{codex-adapter-y81sssk0.js → codex-adapter-4z16v9n0.js} +8 -8
  69. package/dist/{codex-login-f2rcfpp9.js → codex-login-pkepy2j1.js} +2 -2
  70. package/dist/{codex-session-runner-8vx4j6a9.js → codex-session-runner-rvms9y2x.js} +8 -8
  71. package/dist/{commands-kcpvpcrr.js → commands-2a9rtyat.js} +8 -8
  72. package/dist/{controller-ywrmt6yd.js → controller-3k3madbn.js} +4 -4
  73. package/dist/{controllers-connect-kwpvj3rf.js → controllers-connect-v9yn8zb9.js} +30 -30
  74. package/dist/{cursor-adapter-cehv1qns.js → cursor-adapter-98gbhyzn.js} +4 -4
  75. package/dist/{db-ww5d7jq3.js → db-eywwsnjw.js} +9 -7
  76. package/dist/{db-k8ankj20.js → db-ncypk9np.js} +9 -9
  77. package/dist/{devin-adapter-x1022qb1.js → devin-adapter-zeq8b9gz.js} +2 -2
  78. package/dist/{dispatcher-e8gq5m68.js → dispatcher-w94kc575.js} +26 -26
  79. package/dist/{docker-ff6y1190.js → docker-b1e4cqab.js} +2 -2
  80. package/dist/{dsh-adapter-3epjm3af.js → dsh-adapter-47ztej36.js} +4 -4
  81. package/dist/{e2b-8ygwhg0f.js → e2b-bbt8ey45.js} +3 -3
  82. package/dist/{e2b-swhnpw3n.js → e2b-dp804sen.js} +3 -3
  83. package/dist/{event-bus-6axnngdj.js → event-bus-j2jdg3qb.js} +2 -2
  84. package/dist/{external-channel-h3359cwn.js → external-channel-zrkg8ar9.js} +8 -8
  85. package/dist/{grok-adapter-0d4x7gkx.js → grok-adapter-s9ncp9vy.js} +11 -11
  86. package/dist/{handlers-an1gzv21.js → handlers-kqftvjgw.js} +29 -29
  87. package/dist/{heartbeat-6dakeb61.js → heartbeat-9q96p8v9.js} +31 -31
  88. package/dist/{hook-6v6cv6d5.js → hook-56qe10dp.js} +7 -7
  89. package/dist/{hook-sa5xye4y.js → hook-yy6rw42k.js} +8 -8
  90. package/dist/{http-r7f3eky0.js → http-r2dnb8f8.js} +963 -75
  91. package/dist/{index-7ep4gzdk.js → index-3gtmkhc6.js} +26 -26
  92. package/dist/{index-v1v6n7hg.js → index-3k5hkz7r.js} +31 -31
  93. package/dist/{index-rmpw9a8q.js → index-5e101240.js} +26 -26
  94. package/dist/{index-s2wxgzr0.js → index-5wyfjc2c.js} +392 -15
  95. package/dist/{index-zk96aq1k.js → index-vxvhxxbn.js} +7 -7
  96. package/dist/{keepalive-be9vwy5h.js → keepalive-psj6j54c.js} +9 -9
  97. package/dist/{lead-0861cwrf.js → lead-hajz4688.js} +43 -43
  98. package/dist/{maintenance-mzzgc8bn.js → maintenance-ps02sb5m.js} +7 -7
  99. package/dist/{node-mn1zs7an.js → node-mwb5r697.js} +1 -1
  100. package/dist/{oauth-refresh-sweep-cpdnfwdj.js → oauth-refresh-sweep-0284h93b.js} +8 -8
  101. package/dist/{onboard-fh8qs4r3.js → onboard-rbf6bg01.js} +2 -2
  102. package/dist/{opencode-adapter-nm42wwj8.js → opencode-adapter-13dcatgq.js} +8 -8
  103. package/dist/{otel-impl-x6c1c9g4.js → otel-impl-zvqcbhp7.js} +2 -2
  104. package/dist/{pi-mono-adapter-9n2ya240.js → pi-mono-adapter-xxy47q4v.js} +7 -7
  105. package/dist/{pricing-refresh-qb0rv0ap.js → pricing-refresh-kefx9v3t.js} +9 -9
  106. package/dist/{rbac-roles-1gzdwbh1.js → rbac-roles-3m36rxm2.js} +8 -8
  107. package/dist/{rbac-roles-3mraey52.js → rbac-roles-z76xv0hq.js} +9 -9
  108. package/dist/{render-v2-agpa45zz.js → render-v2-wz73bs25.js} +9 -9
  109. package/dist/{resume-x0ydy9s9.js → resume-k4wzge8w.js} +14 -14
  110. package/dist/{setup-as9ga0d6.js → setup-2fp2pypp.js} +2 -2
  111. package/dist/{work-objects-y5g1y7ev.js → work-objects-6k0ywz3m.js} +29 -29
  112. package/dist/{worker-860c3e38.js → worker-5v3rxsdh.js} +43 -43
  113. package/dist/{x-m477fr0h.js → x-4mtdeym1.js} +2 -2
  114. package/openapi.json +777 -6
  115. package/package.json +8 -7
  116. package/src/agentmail/handlers.ts +21 -11
  117. package/src/apps/definition.ts +16 -0
  118. package/src/azure-devops/api.ts +52 -2
  119. package/src/azure-devops/boards-sync.ts +50 -20
  120. package/src/azure-devops/index.ts +1 -0
  121. package/src/azure-devops/outbound.ts +95 -0
  122. package/src/be/boot-seeds.ts +11 -0
  123. package/src/be/db/model-profiles.ts +315 -0
  124. package/src/be/db/tasks/read.ts +7 -0
  125. package/src/be/db.ts +53 -47
  126. package/src/be/migrations/219_relabel_pi_context_compactions.sql +22 -0
  127. package/src/be/migrations/220_model_profiles.sql +137 -0
  128. package/src/be/model-tier-resolution.ts +145 -11
  129. package/src/be/model-validation.ts +8 -1
  130. package/src/be/scripts/typecheck.ts +12 -0
  131. package/src/be/secret-registry.ts +6 -2
  132. package/src/be/seed-model-profiles.ts +41 -0
  133. package/src/be/seed-scripts/catalog/heartbeat-jev-gate.ts +368 -0
  134. package/src/be/seed-scripts/index.ts +9 -0
  135. package/src/be/swarm-config-guard.ts +1 -0
  136. package/src/commands/runner.ts +3 -17
  137. package/src/extensions/catalog.generated.json +17 -0
  138. package/src/extensions/contract-types.generated.ts +1 -1
  139. package/src/extensions/loader.ts +10 -5
  140. package/src/heartbeat/heartbeat.ts +2 -1
  141. package/src/http/agents.ts +49 -2
  142. package/src/http/all-routes.ts +1 -0
  143. package/src/http/apps.ts +30 -3
  144. package/src/http/config.ts +26 -2
  145. package/src/http/core.ts +4 -0
  146. package/src/http/index.ts +11 -1
  147. package/src/http/model-profiles.ts +303 -0
  148. package/src/http/pages-public.ts +11 -5
  149. package/src/http/poll.ts +16 -5
  150. package/src/http/script-runs.ts +1 -1
  151. package/src/http/scripts.ts +25 -2
  152. package/src/http/tasks.ts +18 -0
  153. package/src/http/webhooks.ts +1 -2
  154. package/src/pages/feedback-overlay.ts +65 -6
  155. package/src/providers/acp-targets.ts +1 -1
  156. package/src/providers/amp-adapter.ts +1 -1
  157. package/src/providers/dsh-adapter.ts +1 -1
  158. package/src/providers/pi-mono-extension.ts +17 -3
  159. package/src/providers/reasoning-effort.ts +10 -0
  160. package/src/rbac/legacy-policy.ts +8 -0
  161. package/src/rbac/permissions.ts +5 -0
  162. package/src/scheduler/deferred-task-waits.ts +18 -5
  163. package/src/scheduler/scheduler.ts +7 -1
  164. package/src/scripts-runtime/executors/quickjs-prelude.ts +75 -6
  165. package/src/scripts-runtime/executors/quickjs-runner.ts +85 -8
  166. package/src/scripts-runtime/executors/quickjs-web-shims.ts +1041 -0
  167. package/src/scripts-runtime/extract-args-schema.ts +9 -8
  168. package/src/scripts-runtime/quickjs-compat.ts +464 -0
  169. package/src/scripts-runtime/sdk-allowlist.ts +4 -0
  170. package/src/scripts-runtime/types/stdlib.d.ts +12 -0
  171. package/src/scripts-runtime/types/swarm-extension.d.ts +3 -0
  172. package/src/scripts-runtime/types/swarm-sdk.d.ts +12 -0
  173. package/src/server.ts +10 -1
  174. package/src/slack/blocks.ts +4 -2
  175. package/src/slack/reaction-shortcode.ts +14 -0
  176. package/src/slack/render-v2.ts +12 -7
  177. package/src/slack/responses.ts +5 -4
  178. package/src/slack/thread-buffer.ts +41 -22
  179. package/src/tasks/worker-follow-up.ts +11 -2
  180. package/src/tests/agentmail-filters.test.ts +47 -0
  181. package/src/tests/apps-spike2.test.ts +35 -0
  182. package/src/tests/azure-devops-outbound.test.ts +420 -0
  183. package/src/tests/db-task-read-contract.test.ts +2 -0
  184. package/src/tests/db-task-write-contract.test.ts +1 -0
  185. package/src/tests/deferred-task-wake.test.ts +78 -1
  186. package/src/tests/docs-index-footprint.test.js +2 -2
  187. package/src/tests/dsh-adapter.test.ts +1 -1
  188. package/src/tests/env-flag.test.ts +1 -0
  189. package/src/tests/extensions-catalog.test.ts +1 -0
  190. package/src/tests/extensions-example-comms-adherence-gate.test.ts +413 -0
  191. package/src/tests/extensions-loader.test.ts +15 -0
  192. package/src/tests/heartbeat-jev-gate-credentials.test.ts +168 -0
  193. package/src/tests/heartbeat-jev-gate.test.ts +385 -0
  194. package/src/tests/model-profile-backcompat.test.ts +327 -0
  195. package/src/tests/model-profile-providers.test.ts +302 -0
  196. package/src/tests/model-profile-runtime-secrets.test.ts +223 -0
  197. package/src/tests/model-profiles-api.test.ts +614 -0
  198. package/src/tests/pages-feedback-keys.test.ts +121 -0
  199. package/src/tests/pages-feedback.test.ts +3 -0
  200. package/src/tests/pi-context-compaction.test.ts +193 -0
  201. package/src/tests/profile-claim.test.ts +269 -0
  202. package/src/tests/quickjs-compat.test.ts +213 -0
  203. package/src/tests/rbac-engine.test.ts +21 -0
  204. package/src/tests/script-connections.test.ts +4 -0
  205. package/src/tests/script-executor-conformance.test.ts +252 -1
  206. package/src/tests/script-executor-quickjs.test.ts +96 -0
  207. package/src/tests/script-runs-http.test.ts +89 -0
  208. package/src/tests/scripts-http.test.ts +16 -0
  209. package/src/tests/secret-scrubber.test.ts +10 -0
  210. package/src/tests/seed-model-profiles.test.ts +174 -0
  211. package/src/tests/seed-scripts.test.ts +2 -2
  212. package/src/tests/skill-invocations.test.ts +4 -2
  213. package/src/tests/slack-blocks.test.ts +36 -0
  214. package/src/tests/slack-inline-output.test.ts +14 -0
  215. package/src/tests/slack-reaction-config.test.ts +20 -0
  216. package/src/tests/slack-render-v2.test.ts +37 -0
  217. package/src/tests/slack-thread-buffer.test.ts +98 -1
  218. package/src/tests/workflow-llm.test.ts +61 -0
  219. package/src/tools/model-profiles/list-model-profiles.ts +54 -0
  220. package/src/tools/request-human-input.ts +11 -3
  221. package/src/tools/script-apis.ts +5 -1
  222. package/src/tools/script-upsert.ts +4 -1
  223. package/src/tools/send-task.ts +24 -1
  224. package/src/tools/system-one-decide.ts +104 -0
  225. package/src/tools/task-action.ts +22 -1
  226. package/src/tools/tool-config.ts +6 -0
  227. package/src/types.ts +36 -1
  228. package/src/utils/secret-scrubber.ts +19 -12
  229. package/src/utils/vcs-repo-match.ts +29 -0
  230. package/src/workflows/executors/raw-llm.ts +8 -3
  231. package/src/workflows/executors/system-one-decision.ts +21 -11
  232. package/src/workflows/executors/workflow-llm.ts +22 -0
  233. package/src/workflows/index.ts +21 -12
  234. package/templates/extensions/README.md +1 -0
  235. package/templates/extensions/comms-adherence-gate/README.md +88 -0
  236. package/templates/extensions/comms-adherence-gate/hooks.ts +489 -0
  237. package/templates/extensions/comms-adherence-gate/manifest.json +8 -0
  238. package/dist/cli-5t4f29ez.js +0 -33
@@ -0,0 +1,223 @@
1
+ // An agent's default profile yields to a repo-scoped MODEL_OVERRIDE /
2
+ // REASONING_EFFORT_OVERRIDE only when that row reaches the claiming worker's
3
+ // env. A runtime-token worker without `config.read.secrets` never receives
4
+ // secret repo rows from `/api/config/resolved`, so for it they must not
5
+ // suppress the profile. Runs the real auth, `/api/poll` claim,
6
+ // `/api/config/resolved` and `fetchResolvedEnv` over HTTP.
7
+
8
+ import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
9
+ import { createServer, type IncomingMessage, type Server, type ServerResponse } from "node:http";
10
+ import {
11
+ closeDb,
12
+ createAgent,
13
+ createSwarmRepo,
14
+ createTaskExtended,
15
+ getDbClient,
16
+ initDb,
17
+ upsertSwarmConfig,
18
+ } from "../be/db";
19
+ import { createModelProfile } from "../be/db/model-profiles";
20
+ import { upsertRuntimeInstance } from "../be/multi-runtime";
21
+ import { mintRuntimeToken } from "../be/users";
22
+ import { fetchResolvedEnv } from "../commands/runner";
23
+ import { handleConfig } from "../http/config";
24
+ import { handleCore } from "../http/core";
25
+ import { handlePoll } from "../http/poll";
26
+ import { getPathSegments, parseQueryParams } from "../http/utils";
27
+ import { listenOnFreePort } from "./test-net";
28
+
29
+ const key = "model-profile-runtime-secrets-key";
30
+ const REPO = "desplega-ai/runtime-secret-repo";
31
+ const oldKey = process.env.AGENT_SWARM_API_KEY;
32
+ const oldEncryptionKey = process.env.SECRETS_ENCRYPTION_KEY;
33
+ let server: Server;
34
+ let baseUrl = "";
35
+
36
+ async function dispatch(req: IncomingMessage, res: ServerResponse) {
37
+ if (await handleCore(req, res, req.headers["x-agent-id"] as string | undefined, key)) return;
38
+ const segments = getPathSegments(req.url ?? "");
39
+ const query = parseQueryParams(req.url ?? "");
40
+ const id = req.headers["x-agent-id"] as string | undefined;
41
+ if (await handlePoll(req, res, segments, query, id)) return;
42
+ if (await handleConfig(req, res, segments, query)) return;
43
+ res.writeHead(404).end();
44
+ }
45
+
46
+ beforeAll(async () => {
47
+ process.env.AGENT_SWARM_API_KEY = key;
48
+ process.env.SECRETS_ENCRYPTION_KEY ??= Buffer.alloc(32, 7).toString("base64");
49
+ initDb(":memory:");
50
+ server = createServer((req, res) => {
51
+ dispatch(req, res).catch((error) => {
52
+ console.error(error);
53
+ if (!res.headersSent) res.writeHead(500);
54
+ res.end();
55
+ });
56
+ });
57
+ baseUrl = `http://127.0.0.1:${await listenOnFreePort(server)}`;
58
+ });
59
+
60
+ afterAll(async () => {
61
+ await new Promise<void>((resolve) => server.close(() => resolve()));
62
+ closeDb();
63
+ if (oldKey === undefined) delete process.env.AGENT_SWARM_API_KEY;
64
+ else process.env.AGENT_SWARM_API_KEY = oldKey;
65
+ if (oldEncryptionKey === undefined) delete process.env.SECRETS_ENCRYPTION_KEY;
66
+ else process.env.SECRETS_ENCRYPTION_KEY = oldEncryptionKey;
67
+ });
68
+
69
+ beforeEach(async () => {
70
+ const client = getDbClient();
71
+ await client.run("DELETE FROM agent_tasks");
72
+ await client.run("DELETE FROM swarm_config");
73
+ await client.run("DELETE FROM swarm_repos");
74
+ await client.run("DELETE FROM model_profiles");
75
+ });
76
+
77
+ type Credential = "runtime-token" | "operator-key";
78
+
79
+ /**
80
+ * An agent whose default profile (`gpt-5.6-sol`, high) differs from its
81
+ * retained MODEL_OVERRIDE / REASONING_EFFORT_OVERRIDE (`gpt-6.1-sol`, xhigh),
82
+ * plus the task's repo.
83
+ */
84
+ async function setup(name: string, isLead = false) {
85
+ const agent = await createAgent({ name, isLead, status: "idle", harnessProvider: "codex" });
86
+ await upsertSwarmConfig({
87
+ scope: "agent",
88
+ scopeId: agent.id,
89
+ key: "MODEL_OVERRIDE",
90
+ value: "gpt-6.1-sol",
91
+ });
92
+ await upsertSwarmConfig({
93
+ scope: "agent",
94
+ scopeId: agent.id,
95
+ key: "REASONING_EFFORT_OVERRIDE",
96
+ value: "xhigh",
97
+ });
98
+ await createModelProfile({
99
+ name: `${name}-profile`,
100
+ harness: "codex",
101
+ model: "gpt-5.6-sol",
102
+ effort: "high",
103
+ });
104
+ await upsertSwarmConfig({
105
+ scope: "agent",
106
+ scopeId: agent.id,
107
+ key: "MODEL_PROFILE",
108
+ value: `${name}-profile`,
109
+ exportToEnv: false,
110
+ });
111
+ const repo = await createSwarmRepo({
112
+ name: "runtime-secret-repo",
113
+ url: `https://github.com/${REPO}`,
114
+ });
115
+ return { agentId: agent.id, repoId: repo.id };
116
+ }
117
+
118
+ async function repoRow(repoId: string, key: string, value: string, isSecret: boolean) {
119
+ await upsertSwarmConfig({ scope: "repo", scopeId: repoId, key, value, isSecret });
120
+ }
121
+
122
+ /**
123
+ * Claim one task as the worker and return what it starts: the claim's
124
+ * `resolvedModel`, else the env's MODEL_OVERRIDE, and the trigger's `effort`,
125
+ * else REASONING_EFFORT_OVERRIDE (runner.ts spawnProviderProcess).
126
+ */
127
+ async function workerSelection(
128
+ agentId: string,
129
+ repoId: string,
130
+ credential: Credential,
131
+ ): Promise<{ model: string | null; effort: string | null }> {
132
+ await createTaskExtended("work", { agentId, vcsRepo: REPO });
133
+ const runtimeId = crypto.randomUUID();
134
+ let bearer = key;
135
+ const headers: Record<string, string> = { "X-Agent-ID": agentId };
136
+ if (credential === "runtime-token") {
137
+ await upsertRuntimeInstance({ id: runtimeId, agentId, reportedSlots: 1 });
138
+ bearer = (await mintRuntimeToken(agentId, runtimeId, 60_000)).plaintext;
139
+ headers["X-Runtime-Instance-ID"] = runtimeId;
140
+ }
141
+ const response = await fetch(`${baseUrl}/api/poll`, {
142
+ headers: { ...headers, Authorization: `Bearer ${bearer}` },
143
+ });
144
+ expect(response.status).toBe(200);
145
+ const polled = (await response.json()) as {
146
+ trigger: { task: { id: string; resolvedModel?: string; effort?: string } } | null;
147
+ };
148
+ const task = polled.trigger?.task;
149
+ if (!task) throw new Error("nothing claimed");
150
+ const { env } = await fetchResolvedEnv(baseUrl, bearer, agentId, {}, task.resolvedModel, {
151
+ repoId,
152
+ taskId: task.id,
153
+ provider: "codex",
154
+ });
155
+ await getDbClient().run("UPDATE agent_tasks SET status = 'completed'");
156
+ return {
157
+ model: task.resolvedModel || env.MODEL_OVERRIDE || null,
158
+ effort: task.effort || env.REASONING_EFFORT_OVERRIDE || null,
159
+ };
160
+ }
161
+
162
+ describe("repo overrides a runtime-token worker cannot read do not beat its default profile", () => {
163
+ test("secret repo model and effort: the worker runs its profile, not its retained overrides", async () => {
164
+ const { agentId, repoId } = await setup("secret-both");
165
+ await repoRow(repoId, "MODEL_OVERRIDE", "gpt-5.6-luna", true);
166
+ await repoRow(repoId, "REASONING_EFFORT_OVERRIDE", "low", true);
167
+
168
+ expect(await workerSelection(agentId, repoId, "runtime-token")).toEqual({
169
+ model: "gpt-5.6-sol",
170
+ effort: "high",
171
+ });
172
+ });
173
+
174
+ test("secret repo model only: profile model and effort", async () => {
175
+ const { agentId, repoId } = await setup("secret-model");
176
+ await repoRow(repoId, "MODEL_OVERRIDE", "gpt-5.6-luna", true);
177
+
178
+ expect(await workerSelection(agentId, repoId, "runtime-token")).toEqual({
179
+ model: "gpt-5.6-sol",
180
+ effort: "high",
181
+ });
182
+ });
183
+
184
+ test("secret repo effort only: profile model and effort", async () => {
185
+ const { agentId, repoId } = await setup("secret-effort");
186
+ await repoRow(repoId, "REASONING_EFFORT_OVERRIDE", "low", true);
187
+
188
+ expect(await workerSelection(agentId, repoId, "runtime-token")).toEqual({
189
+ model: "gpt-5.6-sol",
190
+ effort: "high",
191
+ });
192
+ });
193
+
194
+ test("control: a public repo row still beats the profile for a runtime-token worker", async () => {
195
+ const { agentId, repoId } = await setup("public-both");
196
+ await repoRow(repoId, "MODEL_OVERRIDE", "gpt-5.6-luna", false);
197
+ await repoRow(repoId, "REASONING_EFFORT_OVERRIDE", "low", false);
198
+
199
+ expect(await workerSelection(agentId, repoId, "runtime-token")).toEqual({
200
+ model: "gpt-5.6-luna",
201
+ effort: "low",
202
+ });
203
+ });
204
+
205
+ test("a worker that reads secrets gets the secret repo row, so it still wins", async () => {
206
+ const operator = await setup("operator-secret");
207
+ await repoRow(operator.repoId, "MODEL_OVERRIDE", "gpt-5.6-luna", true);
208
+ await repoRow(operator.repoId, "REASONING_EFFORT_OVERRIDE", "low", true);
209
+ expect(await workerSelection(operator.agentId, operator.repoId, "operator-key")).toEqual({
210
+ model: "gpt-5.6-luna",
211
+ effort: "low",
212
+ });
213
+
214
+ await getDbClient().run("DELETE FROM swarm_repos");
215
+ const lead = await setup("lead-secret", true);
216
+ await repoRow(lead.repoId, "MODEL_OVERRIDE", "gpt-5.6-luna", true);
217
+ await repoRow(lead.repoId, "REASONING_EFFORT_OVERRIDE", "low", true);
218
+ expect(await workerSelection(lead.agentId, lead.repoId, "runtime-token")).toEqual({
219
+ model: "gpt-5.6-luna",
220
+ effort: "low",
221
+ });
222
+ });
223
+ });