@desplega.ai/agent-swarm 1.166.0 → 1.168.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 (221) hide show
  1. package/dist/acp-adapter-zhejfecx.js +34 -0
  2. package/dist/{actions-qz3m692w.js → actions-7jg840nr.js} +25 -25
  3. package/dist/{amp-adapter-j1xbmsqz.js → amp-adapter-ye6rnetm.js} +4 -4
  4. package/dist/{anthropic-messages-hst62ws9.js → anthropic-messages-13yn502s.js} +4 -4
  5. package/dist/{app-43vrdmv8.js → app-ffes2kyf.js} +10 -9
  6. package/dist/{assistant-ertffmka.js → assistant-fhm5smyz.js} +26 -26
  7. package/dist/{azure-openai-responses-mh8tvf78.js → azure-openai-responses-9st34pfz.js} +5 -5
  8. package/dist/{boot-reembed-998jtpdg.js → boot-reembed-20q3nzvg.js} +9 -8
  9. package/dist/{boot-reembed-s7k7c6v2.js → boot-reembed-jvfadhsk.js} +9 -8
  10. package/dist/{boot-scrub-sweep-nepsk4t1.js → boot-scrub-sweep-jtfajzve.js} +10 -9
  11. package/dist/{boot-seeds-vvzs1zhk.js → boot-seeds-9qt0j8j2.js} +12 -11
  12. package/dist/{claude-adapter-2zvmekrp.js → claude-adapter-a8znhthz.js} +24 -22
  13. package/dist/{claude-managed-adapter-s49wbk8t.js → claude-managed-adapter-tzwxy803.js} +4 -4
  14. package/dist/{claude-managed-setup-cvhqq8qn.js → claude-managed-setup-nrhfx5b0.js} +3 -3
  15. package/dist/{claude-sdk-session-kkbxtw5q.js → claude-sdk-session-7x7chdrs.js} +7608 -7596
  16. package/dist/{cli-zsh093hk.js → cli-014vns2y.js} +63 -1
  17. package/dist/{cli-8kgvmkak.js → cli-0jngabtb.js} +1 -1
  18. package/dist/{cli-qvfk83fy.js → cli-13xwcqvj.js} +2 -2
  19. package/dist/{cli-0nhdwxd5.js → cli-43jg9xdw.js} +3 -3
  20. package/dist/{cli-cpfmqz3n.js → cli-47zfzshm.js} +5 -5
  21. package/dist/{cli-zy7vwsne.js → cli-5qkcdv8c.js} +16 -9
  22. package/dist/{cli-cwxhbf43.js → cli-5t5my0h8.js} +85 -2
  23. package/dist/{cli-y59h2pk9.js → cli-69egj654.js} +1 -1
  24. package/dist/{cli-68r1j4sg.js → cli-6bpvv8h4.js} +3 -3
  25. package/dist/{cli-yshaazs1.js → cli-74ez9b66.js} +63 -20
  26. package/dist/{cli-32bca7nr.js → cli-7avy4v8w.js} +1 -1
  27. package/dist/{cli-f2n17zhp.js → cli-7mhy3ths.js} +21 -18
  28. package/dist/{cli-q38n4bq0.js → cli-95zs84qw.js} +3 -3
  29. package/dist/{cli-fn697bnt.js → cli-9q0bh411.js} +21 -8
  30. package/dist/{cli-rggdq8wq.js → cli-9q24yyvs.js} +3 -3
  31. package/dist/cli-a35hzdve.js +33 -0
  32. package/dist/{cli-3qn4r334.js → cli-aj1e0h0d.js} +20 -14
  33. package/dist/{cli-rsy933yz.js → cli-bcspch6h.js} +30 -19
  34. package/dist/{cli-x3x0qwky.js → cli-bxqez1p0.js} +51 -23
  35. package/dist/{cli-ng97fjq7.js → cli-cmxkbzzs.js} +33 -7
  36. package/dist/{cli-df56tqdw.js → cli-ee0402n1.js} +198 -70
  37. package/dist/{cli-mwegs4af.js → cli-ew0c3a1j.js} +3 -3
  38. package/dist/{cli-ysnyhws9.js → cli-ewhwpyrb.js} +54 -51
  39. package/dist/{cli-j2jbr8af.js → cli-fhj4y16f.js} +1 -1
  40. package/dist/{cli-8t229cmr.js → cli-fqq06kkb.js} +30 -17
  41. package/dist/{cli-jvj6a0cv.js → cli-g6gx2dn1.js} +54 -19
  42. package/dist/{cli-nexwh8zg.js → cli-g7k7zbcz.js} +12 -5
  43. package/dist/{cli-ecknp7p1.js → cli-j05x7pnb.js} +10 -10
  44. package/dist/{cli-gt8azyet.js → cli-jb931ba2.js} +1 -1
  45. package/dist/{cli-nvvfd8bq.js → cli-jbzbr36n.js} +4091 -3793
  46. package/dist/{cli-3ddybbbx.js → cli-jvmtxqtc.js} +26 -14
  47. package/dist/{cli-pkke6geq.js → cli-km4x66j0.js} +83 -65
  48. package/dist/{cli-b0f70z24.js → cli-mn8qkd8v.js} +1 -1
  49. package/dist/{cli-0qjcsxgz.js → cli-nctfv2yy.js} +99 -9
  50. package/dist/{cli-4r1sjfc4.js → cli-ng1pmdx6.js} +10 -10
  51. package/dist/cli-nkqjj42q.js +104 -0
  52. package/dist/{cli-aqcey741.js → cli-p5jqk8fr.js} +1 -1
  53. package/dist/{cli-66zh26sp.js → cli-pgehz8e1.js} +57 -17
  54. package/dist/cli-q1vnz8wv.js +96 -0
  55. package/dist/{cli-fj86xr3c.js → cli-qd97v6z2.js} +2 -2
  56. package/dist/{cli-p4ztb80h.js → cli-r7eeddkq.js} +13 -8
  57. package/dist/{cli-8ssjez7e.js → cli-rhy7z1rp.js} +22 -11
  58. package/dist/{cli-vzc73wet.js → cli-rr5z1j4p.js} +87 -69
  59. package/dist/{cli-4mevd1hf.js → cli-s9y81s7j.js} +5 -5
  60. package/dist/{cli-dw2q9f7g.js → cli-svah6b6w.js} +4 -4
  61. package/dist/{cli-h3x613ze.js → cli-tc5zec7v.js} +6 -6
  62. package/dist/{cli-ttbsse3f.js → cli-tmhsxhb8.js} +1 -1
  63. package/dist/{cli-feh3dkah.js → cli-v1w7pbd8.js} +2 -1
  64. package/dist/{cli-10zmp1p7.js → cli-vr5avem2.js} +27 -32
  65. package/dist/{cli-bzr0ftht.js → cli-wnb9b2g3.js} +19 -0
  66. package/dist/{cli-krqm5f11.js → cli-xezn9nkh.js} +16 -13
  67. package/dist/{cli-8kt0bmgx.js → cli-xf713zz0.js} +2 -2
  68. package/dist/{cli-6zcfw0x7.js → cli-y195hv1a.js} +1 -1
  69. package/dist/{cli-x7kpr72a.js → cli-ytmmyj7f.js} +2 -2
  70. package/dist/{cli-v2a8x45d.js → cli-z48ggfyq.js} +11 -6
  71. package/dist/{cli-qbksc292.js → cli-zy39d433.js} +2 -0
  72. package/dist/cli.js +28 -28
  73. package/dist/{cloudflare-workers-ai-system-one-tt484t61.js → cloudflare-workers-ai-system-one-sta45jf9.js} +7 -5
  74. package/dist/{codex-adapter-dw1wxqt8.js → codex-adapter-n63k33d3.js} +18 -18
  75. package/dist/{codex-login-terbdsqq.js → codex-login-g1cyt5zm.js} +1 -1
  76. package/dist/{codex-session-runner-3cgb8y1g.js → codex-session-runner-xkxfd81a.js} +18 -18
  77. package/dist/{commands-mhxam6z2.js → commands-q4mfhn51.js} +9 -8
  78. package/dist/{cursor-adapter-2qda3st7.js → cursor-adapter-v8mm6bp5.js} +5 -5
  79. package/dist/{db-sjks418k.js → db-1t0bj3pc.js} +9 -8
  80. package/dist/{dispatcher-1es759rn.js → dispatcher-k1fhavcf.js} +24 -24
  81. package/dist/{dsh-adapter-bek4cyw9.js → dsh-adapter-5423p70m.js} +5 -5
  82. package/dist/{easter-egg-3d-mtpnw06s.js → easter-egg-3d-66zxervv.js} +2 -2
  83. package/dist/{execute-s4ccjq3n.js → execute-h4jgevyw.js} +61 -11
  84. package/dist/{google-generative-ai-d6nphg3q.js → google-generative-ai-b8zxrpax.js} +5 -5
  85. package/dist/{google-vertex-cpshkref.js → google-vertex-nrdd697t.js} +5 -5
  86. package/dist/{grok-adapter-ar9cw9jg.js → grok-adapter-phrndeb8.js} +18 -8
  87. package/dist/{handlers-1gkbhktr.js → handlers-59ywqwzh.js} +27 -27
  88. package/dist/{hook-vxa3tcsy.js → hook-atj0swc2.js} +14 -14
  89. package/dist/{hook-ds2zxq9v.js → hook-mew1chq9.js} +42 -31
  90. package/dist/{http-3h21z9md.js → http-ef2fnn9v.js} +953 -98
  91. package/dist/{index-n99fycq7.js → index-48pb9erw.js} +24 -24
  92. package/dist/{index-bn6cy8ps.js → index-9fc2h7ww.js} +24 -24
  93. package/dist/{index-9b65j1m5.js → index-gr69c43q.js} +9 -8
  94. package/dist/{index-f8xnqrye.js → index-h26wjcfy.js} +43 -24
  95. package/dist/{index-dkfxpkmm.js → index-w2yhye23.js} +25 -25
  96. package/dist/{keepalive-3xspa9jw.js → keepalive-qhcqz56v.js} +11 -10
  97. package/dist/{lead-6qphc124.js → lead-hpk5r3b1.js} +47 -47
  98. package/dist/{llama-cpp-classify-tgj35k8d.js → llama-cpp-classify-6erb7wwv.js} +3 -1
  99. package/dist/{maintenance-0teem1bb.js → maintenance-nvkyp2xe.js} +9 -8
  100. package/dist/{mistral-conversations-8445ddjn.js → mistral-conversations-059y51zt.js} +4 -4
  101. package/dist/{node-9sg7ajby.js → node-qm9eb6z0.js} +1 -1
  102. package/dist/{oauth-refresh-sweep-sh9q25xp.js → oauth-refresh-sweep-ngskthwh.js} +10 -9
  103. package/dist/{onboard-54qmnqxe.js → onboard-yfnkqhxc.js} +2 -2
  104. package/dist/{openai-codex-responses-9ynm88zy.js → openai-codex-responses-jzxkta9b.js} +8 -7
  105. package/dist/{openai-completions-nhqwjmaw.js → openai-completions-663yncfq.js} +4 -4
  106. package/dist/openai-decisions-vj9nkpf8.js +162 -0
  107. package/dist/{openai-responses-xx1c4kgm.js → openai-responses-m3g43rrt.js} +5 -5
  108. package/dist/{opencode-adapter-wjfa2wvd.js → opencode-adapter-yjyqynzf.js} +7 -7
  109. package/dist/{openrouter-images-8yx3487g.js → openrouter-images-kr60n6b9.js} +1 -1
  110. package/dist/{otel-impl-ycqzp2a1.js → otel-impl-hhm64r6m.js} +1 -1
  111. package/dist/{pi-messages-vxdt8v7j.js → pi-messages-xszk613n.js} +1 -1
  112. package/dist/{pi-mono-adapter-e77e9zst.js → pi-mono-adapter-0eg8kw2c.js} +68 -51
  113. package/dist/{pricing-refresh-atnkngqs.js → pricing-refresh-35f91nx2.js} +11 -10
  114. package/dist/{rbac-roles-j78wdksm.js → rbac-roles-97w370qj.js} +10 -9
  115. package/dist/{rbac-roles-9pjphh9q.js → rbac-roles-z6sje3gy.js} +9 -8
  116. package/dist/{render-v2-pxjs49fx.js → render-v2-n4v66bj3.js} +10 -9
  117. package/dist/{runtime-v2t1r79e.js → runtime-h9ha1mq6.js} +4 -4
  118. package/dist/{setup-ymej0xtx.js → setup-m3hhyv5f.js} +2 -2
  119. package/dist/{typesafe-system-one-datfr82p.js → typesafe-system-one-e988vty7.js} +7 -5
  120. package/dist/{virtual-modules-wm8mxdxx.js → virtual-modules-ftr9kwe8.js} +11 -11
  121. package/dist/{work-objects-3nh4ymzb.js → work-objects-mxf2c842.js} +27 -27
  122. package/dist/{worker-qa9fkz5s.js → worker-9t07z1gq.js} +47 -47
  123. package/openapi.json +72 -1
  124. package/package.json +10 -10
  125. package/src/be/memory/providers/openai-embedding.ts +7 -2
  126. package/src/be/memory/raters/llm-summarizer.ts +7 -2
  127. package/src/be/modelsdev-cache.json +61 -0
  128. package/src/be/script-connections.ts +40 -6
  129. package/src/be/seed-pricing.ts +84 -53
  130. package/src/be/swarm-config-guard.ts +14 -0
  131. package/src/commands/provider-credentials.ts +28 -5
  132. package/src/commands/runner.ts +7 -12
  133. package/src/extensions/catalog.generated.json +17 -0
  134. package/src/heartbeat/heartbeat.ts +91 -19
  135. package/src/hooks/hook.ts +72 -27
  136. package/src/http/mcp.ts +5 -0
  137. package/src/http/onboarding.ts +2 -0
  138. package/src/http/script-connections.ts +35 -0
  139. package/src/providers/acp-targets.ts +10 -5
  140. package/src/providers/amp-adapter.ts +1 -1
  141. package/src/providers/claude-adapter.ts +32 -0
  142. package/src/providers/claude-background-keepalive.ts +100 -0
  143. package/src/providers/claude-sdk-session.ts +6 -0
  144. package/src/providers/codex-adapter.ts +30 -3
  145. package/src/providers/codex-oauth/auth-json.ts +21 -4
  146. package/src/providers/codex-oauth/types.ts +2 -0
  147. package/src/providers/dsh-adapter.ts +11 -2
  148. package/src/providers/grok-adapter.ts +18 -2
  149. package/src/providers/opencode-adapter.ts +21 -5
  150. package/src/providers/pi-mono-extension.ts +56 -25
  151. package/src/providers/pricing-sources.md +2 -0
  152. package/src/scheduler/scheduler.ts +82 -13
  153. package/src/scripts-runtime/executors/registry.ts +1 -1
  154. package/src/server.ts +63 -3
  155. package/src/tests/acp-adapter.test.ts +152 -12
  156. package/src/tests/ci-unit-test-report.test.ts +73 -0
  157. package/src/tests/claude-background-keepalive-wiring.test.ts +220 -0
  158. package/src/tests/claude-background-keepalive.fixture.ts +162 -0
  159. package/src/tests/claude-background-keepalive.test.ts +139 -0
  160. package/src/tests/claude-default-route.test.ts +75 -0
  161. package/src/tests/claude-hook-output.test.ts +233 -0
  162. package/src/tests/codex-adapter-pool-auth-revalidate.test.ts +4 -2
  163. package/src/tests/codex-adapter.test.ts +166 -26
  164. package/src/tests/codex-oauth-auth-timestamps.test.ts +76 -0
  165. package/src/tests/codex-oauth.test.ts +4 -1
  166. package/src/tests/connection-embedded-auth.test.ts +51 -0
  167. package/src/tests/context-window.test.ts +2 -2
  168. package/src/tests/credential-check.test.ts +51 -1
  169. package/src/tests/docs-index-footprint.test.js +3 -3
  170. package/src/tests/dsh-adapter.test.ts +24 -2
  171. package/src/tests/enabled-tools.test.ts +269 -0
  172. package/src/tests/entrypoint-codex-oauth-seed.test.ts +12 -0
  173. package/src/tests/extensions-catalog.test.ts +1 -0
  174. package/src/tests/extensions-example-slack-reply-gate.test.ts +756 -0
  175. package/src/tests/grok-adapter.test.ts +13 -0
  176. package/src/tests/haiku-5-5-pricing.test.ts +38 -0
  177. package/src/tests/heartbeat-checklist.test.ts +158 -1
  178. package/src/tests/onboarding.test.ts +44 -0
  179. package/src/tests/opencode-adapter.test.ts +66 -3
  180. package/src/tests/opencode-plugin.test.ts +37 -0
  181. package/src/tests/openrouter-attribution-call-sites.test.ts +204 -0
  182. package/src/tests/openrouter-base-url.test.ts +30 -39
  183. package/src/tests/pi-mono-extension.test.ts +161 -2
  184. package/src/tests/pricing-refresh.test.ts +70 -1
  185. package/src/tests/reasoning-effort.test.ts +3 -1
  186. package/src/tests/schedule-target-type.test.ts +136 -0
  187. package/src/tests/script-connections-http.test.ts +45 -0
  188. package/src/tests/script-connections-mcp.test.ts +41 -0
  189. package/src/tests/script-executor-registry.test.ts +18 -0
  190. package/src/tests/send-task-self-send.test.ts +107 -0
  191. package/src/tests/telemetry-trigger.test.ts +12 -1
  192. package/src/tests/workflow-llm.test.ts +14 -0
  193. package/src/tests/workflow-system-one-decision-providers.test.ts +33 -0
  194. package/src/tools/request-human-input.ts +7 -2
  195. package/src/tools/script-connections/tool.ts +35 -3
  196. package/src/tools/send-task.ts +9 -1
  197. package/src/utils/enabled-tools.ts +27 -0
  198. package/src/utils/internal-ai/complete-structured.ts +11 -3
  199. package/src/utils/openrouter-base-url.ts +23 -0
  200. package/src/workflows/executors/raw-llm.ts +1 -0
  201. package/src/workflows/executors/system-one-decision.ts +2 -0
  202. package/src/workflows/executors/validate.ts +1 -0
  203. package/src/workflows/executors/workflow-llm.ts +9 -2
  204. package/templates/extensions/README.md +1 -0
  205. package/templates/extensions/slack-reply-gate/README.md +98 -0
  206. package/templates/extensions/slack-reply-gate/hooks.ts +582 -0
  207. package/templates/extensions/slack-reply-gate/manifest.json +8 -0
  208. package/templates/skills/agent-browser/SKILL.md +2 -0
  209. package/templates/skills/agent-browser/content.md +2 -0
  210. package/templates/skills/code-quality/content.md +1 -0
  211. package/templates/skills/heartbeat-runbook/content.md +1 -1
  212. package/templates/skills/swarm-scripts/SKILL.md +10 -0
  213. package/templates/skills/swarm-scripts/content.md +10 -0
  214. package/templates/skills/work-on-task/SKILL.md +4 -0
  215. package/templates/skills/work-on-task/content.md +4 -0
  216. package/templates/skills/workflow-iterate/content.md +9 -0
  217. package/dist/acp-adapter-yzfxn8vj.js +0 -23
  218. package/dist/cli-3dpj7atn.js +0 -11
  219. package/dist/cli-gvcgmx69.js +0 -183
  220. package/dist/{cli-jhm6hzjn.js → cli-mxvnnv5q.js} +7 -7
  221. package/dist/{codex-hook-d2rkvf39.js → codex-hook-15dgc4cb.js} +1 -1
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  lazyApi
3
- } from "./cli-nexwh8zg.js";
3
+ } from "./cli-g7k7zbcz.js";
4
4
 
5
5
  // node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.lazy.js
6
6
  var __rewriteRelativeImportExtension = function(path, preserveJsx) {
@@ -1,10 +1,13 @@
1
1
  import {
2
2
  runStopHookSessionSummarySubprocess
3
- } from "./cli-rsy933yz.js";
3
+ } from "./cli-bcspch6h.js";
4
4
  import {
5
5
  buildOtelTraceparentEnv,
6
6
  isHarnessOtelEnabled
7
7
  } from "./cli-q2j38vre.js";
8
+ import {
9
+ fetchInstalledMcpServers
10
+ } from "./cli-tng3131n.js";
8
11
  import {
9
12
  CTX_MODE_NUDGE_EVERY
10
13
  } from "./cli-zm7njwzd.js";
@@ -16,10 +19,7 @@ import {
16
19
  clampContextPercent,
17
20
  computeContextUsedUnified,
18
21
  getContextWindowSize
19
- } from "./cli-32bca7nr.js";
20
- import {
21
- fetchInstalledMcpServers
22
- } from "./cli-tng3131n.js";
22
+ } from "./cli-7avy4v8w.js";
23
23
  import {
24
24
  isClaudeBridgeEffective,
25
25
  resolveClaudeTransport
@@ -32,7 +32,7 @@ import {
32
32
  } from "./cli-2pdaysab.js";
33
33
  import {
34
34
  applyReasoningEffort
35
- } from "./cli-ttbsse3f.js";
35
+ } from "./cli-tmhsxhb8.js";
36
36
  import {
37
37
  swarmRuntimeInstanceId
38
38
  } from "./cli-re8z5p8p.js";
@@ -44,7 +44,7 @@ import {
44
44
  import {
45
45
  init_file_lock,
46
46
  withFileLock
47
- } from "./cli-jhm6hzjn.js";
47
+ } from "./cli-mxvnnv5q.js";
48
48
  import {
49
49
  CLAUDE_CREDENTIALS_HINT,
50
50
  init_credentials,
@@ -56,6 +56,10 @@ import {
56
56
  routeCredentialStatus,
57
57
  routeUnsetEnv
58
58
  } from "./cli-f9w1semm.js";
59
+ import {
60
+ getOpenRouterAttributionHeaders,
61
+ init_openrouter_base_url
62
+ } from "./cli-a35hzdve.js";
59
63
  import {
60
64
  init_env_flag,
61
65
  isEnvFlagEnabled
@@ -95,8 +99,74 @@ import { promisify } from "node:util";
95
99
  init_credentials();
96
100
  init_env_flag();
97
101
  init_file_lock();
102
+ init_openrouter_base_url();
98
103
  init_secret_scrubber();
99
104
 
105
+ // src/providers/claude-background-keepalive.ts
106
+ var BACKGROUND_KEEPALIVE_INTERVAL_MS = 60000;
107
+ var BACKGROUND_KEEPALIVE_MAX_IDLE_MS = 2 * 60 * 60000;
108
+
109
+ class ClaudeBackgroundKeepalive {
110
+ options;
111
+ liveTaskIds = [];
112
+ timer;
113
+ lastMessageAt = Date.now();
114
+ stopped = false;
115
+ intervalMs;
116
+ maxIdleMs;
117
+ constructor(options) {
118
+ this.options = options;
119
+ this.intervalMs = options.intervalMs ?? BACKGROUND_KEEPALIVE_INTERVAL_MS;
120
+ this.maxIdleMs = options.maxIdleMs ?? BACKGROUND_KEEPALIVE_MAX_IDLE_MS;
121
+ }
122
+ observe(message) {
123
+ if (this.stopped)
124
+ return;
125
+ this.lastMessageAt = Date.now();
126
+ if (message.type === "system" && message.subtype === "background_tasks_changed") {
127
+ const tasks = Array.isArray(message.tasks) ? message.tasks : [];
128
+ this.liveTaskIds = tasks.filter((task) => task && task.ambient !== true && typeof task.task_id === "string").map((task) => task.task_id);
129
+ }
130
+ if (this.liveTaskIds.length > 0)
131
+ this.start();
132
+ else
133
+ this.clearTimer();
134
+ }
135
+ stop() {
136
+ this.stopped = true;
137
+ this.liveTaskIds = [];
138
+ this.clearTimer();
139
+ }
140
+ start() {
141
+ if (this.timer !== undefined)
142
+ return;
143
+ if (!this.options.apiUrl || !this.options.taskId)
144
+ return;
145
+ this.timer = setInterval(() => this.tick(), this.intervalMs);
146
+ }
147
+ tick() {
148
+ if (Date.now() - this.lastMessageAt > this.maxIdleMs) {
149
+ this.clearTimer();
150
+ return;
151
+ }
152
+ const { apiUrl, apiKey, agentId, taskId } = this.options;
153
+ fetch(`${apiUrl}/api/active-sessions/heartbeat/${encodeURIComponent(taskId)}`, {
154
+ method: "PUT",
155
+ headers: {
156
+ "Content-Type": "application/json",
157
+ Authorization: `Bearer ${apiKey}`,
158
+ "X-Agent-ID": agentId
159
+ }
160
+ }).catch(() => {});
161
+ }
162
+ clearTimer() {
163
+ if (this.timer === undefined)
164
+ return;
165
+ clearInterval(this.timer);
166
+ this.timer = undefined;
167
+ }
168
+ }
169
+
100
170
  // src/providers/claude-session-events.ts
101
171
  init_secret_scrubber();
102
172
  function finiteNumber(value) {
@@ -628,6 +698,21 @@ function buildClaudeCodeRuntimeEnv(_sourceEnv) {
628
698
  DISABLE_BUG_COMMAND: "1"
629
699
  };
630
700
  }
701
+ function claudeOpenRouterAttributionEnv(env) {
702
+ const headers = getOpenRouterAttributionHeaders(env.ANTHROPIC_BASE_URL ?? "", env);
703
+ const names = new Set(Object.keys(headers).map((name) => name.toLowerCase()));
704
+ if (names.size === 0)
705
+ return {};
706
+ const kept = (env.ANTHROPIC_CUSTOM_HEADERS ?? "").split(`
707
+ `).filter((line) => {
708
+ const colon = line.indexOf(":");
709
+ const name = colon === -1 ? "" : line.slice(0, colon).trim().toLowerCase();
710
+ return line.trim() !== "" && !names.has(name);
711
+ });
712
+ const ours = Object.entries(headers).map(([name, value]) => `${name}: ${value}`);
713
+ return { ANTHROPIC_CUSTOM_HEADERS: [...kept, ...ours].join(`
714
+ `) };
715
+ }
631
716
  function buildClaudeSessionEnvironment(config, model, taskFilePath) {
632
717
  const sourceEnv = { ...withClaudeRouteEnv(config.env || process.env) };
633
718
  delete sourceEnv.AGENT_SWARM_CLAUDE_OAUTH_TOKEN;
@@ -636,6 +721,7 @@ function buildClaudeSessionEnvironment(config, model, taskFilePath) {
636
721
  env: {
637
722
  ENABLE_PROMPT_CACHING_1H: "1",
638
723
  ...sourceEnv,
724
+ ...claudeOpenRouterAttributionEnv(sourceEnv),
639
725
  ...buildClaudeCodeRuntimeEnv(sourceEnv),
640
726
  ...buildClaudeCodeOtelEnv(sourceEnv),
641
727
  ...reasoningApplication.kind === "claude-env" ? reasoningApplication.env : {},
@@ -709,6 +795,7 @@ class ClaudeSession {
709
795
  appliedReasoningEffort;
710
796
  lastAssistantText = "";
711
797
  transcript;
798
+ backgroundKeepalive;
712
799
  deliverSteering;
713
800
  constructor(config, model, taskFilePath, taskFileKey, sessionMcpConfig = null, claudeBinaryArgv = ["claude"], systemPromptFile = null, harnessVariant, harnessVariantMeta, queueSteeringSupported = false, runSessionSummary = runStopHookSessionSummarySubprocess) {
714
801
  this.config = config;
@@ -723,6 +810,7 @@ class ClaudeSession {
723
810
  this.contextWindowSize = getContextWindowSize(model);
724
811
  this.systemPromptFile = systemPromptFile;
725
812
  this.transcript = [`User: ${scrubSecrets(config.prompt)}`];
813
+ this.backgroundKeepalive = new ClaudeBackgroundKeepalive(config);
726
814
  const cmd = this.buildCommand();
727
815
  console.log(`\x1B[2m[${config.role}]\x1B[0m \x1B[36m▸\x1B[0m Spawning Claude (model: ${model}) for task ${config.taskId.slice(0, 8)}`);
728
816
  const sessionEnvironment = buildClaudeSessionEnvironment(config, model, taskFilePath);
@@ -897,6 +985,7 @@ class ClaudeSession {
897
985
  try {
898
986
  await Promise.all([stdoutPromise, stderrPromise]);
899
987
  } finally {
988
+ this.backgroundKeepalive.stop();
900
989
  this.closeStdin();
901
990
  }
902
991
  await logFileHandle.end();
@@ -945,6 +1034,7 @@ ${scrubSecrets(stderrOutput)}`);
945
1034
  if (json.type === "result") {
946
1035
  this.closeStdin();
947
1036
  }
1037
+ this.backgroundKeepalive.observe(json);
948
1038
  const normalized = normalizeClaudeMessage(json, {
949
1039
  taskId: this.config.taskId,
950
1040
  agentId: this.config.agentId,
@@ -1026,7 +1116,7 @@ class ClaudeAdapter {
1026
1116
  if (transport === "sdk" && isClaudeBridgeEffective(sourceEnv)) {
1027
1117
  throw new Error("CLAUDE_TRANSPORT=sdk cannot run with an effective Claude Bridge configuration. Select CLI transport or disable the bridge.");
1028
1118
  }
1029
- const claudeSdk = transport === "sdk" ? await import("./claude-sdk-session-kkbxtw5q.js") : undefined;
1119
+ const claudeSdk = transport === "sdk" ? await import("./claude-sdk-session-7x7chdrs.js") : undefined;
1030
1120
  claudeSdk?.validateClaudeSdkAdditionalArgs([
1031
1121
  ...claudeBinaryArgv.slice(1),
1032
1122
  ...config.additionalArgs ?? []
@@ -1133,4 +1223,4 @@ ${stderr}`.trim();
1133
1223
  }
1134
1224
  }
1135
1225
 
1136
- export { normalizeClaudeMessage, checkClaudeCredentials, withClaudeRouteEnv, getTaskFilePath, cleanupTaskFile, parseClaudeBinary, resolveClaudeQueueSteeringOverride, resolveClaudeBinary, parseClaudeBridgeEnabled, resolveClaudeBridgeEnabled, resolveClaudeBinaryArgv, preseedClaudeTrustDialog, resolveClaudeTrustDirs, mergeMcpConfig, createSessionMcpConfig, buildClaudeCodeOtelEnv, buildClaudeCodeRuntimeEnv, buildClaudeSessionEnvironment, runClaudeSessionSummary, getSystemPromptFilePath, resolveClaudePrompt, ClaudeAdapter };
1226
+ export { ClaudeBackgroundKeepalive, normalizeClaudeMessage, checkClaudeCredentials, withClaudeRouteEnv, getTaskFilePath, cleanupTaskFile, parseClaudeBinary, resolveClaudeQueueSteeringOverride, resolveClaudeBinary, parseClaudeBridgeEnabled, resolveClaudeBridgeEnabled, resolveClaudeBinaryArgv, preseedClaudeTrustDialog, resolveClaudeTrustDirs, mergeMcpConfig, createSessionMcpConfig, buildClaudeCodeOtelEnv, buildClaudeCodeRuntimeEnv, claudeOpenRouterAttributionEnv, buildClaudeSessionEnvironment, runClaudeSessionSummary, getSystemPromptFilePath, resolveClaudePrompt, ClaudeAdapter };
@@ -7,7 +7,7 @@ var package_default;
7
7
  var init_package = __esm(() => {
8
8
  package_default = {
9
9
  name: "@desplega.ai/agent-swarm",
10
- version: "1.166.0",
10
+ version: "1.168.0",
11
11
  description: "agent-swarm.dev — an open-source operating system for AI work: a lead agent breaks goals into tasks, routes them to specialized workers such as Claude Code or Codex, runs each worker in an isolated container, and preserves shared memory, tools, schedules, and review gates so delegated work compounds across sessions.",
12
12
  keywords: [
13
13
  "agent-swarm.dev",
@@ -154,8 +154,8 @@ var init_package = __esm(() => {
154
154
  "start:portless": "portless api.swarm bun --expose-gc src/http.ts",
155
155
  inspector: "bunx @modelcontextprotocol/inspector --transport stdio bun src/stdio.ts",
156
156
  "inspector:http": "bunx @modelcontextprotocol/inspector --transport http https://api.swarm.localhost:1355/mcp",
157
- lint: "biome check src apps/evals packages/ui-e2e packages/model-routing",
158
- "lint:fix": "biome check --write src apps/evals packages/ui-e2e packages/model-routing",
157
+ lint: "biome check src apps/evals packages/ui-e2e packages/model-routing packages/model-catalog",
158
+ "lint:fix": "biome check --write src apps/evals packages/ui-e2e packages/model-routing packages/model-catalog",
159
159
  format: "biome format --write src",
160
160
  "build:binary": "bun build ./src/cli.tsx ./src/providers/pi-binary-root/src/extensions/codemode/worker.ts --root ./src/providers/pi-binary-root --compile --bytecode --format=esm --compile-exec-argv='--expose-gc' --target=bun-linux-x64 --outfile ./dist/agent-swarm",
161
161
  "build:binary:arm64": "bun build ./src/cli.tsx ./src/providers/pi-binary-root/src/extensions/codemode/worker.ts --root ./src/providers/pi-binary-root --compile --bytecode --format=esm --compile-exec-argv='--expose-gc' --target=bun-linux-arm64 --outfile ./dist/agent-swarm",
@@ -209,21 +209,21 @@ var init_package = __esm(() => {
209
209
  dependencies: {
210
210
  "@agentclientprotocol/sdk": "1.7.0",
211
211
  "@ai-sdk/openai": "^3.0.41",
212
- "@anthropic-ai/claude-agent-sdk": "0.3.293",
213
- "@anthropic-ai/sdk": "^0.132.0",
212
+ "@anthropic-ai/claude-agent-sdk": "0.3.295",
213
+ "@anthropic-ai/sdk": "^0.132.1",
214
214
  "@asteasolutions/zod-to-openapi": "^8.0.0",
215
215
  "@aws-sdk/client-bedrock": "3.1048.0",
216
216
  "@cursor/sdk": "1.0.37",
217
217
  "@desplega.ai/business-use": "^0.4.2",
218
218
  "@desplega.ai/localtunnel": "^2.2.0",
219
- "@earendil-works/pi-agent-core": "^1.0.4",
220
- "@earendil-works/pi-ai": "^1.0.4",
221
- "@earendil-works/pi-coding-agent": "^1.0.4",
219
+ "@earendil-works/pi-agent-core": "^1.1.0",
220
+ "@earendil-works/pi-ai": "^1.1.0",
221
+ "@earendil-works/pi-coding-agent": "^1.1.0",
222
222
  "@inkjs/ui": "^2.0.0",
223
223
  "@jitl/quickjs-singlefile-mjs-release-sync": "0.32.0",
224
224
  "@linear/sdk": "^77.0.0",
225
- "@modelcontextprotocol/sdk": "1.29.0",
226
- "@openai/codex": "0.161.0",
225
+ "@modelcontextprotocol/sdk": "1.31.0",
226
+ "@openai/codex": "0.162.0",
227
227
  "@opencode-ai/sdk": "^1.18.35",
228
228
  "@openfort/openfort-node": "^0.9.1",
229
229
  "@opentelemetry/api": "^1.9.1",
@@ -0,0 +1,104 @@
1
+ import {
2
+ isRecord,
3
+ parseClassifierUsage,
4
+ postClassifierRequest,
5
+ requiredNumber
6
+ } from "./cli-q1vnz8wv.js";
7
+ import {
8
+ formatProviderError,
9
+ normalizeProviderError
10
+ } from "./cli-6map995v.js";
11
+
12
+ // node_modules/@earendil-works/pi-ai/dist/api/system-one-shared.js
13
+ function probabilities(label, value, id) {
14
+ if (!isRecord(value))
15
+ throw new Error(`${label} returned invalid probabilities for ${id}`);
16
+ return Object.fromEntries(Object.entries(value).map(([key, probability]) => [
17
+ key,
18
+ requiredNumber(label, probability, `probability for ${id}.${key}`)
19
+ ]));
20
+ }
21
+ function parseAnswers(label, value, context) {
22
+ if (!isRecord(value))
23
+ throw new Error(`${label} returned an unexpected response`);
24
+ const answers = [];
25
+ for (const [id, question] of Object.entries(context.questions)) {
26
+ const answer = value[id];
27
+ if (!isRecord(answer))
28
+ throw new Error(`${label} did not return an answer for ${id}`);
29
+ if (question.type === "choice") {
30
+ if (answer.type !== "choice" || typeof answer.choice !== "string") {
31
+ throw new Error(`${label} did not return a choice answer for ${id}`);
32
+ }
33
+ answers.push([
34
+ id,
35
+ {
36
+ type: "choice",
37
+ choice: answer.choice,
38
+ probabilities: probabilities(label, answer.probabilities, id),
39
+ confidence: requiredNumber(label, answer.confidence, `confidence for ${id}`)
40
+ }
41
+ ]);
42
+ } else if (question.type === "score") {
43
+ if (answer.type !== "score")
44
+ throw new Error(`${label} did not return a score answer for ${id}`);
45
+ answers.push([
46
+ id,
47
+ {
48
+ type: "score",
49
+ score: requiredNumber(label, answer.score, `score for ${id}`),
50
+ confidence: requiredNumber(label, answer.confidence, `confidence for ${id}`)
51
+ }
52
+ ]);
53
+ } else {
54
+ if (answer.type !== "noul")
55
+ throw new Error(`${label} did not return a bool answer for ${id}`);
56
+ answers.push([
57
+ id,
58
+ {
59
+ type: "bool",
60
+ probability: requiredNumber(label, answer.noul, `probability for ${id}`)
61
+ }
62
+ ]);
63
+ }
64
+ }
65
+ return Object.fromEntries(answers);
66
+ }
67
+ function wireRequest(context) {
68
+ return {
69
+ state: context.state,
70
+ questions: Object.fromEntries(Object.entries(context.questions).map(([id, question]) => [
71
+ id,
72
+ question.type === "bool" ? { ...question, type: "noul" } : question
73
+ ]))
74
+ };
75
+ }
76
+ async function classifySystemOne(transport, model, context, options) {
77
+ const output = {
78
+ api: model.api,
79
+ provider: model.provider,
80
+ model: model.id,
81
+ answers: {},
82
+ stopReason: "stop",
83
+ timestamp: Date.now()
84
+ };
85
+ try {
86
+ if (model.api !== transport.api)
87
+ throw new Error(`Unsupported classifier API: ${model.api}`);
88
+ if (context.images?.length)
89
+ throw new Error(`${transport.label} does not support image input`);
90
+ const body = await postClassifierRequest(transport.label, transport.url(model), model, transport.payload(model, wireRequest(context)), options);
91
+ const result = transport.output(body);
92
+ const usage = parseClassifierUsage(result.usage, model);
93
+ if (usage)
94
+ output.usage = usage;
95
+ output.answers = parseAnswers(transport.label, result.answers, context);
96
+ return output;
97
+ } catch (error) {
98
+ output.stopReason = options?.signal?.aborted ? "aborted" : "error";
99
+ output.errorMessage = formatProviderError(normalizeProviderError(error), `${transport.label} error`);
100
+ return output;
101
+ }
102
+ }
103
+
104
+ export { classifySystemOne };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  init_package,
3
3
  package_default
4
- } from "./cli-4r1sjfc4.js";
4
+ } from "./cli-ng1pmdx6.js";
5
5
 
6
6
  // src/commands/shared/client-config.ts
7
7
  init_package();
@@ -18,13 +18,13 @@ import {
18
18
  recoverIncompleteRuns,
19
19
  repointTrackerSyncBySwarmId,
20
20
  resolveLeadOnlyRecoveryAssignment
21
- } from "./cli-df56tqdw.js";
21
+ } from "./cli-ee0402n1.js";
22
22
  import {
23
23
  isMultiRuntimeEnabled
24
24
  } from "./cli-re8z5p8p.js";
25
25
  import {
26
26
  createApprovalFollowUpTask
27
- } from "./cli-jvj6a0cv.js";
27
+ } from "./cli-g6gx2dn1.js";
28
28
  import {
29
29
  MAX_EMPTY_POLLS,
30
30
  assignUnassignedTaskPending,
@@ -75,7 +75,7 @@ import {
75
75
  settleSupersededTaskDependents,
76
76
  supersedeTask,
77
77
  updateAgentStatus
78
- } from "./cli-yshaazs1.js";
78
+ } from "./cli-74ez9b66.js";
79
79
  import {
80
80
  TERMINAL_TASK_STATUSES,
81
81
  init_types
@@ -90,11 +90,18 @@ import {
90
90
  init_env_flag,
91
91
  isEnvFlagEnabled
92
92
  } from "./cli-ej6gbxfb.js";
93
+ import {
94
+ init_package,
95
+ package_default
96
+ } from "./cli-ng1pmdx6.js";
93
97
  import {
94
98
  init_secret_scrubber,
95
99
  scrubSecrets
96
100
  } from "./cli-wt7ccy4r.js";
97
101
 
102
+ // src/heartbeat/heartbeat.ts
103
+ init_package();
104
+
98
105
  // src/be/approval-sweeps.ts
99
106
  init_db();
100
107
  var DEFAULT_APPROVAL_REQUEST_AUTO_CANCELLATION_DAYS = 7;
@@ -356,6 +363,9 @@ function heartbeatChecklistIntervalMs() {
356
363
  function isHeartbeatChecklistDisabled() {
357
364
  return isEnvFlagEnabled("HEARTBEAT_CHECKLIST_DISABLE", false);
358
365
  }
366
+ function isBootTriageAlways() {
367
+ return isEnvFlagEnabled("HEARTBEAT_BOOT_TRIAGE_ALWAYS", false);
368
+ }
359
369
  var heartbeatInterval = null;
360
370
  var checklistInterval = null;
361
371
  var bootTriageTimeout = null;
@@ -1130,19 +1140,7 @@ async function gatherSystemStatus(options) {
1130
1140
  sections.push("- If the retry failed or the work is no longer needed, cancel it");
1131
1141
  sections.push("- Do NOT mark this boot triage as complete until all items are triaged");
1132
1142
  }
1133
- const orphanedTasks = [];
1134
- for (const status of ["pending", "offered"]) {
1135
- const tasks = await getTasksByStatus(status);
1136
- for (const task of tasks) {
1137
- const holderId = status === "offered" ? task.offeredTo : task.agentId;
1138
- if (!holderId)
1139
- continue;
1140
- const agent = agents.find((a) => a.id === holderId);
1141
- if (!agent || agent.status === "offline") {
1142
- orphanedTasks.push(task);
1143
- }
1144
- }
1145
- }
1143
+ const orphanedTasks = await getOrphanedPendingOrOfferedTasks(agents);
1146
1144
  if (orphanedTasks.length > 0) {
1147
1145
  sections.push("");
1148
1146
  sections.push("## Orphaned Tasks [auto-generated, NEEDS ATTENTION]");
@@ -1160,6 +1158,32 @@ async function gatherSystemStatus(options) {
1160
1158
  return sections.join(`
1161
1159
  `);
1162
1160
  }
1161
+ async function getOrphanedPendingOrOfferedTasks(agents) {
1162
+ const orphanedTasks = [];
1163
+ for (const status of ["pending", "offered"]) {
1164
+ const tasks = await getTasksByStatus(status);
1165
+ for (const task of tasks) {
1166
+ const holderId = status === "offered" ? task.offeredTo : task.agentId;
1167
+ if (!holderId)
1168
+ continue;
1169
+ const agent = agents.find((a) => a.id === holderId);
1170
+ if (!agent || agent.status === "offline") {
1171
+ orphanedTasks.push(task);
1172
+ }
1173
+ }
1174
+ }
1175
+ return orphanedTasks;
1176
+ }
1177
+ async function getBootTriageFindings() {
1178
+ const now = Date.now();
1179
+ const agents = await getAllAgents();
1180
+ return {
1181
+ rebootInterrupted: getRebootAffectedTasks().length,
1182
+ stalled: (await getStalledInProgressTasks(stallThresholdMinutes())).length,
1183
+ orphaned: (await getOrphanedPendingOrOfferedTasks(agents)).length,
1184
+ supersededWithoutResume: (await getSupersededTasksWithoutResume(new Date(now - ORPHAN_SUPERSEDE_MIN_AGE_MS).toISOString(), new Date(now - ORPHAN_SUPERSEDE_MAX_AGE_MS).toISOString())).length
1185
+ };
1186
+ }
1163
1187
  async function checkHeartbeatChecklist() {
1164
1188
  const lead = await getLeadAgent();
1165
1189
  if (!lead)
@@ -1323,7 +1347,23 @@ async function createBootTriageTask() {
1323
1347
  LIMIT 1`, [lead.id, ...TERMINAL_TASK_STATUSES]);
1324
1348
  if (existing)
1325
1349
  return;
1326
- const systemStatus = await gatherSystemStatus({ isBootTriage: true });
1350
+ if (!isBootTriageAlways()) {
1351
+ const findings = await getBootTriageFindings().catch((err) => {
1352
+ console.error("[Heartbeat] Boot triage findings failed, creating Lead task:", scrubSecrets(err instanceof Error ? err.message : String(err)));
1353
+ return null;
1354
+ });
1355
+ if (findings && Object.values(findings).every((count) => count === 0)) {
1356
+ console.log(`[Heartbeat] Clean boot (v${package_default.version}): 0 interrupted, stalled, orphaned, or resume-less superseded tasks; no boot-triage task for lead ${lead.name}`);
1357
+ return;
1358
+ }
1359
+ }
1360
+ const systemStatus = await gatherSystemStatus({ isBootTriage: true }).catch((err) => {
1361
+ const reason = scrubSecrets(err instanceof Error ? err.message : String(err));
1362
+ console.error("[Heartbeat] Boot triage status gathering failed:", reason);
1363
+ return `## System status unavailable
1364
+
1365
+ Status gathering failed after this boot (${reason}). Investigate the task, agent, and DB state directly.`;
1366
+ });
1327
1367
  const result = resolveTemplate("heartbeat.boot-triage", {
1328
1368
  system_status: systemStatus,
1329
1369
  heartbeat_content: isEffectivelyEmpty(heartbeatMd) ? "_No standing orders configured._" : heartbeatMd
@@ -0,0 +1,96 @@
1
+ import {
2
+ retryProviderRequest
3
+ } from "./cli-zy39d433.js";
4
+ import {
5
+ headersToRecord,
6
+ providerHeadersToRecord
7
+ } from "./cli-7kyk9jr3.js";
8
+ import {
9
+ calculateCost
10
+ } from "./cli-g7k7zbcz.js";
11
+
12
+ // node_modules/@earendil-works/pi-ai/dist/api/classifier-shared.js
13
+ function httpError(label, response, body) {
14
+ const error = new Error(`${label} returned ${response.status}`);
15
+ error.status = response.status;
16
+ error.headers = response.headers;
17
+ error.body = body;
18
+ return error;
19
+ }
20
+ function timeoutError(timeoutMs) {
21
+ const error = new Error(`Request timed out after ${timeoutMs}ms`);
22
+ error.name = "TimeoutError";
23
+ error.status = undefined;
24
+ error.headers = undefined;
25
+ error.body = "";
26
+ return error;
27
+ }
28
+ function isRecord(value) {
29
+ return typeof value === "object" && value !== null && !Array.isArray(value);
30
+ }
31
+ function requiredNumber(label, value, field) {
32
+ if (typeof value !== "number" || !Number.isFinite(value)) {
33
+ throw new Error(`${label} returned an invalid ${field}`);
34
+ }
35
+ return value;
36
+ }
37
+ function requestHeaders(model, apiKey, optionsHeaders) {
38
+ return providerHeadersToRecord({ authorization: `Bearer ${apiKey}`, "content-type": "application/json" }, model.headers, optionsHeaders) ?? {};
39
+ }
40
+ async function postClassifierRequest(label, url, model, body, options, noRetryStatuses) {
41
+ if (!options?.apiKey)
42
+ throw new Error(`No API key for provider: ${model.provider}`);
43
+ const apiKey = options.apiKey;
44
+ let payload = body;
45
+ const transformed = await options.onPayload?.(payload, model);
46
+ if (transformed !== undefined)
47
+ payload = transformed;
48
+ const requestFetch = options.fetch ?? globalThis.fetch;
49
+ const { response, json } = await retryProviderRequest(async () => {
50
+ const timeoutSignal = options.timeoutMs !== undefined ? AbortSignal.timeout(options.timeoutMs) : undefined;
51
+ const signal = options.signal && timeoutSignal ? AbortSignal.any([options.signal, timeoutSignal]) : options.signal ?? timeoutSignal;
52
+ try {
53
+ const next = await requestFetch(url, {
54
+ method: "POST",
55
+ headers: requestHeaders(model, apiKey, options.headers),
56
+ body: JSON.stringify(payload),
57
+ signal
58
+ });
59
+ if (!next.ok)
60
+ throw httpError(label, next, await next.text());
61
+ return { response: next, json: await next.json() };
62
+ } catch (error) {
63
+ if (timeoutSignal?.aborted && !options.signal?.aborted)
64
+ throw timeoutError(options.timeoutMs);
65
+ throw error;
66
+ }
67
+ }, {
68
+ maxRetries: options.maxRetries ?? 2,
69
+ maxRetryDelayMs: options.maxRetryDelayMs,
70
+ signal: options.signal,
71
+ noRetryStatuses
72
+ });
73
+ await options.onResponse?.({ status: response.status, headers: headersToRecord(response.headers) }, model);
74
+ return json;
75
+ }
76
+ function tokenCount(value) {
77
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
78
+ }
79
+ function parseClassifierUsage(value, model) {
80
+ if (!isRecord(value) || value.input_tokens === undefined && value.output_tokens === undefined)
81
+ return;
82
+ const input = tokenCount(value.input_tokens);
83
+ const output = tokenCount(value.output_tokens);
84
+ const usage = {
85
+ input,
86
+ output,
87
+ cacheRead: 0,
88
+ cacheWrite: 0,
89
+ totalTokens: input + output,
90
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }
91
+ };
92
+ calculateCost(model, usage);
93
+ return usage;
94
+ }
95
+
96
+ export { isRecord, requiredNumber, postClassifierRequest, parseClassifierUsage };
@@ -8,7 +8,7 @@ import {
8
8
  resolveGrammarConstrainedSampling,
9
9
  resolveJsonSchemaStrictSampling,
10
10
  transformMessages
11
- } from "./cli-8kt0bmgx.js";
11
+ } from "./cli-xf713zz0.js";
12
12
  import {
13
13
  sanitizeSurrogates
14
14
  } from "./cli-y2apkfe2.js";
@@ -18,7 +18,7 @@ import {
18
18
  renderSystemMessageUpdate,
19
19
  resolveTranscript,
20
20
  resolveTranscriptTools
21
- } from "./cli-nexwh8zg.js";
21
+ } from "./cli-g7k7zbcz.js";
22
22
  import {
23
23
  parseStreamingJson
24
24
  } from "./cli-1m2e086m.js";
@@ -6,18 +6,20 @@ import {
6
6
  import {
7
7
  parseModelStr,
8
8
  resolveCredential
9
- } from "./cli-f2n17zhp.js";
10
- import {
11
- DEFAULT_OPENROUTER_BASE_URL,
12
- getOpenRouterBaseUrl
13
- } from "./cli-3dpj7atn.js";
9
+ } from "./cli-7mhy3ths.js";
14
10
  import {
15
11
  complete,
16
12
  getBuiltinModel
17
- } from "./cli-pkke6geq.js";
13
+ } from "./cli-km4x66j0.js";
18
14
  import {
19
15
  exports_typebox
20
16
  } from "./cli-rb9m6rpe.js";
17
+ import {
18
+ DEFAULT_OPENROUTER_BASE_URL,
19
+ getOpenRouterAttributionHeaders,
20
+ getOpenRouterBaseUrl,
21
+ init_openrouter_base_url
22
+ } from "./cli-a35hzdve.js";
21
23
  import {
22
24
  init_zod
23
25
  } from "./cli-sd0mxa3c.js";
@@ -31,6 +33,7 @@ import {
31
33
 
32
34
  // src/utils/internal-ai/complete-structured.ts
33
35
  init_zod();
36
+ init_openrouter_base_url();
34
37
  init_secret_scrubber();
35
38
  var CLAUDE_CLI_TIMEOUT_MS = 30000;
36
39
  function stripJsonFences(raw) {
@@ -169,10 +172,12 @@ ${claudeUserPrompt}`, cred.modelDefault, opts.signal, jsonSchema);
169
172
  console.error(scrubSecrets(`internal-ai: unknown model ${provider}/${modelId} (callerTag=${callerTag})`));
170
173
  return null;
171
174
  }
172
- const openRouterBaseUrl = getOpenRouterBaseUrl(opts.env ?? process.env);
175
+ const env = opts.env ?? process.env;
176
+ const openRouterBaseUrl = getOpenRouterBaseUrl(env);
173
177
  if (provider === "openrouter" && openRouterBaseUrl !== DEFAULT_OPENROUTER_BASE_URL) {
174
178
  model = { ...model, baseUrl: openRouterBaseUrl };
175
179
  }
180
+ const headers = provider === "openrouter" ? getOpenRouterAttributionHeaders(model.baseUrl, env) : {};
176
181
  const completeFn = opts._complete ?? complete;
177
182
  const toolChoice = model.api === "anthropic-messages" ? { type: "tool", name: opts.toolName } : "required";
178
183
  let userPrompt = opts.userPrompt;
@@ -189,7 +194,7 @@ ${claudeUserPrompt}`, cred.modelDefault, opts.signal, jsonSchema);
189
194
  parameters: opts.toolSchema
190
195
  }
191
196
  ]
192
- }, { apiKey: cred.apiKey, signal: opts.signal, toolChoice });
197
+ }, { apiKey: cred.apiKey, signal: opts.signal, toolChoice, headers });
193
198
  const toolCall = msg.content.find((c) => c.type === "toolCall" && c.name === opts.toolName);
194
199
  if (!toolCall) {
195
200
  const assistantText = msg.content.filter((content) => content.type === "text").map((content) => content.text).join(`