@cohortapp/agent-sdk 2.16.0 → 2.18.4

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 (529) hide show
  1. package/.claude/settings.json +18 -0
  2. package/.env.example +23 -7
  3. package/README.md +1 -0
  4. package/bin/maestro.mjs +62 -0
  5. package/docs/guides/billing-console-keys.md +60 -0
  6. package/docs/guides/front-door-session.md +54 -9
  7. package/docs/guides/mac-mini.md +20 -25
  8. package/docs/guides/poller-daemon-setup.md +4 -1
  9. package/docs/guides/setup-wizard.md +1 -1
  10. package/docs/runbooks/fleet-rollout.md +156 -0
  11. package/docs/runbooks/mac-mini-bootstrap.md +12 -14
  12. package/lib/action-executor.js +19 -3
  13. package/lib/budget-guard.mjs +279 -3
  14. package/lib/channels/base-adapter.mjs +3 -1
  15. package/lib/channels/contract.mjs +2 -1
  16. package/lib/channels/inbox-item.mjs +8 -0
  17. package/lib/claude-bin.mjs +5 -6
  18. package/lib/cli/doctor-checks.mjs +141 -10
  19. package/lib/cli/global-setup-extras.mjs +5 -1
  20. package/lib/cli/inbox.mjs +100 -15
  21. package/lib/cli/seat-auth.mjs +463 -0
  22. package/lib/cli/session.mjs +80 -12
  23. package/lib/collective/capture.mjs +8 -6
  24. package/lib/collective/global-config.mjs +63 -1
  25. package/lib/collective/presence.mjs +142 -5
  26. package/lib/comms/send-gate.mjs +559 -1
  27. package/lib/context/budget.mjs +327 -0
  28. package/lib/context/history-scope.mjs +138 -0
  29. package/lib/diagnostics/alerts.mjs +49 -0
  30. package/lib/diagnostics/cadence-output-freshness.mjs +288 -0
  31. package/lib/engine/agents/definitions.mjs +343 -0
  32. package/lib/engine/agents/persist.mjs +275 -0
  33. package/lib/engine/agents/runtime.mjs +748 -0
  34. package/lib/engine/agents/usage.mjs +95 -0
  35. package/lib/engine/auth-status.mjs +139 -0
  36. package/lib/engine/budget.mjs +194 -0
  37. package/lib/engine/cli.mjs +1204 -0
  38. package/lib/engine/commands/index.mjs +269 -0
  39. package/lib/engine/context/budget.mjs +219 -0
  40. package/lib/engine/context/cache.mjs +125 -0
  41. package/lib/engine/context/child-env.mjs +215 -0
  42. package/lib/engine/context/compaction.mjs +342 -0
  43. package/lib/engine/context/images.mjs +90 -0
  44. package/lib/engine/context/instructions.mjs +327 -0
  45. package/lib/engine/context/lazy-instructions.mjs +169 -0
  46. package/lib/engine/context/manager.mjs +182 -0
  47. package/lib/engine/context/real-path.mjs +91 -0
  48. package/lib/engine/context/secret-values.mjs +163 -0
  49. package/lib/engine/context/settings.mjs +274 -0
  50. package/lib/engine/context/stream-input.mjs +159 -0
  51. package/lib/engine/guard.mjs +152 -0
  52. package/lib/engine/hooks.mjs +713 -0
  53. package/lib/engine/loop.mjs +560 -0
  54. package/lib/engine/mcp/client.mjs +254 -0
  55. package/lib/engine/mcp/config.mjs +301 -0
  56. package/lib/engine/mcp/http.mjs +201 -0
  57. package/lib/engine/mcp/index.mjs +146 -0
  58. package/lib/engine/mcp/jsonrpc.mjs +147 -0
  59. package/lib/engine/mcp/naming.mjs +66 -0
  60. package/lib/engine/mcp/resources.mjs +89 -0
  61. package/lib/engine/mcp/results.mjs +133 -0
  62. package/lib/engine/mcp/stdio.mjs +137 -0
  63. package/lib/engine/mcp/supervisor.mjs +116 -0
  64. package/lib/engine/messages.mjs +104 -0
  65. package/lib/engine/output/json.mjs +164 -0
  66. package/lib/engine/output/stream-json.mjs +266 -0
  67. package/lib/engine/permissions.mjs +845 -0
  68. package/lib/engine/process-identity.mjs +164 -0
  69. package/lib/engine/process-tree.mjs +551 -0
  70. package/lib/engine/prompt.mjs +60 -0
  71. package/lib/engine/session/store.mjs +299 -0
  72. package/lib/engine/session-runtime/args.mjs +97 -0
  73. package/lib/engine/session-runtime/host.mjs +143 -0
  74. package/lib/engine/session-runtime/inbox.mjs +122 -0
  75. package/lib/engine/session-runtime/notifications.mjs +129 -0
  76. package/lib/engine/session-runtime/registry.mjs +328 -0
  77. package/lib/engine/session-runtime/runner.mjs +344 -0
  78. package/lib/engine/session-runtime/socket.mjs +212 -0
  79. package/lib/engine/session-runtime/wakeup.mjs +115 -0
  80. package/lib/engine/skills/index.mjs +321 -0
  81. package/lib/engine/tools/bash-background.mjs +533 -0
  82. package/lib/engine/tools/bash.mjs +216 -0
  83. package/lib/engine/tools/edit.mjs +97 -0
  84. package/lib/engine/tools/glob.mjs +81 -0
  85. package/lib/engine/tools/grep.mjs +224 -0
  86. package/lib/engine/tools/index.mjs +84 -0
  87. package/lib/engine/tools/list-agents.mjs +32 -0
  88. package/lib/engine/tools/ls.mjs +127 -0
  89. package/lib/engine/tools/monitor.mjs +82 -0
  90. package/lib/engine/tools/notebook-edit.mjs +218 -0
  91. package/lib/engine/tools/read.mjs +103 -0
  92. package/lib/engine/tools/schedule-wakeup.mjs +45 -0
  93. package/lib/engine/tools/schema.mjs +144 -0
  94. package/lib/engine/tools/send-message.mjs +77 -0
  95. package/lib/engine/tools/session.mjs +70 -0
  96. package/lib/engine/tools/todo.mjs +144 -0
  97. package/lib/engine/tools/toolsearch.mjs +217 -0
  98. package/lib/engine/tools/walk.mjs +193 -0
  99. package/lib/engine/tools/web-switch.mjs +31 -0
  100. package/lib/engine/tools/webfetch-html.mjs +387 -0
  101. package/lib/engine/tools/webfetch-net.mjs +340 -0
  102. package/lib/engine/tools/webfetch.mjs +198 -0
  103. package/lib/engine/tools/websearch.mjs +91 -0
  104. package/lib/engine/tools/workflow.mjs +95 -0
  105. package/lib/engine/tools/write.mjs +76 -0
  106. package/lib/engine/tui/line-editor.mjs +137 -0
  107. package/lib/engine/tui/render.mjs +86 -0
  108. package/lib/engine/tui/tui.mjs +274 -0
  109. package/lib/engine/wire/anthropic-messages.mjs +263 -0
  110. package/lib/engine/wire/effort.mjs +36 -0
  111. package/lib/engine/wire/errors.mjs +496 -0
  112. package/lib/engine/wire/http.mjs +441 -0
  113. package/lib/engine/wire/index.mjs +76 -0
  114. package/lib/engine/wire/openai-chat.mjs +332 -0
  115. package/lib/engine/wire/prompt-cache.mjs +79 -0
  116. package/lib/engine/wire/search.mjs +140 -0
  117. package/lib/engine/wire/sse.mjs +114 -0
  118. package/lib/engine/wire/stall.mjs +349 -0
  119. package/lib/engine/wire/token-provider.mjs +175 -0
  120. package/lib/engine/wire/usage.mjs +192 -0
  121. package/lib/engine/workflow/host.mjs +524 -0
  122. package/lib/engine/workflow/journal.mjs +188 -0
  123. package/lib/engine/workflow/json-schema.mjs +171 -0
  124. package/lib/engine/workflow/meta.mjs +329 -0
  125. package/lib/engine/workflow/notifications.mjs +52 -0
  126. package/lib/engine/workflow/runtime.mjs +447 -0
  127. package/lib/engine/workflow/sandbox.mjs +534 -0
  128. package/lib/engine/workflow/worker.mjs +141 -0
  129. package/lib/engine/workflow/worktree.mjs +74 -0
  130. package/lib/execution/disposition.mjs +1 -1
  131. package/lib/execution/intake.mjs +10 -0
  132. package/lib/execution/surface-policy.mjs +15 -0
  133. package/lib/learning/curator.mjs +8 -6
  134. package/lib/learning/reflect.mjs +8 -6
  135. package/lib/model-router/catalog/cohort.yaml +137 -0
  136. package/lib/model-router/catalog.mjs +118 -1
  137. package/lib/model-router/economics.mjs +9 -0
  138. package/lib/model-router/failover.mjs +67 -16
  139. package/lib/model-router/llm-task.mjs +39 -3
  140. package/lib/model-router/resolve.mjs +95 -3
  141. package/lib/model-router/spawn.mjs +46 -47
  142. package/lib/model-router/taxonomy.mjs +126 -4
  143. package/lib/org/cost-sync.mjs +141 -11
  144. package/lib/org/inbound/broadcast.mjs +289 -0
  145. package/lib/org/inbound/collective.mjs +375 -0
  146. package/lib/org/inbound/directedness.mjs +96 -8
  147. package/lib/org/inbound/facts.mjs +82 -4
  148. package/lib/org/inbound/hydrate.mjs +555 -51
  149. package/lib/org/inbound/project.mjs +22 -0
  150. package/lib/org/inbound/surfaces.mjs +14 -0
  151. package/lib/org/llm-token.mjs +879 -0
  152. package/lib/org/mesh.mjs +61 -0
  153. package/lib/org/messaging.mjs +3 -1
  154. package/lib/org/protocol.checksum +1 -1
  155. package/lib/org/protocol.mjs +15 -0
  156. package/lib/org/quota.mjs +520 -0
  157. package/lib/org/tool-surface.mjs +104 -16
  158. package/lib/org/ui-parity.mjs +16 -1
  159. package/lib/org/work-ledger.mjs +37 -6
  160. package/lib/rate-guard.mjs +114 -1
  161. package/lib/resource-governor.mjs +41 -6
  162. package/lib/runtime/adapter.mjs +823 -0
  163. package/lib/runtime/child-env.mjs +191 -0
  164. package/lib/runtime/legacy-shell-guard.mjs +97 -0
  165. package/lib/runtime/seat-engine.mjs +162 -0
  166. package/lib/session/ask-ledger.mjs +271 -0
  167. package/lib/session/current-work.mjs +676 -0
  168. package/lib/session/feed-core.mjs +40 -3
  169. package/lib/session/launch-args.mjs +56 -4
  170. package/lib/session/status-summary.mjs +26 -9
  171. package/lib/session/upgrade-notice.mjs +42 -0
  172. package/lib/setup/claude-probe.mjs +117 -13
  173. package/lib/setup/enrich.mjs +13 -10
  174. package/lib/setup/sections/model.mjs +39 -13
  175. package/lib/telemetry/collect.mjs +208 -9
  176. package/lib/upgrade/ignored-drift.mjs +105 -0
  177. package/lib/voice/post-call-brief.mjs +30 -17
  178. package/package.json +15 -3
  179. package/plugins/maestro-skills/skills/board-work.md +5 -0
  180. package/plugins/maestro-skills/skills/inbound-triage.md +56 -15
  181. package/plugins/maestro-skills/skills/main-session.md +18 -7
  182. package/scripts/ci/check-tarball-fidelity.mjs +126 -2
  183. package/scripts/ci/run-tests.mjs +47 -19
  184. package/scripts/cohort-llm/api-key-helper.mjs +92 -0
  185. package/scripts/collective/hook-runner.mjs +29 -2
  186. package/scripts/continuous-monitor.sh +13 -0
  187. package/scripts/cost/track-claude-usage.mjs +15 -0
  188. package/scripts/daemon/agent-daemon.mjs +408 -20
  189. package/scripts/daemon/assurance.mjs +48 -12
  190. package/scripts/daemon/cadence-consumer.mjs +218 -68
  191. package/scripts/daemon/cadence-handlers.mjs +73 -4
  192. package/scripts/daemon/classifier.mjs +75 -26
  193. package/scripts/daemon/context-compiler.mjs +104 -59
  194. package/scripts/daemon/deliver.mjs +30 -1
  195. package/scripts/daemon/dispatcher.mjs +804 -157
  196. package/scripts/daemon/health.mjs +14 -1
  197. package/scripts/daemon/lib/session-router.mjs +310 -42
  198. package/scripts/daemon/maestro-daemon.mjs +11 -0
  199. package/scripts/daemon/prompt-builder.mjs +121 -12
  200. package/scripts/daemon/responder.mjs +315 -146
  201. package/scripts/daemon/sdk-version.mjs +98 -16
  202. package/scripts/eval/probe-gateway.mjs +635 -0
  203. package/scripts/eval/replay/extract.mjs +270 -0
  204. package/scripts/eval/replay/grade.mjs +260 -0
  205. package/scripts/eval/replay/lib/config.mjs +50 -0
  206. package/scripts/eval/replay/lib/effects.mjs +65 -0
  207. package/scripts/eval/replay/lib/fixture.mjs +188 -0
  208. package/scripts/eval/replay/lib/judge.mjs +72 -0
  209. package/scripts/eval/replay/lib/redact.mjs +136 -0
  210. package/scripts/eval/replay/lib/sandbox.mjs +170 -0
  211. package/scripts/eval/replay/lib/schema-check.mjs +63 -0
  212. package/scripts/eval/replay/lib/transcript.mjs +76 -0
  213. package/scripts/eval/replay/mcp-replay-stub.mjs +101 -0
  214. package/scripts/eval/replay/report.mjs +185 -0
  215. package/scripts/eval/replay/run.mjs +404 -0
  216. package/scripts/fleet/rollout.mjs +1094 -0
  217. package/scripts/hooks/pre-send-audit.sh +36 -245
  218. package/scripts/hooks/pre-write-yaml-validate.mjs +275 -0
  219. package/scripts/hooks/validate-state-yaml.sh +190 -0
  220. package/scripts/huddle/huddle-llm.mjs +361 -0
  221. package/scripts/huddle/huddle-server.mjs +46 -121
  222. package/scripts/local-triggers/autoupdate.sh +448 -78
  223. package/scripts/local-triggers/run-trigger.sh +13 -0
  224. package/scripts/maintenance/pin-integrity.mjs +364 -0
  225. package/scripts/poll-slack-events.sh +41 -9
  226. package/scripts/poller/slack-socket-mode.mjs +28 -3
  227. package/scripts/session/supervisor.mjs +80 -13
  228. package/scripts/spawn-session.sh +13 -0
  229. package/bin/maestro.test.mjs +0 -1574
  230. package/lib/action-executor.test.mjs +0 -871
  231. package/lib/archetype.test.mjs +0 -132
  232. package/lib/assurance/plan-note.test.mjs +0 -234
  233. package/lib/assurance/room-budget.test.mjs +0 -486
  234. package/lib/assurance/tier.test.mjs +0 -174
  235. package/lib/autonomy.test.mjs +0 -66
  236. package/lib/backlog.test.mjs +0 -302
  237. package/lib/backup/policy.test.mjs +0 -305
  238. package/lib/budget-escalate.test.mjs +0 -232
  239. package/lib/budget-guard.envelope.test.mjs +0 -476
  240. package/lib/budget-guard.test.mjs +0 -427
  241. package/lib/cadence-bus-requeue.test.mjs +0 -83
  242. package/lib/cadence-bus-schedule.test.mjs +0 -194
  243. package/lib/cadence-bus.test.mjs +0 -720
  244. package/lib/cadences.test.mjs +0 -230
  245. package/lib/capability/inventory.test.mjs +0 -232
  246. package/lib/capability.test.mjs +0 -78
  247. package/lib/channels/base-adapter.test.mjs +0 -590
  248. package/lib/channels/channels.test.mjs +0 -371
  249. package/lib/channels/contract.test.mjs +0 -162
  250. package/lib/channels/inbox-item.test.mjs +0 -368
  251. package/lib/channels/orgmail/adapter.test.mjs +0 -448
  252. package/lib/channels/pairing.test.mjs +0 -270
  253. package/lib/channels/repeat-suppressor.test.mjs +0 -134
  254. package/lib/channels/slack-adapter.test.mjs +0 -212
  255. package/lib/channels/telegram-adapter.test.mjs +0 -306
  256. package/lib/channels/voice/adapter.test.mjs +0 -278
  257. package/lib/channels/whatsapp/adapter-baileys.test.mjs +0 -359
  258. package/lib/channels/whatsapp/baileys-typing.test.mjs +0 -154
  259. package/lib/charter.test.mjs +0 -89
  260. package/lib/claude-bin.test.mjs +0 -131
  261. package/lib/cli/board.test.mjs +0 -227
  262. package/lib/cli/design.test.mjs +0 -270
  263. package/lib/cli/doctor-checks.test.mjs +0 -336
  264. package/lib/cli/global-setup-extras.test.mjs +0 -462
  265. package/lib/cli/inbox.test.mjs +0 -230
  266. package/lib/cli/session-ack.test.mjs +0 -63
  267. package/lib/cli/session.test.mjs +0 -613
  268. package/lib/collective/capture.test.mjs +0 -121
  269. package/lib/collective/cards.test.mjs +0 -114
  270. package/lib/collective/config.test.mjs +0 -123
  271. package/lib/collective/global-config.test.mjs +0 -220
  272. package/lib/collective/global-skills.test.mjs +0 -126
  273. package/lib/collective/presence.test.mjs +0 -95
  274. package/lib/collective/recall.test.mjs +0 -116
  275. package/lib/collective/vendor-skills.test.mjs +0 -306
  276. package/lib/comms/send-gate.test.mjs +0 -770
  277. package/lib/comms.test.mjs +0 -41
  278. package/lib/cost/ledger-row.test.mjs +0 -183
  279. package/lib/design/design-md.test.mjs +0 -318
  280. package/lib/design/fixtures/DESIGN.golden.md +0 -238
  281. package/lib/design/fixtures/PRODUCT.golden.md +0 -67
  282. package/lib/design/fixtures/foundation.json +0 -133
  283. package/lib/design/refresh-gate.test.mjs +0 -144
  284. package/lib/design/write.test.mjs +0 -241
  285. package/lib/diagnostics/alerts.test.mjs +0 -318
  286. package/lib/diagnostics/backup-freshness.test.mjs +0 -185
  287. package/lib/diagnostics/counters.test.mjs +0 -206
  288. package/lib/diagnostics/events.test.mjs +0 -290
  289. package/lib/diagnostics/otel.test.mjs +0 -196
  290. package/lib/diagnostics/trace.test.mjs +0 -251
  291. package/lib/env-compat.test.mjs +0 -104
  292. package/lib/execution/disposition.test.mjs +0 -553
  293. package/lib/execution/drive.test.mjs +0 -270
  294. package/lib/execution/effects.test.mjs +0 -344
  295. package/lib/execution/intake.test.mjs +0 -389
  296. package/lib/execution/journal.test.mjs +0 -261
  297. package/lib/execution/match.test.mjs +0 -235
  298. package/lib/execution/pipeline.test.mjs +0 -392
  299. package/lib/execution/route.test.mjs +0 -186
  300. package/lib/execution/surface-policy.test.mjs +0 -162
  301. package/lib/fs-atomic.test.mjs +0 -72
  302. package/lib/fs-ownership.test.mjs +0 -158
  303. package/lib/goals/admission.test.mjs +0 -164
  304. package/lib/goals/classify.test.mjs +0 -167
  305. package/lib/goals/collaborate.test.mjs +0 -336
  306. package/lib/goals/gaps.test.mjs +0 -284
  307. package/lib/goals/loop.test.mjs +0 -845
  308. package/lib/hooks/bus.test.mjs +0 -387
  309. package/lib/identity/persona.test.mjs +0 -142
  310. package/lib/kpi-sensors.test.mjs +0 -278
  311. package/lib/kpi.test.mjs +0 -244
  312. package/lib/learning/config.test.mjs +0 -75
  313. package/lib/learning/counters.test.mjs +0 -69
  314. package/lib/learning/curator-consolidate.test.mjs +0 -238
  315. package/lib/learning/curator.test.mjs +0 -106
  316. package/lib/learning/reflect.test.mjs +0 -0
  317. package/lib/learning/session-index.test.mjs +0 -125
  318. package/lib/learning/skill-writer.test.mjs +0 -210
  319. package/lib/mandate/audit.test.mjs +0 -195
  320. package/lib/mandate/contract.test.mjs +0 -185
  321. package/lib/mandate/derive.test.mjs +0 -274
  322. package/lib/mandate/model.test.mjs +0 -164
  323. package/lib/mandate/refresh.test.mjs +0 -389
  324. package/lib/mcp/server.test.mjs +0 -426
  325. package/lib/model-router/auth-profiles.test.mjs +0 -580
  326. package/lib/model-router/catalog.test.mjs +0 -385
  327. package/lib/model-router/economics.test.mjs +0 -438
  328. package/lib/model-router/failover.test.mjs +0 -439
  329. package/lib/model-router/health.test.mjs +0 -338
  330. package/lib/model-router/integration-coverage.test.mjs +0 -831
  331. package/lib/model-router/integration.test.mjs +0 -564
  332. package/lib/model-router/ledger.test.mjs +0 -415
  333. package/lib/model-router/llm-task.test.mjs +0 -392
  334. package/lib/model-router/org-credentials.test.mjs +0 -265
  335. package/lib/model-router/pricing-refresh.test.mjs +0 -286
  336. package/lib/model-router/reconcile.test.mjs +0 -316
  337. package/lib/model-router/repair.test.mjs +0 -180
  338. package/lib/model-router/spawn.test.mjs +0 -446
  339. package/lib/model-router/taxonomy.test.mjs +0 -410
  340. package/lib/model-router.test.mjs +0 -1207
  341. package/lib/org/activity.test.mjs +0 -134
  342. package/lib/org/approvals.test.mjs +0 -216
  343. package/lib/org/awareness.test.mjs +0 -159
  344. package/lib/org/board-mine-cache.test.mjs +0 -53
  345. package/lib/org/board.test.mjs +0 -187
  346. package/lib/org/bootstrap-context.test.mjs +0 -153
  347. package/lib/org/client.test.mjs +0 -1206
  348. package/lib/org/cohort-client.test.mjs +0 -126
  349. package/lib/org/cost-sync.test.mjs +0 -153
  350. package/lib/org/doctor.test.mjs +0 -346
  351. package/lib/org/engagement-ledger.test.mjs +0 -112
  352. package/lib/org/engagement.test.mjs +0 -739
  353. package/lib/org/handoff.test.mjs +0 -269
  354. package/lib/org/inbound/directedness.test.mjs +0 -668
  355. package/lib/org/inbound/facts.test.mjs +0 -471
  356. package/lib/org/inbound/hydrate.test.mjs +0 -453
  357. package/lib/org/inbound/index.test.mjs +0 -429
  358. package/lib/org/inbound/project.test.mjs +0 -287
  359. package/lib/org/integration-tools.test.mjs +0 -160
  360. package/lib/org/keys.test.mjs +0 -92
  361. package/lib/org/knowledge.test.mjs +0 -326
  362. package/lib/org/leases.test.mjs +0 -235
  363. package/lib/org/mesh-directives.test.mjs +0 -110
  364. package/lib/org/mesh-integration.test.mjs +0 -127
  365. package/lib/org/mesh.test.mjs +0 -400
  366. package/lib/org/messaging.test.mjs +0 -471
  367. package/lib/org/param-contract.test.mjs +0 -477
  368. package/lib/org/policy.test.mjs +0 -237
  369. package/lib/org/protocol.checksum.test.mjs +0 -90
  370. package/lib/org/protocol.test.mjs +0 -323
  371. package/lib/org/push.test.mjs +0 -792
  372. package/lib/org/registry.test.mjs +0 -100
  373. package/lib/org/resource-tools.test.mjs +0 -361
  374. package/lib/org/tool-access.test.mjs +0 -144
  375. package/lib/org/tool-surface-integration.test.mjs +0 -120
  376. package/lib/org/tool-surface.test.mjs +0 -1268
  377. package/lib/org/typing.test.mjs +0 -291
  378. package/lib/org/ui-parity.test.mjs +0 -560
  379. package/lib/org/verify.test.mjs +0 -194
  380. package/lib/org/work-ledger.test.mjs +0 -273
  381. package/lib/plan/adoption-e2e.test.mjs +0 -366
  382. package/lib/plan/budget-enforcement.test.mjs +0 -400
  383. package/lib/plan/compile.test.mjs +0 -382
  384. package/lib/plan/emit.test.mjs +0 -269
  385. package/lib/plan/explain.test.mjs +0 -188
  386. package/lib/prompts/parallelism.test.mjs +0 -177
  387. package/lib/rag/rag.test.mjs +0 -505
  388. package/lib/rate-guard.test.mjs +0 -272
  389. package/lib/reactive-gate.test.mjs +0 -57
  390. package/lib/render.test.mjs +0 -68
  391. package/lib/resource-governor.test.mjs +0 -488
  392. package/lib/scheduling/dynamic-jobs.test.mjs +0 -344
  393. package/lib/scheduling/jitter.test.mjs +0 -140
  394. package/lib/secrets/broker.test.mjs +0 -280
  395. package/lib/secrets/providers.test.mjs +0 -274
  396. package/lib/security/audit-engine.test.mjs +0 -424
  397. package/lib/security/coerce-args.test.mjs +0 -281
  398. package/lib/security/dangerous-tools.test.mjs +0 -68
  399. package/lib/security/external-content.test.mjs +0 -84
  400. package/lib/security/redact.test.mjs +0 -441
  401. package/lib/security/secret-equal.test.mjs +0 -55
  402. package/lib/session/config.test.mjs +0 -92
  403. package/lib/session/feed-core.test.mjs +0 -198
  404. package/lib/session/first-run.test.mjs +0 -121
  405. package/lib/session/frontdoor.test.mjs +0 -205
  406. package/lib/session/handoffs.test.mjs +0 -183
  407. package/lib/session/identity.test.mjs +0 -180
  408. package/lib/session/inbox-claims.test.mjs +0 -286
  409. package/lib/session/launch-args.test.mjs +0 -157
  410. package/lib/session/liveness.test.mjs +0 -100
  411. package/lib/session/status-summary.test.mjs +0 -118
  412. package/lib/session-permissions.test.mjs +0 -120
  413. package/lib/setup/claude-probe.test.mjs +0 -187
  414. package/lib/setup/completeness.test.mjs +0 -110
  415. package/lib/setup/context-pack.test.mjs +0 -89
  416. package/lib/setup/enrich.test.mjs +0 -115
  417. package/lib/setup/enroll-from-cohort.test.mjs +0 -300
  418. package/lib/setup/integration.test.mjs +0 -162
  419. package/lib/setup/io.test.mjs +0 -77
  420. package/lib/setup/runner.test.mjs +0 -132
  421. package/lib/setup/sections/identity.test.mjs +0 -234
  422. package/lib/setup/sections/inventory.test.mjs +0 -198
  423. package/lib/setup/sections/learning.test.mjs +0 -81
  424. package/lib/setup/sections/mandate.test.mjs +0 -388
  425. package/lib/setup/sections/messaging.test.mjs +0 -127
  426. package/lib/setup/sections/model.test.mjs +0 -240
  427. package/lib/setup/sections/org.test.mjs +0 -346
  428. package/lib/setup/sections/orgmail.test.mjs +0 -118
  429. package/lib/setup/sections/recovery.test.mjs +0 -98
  430. package/lib/setup/sections/subagents.test.mjs +0 -429
  431. package/lib/setup/sections/verify.test.mjs +0 -175
  432. package/lib/setup/sot.test.mjs +0 -81
  433. package/lib/setup/state.test.mjs +0 -115
  434. package/lib/singleton.test.mjs +0 -151
  435. package/lib/subagents/cli.test.mjs +0 -389
  436. package/lib/subagents/client.test.mjs +0 -309
  437. package/lib/subagents/gap.test.mjs +0 -234
  438. package/lib/subagents/lock.test.mjs +0 -248
  439. package/lib/subagents/manifest.test.mjs +0 -175
  440. package/lib/subagents/refs.test.mjs +0 -204
  441. package/lib/subagents/resolve.test.mjs +0 -422
  442. package/lib/subagents/schema.test.mjs +0 -328
  443. package/lib/telemetry/alerts.test.mjs +0 -109
  444. package/lib/telemetry/collect.test.mjs +0 -1274
  445. package/lib/tool-definitions-integration.test.mjs +0 -83
  446. package/lib/tool-definitions.test.mjs +0 -437
  447. package/lib/upgrade/global-refresh.test.mjs +0 -65
  448. package/lib/upgrade/launchd-reconcile.test.mjs +0 -272
  449. package/lib/upgrade/post-steps.test.mjs +0 -200
  450. package/lib/upgrade/verify.test.mjs +0 -164
  451. package/lib/util/fetch-timeout.test.mjs +0 -202
  452. package/lib/util/reconnect.test.mjs +0 -369
  453. package/lib/util/unhandled.test.mjs +0 -216
  454. package/lib/voice/outbound.test.mjs +0 -69
  455. package/lib/voice/session-rotation.test.mjs +0 -114
  456. package/lib/voice/stt.test.mjs +0 -226
  457. package/lib/voice/voice.test.mjs +0 -990
  458. package/scripts/cadence/enqueue-cadence-tick.test.mjs +0 -187
  459. package/scripts/ci/check-docs-accuracy.test.mjs +0 -409
  460. package/scripts/ci/check-durable-write-seam.test.mjs +0 -90
  461. package/scripts/ci/check-no-build-artifacts.test.mjs +0 -71
  462. package/scripts/ci/check-no-residual-identity.test.mjs +0 -202
  463. package/scripts/ci/check-skill-packs.test.mjs +0 -495
  464. package/scripts/ci/check-subagent-frontmatter.test.mjs +0 -124
  465. package/scripts/ci/check.test.mjs +0 -194
  466. package/scripts/ci/conformance-org-api.test.mjs +0 -425
  467. package/scripts/cloud-relay/voice/relay-identity.test.mjs +0 -96
  468. package/scripts/collective/hook-runner.test.mjs +0 -173
  469. package/scripts/cost/fleet-digest.test.mjs +0 -207
  470. package/scripts/cost/track-claude-usage-pricing.test.mjs +0 -183
  471. package/scripts/cost/track-claude-usage.test.mjs +0 -148
  472. package/scripts/daemon/agent-daemon-board-mine.test.mjs +0 -96
  473. package/scripts/daemon/agent-daemon-design.test.mjs +0 -238
  474. package/scripts/daemon/agent-daemon-frontdoor.test.mjs +0 -60
  475. package/scripts/daemon/agent-daemon.test.mjs +0 -995
  476. package/scripts/daemon/assurance-e2e.test.mjs +0 -613
  477. package/scripts/daemon/assurance.test.mjs +0 -1791
  478. package/scripts/daemon/board-mirror.test.mjs +0 -165
  479. package/scripts/daemon/cadence-consumer-frontdoor.test.mjs +0 -393
  480. package/scripts/daemon/cadence-consumer-governance.test.mjs +0 -276
  481. package/scripts/daemon/cadence-consumer.test.mjs +0 -776
  482. package/scripts/daemon/cadence-handlers.test.mjs +0 -837
  483. package/scripts/daemon/classifier-identity.test.mjs +0 -137
  484. package/scripts/daemon/classifier.test.mjs +0 -266
  485. package/scripts/daemon/classify-kind.test.mjs +0 -40
  486. package/scripts/daemon/context-compiler.test.mjs +0 -300
  487. package/scripts/daemon/deliver.test.mjs +0 -564
  488. package/scripts/daemon/dispatcher-cooldown.test.mjs +0 -122
  489. package/scripts/daemon/dispatcher-governance.test.mjs +0 -1013
  490. package/scripts/daemon/dispatcher-resume.test.mjs +0 -166
  491. package/scripts/daemon/execution-ladder.test.mjs +0 -470
  492. package/scripts/daemon/goal-steward-cadence.test.mjs +0 -312
  493. package/scripts/daemon/inbox-deferral-session.test.mjs +0 -49
  494. package/scripts/daemon/inbox-deferral.test.mjs +0 -336
  495. package/scripts/daemon/inbox-wake.test.mjs +0 -199
  496. package/scripts/daemon/integration.test.mjs +0 -149
  497. package/scripts/daemon/lib/self-echo.test.mjs +0 -153
  498. package/scripts/daemon/lib/session-router.test.mjs +0 -295
  499. package/scripts/daemon/prompt-builder-preamble.test.mjs +0 -210
  500. package/scripts/daemon/prompt-builder.test.mjs +0 -344
  501. package/scripts/daemon/responder-cost.test.mjs +0 -68
  502. package/scripts/daemon/responder-history.test.mjs +0 -185
  503. package/scripts/daemon/sdk-version.test.mjs +0 -31
  504. package/scripts/daemon/session-lock.test.mjs +0 -252
  505. package/scripts/daemon/session-outcomes.test.mjs +0 -533
  506. package/scripts/daemon/typing-registry.test.mjs +0 -102
  507. package/scripts/hooks/pre-send-audit.test.mjs +0 -354
  508. package/scripts/huddle/huddle-prompt.test.mjs +0 -176
  509. package/scripts/local-triggers/autoupdate.test.mjs +0 -518
  510. package/scripts/local-triggers/generate-plists.test.mjs +0 -456
  511. package/scripts/media-generation/brand-clause.test.mjs +0 -135
  512. package/scripts/org/send-orgmail.first-contact.test.mjs +0 -102
  513. package/scripts/poller/inbox-privilege-injection.test.mjs +0 -167
  514. package/scripts/poller/inbox-scan-poller.test.mjs +0 -295
  515. package/scripts/poller/lib/cloud-relay-dedup.test.mjs +0 -133
  516. package/scripts/poller/slack-socket-mode.test.mjs +0 -805
  517. package/scripts/poller-launchd/install.test.mjs +0 -243
  518. package/scripts/restore-from-backup.test.mjs +0 -181
  519. package/scripts/session/feed.test.mjs +0 -196
  520. package/scripts/session/supervisor-sh.test.mjs +0 -218
  521. package/scripts/session/supervisor.test.mjs +0 -482
  522. package/scripts/setup/configure-macos.test.mjs +0 -306
  523. package/scripts/setup/gen-subagent-manifest.test.mjs +0 -124
  524. package/scripts/setup/generate-agent-package-json.test.mjs +0 -143
  525. package/scripts/setup/generate-capability.test.mjs +0 -134
  526. package/scripts/setup/init-agent.test.mjs +0 -370
  527. package/scripts/setup/init-skill-marketplace.test.mjs +0 -193
  528. package/scripts/vendor/sync-skill-packs.test.mjs +0 -103
  529. package/scripts/watchdog/memory-watchdog.test.mjs +0 -64
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * lib/model-router/taxonomy.mjs — FailoverReason taxonomy + classifiers.
3
3
  *
4
- * The closed, 16-value `FAILOVER_REASONS` enum and its `DISPOSITION` table are
4
+ * The closed, 20-value `FAILOVER_REASONS` enum and its `DISPOSITION` table are
5
5
  * the *contract* between every outcome-producing surface (CLI stream-json,
6
6
  * result-json subtype, stderr, direct-api HTTP) and `failover.mjs`. The
7
7
  * disposition column semantics — retrySame, failoverNext, cooldownScope,
@@ -30,7 +30,7 @@
30
30
  * name and an alias under that exact byte sequence so either spelling resolves.
31
31
  */
32
32
 
33
- import { classifyStderr as rateGuardClassifyStderr } from "../rate-guard.mjs";
33
+ import { classifyStderr as rateGuardClassifyStderr, classifyCohortRefusal } from "../rate-guard.mjs";
34
34
 
35
35
  // ---------------------------------------------------------------------------
36
36
  // The closed 16-value enum. Order is the SPEC §4.5 order; do not reorder
@@ -40,9 +40,57 @@ import { classifyStderr as rateGuardClassifyStderr } from "../rate-guard.mjs";
40
40
  /** @typedef {(
41
41
  * "auth"|"auth_permanent"|"billing"|"rate_limit"|"overloaded"|"server_error"|
42
42
  * "timeout"|"model_not_found"|"context_overflow"|"payload_too_large"|
43
- * "content_policy"|"provider_policy"|"format"|"empty_response"|"network"|"unknown"
43
+ * "content_policy"|"provider_policy"|"format"|"empty_response"|"network"|"unknown"|
44
+ * "local_refusal"|"gateway_retry"|"budget_refusal"|"request_timeout"
44
45
  * )} FailoverReason */
45
46
 
47
+ /**
48
+ * Gateway codes that mean "cohort-llm cannot serve right now" (CF-136), never a
49
+ * credential, quota or model fault. Hard refusals (cohort_limit_exceeded,
50
+ * cohort_credits_exhausted, cohort_spend_limit_reached) are NOT here: they keep
51
+ * their non-retrying rate_limit / billing classification.
52
+ */
53
+ export const GATEWAY_RETRY_CODES = Object.freeze(["cohort_concurrency_exceeded", "cohort_unavailable"]);
54
+
55
+ /**
56
+ * Gateway codes for a HARD budget refusal (CF-143, design §4.6): the tenant's
57
+ * seat window, credits or spend limit. Sent with `x-should-retry: false`. The
58
+ * refusal is about the tenant's budget, not the credential's health, so it
59
+ * classifies as `budget_refusal`: no credential cooling, no breaker, and no
60
+ * failover — another credential of the same tenant is refused again, or worse,
61
+ * slips past an operator's limit.
62
+ */
63
+ export const BUDGET_REFUSAL_CODES = Object.freeze(["cohort_limit_exceeded", "cohort_credits_exhausted", "cohort_spend_limit_reached"]);
64
+
65
+ /**
66
+ * Same-candidate backoff ladder for `request_timeout` (a 408: the request body
67
+ * stalled, so the model never saw it). One sleep per retry; the ladder's length
68
+ * is the retry count. Matches the engine's ladder (lib/engine/wire/http.mjs
69
+ * backoffMs: 500 ms, 1 s). Named here pending owner confirmation (CF-143).
70
+ */
71
+ export const REQUEST_TIMEOUT_BACKOFF_MS = Object.freeze([500, 1000]);
72
+
73
+ /** A same-candidate retry never sleeps through a retry-after longer than this (the engine's DEFAULT_MAX_RETRY_AFTER_MS). */
74
+ export const MAX_SAME_RETRY_AFTER_MS = 30_000;
75
+
76
+ /**
77
+ * The cohort code in a gateway error body on either wire — the top-level
78
+ * `cohort` refusal object first, then `error.code`. null when there is none.
79
+ * @param {object|string|null} body
80
+ * @returns {string|null}
81
+ */
82
+ export function cohortCodeOf(body) {
83
+ let o = body;
84
+ if (typeof body === "string") {
85
+ try { o = JSON.parse(body); } catch { return null; }
86
+ }
87
+ if (!o || typeof o !== "object") return null;
88
+ const top = o.cohort && typeof o.cohort === "object" && typeof o.cohort.code === "string" ? o.cohort.code : null;
89
+ if (top && top.startsWith("cohort_")) return top;
90
+ const e = o.error && typeof o.error === "object" && typeof o.error.code === "string" ? o.error.code : null;
91
+ return e && e.startsWith("cohort_") ? e : null;
92
+ }
93
+
46
94
  export const FAILOVER_REASONS = Object.freeze([
47
95
  "auth",
48
96
  "auth_permanent",
@@ -60,6 +108,29 @@ export const FAILOVER_REASONS = Object.freeze([
60
108
  "empty_response",
61
109
  "network",
62
110
  "unknown",
111
+ // Appended (CF-11, Cohort Compute W2-E): the retarget was refused BEFORE any
112
+ // request left the machine (no third-party token, a subscription token as the
113
+ // token, a helper aimed at the wrong gateway). Nothing about the backend is
114
+ // known, so it fails over without opening any breaker.
115
+ "local_refusal",
116
+ // Appended (CF-136, Cohort Compute W7): the cohort-llm gateway is briefly
117
+ // unable to serve — 429 `cohort_concurrency_exceeded` (retry-after: 2) or 503
118
+ // `cohort_unavailable` (its OWN provider key refused upstream). Neither says
119
+ // anything about this seat's credential or the model: no credential cooling,
120
+ // no breaker, no failover to another provider's credential. The walk stops
121
+ // and hands the gateway's refusal (and retry-after) back to the caller.
122
+ "gateway_retry",
123
+ // Appended (CF-143, Cohort Compute W8): a HARD budget refusal from the
124
+ // gateway — 429 `cohort_limit_exceeded`, 402 `cohort_credits_exhausted` /
125
+ // `cohort_spend_limit_reached` (x-should-retry: false). The tenant's budget,
126
+ // not the credential: no cooling, no breaker, no failover, never retried. The
127
+ // walk stops and returns the refusal object (resetsAt) to the caller.
128
+ "budget_refusal",
129
+ // Appended (CF-143): a 408 — the request body stalled before the model saw
130
+ // it. Always safe to resend: the same candidate is retried on the
131
+ // REQUEST_TIMEOUT_BACKOFF_MS ladder before failing over. A generic `timeout`
132
+ // (a turn that ran too long) keeps its single immediate retry.
133
+ "request_timeout",
63
134
  ]);
64
135
 
65
136
  const REASON_SET = new Set(FAILOVER_REASONS);
@@ -130,6 +201,9 @@ export function cooldownMsFor(reason, strike = 0) {
130
201
  // | "jittered" (once, jittered) | "backoff" (once, backoff)
131
202
  // | "cli" (the CLI already retried server_error ≤10× internally;
132
203
  // we do not re-retry the same candidate ourselves).
204
+ // retryBackoffMs — when present, the same candidate is retried once per
205
+ // entry, sleeping that many ms first (a retry-after the
206
+ // attempt reported wins, up to MAX_SAME_RETRY_AFTER_MS).
133
207
  // failoverNext — may we advance to the next candidate in the chain?
134
208
  // cooldownScope — which breaker key cools: none | backend | backend:model |
135
209
  // model-id | operator
@@ -153,6 +227,35 @@ export function cooldownMsFor(reason, strike = 0) {
153
227
  // ---------------------------------------------------------------------------
154
228
 
155
229
  export const DISPOSITION = Object.freeze({
230
+ gateway_retry: Object.freeze({
231
+ retrySame: false, // the engine's HTTP edge already retried; the caller holds the seat on the refusal
232
+ failoverNext: false, // the gateway is the route; another provider's credential is not a fix
233
+ cooldownScope: "none",
234
+ cooldownMs: 0,
235
+ opensBreaker: false,
236
+ }),
237
+ budget_refusal: Object.freeze({
238
+ retrySame: false, // x-should-retry: false — retrying a drained window only burns requests
239
+ failoverNext: false, // the same tenant is refused again, or an operator limit is bypassed
240
+ cooldownScope: "none", // the credential is healthy
241
+ cooldownMs: 0,
242
+ opensBreaker: false, // the backend is healthy
243
+ }),
244
+ request_timeout: Object.freeze({
245
+ retrySame: "backoff",
246
+ retryBackoffMs: REQUEST_TIMEOUT_BACKOFF_MS, // one sleep per same-candidate retry, then fail over
247
+ failoverNext: true,
248
+ cooldownScope: "none",
249
+ cooldownMs: 0,
250
+ opensBreaker: false,
251
+ }),
252
+ local_refusal: Object.freeze({
253
+ retrySame: false,
254
+ failoverNext: true,
255
+ cooldownScope: "none",
256
+ cooldownMs: 0,
257
+ opensBreaker: false,
258
+ }),
156
259
  auth: Object.freeze({
157
260
  retrySame: false,
158
261
  failoverNext: true,
@@ -459,6 +562,15 @@ export function classifyStderr(text) {
459
562
  if (typeof text !== "string" || !text) return null;
460
563
  const t = text.toLowerCase();
461
564
 
565
+ // A cohort-llm transient code in a run's output (an engine JSON result, an
566
+ // echoed gateway body) is checked first: its body also says "rate_limit_error"
567
+ // or "503", which the patterns below would read as a provider fault (CF-136).
568
+ // Only a parsed refusal object counts, never the bare word: a transcript or a
569
+ // file that merely mentions the code must not mask a real 401 or overflow.
570
+ const refusal = classifyCohortRefusal(text);
571
+ if (refusal && GATEWAY_RETRY_CODES.includes(refusal.code)) return "gateway_retry";
572
+ if (refusal && BUDGET_REFUSAL_CODES.includes(refusal.code)) return "budget_refusal";
573
+
462
574
  if (STDERR_CONTEXT_OVERFLOW.some((p) => t.includes(p))) return "context_overflow";
463
575
  if (STDERR_CONTENT_POLICY.some((p) => t.includes(p))) return "content_policy";
464
576
  if (STDERR_AUTH.some((p) => t.includes(p))) return "auth";
@@ -743,6 +855,16 @@ export function classifyHttp(status, body) {
743
855
  const { text, code } = flattenHttpBody(body);
744
856
  const st = Number.isFinite(status) ? Number(status) : null;
745
857
 
858
+ // ── 0. cohort-llm transient codes (CF-136). Before every status rule: a 429
859
+ // concurrency cap must not read as rate_limit (credential cooling +
860
+ // breaker + failover), and a 503 provider-key outage must not read as
861
+ // overloaded or auth. ─────────────────────────────────────────────────
862
+ const cohortCode = cohortCodeOf(body);
863
+ if (cohortCode && GATEWAY_RETRY_CODES.includes(cohortCode)) return "gateway_retry";
864
+ // Hard budget refusals (CF-143): before the 429 → rate_limit and 402 → billing
865
+ // rows, which cool the credential, open a breaker and fail over.
866
+ if (cohortCode && BUDGET_REFUSAL_CODES.includes(cohortCode)) return "budget_refusal";
867
+
746
868
  // ── 1. Body-pattern signals that override status (deterministic refusals,
747
869
  // OpenRouter policy blocks, content policy) ─────────────────────────
748
870
  if (hasAny(text, CONTENT_POLICY_PATTERNS)) return "content_policy";
@@ -792,7 +914,7 @@ export function classifyHttp(status, body) {
792
914
  if (st === 413) return "payload_too_large";
793
915
  if (st === 429) return "rate_limit";
794
916
  if (st === 400) return classify400(text, code);
795
- if (st === 408) return "timeout";
917
+ if (st === 408) return "request_timeout"; // the body stalled; safe to resend (CF-143)
796
918
  if (st === 500 || st === 502) {
797
919
  // Some gateways return request-validation errors as 5xx — fail fast.
798
920
  if (hasAny(text, REQUEST_VALIDATION_PATTERNS) || code === "invalid_request_error")
@@ -26,6 +26,17 @@
26
26
  * `${decision_id}:${session_id}:${ts}` || a content hash). The server is
27
27
  * idempotent on cost.report, so the same row reported twice is a no-op replay.
28
28
  *
29
+ * cohort-llm rows: a row whose session cohort-llm served carries
30
+ * `billed_by:"cohort-llm"`. The gateway already metered it at the point of
31
+ * service (design §8.3), so reporting it again would double-count it in hq.
32
+ * Such rows are SKIPPED here and counted in `skippedBilledBy`.
33
+ *
34
+ * Per-row results (CF-17): hq's cost.report now answers per row —
35
+ * `results[]` of {index, id, status: ingested|duplicate|rejected, code?, error?}.
36
+ * A batch whose frame carries it is counted row by row (ingested + duplicate
37
+ * are delivered; rejected are failed, with their codes); an older server that
38
+ * only returns `frame.ok` is counted per batch as before.
39
+ *
29
40
  * Node builtins only. Fail-open (a down/disabled server reports nothing rather
30
41
  * than throwing). `fetchImpl`, `now`, and `ledgerDir`/`agentRoot` are injectable
31
42
  * for deterministic tests. ESM.
@@ -134,7 +145,9 @@ export function toReportRow(row, deps = {}) {
134
145
  const day = (row && row.ts && String(row.ts).slice(0, 10)) || dayStr(deps);
135
146
  return {
136
147
  id,
137
- agent: row.agent ?? null,
148
+ // Tracker rows carry no `agent`, and hq rejects a row without one — the
149
+ // seat's own Cohort identity is the default (deps.agent, see seatAgentId).
150
+ agent: row.agent ?? deps.agent ?? null,
138
151
  model: row.model ?? null,
139
152
  backend: row.backend ?? null,
140
153
  input_tokens: numOr0(row.input_tokens),
@@ -152,8 +165,11 @@ export function toReportRow(row, deps = {}) {
152
165
  decision_id: row.decision_id ?? null,
153
166
  session_id: row.session_id ?? null,
154
167
  // G16: attribution fields — DO NOT STRIP. cadence/source surface coordination
155
- // spend in the rollup so an LLM-in-the-loop violation is visible.
156
- source: row.source ?? null,
168
+ // spend in the rollup so an LLM-in-the-loop violation is visible. `source` is
169
+ // mapped into the vocabulary hq accepts (an out-of-set source used to reject
170
+ // the whole batch); the ledger's own spelling rides in `source_detail`.
171
+ source: reportSource(row.source),
172
+ source_detail: row.source ?? null,
157
173
  cadence: row.cadence ?? row.lane ?? null,
158
174
  task_class: row.task_class ?? null,
159
175
  ts: row.ts ?? null,
@@ -166,6 +182,69 @@ function numOr0(v) {
166
182
  return Number.isFinite(n) ? n : 0;
167
183
  }
168
184
 
185
+ // ---------------------------------------------------------------------------
186
+ // Wire vocabulary: the sources hq accepts, and the seat identity
187
+ // ---------------------------------------------------------------------------
188
+
189
+ /**
190
+ * The `source` values hq's cost.report accepts
191
+ * (hq src/server/methods/cost/report.ts VALID_SOURCES). A row outside this set
192
+ * is a BAD_REQUEST that rejects the entire batch — and every live ledger writer
193
+ * (`--source dispatcher|responder|cadence-consumer`, `messaging`, `lookup`)
194
+ * was outside it, so no seat's spend reached hq (A2 §6.3).
195
+ */
196
+ export const HQ_REPORT_SOURCES = Object.freeze(["router", "cadence", "channel", "manual", "org-server"]);
197
+
198
+ /**
199
+ * Live ledger source → hq source. hq is widening its list to the live names
200
+ * (Cohort Compute W1-D); this map is valid against both the old and the new
201
+ * list, so the push never depends on which hq is deployed.
202
+ */
203
+ const LIVE_SOURCE_MAP = Object.freeze({
204
+ "cadence-consumer": "cadence",
205
+ "handoff-timeout": "cadence",
206
+ dispatcher: "router",
207
+ backlog: "router",
208
+ lookup: "router",
209
+ classifier: "router",
210
+ responder: "channel",
211
+ messaging: "channel",
212
+ ack: "channel",
213
+ });
214
+
215
+ /**
216
+ * Map a ledger row's source onto hq's accepted vocabulary. Pure.
217
+ * Absent → null (hq defaults it); an accepted value passes through; a known
218
+ * live source maps; anything else is "router", hq's own default for a routed
219
+ * spend row, so one unfamiliar writer can never reject a batch again.
220
+ * @param {unknown} source
221
+ * @returns {string|null}
222
+ */
223
+ export function reportSource(source) {
224
+ if (source == null || source === "") return null;
225
+ const s = String(source);
226
+ if (HQ_REPORT_SOURCES.includes(s)) return s;
227
+ return LIVE_SOURCE_MAP[s] || "router";
228
+ }
229
+
230
+ /**
231
+ * The seat's Cohort identity — the member slug hq authenticates this seat's key
232
+ * as — for rows that carry no `agent`. Explicit enrolment wins:
233
+ * `org.cohort.agentId` in config/org.yaml, then COHORT_AGENT_ID (the canonical
234
+ * provisioning variable). Pure over its inputs. null when the seat has neither:
235
+ * a guessed slug (a directory name) would be attributed to the wrong member,
236
+ * and hq defaults a missing agent to the authenticated actor.
237
+ * @param {{cfg?:object, env?:object}} o
238
+ * @returns {string|null}
239
+ */
240
+ export function seatAgentId({ cfg, env = process.env } = {}) {
241
+ const n = (cfg && cfg.org && cfg.org.cohort) || {};
242
+ for (const v of [n.agentId, env && env.COHORT_AGENT_ID]) {
243
+ if (typeof v === "string" && v.trim()) return v.trim();
244
+ }
245
+ return null;
246
+ }
247
+
169
248
  // ---------------------------------------------------------------------------
170
249
  // Report — batched, idempotent push to cost.report
171
250
  // ---------------------------------------------------------------------------
@@ -188,8 +267,41 @@ function numOr0(v) {
188
267
  * localUSD:number, batches:Array<{count:number, ok:boolean, code?:string}>
189
268
  * }>}
190
269
  */
270
+ /** The billed_by value of rows cohort-llm metered itself. */
271
+ export const COHORT_LLM_BILLED_BY = "cohort-llm";
272
+
273
+ /**
274
+ * Count a batch outcome from hq's per-row results when present. PURE.
275
+ * @param {object|null} frame the cost.report response frame
276
+ * @param {number} sent rows in the batch
277
+ * @returns {{perRow:boolean, delivered:number, failed:number, ingested:number, duplicate:number, rejected:number, rejections:Array<{id:string|null, code:string|null, error:string|null}>}}
278
+ */
279
+ export function countBatchResults(frame, sent) {
280
+ const result = frame && frame.ok && frame.result && typeof frame.result === "object" ? frame.result : null;
281
+ const results = result && Array.isArray(result.results) ? result.results : null;
282
+ if (!results) {
283
+ const ok = !!(frame && frame.ok);
284
+ return { perRow: false, delivered: ok ? sent : 0, failed: ok ? 0 : sent, ingested: ok ? sent : 0, duplicate: 0, rejected: ok ? 0 : sent, rejections: [] };
285
+ }
286
+ let ingested = 0, duplicate = 0, rejected = 0;
287
+ const rejections = [];
288
+ for (const r of results) {
289
+ const status = r && r.status;
290
+ // "accepted" is tolerated as a synonym of "ingested" (the design's wording).
291
+ if (status === "ingested" || status === "accepted") ingested++;
292
+ else if (status === "duplicate") duplicate++;
293
+ else { rejected++; rejections.push({ id: (r && r.id) ?? null, code: (r && r.code) ?? null, error: (r && r.error) ?? null }); }
294
+ }
295
+ // Rows the server never mentioned did not land.
296
+ const unaccounted = Math.max(0, sent - results.length);
297
+ return { perRow: true, delivered: ingested + duplicate, failed: rejected + unaccounted, ingested, duplicate, rejected: rejected + unaccounted, rejections };
298
+ }
299
+
191
300
  export async function reportLedger(o = {}) {
192
- const rows = readLedgerRows(o);
301
+ const allRows = readLedgerRows(o);
302
+ const rows = allRows.filter((r) => !(r && r.billed_by === COHORT_LLM_BILLED_BY));
303
+ const skippedBilledBy = allRows.length - rows.length;
304
+ const log = typeof o.log === "function" ? o.log : null;
193
305
  // Bill through the ONE definition (lib/cost/ledger-row.mjs), not `estimated_usd`:
194
306
  // the authoritative `total_cost_usd` where present, the cache-aware estimate as
195
307
  // a fallback, and unmeasured sessions IMPUTED rather than summed as free. The
@@ -200,15 +312,16 @@ export async function reportLedger(o = {}) {
200
312
  const localUSD = round6(summary.measuredUsd + imputeUnmeasured(summary).imputedUsd);
201
313
 
202
314
  if (!isEnabled(o.cfg)) {
203
- return { enabled: false, rowsRead: rows.length, reported: 0, failed: 0, localUSD, batches: [] };
315
+ return { enabled: false, rowsRead: allRows.length, reported: 0, failed: 0, skippedBilledBy, localUSD, batches: [] };
204
316
  }
205
317
  if (rows.length === 0) {
206
- return { enabled: true, rowsRead: 0, reported: 0, failed: 0, localUSD, batches: [] };
318
+ return { enabled: true, rowsRead: allRows.length, reported: 0, failed: 0, skippedBilledBy, localUSD, batches: [] };
207
319
  }
208
320
 
209
321
  const c = configFromAgent(o.cfg);
210
322
  const batchSize = Number.isFinite(o.batchSize) && o.batchSize > 0 ? Math.floor(o.batchSize) : REPORT_BATCH;
211
- const wireRows = rows.map((r) => toReportRow(r, o));
323
+ const agent = o.agent !== undefined ? o.agent : seatAgentId({ cfg: o.cfg, env: o.env || process.env });
324
+ const wireRows = rows.map((r) => toReportRow(r, { ...o, agent }));
212
325
 
213
326
  let reported = 0;
214
327
  let failed = 0;
@@ -225,11 +338,28 @@ export async function reportLedger(o = {}) {
225
338
  fetchImpl: o.fetchImpl,
226
339
  });
227
340
  const ok = !!(frame && frame.ok);
228
- if (ok) reported += slice.length; else failed += slice.length;
229
- batches.push({ count: slice.length, ok, code: ok ? undefined : (frame && frame.error && frame.error.code) || "error" });
341
+ const counts = countBatchResults(frame, slice.length);
342
+ reported += counts.delivered;
343
+ failed += counts.failed;
344
+ batches.push({
345
+ count: slice.length,
346
+ ok,
347
+ code: ok ? undefined : (frame && frame.error && frame.error.code) || "error",
348
+ ...(counts.perRow ? { ingested: counts.ingested, duplicate: counts.duplicate, rejected: counts.rejected, rejections: counts.rejections } : {}),
349
+ });
350
+ if (log) {
351
+ try {
352
+ if (counts.perRow) {
353
+ const codes = [...new Set(counts.rejections.map((r) => r.code).filter(Boolean))];
354
+ log(counts.rejected ? "warn" : "info", `cost.report batch of ${slice.length}: ${counts.ingested} ingested, ${counts.duplicate} duplicate, ${counts.rejected} rejected${codes.length ? ` (${codes.join(", ")})` : ""}`);
355
+ } else {
356
+ log(ok ? "info" : "warn", `cost.report batch of ${slice.length}: ${ok ? "ok" : `failed (${batches[batches.length - 1].code})`} (server sent no per-row results)`);
357
+ }
358
+ } catch { /* logging must not break the push */ }
359
+ }
230
360
  }
231
361
 
232
- return { enabled: true, rowsRead: rows.length, reported, failed, localUSD, batches };
362
+ return { enabled: true, rowsRead: allRows.length, reported, failed, skippedBilledBy, localUSD, batches };
233
363
  }
234
364
 
235
365
  function round6(n) {
@@ -238,4 +368,4 @@ function round6(n) {
238
368
 
239
369
  export const _internals = { ledgerFile, ledgerDir, dayStr, stableJson, REPORT_BATCH };
240
370
 
241
- export default { readLedgerRows, rowId, toReportRow, reportLedger };
371
+ export default { readLedgerRows, rowId, toReportRow, reportLedger, reportSource, seatAgentId, countBatchResults, COHORT_LLM_BILLED_BY };