@desplega.ai/agent-swarm 1.158.0 → 1.160.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 (378) hide show
  1. package/README.md +1 -1
  2. package/dist/{acp-adapter-xaq4n6pc.js → acp-adapter-2mjwcsfh.js} +124 -27
  3. package/dist/{actions-p9hz5yat.js → actions-e43rxskr.js} +35 -30
  4. package/dist/{anthropic-messages-51pz26qe.js → anthropic-messages-5qx80j9k.js} +106 -16
  5. package/dist/{app-g26wdcz6.js → app-mc7xfagn.js} +12 -10
  6. package/dist/{assistant-wydfqkb3.js → assistant-sedkzv3b.js} +42 -31
  7. package/dist/{azure-openai-responses-8gksqtmz.js → azure-openai-responses-nfb21z9a.js} +13 -14
  8. package/dist/{boot-reembed-se8zmdcp.js → boot-reembed-cqz9swn5.js} +13 -12
  9. package/dist/{boot-reembed-g5ckg9yb.js → boot-reembed-m3656j2n.js} +14 -13
  10. package/dist/{boot-scrub-logs-wfxqan61.js → boot-scrub-logs-xbxf5d7w.js} +11 -9
  11. package/dist/boot-seeds-gsetbs5y.js +28 -0
  12. package/dist/{claude-adapter-tv2x4f36.js → claude-adapter-dc6289mc.js} +28 -23
  13. package/dist/{claude-managed-adapter-41b2s7fd.js → claude-managed-adapter-naaw51me.js} +4 -3
  14. package/dist/{claude-managed-setup-60g8aazx.js → claude-managed-setup-eacvchx0.js} +12 -3
  15. package/dist/claude-sdk-session-bqp2xvym.js +29722 -0
  16. package/dist/{cli-987hg0aa.js → cli-0jwe9034.js} +2 -2
  17. package/dist/{cli-vj6h9kc2.js → cli-0m7wct0d.js} +59 -1
  18. package/dist/{cli-c07r9a2m.js → cli-0q3avq3s.js} +12 -11
  19. package/dist/{cli-19jenss3.js → cli-0wgxmwf6.js} +31 -10
  20. package/dist/cli-0x01kwrd.js +491 -0
  21. package/dist/cli-0xqde53b.js +176 -0
  22. package/dist/cli-10nhj6xq.js +8355 -0
  23. package/dist/{cli-tt9k8r35.js → cli-12tszpk9.js} +479 -910
  24. package/dist/{cli-5b6b62tz.js → cli-13hw4z91.js} +1 -1
  25. package/dist/{cli-cqt19wg6.js → cli-1a2j50m0.js} +11 -2
  26. package/dist/{cli-xberxa1r.js → cli-25fcq4b4.js} +2 -2
  27. package/dist/{cli-npssxgaz.js → cli-2pdaysab.js} +52 -2
  28. package/dist/cli-3qhyh31j.js +166 -0
  29. package/dist/cli-447dt1h3.js +3828 -0
  30. package/dist/cli-4derpgyk.js +115 -0
  31. package/dist/{cli-00w2c66p.js → cli-523679eh.js} +13 -2
  32. package/dist/{cli-y95wp3tg.js → cli-58e8yr0d.js} +1 -1
  33. package/dist/{cli-24k806sc.js → cli-5nbmpy8w.js} +3 -3
  34. package/dist/{cli-53sz8gkz.js → cli-6cs6wzxt.js} +1 -1
  35. package/dist/cli-741yb0xx.js +1027 -0
  36. package/dist/cli-74h9hscv.js +69 -0
  37. package/dist/cli-7cxw8b60.js +14910 -0
  38. package/dist/{cli-xhaeg2nc.js → cli-7e398j1h.js} +1 -1
  39. package/dist/cli-7kyk9jr3.js +22 -0
  40. package/dist/cli-89s4ngw3.js +183 -0
  41. package/dist/{cli-9b13ggd1.js → cli-89v6kyq7.js} +1 -0
  42. package/dist/{cli-ftfvggzx.js → cli-9hsgwz4w.js} +1045 -581
  43. package/dist/{cli-rqz8qbw7.js → cli-a8dvhzrt.js} +59 -20
  44. package/dist/{cli-j57qkrpq.js → cli-azhgzetg.js} +395 -56
  45. package/dist/cli-br0qdrnp.js +2150 -0
  46. package/dist/{cli-rj19hc34.js → cli-btfv8y88.js} +1 -1
  47. package/dist/{cli-8qmme6vd.js → cli-c8a36vcc.js} +2 -2
  48. package/dist/cli-cpvsaqsw.js +333 -0
  49. package/dist/cli-d5czme6m.js +17805 -0
  50. package/dist/{cli-h38x0qhg.js → cli-et8m9xsv.js} +4 -3
  51. package/dist/{cli-x24pshtq.js → cli-evvtk35y.js} +6 -6
  52. package/dist/{cli-7s3syk22.js → cli-f8gy6r2r.js} +5 -5
  53. package/dist/{cli-zt1227k1.js → cli-fkqhbg8g.js} +5 -3
  54. package/dist/{cli-9httdpyq.js → cli-g2whhdrf.js} +1 -1
  55. package/dist/cli-gt4hxb8p.js +99 -0
  56. package/dist/{cli-s8eet254.js → cli-jajnsm8q.js} +37184 -53838
  57. package/dist/{cli-7gtvvpbh.js → cli-jayjpjmb.js} +21 -12
  58. package/dist/{cli-68w09sjw.js → cli-jyk0ynw2.js} +27 -89
  59. package/dist/{cli-aehm1rbq.js → cli-jz4w45qt.js} +1152 -61
  60. package/dist/cli-kmcpfwn2.js +333856 -0
  61. package/dist/{cli-4198qc9v.js → cli-ktmyrtpn.js} +19 -3
  62. package/dist/{cli-j55n64b6.js → cli-mfe5btp9.js} +505 -189
  63. package/dist/{cli-q67y4wzm.js → cli-phsaj4xq.js} +24 -2
  64. package/dist/{cli-1hkrch3k.js → cli-pm1rst0h.js} +46 -7
  65. package/dist/{cli-pw9rj5t9.js → cli-pqetb83f.js} +1 -176
  66. package/dist/{cli-ggm92wcn.js → cli-ptr8h7kp.js} +20 -1
  67. package/dist/{cli-s8s499xy.js → cli-qbksc292.js} +3 -2
  68. package/dist/{cli-47m6dkd3.js → cli-qydf22qq.js} +597 -346
  69. package/dist/{cli-3gmcwfyx.js → cli-r1mxd1xh.js} +13 -6
  70. package/dist/cli-rb9m6rpe.js +4980 -0
  71. package/dist/cli-rykfmgwg.js +102 -0
  72. package/dist/{cli-5d10vd2m.js → cli-shkxm1vn.js} +6 -5
  73. package/dist/{cli-eetrvv91.js → cli-t3p77qk1.js} +1 -1
  74. package/dist/cli-tfky2838.js +756 -0
  75. package/dist/{cli-g8djp6qe.js → cli-tmqt7dpj.js} +40 -0
  76. package/dist/{cli-fgqxwrpv.js → cli-tpq83z89.js} +1631 -127
  77. package/dist/cli-trc24wk2.js +143 -0
  78. package/dist/{cli-aky8tctm.js → cli-tzx2ke1v.js} +3 -3
  79. package/dist/cli-v7fn5gms.js +1425 -0
  80. package/dist/{cli-wscdvcqz.js → cli-vctdqs5h.js} +1 -1
  81. package/dist/{cli-cw9zck8j.js → cli-xjcywfsf.js} +8 -4
  82. package/dist/{cli-k0g1sjzm.js → cli-ybgv4kw2.js} +18 -11
  83. package/dist/{cli-rjehvrts.js → cli-ydmcgve1.js} +8 -8
  84. package/dist/{cli-vhjwh6cz.js → cli-ygwm91nt.js} +1 -1
  85. package/dist/{cli-mcnhgnn1.js → cli-ykk5jaax.js} +184 -26
  86. package/dist/{cli-de1396j2.js → cli-ywybs5as.js} +7 -1
  87. package/dist/cli-zpep9q5e.js +491 -0
  88. package/dist/cli.js +41 -32
  89. package/dist/cloudflare-workers-ai-system-one-k6fbxw9d.js +46 -0
  90. package/dist/{codex-adapter-cnm8wk11.js → codex-adapter-nmxcatww.js} +26 -17
  91. package/dist/{codex-hook-9hptmdhj.js → codex-hook-20me6byw.js} +2 -2
  92. package/dist/{codex-login-x3jk4etn.js → codex-login-w6fz4xbz.js} +50 -5
  93. package/dist/{codex-session-runner-gfg547zj.js → codex-session-runner-90aa80ev.js} +24 -17
  94. package/dist/{commands-e3bn0p75.js → commands-8wcq1kn7.js} +11 -9
  95. package/dist/{db-03v9kzr2.js → db-j40kc4se.js} +25 -11
  96. package/dist/{devin-adapter-8vgkwh29.js → devin-adapter-dpsv6hd9.js} +1 -1
  97. package/dist/{dispatcher-nrmg8yht.js → dispatcher-4zqy768d.js} +34 -29
  98. package/dist/{dsh-adapter-k03nfqa6.js → dsh-adapter-mpnzgztj.js} +3 -3
  99. package/dist/{e2b-93x1k0j5.js → e2b-ez07znam.js} +4 -3
  100. package/dist/execute-6e7qeyk4.js +1017 -0
  101. package/dist/{google-generative-ai-bxmv50nc.js → google-generative-ai-k6n5nbpp.js} +6 -5
  102. package/dist/{google-vertex-809af514.js → google-vertex-44xxtcvp.js} +6 -5
  103. package/dist/{handlers-aqcedbyb.js → handlers-kjdce1w5.js} +37 -32
  104. package/dist/{hook-ybvmrmhq.js → hook-czx13n12.js} +19 -15
  105. package/dist/{hook-7vkskkre.js → hook-r2t4bqfg.js} +18 -14
  106. package/dist/{http-2eqa8t24.js → http-3bbb19be.js} +900 -145
  107. package/dist/{index-f6cryxwe.js → index-4a5njnm1.js} +2 -59
  108. package/dist/{index-x71bvq0p.js → index-668me3f0.js} +35 -30
  109. package/dist/{index-c6zsv1bd.js → index-7aj368tw.js} +13 -12
  110. package/dist/{index-1yks3qcr.js → index-fg0dsp6c.js} +34 -29
  111. package/dist/{index-chgdkq36.js → index-kkv84jq9.js} +34 -29
  112. package/dist/{index-a3vn96n8.js → index-zg7vp46x.js} +198 -54
  113. package/dist/{keepalive-vtkf5n99.js → keepalive-yra8dy57.js} +13 -11
  114. package/dist/{lead-h3sp357x.js → lead-va0pad2c.js} +54 -49
  115. package/dist/llama-cpp-classify-tgj35k8d.js +335 -0
  116. package/dist/{maintenance-jzeqtk3q.js → maintenance-hqewtmyg.js} +14 -13
  117. package/dist/{mistral-conversations-zx0kn554.js → mistral-conversations-pc3ntnxt.js} +14 -17
  118. package/dist/{oauth-refresh-sweep-qp3dzmdp.js → oauth-refresh-sweep-7rdt1m9n.js} +12 -10
  119. package/dist/{onboard-tmsp0qbz.js → onboard-j6ms6d0x.js} +6 -5
  120. package/dist/{openai-codex-responses-4vjqzvjp.js → openai-codex-responses-bbancrr0.js} +21 -8
  121. package/dist/{openai-completions-9hjrqbw8.js → openai-completions-jpaj5g2b.js} +9 -12
  122. package/dist/{openai-responses-7b1pn0xt.js → openai-responses-200k1wx4.js} +23 -18
  123. package/dist/{opencode-adapter-x6x2da1z.js → opencode-adapter-89rabkw2.js} +8 -7
  124. package/dist/{openrouter-images-9f9ek4rw.js → openrouter-images-8yx3487g.js} +5 -7
  125. package/dist/{otel-impl-9mz73q65.js → otel-impl-a792rjz0.js} +1 -1
  126. package/dist/{pi-messages-txpjj39q.js → pi-messages-vxdt8v7j.js} +2 -1
  127. package/dist/{pi-mono-adapter-m6dvv0dz.js → pi-mono-adapter-g7qzvr3v.js} +295 -70
  128. package/dist/pricing-refresh-wx0tp7cb.js +39 -0
  129. package/dist/quickjs-r1pq2107.wasm +0 -0
  130. package/dist/{rbac-roles-c9wqz83x.js → rbac-roles-8tzawacv.js} +12 -10
  131. package/dist/{render-v2-gryrh9h8.js → render-v2-mm7zswb8.js} +12 -10
  132. package/dist/runtime-zyssgse0.js +24 -0
  133. package/dist/{setup-q9720bt6.js → setup-8y70ys6t.js} +6 -5
  134. package/dist/typesafe-system-one-2ef30pec.js +27 -0
  135. package/dist/{virtual-modules-3htpgn8x.js → virtual-modules-6nmk7h3n.js} +22 -13
  136. package/dist/{work-objects-b76x7deb.js → work-objects-fg6xkb0n.js} +37 -32
  137. package/dist/{worker-6vrxbv41.js → worker-wmwhxsph.js} +54 -49
  138. package/openapi.json +1314 -91
  139. package/package.json +12 -11
  140. package/src/be/db/agents.ts +20 -0
  141. package/src/be/db/api-keys.ts +70 -0
  142. package/src/be/db/tasks/human-free.ts +442 -0
  143. package/src/be/db/tasks/read.ts +56 -1
  144. package/src/be/db/tasks/write.ts +107 -11
  145. package/src/be/db.ts +274 -188
  146. package/src/be/harness-model-support.ts +182 -0
  147. package/src/be/human-free-drain.ts +67 -0
  148. package/src/be/memory/providers/sqlite-store.ts +51 -15
  149. package/src/be/migrations/182_task_human_free_flag.sql +63 -0
  150. package/src/be/migrations/183_human_free_reclassify_queue.sql +25 -0
  151. package/src/be/migrations/184_model_catalog.sql +120 -0
  152. package/src/be/migrations/185_api_key_auth_failures.sql +6 -0
  153. package/src/be/migrations/186_api_key_seat_mismatch.sql +8 -0
  154. package/src/be/model-catalog-store.ts +644 -0
  155. package/src/be/model-tier-keys.ts +46 -0
  156. package/src/be/model-tier-resolution.ts +534 -0
  157. package/src/be/model-validation.ts +188 -0
  158. package/src/be/models-catalog.ts +61 -7
  159. package/src/be/modelsdev-cache.json +82 -0
  160. package/src/be/modelsdev-cache.ts +2 -0
  161. package/src/be/pricing-refresh.ts +154 -12
  162. package/src/be/scripts/typecheck.ts +15 -0
  163. package/src/be/seed/runner.ts +18 -12
  164. package/src/be/seed/types.ts +1 -0
  165. package/src/be/seed-scripts/catalog/task-failure-audit.ts +61 -9
  166. package/src/be/swarm-config-guard.ts +8 -0
  167. package/src/cli.tsx +10 -0
  168. package/src/commands/claude-managed-setup.ts +3 -1
  169. package/src/commands/codex-login.ts +67 -0
  170. package/src/commands/credential-outcome-report.ts +191 -0
  171. package/src/commands/rate-limit-outcome.ts +34 -4
  172. package/src/commands/runner.ts +357 -186
  173. package/src/commands/seat-mismatch-report.ts +46 -0
  174. package/src/extensions/apply-task-create.ts +27 -1
  175. package/src/extensions/contract-types.generated.ts +1 -1
  176. package/src/heartbeat/heartbeat.ts +79 -17
  177. package/src/heartbeat/templates.ts +2 -2
  178. package/src/http/agents.ts +62 -1
  179. package/src/http/api-keys.ts +140 -2
  180. package/src/http/codex-oauth-keep-warm.ts +3 -5
  181. package/src/http/index.ts +8 -0
  182. package/src/http/memory.ts +12 -5
  183. package/src/http/models-catalog.ts +357 -8
  184. package/src/http/poll.ts +54 -3
  185. package/src/http/schedules.ts +51 -6
  186. package/src/http/script-runs.ts +12 -0
  187. package/src/http/session-data.ts +2 -5
  188. package/src/http/stats.ts +6 -8
  189. package/src/http/status.ts +15 -1
  190. package/src/http/tasks.ts +90 -7
  191. package/src/http/usage-cache.ts +56 -15
  192. package/src/http/workflows.ts +46 -8
  193. package/src/mcp-client/http-client.ts +5 -0
  194. package/src/memory/recall-query.ts +74 -0
  195. package/src/prompts/base-prompt.ts +15 -4
  196. package/src/prompts/session-templates.ts +23 -1
  197. package/src/providers/acp-adapter.ts +1 -0
  198. package/src/providers/claude-adapter.ts +14 -5
  199. package/src/providers/claude-managed-adapter.ts +1 -0
  200. package/src/providers/claude-managed-models.ts +47 -161
  201. package/src/providers/claude-sdk-session.ts +3 -1
  202. package/src/providers/codex-adapter.ts +14 -14
  203. package/src/providers/codex-models.ts +65 -198
  204. package/src/providers/devin-adapter.ts +1 -0
  205. package/src/providers/dsh-adapter.ts +2 -1
  206. package/src/providers/harness-version.ts +83 -7
  207. package/src/providers/opencode-adapter.ts +53 -1
  208. package/src/providers/pi-binary-root/src/extensions/codemode/worker.ts +11 -0
  209. package/src/providers/pi-codemode-runtime.ts +17 -0
  210. package/src/providers/pi-mono-adapter.ts +361 -83
  211. package/src/providers/pi-mono-extension.ts +16 -1
  212. package/src/providers/reasoning-effort.ts +44 -140
  213. package/src/providers/types.ts +8 -0
  214. package/src/rbac/legacy-policy.ts +43 -0
  215. package/src/rbac/permissions.ts +15 -0
  216. package/src/scripts-runtime/sdk-allowlist.ts +4 -0
  217. package/src/scripts-runtime/types/stdlib.d.ts +15 -0
  218. package/src/scripts-runtime/types/swarm-extension.d.ts +3 -0
  219. package/src/scripts-runtime/types/swarm-sdk.d.ts +15 -0
  220. package/src/server.ts +6 -0
  221. package/src/slack/assistant.ts +13 -0
  222. package/src/slack/message-text.ts +56 -2
  223. package/src/slack/render-v2.ts +87 -37
  224. package/src/slack/session-status.ts +541 -0
  225. package/src/slack/thread-buffer.ts +10 -2
  226. package/src/tasks/sibling-awareness.ts +3 -2
  227. package/src/telemetry.ts +5 -4
  228. package/src/templates.d.ts +6 -0
  229. package/src/tests/agents-list-model-display.test.ts +4 -6
  230. package/src/tests/api-key-auth-failure-route.test.ts +142 -0
  231. package/src/tests/api-key-tracking.test.ts +368 -1
  232. package/src/tests/base-prompt.test.ts +43 -2
  233. package/src/tests/claude-adapter.test.ts +68 -0
  234. package/src/tests/claude-managed-adapter.test.ts +12 -18
  235. package/src/tests/codex-adapter.test.ts +2 -1
  236. package/src/tests/codex-auth-failure.test.ts +52 -0
  237. package/src/tests/codex-gpt6-release.test.ts +66 -5
  238. package/src/tests/codex-login.test.ts +116 -0
  239. package/src/tests/context-window.test.ts +7 -5
  240. package/src/tests/credential-exhaustion-policy.test.ts +118 -3
  241. package/src/tests/credential-outcome-report.test.ts +171 -0
  242. package/src/tests/credential-status-api.test.ts +103 -2
  243. package/src/tests/db-extraction-contract.test.ts +1 -0
  244. package/src/tests/db-task-read-contract.test.ts +10 -1
  245. package/src/tests/db-task-write-contract.test.ts +535 -2
  246. package/src/tests/docs-index-footprint.test.js +3 -3
  247. package/src/tests/dokploy-deploy.test.ts +315 -0
  248. package/src/tests/dsh-adapter.test.ts +1 -1
  249. package/src/tests/explicit-model-validation.test.ts +583 -0
  250. package/src/tests/extensions-pre-task.test.ts +73 -0
  251. package/src/tests/fetch-resolved-env.test.ts +46 -0
  252. package/src/tests/hang-watchdog-worker.ts +47 -0
  253. package/src/tests/hang-watchdog.test.ts +45 -0
  254. package/src/tests/hang-watchdog.ts +30 -0
  255. package/src/tests/harness-model-guard.test.ts +123 -0
  256. package/src/tests/harness-model-support.test.ts +374 -0
  257. package/src/tests/harness-version.test.ts +70 -3
  258. package/src/tests/heartbeat-checklist-config.test.ts +71 -0
  259. package/src/tests/heartbeat-checklist.test.ts +4 -2
  260. package/src/tests/heartbeat-orphan-resume.test.ts +48 -0
  261. package/src/tests/http-task-write-ownership.test.ts +172 -0
  262. package/src/tests/human-free-flag.test.ts +279 -0
  263. package/src/tests/human-free-oracle.ts +57 -0
  264. package/src/tests/human-free-reclassify.test.ts +669 -0
  265. package/src/tests/memory-fts-populate.test.ts +139 -0
  266. package/src/tests/memory-http-recall-gating.test.ts +125 -1
  267. package/src/tests/migration-046-budgets.test.ts +3 -3
  268. package/src/tests/model-catalog-migration.test.ts +134 -0
  269. package/src/tests/model-catalog-table.test.ts +249 -0
  270. package/src/tests/model-catalog-write-access.test.ts +387 -0
  271. package/src/tests/model-rate-limit-windows.test.ts +19 -0
  272. package/src/tests/model-tier-resolution.test.ts +611 -0
  273. package/src/tests/models-catalog.test.ts +1 -1
  274. package/src/tests/multi-runtime-enabled-stats.test.ts +65 -1
  275. package/src/tests/opencode-adapter.test.ts +157 -2
  276. package/src/tests/package-publish.test.ts +137 -36
  277. package/src/tests/pi-codemode.test.ts +246 -0
  278. package/src/tests/pi-mono-adapter.test.ts +3 -0
  279. package/src/tests/pi-native-mcp.test.ts +193 -0
  280. package/src/tests/pi-tool-exposure.test.ts +374 -0
  281. package/src/tests/preload.ts +3 -0
  282. package/src/tests/pricing-routes.test.ts +2 -2
  283. package/src/tests/prompt-template-session.test.ts +5 -3
  284. package/src/tests/rate-limit-event.test.ts +65 -0
  285. package/src/tests/rate-limit-outcome.test.ts +90 -0
  286. package/src/tests/rbac-engine.test.ts +77 -3
  287. package/src/tests/reasoning-effort.test.ts +109 -0
  288. package/src/tests/refresh-modelsdev-carryforward.test.ts +5 -5
  289. package/src/tests/runner-key-completion-outcome.test.ts +112 -0
  290. package/src/tests/runner-key-outcome-late-reset.test.ts +224 -0
  291. package/src/tests/runner-registration-retry.test.ts +149 -0
  292. package/src/tests/runtime-model-catalog.test.ts +86 -0
  293. package/src/tests/script-connections.test.ts +9 -0
  294. package/src/tests/seat-mismatch-report.test.ts +85 -0
  295. package/src/tests/seed-scripts.test.ts +138 -0
  296. package/src/tests/seed.test.ts +25 -0
  297. package/src/tests/session-costs.test.ts +14 -11
  298. package/src/tests/slack-message-text.test.ts +101 -0
  299. package/src/tests/slack-render-v2.test.ts +96 -0
  300. package/src/tests/slack-session-status.test.ts +626 -0
  301. package/src/tests/slack-thread-buffer-context.test.ts +93 -0
  302. package/src/tests/status.test.ts +29 -0
  303. package/src/tests/steering-promotion.test.ts +51 -0
  304. package/src/tests/store-progress-ownership.test.ts +136 -0
  305. package/src/tests/subscription-plans.test.ts +28 -0
  306. package/src/tests/task-cascade-fail.test.ts +148 -2
  307. package/src/tests/task-citations.test.ts +3 -2
  308. package/src/tests/tool-annotations.test.ts +1 -1
  309. package/src/tests/usage-cache.test.ts +166 -0
  310. package/src/tests/workflow-agent-task.test.ts +30 -0
  311. package/src/tests/workflow-executors.test.ts +5 -2
  312. package/src/tests/workflow-system-one-decision-review.test.ts +1021 -0
  313. package/src/tests/workflow-system-one-decision-save-warnings.test.ts +365 -0
  314. package/src/tests/workflow-system-one-decision.test.ts +2063 -0
  315. package/src/tests/workflow-triggers-v2.test.ts +320 -0
  316. package/src/tools/model-catalog/index.ts +137 -0
  317. package/src/tools/schedules/create-schedule.ts +17 -2
  318. package/src/tools/schedules/patch-schedule.ts +31 -1
  319. package/src/tools/schedules/update-schedule.ts +31 -1
  320. package/src/tools/send-task.ts +18 -1
  321. package/src/tools/store-progress.ts +49 -4
  322. package/src/tools/task-action.ts +14 -1
  323. package/src/tools/tool-config.ts +3 -1
  324. package/src/tools/workflows/create-workflow.ts +17 -4
  325. package/src/tools/workflows/patch-workflow-node.ts +17 -7
  326. package/src/tools/workflows/patch-workflow.ts +9 -2
  327. package/src/tools/workflows/update-workflow.ts +17 -4
  328. package/src/types.ts +41 -1
  329. package/src/utils/codex-auth-failure.ts +34 -0
  330. package/src/utils/context-window.ts +47 -53
  331. package/src/utils/credentials.ts +72 -30
  332. package/src/utils/error-tracker.ts +80 -0
  333. package/src/utils/harness-cli-version.ts +115 -0
  334. package/src/utils/harness-model-error.ts +33 -0
  335. package/src/utils/model-rate-limit-windows.ts +12 -0
  336. package/src/utils/runtime-model-catalog.ts +152 -0
  337. package/src/utils/spawn-model-guard.ts +43 -0
  338. package/src/utils/subscription-plans.ts +22 -0
  339. package/src/workflows/approval-resolution.ts +39 -0
  340. package/src/workflows/definition.ts +7 -0
  341. package/src/workflows/engine.ts +69 -1
  342. package/src/workflows/executors/agent-task.ts +16 -6
  343. package/src/workflows/executors/base.ts +53 -0
  344. package/src/workflows/executors/human-in-the-loop.ts +8 -8
  345. package/src/workflows/executors/index.ts +1 -0
  346. package/src/workflows/executors/registry.ts +2 -0
  347. package/src/workflows/executors/system-one-decision.ts +1232 -0
  348. package/src/workflows/executors/system-one-providers.ts +301 -0
  349. package/src/workflows/executors/workflow-llm.ts +13 -1
  350. package/src/workflows/model-validation.ts +45 -0
  351. package/src/workflows/patch-definition.ts +5 -0
  352. package/src/workflows/readiness.ts +86 -0
  353. package/src/workflows/recovery.ts +12 -12
  354. package/src/workflows/resume.ts +23 -9
  355. package/src/workflows/triggers.ts +85 -0
  356. package/templates/schedules/daily-blocker-digest/content.md +4 -5
  357. package/templates/skills/agent-browser/SKILL.md +6 -5
  358. package/templates/skills/agent-browser/content.md +6 -5
  359. package/templates/skills/heartbeat-runbook/config.json +1 -1
  360. package/templates/skills/heartbeat-runbook/content.md +2 -2
  361. package/templates/skills/swarm-scripts/SKILL.md +1 -1
  362. package/templates/skills/swarm-scripts/content.md +1 -1
  363. package/templates/skills/workflow-iterate/content.md +82 -1
  364. package/templates/workflows/claude-code-changelog-watch/content.md +2 -0
  365. package/templates/workflows/pr-review-status-sweep/content.md +2 -0
  366. package/dist/boot-seeds-s3q01xj3.js +0 -26
  367. package/dist/claude-sdk-session-t442qz6f.js +0 -39108
  368. package/dist/cli-0xjkj1j7.js +0 -61
  369. package/dist/cli-20cj37vt.js +0 -335561
  370. package/dist/cli-2djtr51b.js +0 -48
  371. package/dist/cli-65hjmev5.js +0 -1141
  372. package/dist/cli-aw1jwp1r.js +0 -14075
  373. package/dist/cli-h967nb6r.js +0 -6603
  374. package/dist/cli-vapyk8sw.js +0 -20
  375. package/dist/cli-ws6rqycm.js +0 -247
  376. package/dist/pricing-refresh-v1cbr9te.js +0 -27
  377. package/src/providers/modelsdev-reasoning.json +0 -6453
  378. package/dist/{index-f24115ge.js → index-2wsdsnmm.js} +3 -3
@@ -0,0 +1,193 @@
1
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import type { AgentSession, ExtensionFactory } from "@earendil-works/pi-coding-agent";
6
+ import { PiMonoAdapter, piExtensionFactories, toPiMcpServers } from "../providers/pi-mono-adapter";
7
+
8
+ describe("toPiMcpServers", () => {
9
+ test("maps stdio, http and sse entries to pi configs with direct exposure", () => {
10
+ const servers = toPiMcpServers({
11
+ local: { command: "context-mode", args: ["--stdio"], env: { TOKEN: "abc" } },
12
+ remote: { type: "http", url: "https://mcp.example.com/mcp", headers: { "X-Key": "k1" } },
13
+ legacy: { type: "sse", url: "https://sse.example.com/mcp", headers: {} },
14
+ });
15
+ expect(servers).toEqual({
16
+ local: {
17
+ type: "stdio",
18
+ command: "context-mode",
19
+ args: ["--stdio"],
20
+ env: { TOKEN: "abc" },
21
+ exposure: "direct",
22
+ },
23
+ remote: {
24
+ type: "http",
25
+ url: "https://mcp.example.com/mcp",
26
+ headers: { "X-Key": "k1" },
27
+ exposure: "direct",
28
+ },
29
+ legacy: { type: "http", url: "https://sse.example.com/mcp", headers: {}, exposure: "direct" },
30
+ });
31
+ });
32
+
33
+ test("no installed servers maps to an empty set", () => {
34
+ expect(toPiMcpServers(null)).toEqual({});
35
+ });
36
+
37
+ test("secret values are escaped for pi's config-value resolver", () => {
38
+ const servers = toPiMcpServers({
39
+ remote: { url: "https://x.example.com", headers: { Authorization: "!rm -rf ~ $HOME" } },
40
+ });
41
+ const remote = servers.remote as { headers: Record<string, string> };
42
+ expect(remote.headers.Authorization).toBe("$!rm -rf ~ $$HOME");
43
+ });
44
+ });
45
+
46
+ describe("piExtensionFactories — installed MCP", () => {
47
+ const swarm: ExtensionFactory = () => {};
48
+ test("adds pi's MCP extension only when the agent has installed servers", () => {
49
+ expect(piExtensionFactories(swarm, { toolDeferral: false })).toHaveLength(1);
50
+ expect(piExtensionFactories(swarm, { toolDeferral: false, installedMcp: true })).toHaveLength(
51
+ 2,
52
+ );
53
+ });
54
+ });
55
+
56
+ describe("PiMonoAdapter.createSession — native MCP for installed servers", () => {
57
+ const envKeys = [
58
+ "PI_CODING_AGENT_DIR",
59
+ "OPENROUTER_API_KEY",
60
+ "OPENROUTER_BASE_URL",
61
+ "PI_TOOL_DEFERRAL",
62
+ ] as const;
63
+ const saved: Record<string, string | undefined> = {};
64
+ let dir = "";
65
+
66
+ beforeEach(() => {
67
+ for (const key of envKeys) saved[key] = process.env[key];
68
+ dir = mkdtempSync(join(tmpdir(), "pi-native-mcp-"));
69
+ process.env.PI_CODING_AGENT_DIR = join(dir, "agent");
70
+ process.env.OPENROUTER_API_KEY = "example-test-key";
71
+ // The session prompts on creation; keep that request off the network.
72
+ process.env.OPENROUTER_BASE_URL = "http://127.0.0.1:9/api/v1";
73
+ delete process.env.PI_TOOL_DEFERRAL;
74
+ mkdirSync(join(dir, "agent"), { recursive: true });
75
+ });
76
+ afterEach(() => {
77
+ for (const key of envKeys) {
78
+ if (saved[key] === undefined) delete process.env[key];
79
+ else process.env[key] = saved[key];
80
+ }
81
+ rmSync(dir, { recursive: true, force: true });
82
+ });
83
+
84
+ /** Minimal streamable-HTTP MCP server with one `echo` tool; records a header. */
85
+ function serveThirdParty(seen: Array<string | null>) {
86
+ return Bun.serve({
87
+ port: 0,
88
+ async fetch(req) {
89
+ if (req.method !== "POST") return new Response(null, { status: 405 });
90
+ seen.push(req.headers.get("x-test"));
91
+ const body = (await req.json()) as {
92
+ id?: number;
93
+ method: string;
94
+ params?: { protocolVersion?: string };
95
+ };
96
+ if (body.id === undefined) return new Response(null, { status: 202 });
97
+ const result =
98
+ body.method === "initialize"
99
+ ? {
100
+ protocolVersion: body.params?.protocolVersion,
101
+ capabilities: { tools: {} },
102
+ serverInfo: { name: "third", version: "1" },
103
+ }
104
+ : body.method === "tools/list"
105
+ ? { tools: [{ name: "echo", description: "Echo", inputSchema: { type: "object" } }] }
106
+ : {};
107
+ return Response.json({ jsonrpc: "2.0", id: body.id, result });
108
+ },
109
+ });
110
+ }
111
+
112
+ test("installed HTTP servers connect through pi; mcp.json files are ignored", async () => {
113
+ const seen: Array<string | null> = [];
114
+ const third = serveThirdParty(seen);
115
+ const thirdUrl = `http://localhost:${third.port}/mcp`;
116
+ // Positive control shape: with pi's default loader these would register too.
117
+ writeFileSync(
118
+ join(dir, "agent", "mcp.json"),
119
+ JSON.stringify({ mcpServers: { stray: { url: thirdUrl, exposure: "direct" } } }),
120
+ );
121
+ mkdirSync(join(dir, ".pi"), { recursive: true });
122
+ writeFileSync(
123
+ join(dir, ".pi", "mcp.json"),
124
+ JSON.stringify({ mcpServers: { projstray: { url: thirdUrl, exposure: "direct" } } }),
125
+ );
126
+ const swarm = Bun.serve({
127
+ port: 0,
128
+ async fetch(req) {
129
+ const url = new URL(req.url);
130
+ if (url.pathname === "/mcp") {
131
+ const body = (await req.json()) as { id?: number; method?: string };
132
+ const result =
133
+ body.method === "tools/list"
134
+ ? { tools: [] }
135
+ : { protocolVersion: "2025-03-26", capabilities: {} };
136
+ return Response.json({ jsonrpc: "2.0", id: body.id ?? 1, result });
137
+ }
138
+ if (url.pathname.endsWith("/mcp-servers")) {
139
+ return Response.json({
140
+ servers: [
141
+ {
142
+ name: "third",
143
+ transport: "http",
144
+ isActive: true,
145
+ isEnabled: true,
146
+ url: thirdUrl,
147
+ headers: JSON.stringify({ "X-Test": "!echo $HOME" }),
148
+ },
149
+ {
150
+ name: "broken",
151
+ transport: "stdio",
152
+ isActive: true,
153
+ isEnabled: true,
154
+ command: join(dir, "missing-binary"),
155
+ args: "[]",
156
+ },
157
+ ],
158
+ });
159
+ }
160
+ return new Response("ok");
161
+ },
162
+ });
163
+ let session: AgentSession | undefined;
164
+ try {
165
+ const provider = await new PiMonoAdapter().createSession({
166
+ prompt: "hello",
167
+ systemPrompt: "",
168
+ model: "openrouter/google/gemini-3-flash-preview",
169
+ role: "worker",
170
+ agentId: "test-agent",
171
+ taskId: "test-task",
172
+ apiUrl: `http://localhost:${swarm.port}`,
173
+ apiKey: "example-test-key",
174
+ cwd: dir,
175
+ logFile: join(dir, "session.log"),
176
+ });
177
+ session = (provider as unknown as { agentSession: AgentSession }).agentSession;
178
+ const deadline = Date.now() + 5000;
179
+ while (!session.getActiveToolNames().includes("mcp__third__echo") && Date.now() < deadline) {
180
+ await Bun.sleep(50);
181
+ }
182
+ const active = session.getActiveToolNames();
183
+ expect(active).toContain("mcp__third__echo");
184
+ expect(active.some((name) => name.includes("stray"))).toBe(false);
185
+ // Header values reach the server verbatim: no shell, no $VAR expansion.
186
+ expect(seen).toContain("!echo $HOME");
187
+ } finally {
188
+ session?.dispose();
189
+ swarm.stop(true);
190
+ third.stop(true);
191
+ }
192
+ });
193
+ });
@@ -0,0 +1,374 @@
1
+ import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test";
2
+ import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import type { ExtensionFactory, ToolDefinition } from "@earendil-works/pi-coding-agent";
6
+ import * as piCodingAgent from "@earendil-works/pi-coding-agent";
7
+ import type { McpHttpClient, McpTool, McpToolCallResult } from "../mcp-client/http-client";
8
+ import {
9
+ createPiResourceLoader,
10
+ isCoreSwarmTool,
11
+ isPiToolDeferralEnabled,
12
+ mcpToolsToDefinitions,
13
+ PiMonoAdapter,
14
+ piDefaultToolAdditions,
15
+ piExtensionFactories,
16
+ SWARM_TOOL_NAMESPACE,
17
+ } from "../providers/pi-mono-adapter";
18
+ import type { ProviderSessionConfig } from "../providers/types";
19
+
20
+ const inputSchema = { type: "object", properties: {} };
21
+
22
+ function tool(name: string, extra: Partial<McpTool> = {}): McpTool {
23
+ return { name, description: `${name} tool`, inputSchema, ...extra };
24
+ }
25
+
26
+ function fakeClient(result: McpToolCallResult): McpHttpClient {
27
+ return { callTool: async () => result } as unknown as McpHttpClient;
28
+ }
29
+
30
+ function execute(def: ToolDefinition) {
31
+ return def.execute("call-1", {}, undefined, undefined, undefined as never);
32
+ }
33
+
34
+ function withEnv(key: string, value: string | undefined, fn: () => Promise<void> | void) {
35
+ return async () => {
36
+ const prev = process.env[key];
37
+ if (value === undefined) delete process.env[key];
38
+ else process.env[key] = value;
39
+ try {
40
+ await fn();
41
+ } finally {
42
+ if (prev === undefined) delete process.env[key];
43
+ else process.env[key] = prev;
44
+ }
45
+ };
46
+ }
47
+
48
+ describe("pi tool exposure mapping", () => {
49
+ const tools = [
50
+ tool("store-progress"),
51
+ tool("create-page"),
52
+ tool("slack-post", { _meta: { "anthropic/alwaysLoad": true } }),
53
+ ];
54
+ const client = fakeClient({ content: [] });
55
+
56
+ test("deferral on: core and manifest-preloaded tools stay direct, the rest defer", () => {
57
+ const defs = mcpToolsToDefinitions(client, tools, { deferNonCore: true });
58
+ const exposure = Object.fromEntries(defs.map((d) => [d.name, d.exposure]));
59
+ expect(exposure).toEqual({
60
+ "store-progress": "direct",
61
+ "create-page": "deferred",
62
+ "slack-post": "direct",
63
+ });
64
+ });
65
+
66
+ test("deferral off: no exposure is set, so pi declares every tool", () => {
67
+ const defs = mcpToolsToDefinitions(client, tools);
68
+ expect(defs.map((d) => d.exposure)).toEqual([undefined, undefined, undefined]);
69
+ });
70
+
71
+ test("core set follows CORE_TOOLS and alwaysLoad metadata only", () => {
72
+ expect(isCoreSwarmTool(tool("store-progress"))).toBe(true);
73
+ expect(isCoreSwarmTool(tool("create-page"))).toBe(false);
74
+ expect(isCoreSwarmTool(tool("create-page", { _meta: { "anthropic/alwaysLoad": true } }))).toBe(
75
+ true,
76
+ );
77
+ expect(isCoreSwarmTool(tool("create-page", { _meta: { "anthropic/alwaysLoad": false } }))).toBe(
78
+ false,
79
+ );
80
+ });
81
+
82
+ test("namespace is applied only when passed", () => {
83
+ const [withNs] = mcpToolsToDefinitions(client, [tool("create-page")], {
84
+ namespace: SWARM_TOOL_NAMESPACE,
85
+ });
86
+ const [withoutNs] = mcpToolsToDefinitions(client, [tool("create-page")]);
87
+ expect(withNs?.namespace).toEqual({ name: "agent-swarm" });
88
+ expect(withoutNs?.namespace).toBeUndefined();
89
+ });
90
+ });
91
+
92
+ describe("pi structuredContent pass-through", () => {
93
+ const outputSchema = {
94
+ type: "object",
95
+ properties: { success: { type: "boolean" }, details: {} },
96
+ additionalProperties: true,
97
+ };
98
+
99
+ test("returns structuredContent next to the text and keeps the server outputSchema", async () => {
100
+ const structuredContent = { success: true, message: "ok", details: { id: "t1" } };
101
+ const [def] = mcpToolsToDefinitions(
102
+ fakeClient({ content: [{ type: "text", text: "ok" }], structuredContent }),
103
+ [tool("get-task-details", { outputSchema })],
104
+ );
105
+ expect(def?.outputSchema).toEqual(outputSchema);
106
+ const result = await execute(def!);
107
+ expect(result.content).toEqual([{ type: "text", text: "ok" }]);
108
+ expect(result.structuredContent).toEqual(structuredContent);
109
+ });
110
+
111
+ test("tools without structuredContent or outputSchema are unchanged", async () => {
112
+ const [def] = mcpToolsToDefinitions(fakeClient({ content: [{ type: "text", text: "hi" }] }), [
113
+ tool("legacy"),
114
+ ]);
115
+ expect(def?.outputSchema).toBeUndefined();
116
+ const result = await execute(def!);
117
+ expect(result.structuredContent).toBeUndefined();
118
+ expect(result.content).toEqual([{ type: "text", text: "hi" }]);
119
+ });
120
+
121
+ test("isError still throws so pi reports the failure", async () => {
122
+ const [def] = mcpToolsToDefinitions(
123
+ fakeClient({
124
+ content: [{ type: "text", text: "boom" }],
125
+ structuredContent: { success: false },
126
+ isError: true,
127
+ }),
128
+ [tool("store-progress", { outputSchema })],
129
+ );
130
+ await expect(execute(def!)).rejects.toThrow("boom");
131
+ });
132
+ });
133
+
134
+ describe("pi extension factories and default tools per flag", () => {
135
+ const swarm: ExtensionFactory = () => {};
136
+
137
+ test("deferral off: only the swarm extension, no default-tool additions", () => {
138
+ expect(piExtensionFactories(swarm, { toolDeferral: false })).toEqual([swarm]);
139
+ expect(piDefaultToolAdditions({ toolDeferral: false })).toEqual([]);
140
+ });
141
+
142
+ test("deferral on: swarm extension plus tool_search", () => {
143
+ const factories = piExtensionFactories(swarm, { toolDeferral: true });
144
+ expect(factories).toHaveLength(2);
145
+ expect(factories[0]).toBe(swarm);
146
+ expect(piDefaultToolAdditions({ toolDeferral: true })).toEqual(["+tool_search"]);
147
+ });
148
+ });
149
+
150
+ describe("pi resource loader trust", () => {
151
+ test("task repo context files never reach the session; the server prompt does", async () => {
152
+ const root = mkdtempSync(join(tmpdir(), "pi-loader-"));
153
+ try {
154
+ const cwd = join(root, "repo");
155
+ const agentDir = join(root, "agent");
156
+ mkdirSync(cwd, { recursive: true });
157
+ mkdirSync(agentDir, { recursive: true });
158
+ writeFileSync(join(root, "AGENTS.md"), "ancestor instructions");
159
+ writeFileSync(join(cwd, "AGENTS.md"), "repo instructions");
160
+ writeFileSync(join(cwd, "CLAUDE.md"), "repo claude instructions");
161
+
162
+ const { resourceLoader } = await createPiResourceLoader({
163
+ cwd,
164
+ agentDir,
165
+ systemPrompt: "server prompt",
166
+ extensionFactories: [() => {}],
167
+ });
168
+
169
+ expect(resourceLoader.getAgentsFiles().agentsFiles).toEqual([]);
170
+ expect(resourceLoader.getAppendSystemPrompt()).toEqual(["server prompt"]);
171
+ expect(resourceLoader.getExtensions().extensions).toHaveLength(1);
172
+ } finally {
173
+ rmSync(root, { recursive: true, force: true });
174
+ }
175
+ });
176
+ });
177
+
178
+ describe("PI_TOOL_DEFERRAL flag", () => {
179
+ test("defaults off and parses boolean literals", () => {
180
+ expect(isPiToolDeferralEnabled({})).toBe(false);
181
+ expect(isPiToolDeferralEnabled({ PI_TOOL_DEFERRAL: "true" })).toBe(true);
182
+ expect(isPiToolDeferralEnabled({ PI_TOOL_DEFERRAL: "1" })).toBe(true);
183
+ expect(isPiToolDeferralEnabled({ PI_TOOL_DEFERRAL: "false" })).toBe(false);
184
+ });
185
+
186
+ test(
187
+ "traits.hasToolSearch follows the flag live",
188
+ withEnv("PI_TOOL_DEFERRAL", undefined, () => {
189
+ const adapter = new PiMonoAdapter();
190
+ expect(adapter.traits.hasToolSearch).toBe(false);
191
+ process.env.PI_TOOL_DEFERRAL = "true";
192
+ expect(adapter.traits.hasToolSearch).toBe(true);
193
+ }),
194
+ );
195
+ });
196
+
197
+ describe("PiMonoAdapter.createSession — tool deferral wiring", () => {
198
+ let spy: ReturnType<typeof spyOn> | undefined;
199
+ afterEach(() => spy?.mockRestore());
200
+
201
+ function serveTools() {
202
+ return Bun.serve({
203
+ port: 0,
204
+ async fetch(req) {
205
+ const url = new URL(req.url);
206
+ if (url.pathname === "/mcp") {
207
+ const body = (await req.json()) as { method?: string };
208
+ const tools =
209
+ body.method === "tools/list"
210
+ ? [tool("store-progress"), tool("create-page"), tool("memory-search")]
211
+ : undefined;
212
+ return Response.json({
213
+ jsonrpc: "2.0",
214
+ id: 1,
215
+ result: { protocolVersion: "2025-03-26", capabilities: {}, tools },
216
+ });
217
+ }
218
+ return Response.json({ servers: [] });
219
+ },
220
+ });
221
+ }
222
+
223
+ async function captureSessionOptions(): Promise<Record<string, unknown>> {
224
+ let captured: Record<string, unknown> = {};
225
+ spy = spyOn(piCodingAgent, "createAgentSession").mockImplementation((async (
226
+ opts: Record<string, unknown>,
227
+ ) => {
228
+ captured = opts;
229
+ return {
230
+ session: {
231
+ sessionId: "fake-session",
232
+ isStreaming: false,
233
+ model: undefined,
234
+ subscribe: () => () => {},
235
+ dispose: () => {},
236
+ bindExtensions: async () => {},
237
+ },
238
+ };
239
+ }) as typeof piCodingAgent.createAgentSession);
240
+ const server = serveTools();
241
+ try {
242
+ const config: ProviderSessionConfig = {
243
+ prompt: "hello",
244
+ systemPrompt: "",
245
+ model: "openrouter/google/gemini-3-flash-preview",
246
+ role: "worker",
247
+ agentId: "test-agent",
248
+ taskId: "test-task",
249
+ apiUrl: `http://localhost:${server.port}`,
250
+ apiKey: "example-test-key",
251
+ cwd: "/tmp",
252
+ logFile: `/tmp/pi-deferral-test-${Date.now()}-${Math.random().toString(36).slice(2)}.log`,
253
+ };
254
+ await new PiMonoAdapter().createSession(config);
255
+ } finally {
256
+ server.stop(true);
257
+ }
258
+ return captured;
259
+ }
260
+
261
+ test(
262
+ "flag on: non-core tools deferred and tool_search enabled by default",
263
+ withEnv("PI_TOOL_DEFERRAL", "true", async () => {
264
+ const opts = await captureSessionOptions();
265
+ const tools = opts.customTools as ToolDefinition[];
266
+ expect(Object.fromEntries(tools.map((t) => [t.name, t.exposure]))).toEqual({
267
+ "store-progress": "direct",
268
+ "create-page": "deferred",
269
+ "memory-search": "direct",
270
+ });
271
+ const settings = opts.settingsManager as piCodingAgent.SettingsManager;
272
+ expect(settings.getDefaultTools()).toContain("tool_search");
273
+ }),
274
+ );
275
+
276
+ test(
277
+ "flag off: every tool declared and no tool_search",
278
+ withEnv("PI_TOOL_DEFERRAL", undefined, async () => {
279
+ const opts = await captureSessionOptions();
280
+ const tools = opts.customTools as ToolDefinition[];
281
+ expect(tools.map((t) => t.exposure)).toEqual([undefined, undefined, undefined]);
282
+ const settings = opts.settingsManager as piCodingAgent.SettingsManager;
283
+ expect(settings.getDefaultTools() ?? []).not.toContain("tool_search");
284
+ }),
285
+ );
286
+ });
287
+
288
+ describe("PiMonoAdapter.createSession — real pi session", () => {
289
+ const envKeys = [
290
+ "PI_TOOL_DEFERRAL",
291
+ "PI_CODING_AGENT_DIR",
292
+ "OPENROUTER_API_KEY",
293
+ "OPENROUTER_BASE_URL",
294
+ ] as const;
295
+ const saved: Record<string, string | undefined> = {};
296
+ let agentDir = "";
297
+
298
+ beforeEach(() => {
299
+ for (const key of envKeys) saved[key] = process.env[key];
300
+ agentDir = mkdtempSync(join(tmpdir(), "pi-agent-dir-"));
301
+ process.env.PI_CODING_AGENT_DIR = agentDir;
302
+ process.env.OPENROUTER_API_KEY = "example-test-key";
303
+ // The session prompts on creation; keep that request off the network.
304
+ process.env.OPENROUTER_BASE_URL = "http://127.0.0.1:9/api/v1";
305
+ });
306
+ afterEach(() => {
307
+ for (const key of envKeys) {
308
+ if (saved[key] === undefined) delete process.env[key];
309
+ else process.env[key] = saved[key];
310
+ }
311
+ rmSync(agentDir, { recursive: true, force: true });
312
+ });
313
+
314
+ async function realSession() {
315
+ const server = Bun.serve({
316
+ port: 0,
317
+ async fetch(req) {
318
+ const url = new URL(req.url);
319
+ if (url.pathname !== "/mcp") return Response.json({ servers: [] });
320
+ const body = (await req.json()) as { id?: number; method?: string };
321
+ const result =
322
+ body.method === "tools/list"
323
+ ? { tools: [tool("store-progress"), tool("create-page")] }
324
+ : { protocolVersion: "2025-03-26", capabilities: {} };
325
+ return Response.json({ jsonrpc: "2.0", id: body.id ?? 1, result });
326
+ },
327
+ });
328
+ try {
329
+ const session = await new PiMonoAdapter().createSession({
330
+ prompt: "hello",
331
+ systemPrompt: "SWARM-PROMPT-MARKER",
332
+ model: "openrouter/google/gemini-3-flash-preview",
333
+ role: "worker",
334
+ agentId: "test-agent",
335
+ taskId: "test-task",
336
+ apiUrl: `http://localhost:${server.port}`,
337
+ apiKey: "example-test-key",
338
+ cwd: agentDir,
339
+ logFile: join(agentDir, "session.log"),
340
+ });
341
+ return (session as unknown as { agentSession: piCodingAgent.AgentSession }).agentSession;
342
+ } finally {
343
+ server.stop(true);
344
+ }
345
+ }
346
+
347
+ test("the swarm system prompt reaches the session", async () => {
348
+ const session = await realSession();
349
+ expect(session.systemPrompt).toContain("SWARM-PROMPT-MARKER");
350
+ session.dispose();
351
+ });
352
+
353
+ test("extensions in the task repo's .pi/ never load into the worker", async () => {
354
+ const marker = join(agentDir, "repo-extension-ran");
355
+ mkdirSync(join(agentDir, ".pi", "extensions"), { recursive: true });
356
+ writeFileSync(
357
+ join(agentDir, ".pi", "extensions", "repo.ts"),
358
+ `import { writeFileSync } from "node:fs";\nexport default () => writeFileSync(${JSON.stringify(marker)}, "1");\n`,
359
+ );
360
+ const session = await realSession();
361
+ expect(session.systemPrompt).toContain("SWARM-PROMPT-MARKER");
362
+ expect(existsSync(marker)).toBe(false);
363
+ session.dispose();
364
+ });
365
+
366
+ test("deferral on: deferred tools stay undeclared until tool_search loads them", async () => {
367
+ process.env.PI_TOOL_DEFERRAL = "true";
368
+ const session = await realSession();
369
+ expect(session.getActiveToolNames()).toContain("tool_search");
370
+ expect(session.getActiveToolNames()).toContain("store-progress");
371
+ expect(session.getActiveToolNames()).not.toContain("create-page");
372
+ session.dispose();
373
+ });
374
+ });
@@ -18,6 +18,9 @@ import { closeDb, getDb, initDb } from "../be/db";
18
18
  import "../be/seed-prompt-templates";
19
19
  import { getAllTemplateDefinitions } from "../prompts/registry";
20
20
  import { clearVolatileSecretsForTesting } from "../utils/secret-scrubber";
21
+ import { startHangWatchdog } from "./hang-watchdog";
22
+
23
+ startHangWatchdog();
21
24
 
22
25
  // @hono/node-server (pulled in transitively by @modelcontextprotocol/sdk's
23
26
  // streamableHttp transport) replaces globalThis.Response/Request with its own
@@ -17,7 +17,7 @@ import { seedPricingFromModelsDev } from "../be/seed-pricing";
17
17
  import { handleCore } from "../http/core";
18
18
  import { handlePricing } from "../http/pricing";
19
19
  import { getPathSegments, parseQueryParams } from "../http/utils";
20
- import { CODEX_MODEL_PRICING } from "../providers/codex-models";
20
+ import { codexModelPricingTable } from "../providers/codex-models";
21
21
  import { listenOnFreePort } from "./test-net";
22
22
 
23
23
  const TEST_DB_PATH = "./test-pricing-routes.sqlite";
@@ -112,7 +112,7 @@ describe("Phase 6 — /api/pricing REST surface", () => {
112
112
  (r: { provider: string; effectiveFrom: number }) =>
113
113
  r.provider === "codex" && r.effectiveFrom === 0,
114
114
  );
115
- for (const model of Object.keys(CODEX_MODEL_PRICING)) {
115
+ for (const model of Object.keys(codexModelPricingTable())) {
116
116
  for (const tokenClass of ["input", "cached_input", "output"]) {
117
117
  expect(
118
118
  seedRows.some(
@@ -35,6 +35,8 @@ const SYSTEM_TEMPLATES = [
35
35
  "system.agent.steering",
36
36
  "system.agent.steering.delivery",
37
37
  "system.agent.tool_preload",
38
+ "system.agent.tool_discovery.search",
39
+ "system.agent.tool_discovery.direct",
38
40
  "system.agent.tools_skills",
39
41
  "system.agent.worker",
40
42
  "system.agent.worker.remote",
@@ -112,11 +114,11 @@ describe("Session templates: registration", () => {
112
114
  expect(sessionSystemEventTypes("session")).toEqual([...SESSION_TEMPLATES].sort());
113
115
  });
114
116
 
115
- test("registers 26 system and session templates in total", () => {
117
+ test("registers 28 system and session templates in total", () => {
116
118
  const all = getAllTemplateDefinitions();
117
119
  const sessionSystem = all.filter((d) => d.category === "system" || d.category === "session");
118
- // 21 system blocks + 5 session composites.
119
- expect(sessionSystem.length).toBe(26);
120
+ // 23 system blocks + 5 session composites.
121
+ expect(sessionSystem.length).toBe(28);
120
122
  });
121
123
 
122
124
  test("drops the v1 blocks that prompt v2 deleted", () => {