@cohortapp/agent-sdk 2.17.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 (526) hide show
  1. package/.claude/settings.json +18 -0
  2. package/.env.example +18 -5
  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/setup-wizard.md +1 -1
  9. package/docs/runbooks/fleet-rollout.md +156 -0
  10. package/docs/runbooks/mac-mini-bootstrap.md +12 -14
  11. package/lib/action-executor.js +19 -3
  12. package/lib/budget-guard.mjs +279 -3
  13. package/lib/channels/base-adapter.mjs +3 -1
  14. package/lib/channels/contract.mjs +2 -1
  15. package/lib/channels/inbox-item.mjs +8 -0
  16. package/lib/claude-bin.mjs +5 -6
  17. package/lib/cli/doctor-checks.mjs +141 -10
  18. package/lib/cli/global-setup-extras.mjs +5 -1
  19. package/lib/cli/inbox.mjs +100 -15
  20. package/lib/cli/seat-auth.mjs +463 -0
  21. package/lib/cli/session.mjs +80 -12
  22. package/lib/collective/capture.mjs +8 -6
  23. package/lib/collective/global-config.mjs +63 -1
  24. package/lib/collective/presence.mjs +142 -5
  25. package/lib/comms/send-gate.mjs +559 -1
  26. package/lib/diagnostics/alerts.mjs +49 -0
  27. package/lib/diagnostics/cadence-output-freshness.mjs +288 -0
  28. package/lib/engine/agents/definitions.mjs +343 -0
  29. package/lib/engine/agents/persist.mjs +275 -0
  30. package/lib/engine/agents/runtime.mjs +748 -0
  31. package/lib/engine/agents/usage.mjs +95 -0
  32. package/lib/engine/auth-status.mjs +139 -0
  33. package/lib/engine/budget.mjs +194 -0
  34. package/lib/engine/cli.mjs +1204 -0
  35. package/lib/engine/commands/index.mjs +269 -0
  36. package/lib/engine/context/budget.mjs +219 -0
  37. package/lib/engine/context/cache.mjs +125 -0
  38. package/lib/engine/context/child-env.mjs +215 -0
  39. package/lib/engine/context/compaction.mjs +342 -0
  40. package/lib/engine/context/images.mjs +90 -0
  41. package/lib/engine/context/instructions.mjs +327 -0
  42. package/lib/engine/context/lazy-instructions.mjs +169 -0
  43. package/lib/engine/context/manager.mjs +182 -0
  44. package/lib/engine/context/real-path.mjs +91 -0
  45. package/lib/engine/context/secret-values.mjs +163 -0
  46. package/lib/engine/context/settings.mjs +274 -0
  47. package/lib/engine/context/stream-input.mjs +159 -0
  48. package/lib/engine/guard.mjs +152 -0
  49. package/lib/engine/hooks.mjs +713 -0
  50. package/lib/engine/loop.mjs +560 -0
  51. package/lib/engine/mcp/client.mjs +254 -0
  52. package/lib/engine/mcp/config.mjs +301 -0
  53. package/lib/engine/mcp/http.mjs +201 -0
  54. package/lib/engine/mcp/index.mjs +146 -0
  55. package/lib/engine/mcp/jsonrpc.mjs +147 -0
  56. package/lib/engine/mcp/naming.mjs +66 -0
  57. package/lib/engine/mcp/resources.mjs +89 -0
  58. package/lib/engine/mcp/results.mjs +133 -0
  59. package/lib/engine/mcp/stdio.mjs +137 -0
  60. package/lib/engine/mcp/supervisor.mjs +116 -0
  61. package/lib/engine/messages.mjs +104 -0
  62. package/lib/engine/output/json.mjs +164 -0
  63. package/lib/engine/output/stream-json.mjs +266 -0
  64. package/lib/engine/permissions.mjs +845 -0
  65. package/lib/engine/process-identity.mjs +164 -0
  66. package/lib/engine/process-tree.mjs +551 -0
  67. package/lib/engine/prompt.mjs +60 -0
  68. package/lib/engine/session/store.mjs +299 -0
  69. package/lib/engine/session-runtime/args.mjs +97 -0
  70. package/lib/engine/session-runtime/host.mjs +143 -0
  71. package/lib/engine/session-runtime/inbox.mjs +122 -0
  72. package/lib/engine/session-runtime/notifications.mjs +129 -0
  73. package/lib/engine/session-runtime/registry.mjs +328 -0
  74. package/lib/engine/session-runtime/runner.mjs +344 -0
  75. package/lib/engine/session-runtime/socket.mjs +212 -0
  76. package/lib/engine/session-runtime/wakeup.mjs +115 -0
  77. package/lib/engine/skills/index.mjs +321 -0
  78. package/lib/engine/tools/bash-background.mjs +533 -0
  79. package/lib/engine/tools/bash.mjs +216 -0
  80. package/lib/engine/tools/edit.mjs +97 -0
  81. package/lib/engine/tools/glob.mjs +81 -0
  82. package/lib/engine/tools/grep.mjs +224 -0
  83. package/lib/engine/tools/index.mjs +84 -0
  84. package/lib/engine/tools/list-agents.mjs +32 -0
  85. package/lib/engine/tools/ls.mjs +127 -0
  86. package/lib/engine/tools/monitor.mjs +82 -0
  87. package/lib/engine/tools/notebook-edit.mjs +218 -0
  88. package/lib/engine/tools/read.mjs +103 -0
  89. package/lib/engine/tools/schedule-wakeup.mjs +45 -0
  90. package/lib/engine/tools/schema.mjs +144 -0
  91. package/lib/engine/tools/send-message.mjs +77 -0
  92. package/lib/engine/tools/session.mjs +70 -0
  93. package/lib/engine/tools/todo.mjs +144 -0
  94. package/lib/engine/tools/toolsearch.mjs +217 -0
  95. package/lib/engine/tools/walk.mjs +193 -0
  96. package/lib/engine/tools/web-switch.mjs +31 -0
  97. package/lib/engine/tools/webfetch-html.mjs +387 -0
  98. package/lib/engine/tools/webfetch-net.mjs +340 -0
  99. package/lib/engine/tools/webfetch.mjs +198 -0
  100. package/lib/engine/tools/websearch.mjs +91 -0
  101. package/lib/engine/tools/workflow.mjs +95 -0
  102. package/lib/engine/tools/write.mjs +76 -0
  103. package/lib/engine/tui/line-editor.mjs +137 -0
  104. package/lib/engine/tui/render.mjs +86 -0
  105. package/lib/engine/tui/tui.mjs +274 -0
  106. package/lib/engine/wire/anthropic-messages.mjs +263 -0
  107. package/lib/engine/wire/effort.mjs +36 -0
  108. package/lib/engine/wire/errors.mjs +496 -0
  109. package/lib/engine/wire/http.mjs +441 -0
  110. package/lib/engine/wire/index.mjs +76 -0
  111. package/lib/engine/wire/openai-chat.mjs +332 -0
  112. package/lib/engine/wire/prompt-cache.mjs +79 -0
  113. package/lib/engine/wire/search.mjs +140 -0
  114. package/lib/engine/wire/sse.mjs +114 -0
  115. package/lib/engine/wire/stall.mjs +349 -0
  116. package/lib/engine/wire/token-provider.mjs +175 -0
  117. package/lib/engine/wire/usage.mjs +192 -0
  118. package/lib/engine/workflow/host.mjs +524 -0
  119. package/lib/engine/workflow/journal.mjs +188 -0
  120. package/lib/engine/workflow/json-schema.mjs +171 -0
  121. package/lib/engine/workflow/meta.mjs +329 -0
  122. package/lib/engine/workflow/notifications.mjs +52 -0
  123. package/lib/engine/workflow/runtime.mjs +447 -0
  124. package/lib/engine/workflow/sandbox.mjs +534 -0
  125. package/lib/engine/workflow/worker.mjs +141 -0
  126. package/lib/engine/workflow/worktree.mjs +74 -0
  127. package/lib/execution/disposition.mjs +1 -1
  128. package/lib/execution/intake.mjs +10 -0
  129. package/lib/execution/surface-policy.mjs +15 -0
  130. package/lib/learning/curator.mjs +8 -6
  131. package/lib/learning/reflect.mjs +8 -6
  132. package/lib/model-router/catalog/cohort.yaml +137 -0
  133. package/lib/model-router/catalog.mjs +118 -1
  134. package/lib/model-router/failover.mjs +67 -16
  135. package/lib/model-router/llm-task.mjs +39 -3
  136. package/lib/model-router/resolve.mjs +89 -3
  137. package/lib/model-router/spawn.mjs +46 -47
  138. package/lib/model-router/taxonomy.mjs +126 -4
  139. package/lib/org/cost-sync.mjs +141 -11
  140. package/lib/org/inbound/broadcast.mjs +289 -0
  141. package/lib/org/inbound/collective.mjs +375 -0
  142. package/lib/org/inbound/directedness.mjs +96 -8
  143. package/lib/org/inbound/facts.mjs +78 -2
  144. package/lib/org/inbound/project.mjs +22 -0
  145. package/lib/org/inbound/surfaces.mjs +14 -0
  146. package/lib/org/llm-token.mjs +879 -0
  147. package/lib/org/mesh.mjs +61 -0
  148. package/lib/org/messaging.mjs +3 -1
  149. package/lib/org/protocol.checksum +1 -1
  150. package/lib/org/protocol.mjs +15 -0
  151. package/lib/org/quota.mjs +520 -0
  152. package/lib/org/tool-surface.mjs +104 -16
  153. package/lib/org/ui-parity.mjs +16 -1
  154. package/lib/org/work-ledger.mjs +37 -6
  155. package/lib/rate-guard.mjs +114 -1
  156. package/lib/resource-governor.mjs +41 -6
  157. package/lib/runtime/adapter.mjs +823 -0
  158. package/lib/runtime/child-env.mjs +191 -0
  159. package/lib/runtime/legacy-shell-guard.mjs +97 -0
  160. package/lib/runtime/seat-engine.mjs +162 -0
  161. package/lib/session/ask-ledger.mjs +271 -0
  162. package/lib/session/current-work.mjs +676 -0
  163. package/lib/session/feed-core.mjs +40 -3
  164. package/lib/session/launch-args.mjs +56 -4
  165. package/lib/session/status-summary.mjs +26 -9
  166. package/lib/session/upgrade-notice.mjs +42 -0
  167. package/lib/setup/claude-probe.mjs +117 -13
  168. package/lib/setup/enrich.mjs +13 -10
  169. package/lib/setup/sections/model.mjs +39 -13
  170. package/lib/telemetry/collect.mjs +208 -9
  171. package/lib/upgrade/ignored-drift.mjs +105 -0
  172. package/lib/voice/post-call-brief.mjs +30 -17
  173. package/package.json +13 -3
  174. package/plugins/maestro-skills/skills/board-work.md +5 -0
  175. package/plugins/maestro-skills/skills/inbound-triage.md +56 -15
  176. package/plugins/maestro-skills/skills/main-session.md +18 -7
  177. package/scripts/ci/check-tarball-fidelity.mjs +126 -2
  178. package/scripts/ci/run-tests.mjs +47 -19
  179. package/scripts/cohort-llm/api-key-helper.mjs +92 -0
  180. package/scripts/collective/hook-runner.mjs +29 -2
  181. package/scripts/continuous-monitor.sh +13 -0
  182. package/scripts/cost/track-claude-usage.mjs +15 -0
  183. package/scripts/daemon/agent-daemon.mjs +408 -20
  184. package/scripts/daemon/assurance.mjs +48 -12
  185. package/scripts/daemon/cadence-consumer.mjs +218 -68
  186. package/scripts/daemon/cadence-handlers.mjs +73 -4
  187. package/scripts/daemon/classifier.mjs +75 -26
  188. package/scripts/daemon/context-compiler.mjs +51 -37
  189. package/scripts/daemon/deliver.mjs +30 -1
  190. package/scripts/daemon/dispatcher.mjs +595 -149
  191. package/scripts/daemon/health.mjs +14 -1
  192. package/scripts/daemon/maestro-daemon.mjs +11 -0
  193. package/scripts/daemon/prompt-builder.mjs +24 -0
  194. package/scripts/daemon/responder.mjs +246 -79
  195. package/scripts/daemon/sdk-version.mjs +98 -16
  196. package/scripts/eval/probe-gateway.mjs +635 -0
  197. package/scripts/eval/replay/extract.mjs +270 -0
  198. package/scripts/eval/replay/grade.mjs +260 -0
  199. package/scripts/eval/replay/lib/config.mjs +50 -0
  200. package/scripts/eval/replay/lib/effects.mjs +65 -0
  201. package/scripts/eval/replay/lib/fixture.mjs +188 -0
  202. package/scripts/eval/replay/lib/judge.mjs +72 -0
  203. package/scripts/eval/replay/lib/redact.mjs +136 -0
  204. package/scripts/eval/replay/lib/sandbox.mjs +170 -0
  205. package/scripts/eval/replay/lib/schema-check.mjs +63 -0
  206. package/scripts/eval/replay/lib/transcript.mjs +76 -0
  207. package/scripts/eval/replay/mcp-replay-stub.mjs +101 -0
  208. package/scripts/eval/replay/report.mjs +185 -0
  209. package/scripts/eval/replay/run.mjs +404 -0
  210. package/scripts/fleet/rollout.mjs +1094 -0
  211. package/scripts/hooks/pre-send-audit.sh +36 -245
  212. package/scripts/hooks/pre-write-yaml-validate.mjs +275 -0
  213. package/scripts/hooks/validate-state-yaml.sh +190 -0
  214. package/scripts/huddle/huddle-llm.mjs +361 -0
  215. package/scripts/huddle/huddle-server.mjs +46 -121
  216. package/scripts/local-triggers/autoupdate.sh +448 -78
  217. package/scripts/local-triggers/run-trigger.sh +13 -0
  218. package/scripts/maintenance/pin-integrity.mjs +364 -0
  219. package/scripts/poll-slack-events.sh +41 -9
  220. package/scripts/poller/slack-socket-mode.mjs +28 -3
  221. package/scripts/session/supervisor.mjs +80 -13
  222. package/scripts/spawn-session.sh +13 -0
  223. package/bin/maestro.test.mjs +0 -1574
  224. package/lib/action-executor.test.mjs +0 -871
  225. package/lib/archetype.test.mjs +0 -132
  226. package/lib/assurance/plan-note.test.mjs +0 -234
  227. package/lib/assurance/room-budget.test.mjs +0 -486
  228. package/lib/assurance/tier.test.mjs +0 -174
  229. package/lib/autonomy.test.mjs +0 -66
  230. package/lib/backlog.test.mjs +0 -302
  231. package/lib/backup/policy.test.mjs +0 -305
  232. package/lib/budget-escalate.test.mjs +0 -232
  233. package/lib/budget-guard.envelope.test.mjs +0 -476
  234. package/lib/budget-guard.test.mjs +0 -427
  235. package/lib/cadence-bus-requeue.test.mjs +0 -83
  236. package/lib/cadence-bus-schedule.test.mjs +0 -194
  237. package/lib/cadence-bus.test.mjs +0 -720
  238. package/lib/cadences.test.mjs +0 -230
  239. package/lib/capability/inventory.test.mjs +0 -232
  240. package/lib/capability.test.mjs +0 -78
  241. package/lib/channels/base-adapter.test.mjs +0 -590
  242. package/lib/channels/channels.test.mjs +0 -371
  243. package/lib/channels/contract.test.mjs +0 -162
  244. package/lib/channels/inbox-item.test.mjs +0 -368
  245. package/lib/channels/orgmail/adapter.test.mjs +0 -448
  246. package/lib/channels/pairing.test.mjs +0 -270
  247. package/lib/channels/repeat-suppressor.test.mjs +0 -134
  248. package/lib/channels/slack-adapter.test.mjs +0 -212
  249. package/lib/channels/telegram-adapter.test.mjs +0 -306
  250. package/lib/channels/voice/adapter.test.mjs +0 -278
  251. package/lib/channels/whatsapp/adapter-baileys.test.mjs +0 -359
  252. package/lib/channels/whatsapp/baileys-typing.test.mjs +0 -154
  253. package/lib/charter.test.mjs +0 -89
  254. package/lib/claude-bin.test.mjs +0 -131
  255. package/lib/cli/board.test.mjs +0 -227
  256. package/lib/cli/design.test.mjs +0 -270
  257. package/lib/cli/doctor-checks.test.mjs +0 -336
  258. package/lib/cli/global-setup-extras.test.mjs +0 -462
  259. package/lib/cli/inbox.test.mjs +0 -230
  260. package/lib/cli/session-ack.test.mjs +0 -63
  261. package/lib/cli/session.test.mjs +0 -613
  262. package/lib/collective/capture.test.mjs +0 -121
  263. package/lib/collective/cards.test.mjs +0 -114
  264. package/lib/collective/config.test.mjs +0 -123
  265. package/lib/collective/global-config.test.mjs +0 -220
  266. package/lib/collective/global-skills.test.mjs +0 -126
  267. package/lib/collective/presence.test.mjs +0 -95
  268. package/lib/collective/recall.test.mjs +0 -116
  269. package/lib/collective/vendor-skills.test.mjs +0 -306
  270. package/lib/comms/send-gate.test.mjs +0 -770
  271. package/lib/comms.test.mjs +0 -41
  272. package/lib/context/budget.test.mjs +0 -252
  273. package/lib/context/history-scope.test.mjs +0 -79
  274. package/lib/cost/ledger-row.test.mjs +0 -183
  275. package/lib/design/design-md.test.mjs +0 -318
  276. package/lib/design/fixtures/DESIGN.golden.md +0 -238
  277. package/lib/design/fixtures/PRODUCT.golden.md +0 -67
  278. package/lib/design/fixtures/foundation.json +0 -133
  279. package/lib/design/refresh-gate.test.mjs +0 -144
  280. package/lib/design/write.test.mjs +0 -241
  281. package/lib/diagnostics/alerts.test.mjs +0 -318
  282. package/lib/diagnostics/backup-freshness.test.mjs +0 -185
  283. package/lib/diagnostics/counters.test.mjs +0 -206
  284. package/lib/diagnostics/events.test.mjs +0 -290
  285. package/lib/diagnostics/otel.test.mjs +0 -196
  286. package/lib/diagnostics/trace.test.mjs +0 -251
  287. package/lib/env-compat.test.mjs +0 -104
  288. package/lib/execution/disposition.test.mjs +0 -553
  289. package/lib/execution/drive.test.mjs +0 -270
  290. package/lib/execution/effects.test.mjs +0 -344
  291. package/lib/execution/intake.test.mjs +0 -389
  292. package/lib/execution/journal.test.mjs +0 -261
  293. package/lib/execution/match.test.mjs +0 -235
  294. package/lib/execution/pipeline.test.mjs +0 -392
  295. package/lib/execution/route.test.mjs +0 -186
  296. package/lib/execution/surface-policy.test.mjs +0 -162
  297. package/lib/fs-atomic.test.mjs +0 -72
  298. package/lib/fs-ownership.test.mjs +0 -158
  299. package/lib/goals/admission.test.mjs +0 -164
  300. package/lib/goals/classify.test.mjs +0 -167
  301. package/lib/goals/collaborate.test.mjs +0 -336
  302. package/lib/goals/gaps.test.mjs +0 -284
  303. package/lib/goals/loop.test.mjs +0 -845
  304. package/lib/hooks/bus.test.mjs +0 -387
  305. package/lib/identity/persona.test.mjs +0 -142
  306. package/lib/kpi-sensors.test.mjs +0 -278
  307. package/lib/kpi.test.mjs +0 -244
  308. package/lib/learning/config.test.mjs +0 -75
  309. package/lib/learning/counters.test.mjs +0 -69
  310. package/lib/learning/curator-consolidate.test.mjs +0 -238
  311. package/lib/learning/curator.test.mjs +0 -106
  312. package/lib/learning/reflect.test.mjs +0 -0
  313. package/lib/learning/session-index.test.mjs +0 -125
  314. package/lib/learning/skill-writer.test.mjs +0 -210
  315. package/lib/mandate/audit.test.mjs +0 -195
  316. package/lib/mandate/contract.test.mjs +0 -185
  317. package/lib/mandate/derive.test.mjs +0 -274
  318. package/lib/mandate/model.test.mjs +0 -164
  319. package/lib/mandate/refresh.test.mjs +0 -389
  320. package/lib/mcp/server.test.mjs +0 -426
  321. package/lib/model-router/auth-profiles.test.mjs +0 -580
  322. package/lib/model-router/catalog.test.mjs +0 -385
  323. package/lib/model-router/economics.test.mjs +0 -438
  324. package/lib/model-router/failover.test.mjs +0 -439
  325. package/lib/model-router/health.test.mjs +0 -338
  326. package/lib/model-router/integration-coverage.test.mjs +0 -831
  327. package/lib/model-router/integration.test.mjs +0 -564
  328. package/lib/model-router/ledger.test.mjs +0 -415
  329. package/lib/model-router/llm-task.test.mjs +0 -392
  330. package/lib/model-router/org-credentials.test.mjs +0 -265
  331. package/lib/model-router/pricing-refresh.test.mjs +0 -286
  332. package/lib/model-router/reconcile.test.mjs +0 -316
  333. package/lib/model-router/repair.test.mjs +0 -180
  334. package/lib/model-router/spawn.test.mjs +0 -446
  335. package/lib/model-router/taxonomy.test.mjs +0 -410
  336. package/lib/model-router.test.mjs +0 -1207
  337. package/lib/org/activity.test.mjs +0 -134
  338. package/lib/org/approvals.test.mjs +0 -216
  339. package/lib/org/awareness.test.mjs +0 -159
  340. package/lib/org/board-mine-cache.test.mjs +0 -53
  341. package/lib/org/board.test.mjs +0 -187
  342. package/lib/org/bootstrap-context.test.mjs +0 -153
  343. package/lib/org/client.test.mjs +0 -1206
  344. package/lib/org/cohort-client.test.mjs +0 -126
  345. package/lib/org/cost-sync.test.mjs +0 -153
  346. package/lib/org/doctor.test.mjs +0 -346
  347. package/lib/org/engagement-ledger.test.mjs +0 -112
  348. package/lib/org/engagement.test.mjs +0 -739
  349. package/lib/org/handoff.test.mjs +0 -269
  350. package/lib/org/inbound/directedness.test.mjs +0 -668
  351. package/lib/org/inbound/facts.test.mjs +0 -471
  352. package/lib/org/inbound/hydrate.test.mjs +0 -908
  353. package/lib/org/inbound/index.test.mjs +0 -429
  354. package/lib/org/inbound/project.test.mjs +0 -287
  355. package/lib/org/integration-tools.test.mjs +0 -160
  356. package/lib/org/keys.test.mjs +0 -92
  357. package/lib/org/knowledge.test.mjs +0 -326
  358. package/lib/org/leases.test.mjs +0 -235
  359. package/lib/org/mesh-directives.test.mjs +0 -110
  360. package/lib/org/mesh-integration.test.mjs +0 -127
  361. package/lib/org/mesh.test.mjs +0 -400
  362. package/lib/org/messaging.test.mjs +0 -471
  363. package/lib/org/param-contract.test.mjs +0 -477
  364. package/lib/org/policy.test.mjs +0 -237
  365. package/lib/org/protocol.checksum.test.mjs +0 -90
  366. package/lib/org/protocol.test.mjs +0 -323
  367. package/lib/org/push.test.mjs +0 -792
  368. package/lib/org/registry.test.mjs +0 -100
  369. package/lib/org/resource-tools.test.mjs +0 -361
  370. package/lib/org/tool-access.test.mjs +0 -144
  371. package/lib/org/tool-surface-integration.test.mjs +0 -120
  372. package/lib/org/tool-surface.test.mjs +0 -1268
  373. package/lib/org/typing.test.mjs +0 -291
  374. package/lib/org/ui-parity.test.mjs +0 -560
  375. package/lib/org/verify.test.mjs +0 -194
  376. package/lib/org/work-ledger.test.mjs +0 -273
  377. package/lib/plan/adoption-e2e.test.mjs +0 -366
  378. package/lib/plan/budget-enforcement.test.mjs +0 -400
  379. package/lib/plan/compile.test.mjs +0 -382
  380. package/lib/plan/emit.test.mjs +0 -269
  381. package/lib/plan/explain.test.mjs +0 -188
  382. package/lib/prompts/parallelism.test.mjs +0 -177
  383. package/lib/rag/rag.test.mjs +0 -505
  384. package/lib/rate-guard.test.mjs +0 -272
  385. package/lib/reactive-gate.test.mjs +0 -57
  386. package/lib/render.test.mjs +0 -68
  387. package/lib/resource-governor.test.mjs +0 -488
  388. package/lib/scheduling/dynamic-jobs.test.mjs +0 -344
  389. package/lib/scheduling/jitter.test.mjs +0 -140
  390. package/lib/secrets/broker.test.mjs +0 -280
  391. package/lib/secrets/providers.test.mjs +0 -274
  392. package/lib/security/audit-engine.test.mjs +0 -424
  393. package/lib/security/coerce-args.test.mjs +0 -281
  394. package/lib/security/dangerous-tools.test.mjs +0 -68
  395. package/lib/security/external-content.test.mjs +0 -84
  396. package/lib/security/redact.test.mjs +0 -441
  397. package/lib/security/secret-equal.test.mjs +0 -55
  398. package/lib/session/config.test.mjs +0 -92
  399. package/lib/session/feed-core.test.mjs +0 -198
  400. package/lib/session/first-run.test.mjs +0 -121
  401. package/lib/session/frontdoor.test.mjs +0 -205
  402. package/lib/session/handoffs.test.mjs +0 -183
  403. package/lib/session/identity.test.mjs +0 -180
  404. package/lib/session/inbox-claims.test.mjs +0 -286
  405. package/lib/session/launch-args.test.mjs +0 -157
  406. package/lib/session/liveness.test.mjs +0 -100
  407. package/lib/session/status-summary.test.mjs +0 -118
  408. package/lib/session-permissions.test.mjs +0 -120
  409. package/lib/setup/claude-probe.test.mjs +0 -187
  410. package/lib/setup/completeness.test.mjs +0 -110
  411. package/lib/setup/context-pack.test.mjs +0 -89
  412. package/lib/setup/enrich.test.mjs +0 -115
  413. package/lib/setup/enroll-from-cohort.test.mjs +0 -300
  414. package/lib/setup/integration.test.mjs +0 -162
  415. package/lib/setup/io.test.mjs +0 -77
  416. package/lib/setup/runner.test.mjs +0 -132
  417. package/lib/setup/sections/identity.test.mjs +0 -234
  418. package/lib/setup/sections/inventory.test.mjs +0 -198
  419. package/lib/setup/sections/learning.test.mjs +0 -81
  420. package/lib/setup/sections/mandate.test.mjs +0 -388
  421. package/lib/setup/sections/messaging.test.mjs +0 -127
  422. package/lib/setup/sections/model.test.mjs +0 -240
  423. package/lib/setup/sections/org.test.mjs +0 -346
  424. package/lib/setup/sections/orgmail.test.mjs +0 -118
  425. package/lib/setup/sections/recovery.test.mjs +0 -98
  426. package/lib/setup/sections/subagents.test.mjs +0 -429
  427. package/lib/setup/sections/verify.test.mjs +0 -175
  428. package/lib/setup/sot.test.mjs +0 -81
  429. package/lib/setup/state.test.mjs +0 -115
  430. package/lib/singleton.test.mjs +0 -151
  431. package/lib/subagents/cli.test.mjs +0 -389
  432. package/lib/subagents/client.test.mjs +0 -309
  433. package/lib/subagents/gap.test.mjs +0 -234
  434. package/lib/subagents/lock.test.mjs +0 -248
  435. package/lib/subagents/manifest.test.mjs +0 -175
  436. package/lib/subagents/refs.test.mjs +0 -204
  437. package/lib/subagents/resolve.test.mjs +0 -422
  438. package/lib/subagents/schema.test.mjs +0 -328
  439. package/lib/telemetry/alerts.test.mjs +0 -109
  440. package/lib/telemetry/collect.test.mjs +0 -1274
  441. package/lib/tool-definitions-integration.test.mjs +0 -83
  442. package/lib/tool-definitions.test.mjs +0 -437
  443. package/lib/upgrade/global-refresh.test.mjs +0 -65
  444. package/lib/upgrade/launchd-reconcile.test.mjs +0 -272
  445. package/lib/upgrade/post-steps.test.mjs +0 -200
  446. package/lib/upgrade/verify.test.mjs +0 -164
  447. package/lib/util/fetch-timeout.test.mjs +0 -202
  448. package/lib/util/reconnect.test.mjs +0 -369
  449. package/lib/util/unhandled.test.mjs +0 -216
  450. package/lib/voice/outbound.test.mjs +0 -69
  451. package/lib/voice/session-rotation.test.mjs +0 -114
  452. package/lib/voice/stt.test.mjs +0 -226
  453. package/lib/voice/voice.test.mjs +0 -990
  454. package/scripts/cadence/enqueue-cadence-tick.test.mjs +0 -187
  455. package/scripts/ci/check-docs-accuracy.test.mjs +0 -409
  456. package/scripts/ci/check-durable-write-seam.test.mjs +0 -90
  457. package/scripts/ci/check-no-build-artifacts.test.mjs +0 -71
  458. package/scripts/ci/check-no-residual-identity.test.mjs +0 -202
  459. package/scripts/ci/check-skill-packs.test.mjs +0 -495
  460. package/scripts/ci/check-subagent-frontmatter.test.mjs +0 -124
  461. package/scripts/ci/check.test.mjs +0 -194
  462. package/scripts/ci/conformance-org-api.test.mjs +0 -425
  463. package/scripts/cloud-relay/voice/relay-identity.test.mjs +0 -96
  464. package/scripts/collective/hook-runner.test.mjs +0 -173
  465. package/scripts/cost/fleet-digest.test.mjs +0 -207
  466. package/scripts/cost/track-claude-usage-pricing.test.mjs +0 -183
  467. package/scripts/cost/track-claude-usage.test.mjs +0 -148
  468. package/scripts/daemon/agent-daemon-board-mine.test.mjs +0 -96
  469. package/scripts/daemon/agent-daemon-design.test.mjs +0 -238
  470. package/scripts/daemon/agent-daemon-frontdoor.test.mjs +0 -60
  471. package/scripts/daemon/agent-daemon.test.mjs +0 -995
  472. package/scripts/daemon/assurance-e2e.test.mjs +0 -613
  473. package/scripts/daemon/assurance.test.mjs +0 -1791
  474. package/scripts/daemon/board-mirror.test.mjs +0 -165
  475. package/scripts/daemon/cadence-consumer-frontdoor.test.mjs +0 -393
  476. package/scripts/daemon/cadence-consumer-governance.test.mjs +0 -276
  477. package/scripts/daemon/cadence-consumer.test.mjs +0 -776
  478. package/scripts/daemon/cadence-handlers.test.mjs +0 -837
  479. package/scripts/daemon/classifier-identity.test.mjs +0 -137
  480. package/scripts/daemon/classifier.test.mjs +0 -266
  481. package/scripts/daemon/classify-kind.test.mjs +0 -40
  482. package/scripts/daemon/context-compiler.test.mjs +0 -406
  483. package/scripts/daemon/deliver.test.mjs +0 -564
  484. package/scripts/daemon/dispatcher-cooldown.test.mjs +0 -122
  485. package/scripts/daemon/dispatcher-governance.test.mjs +0 -1013
  486. package/scripts/daemon/dispatcher-resume.test.mjs +0 -166
  487. package/scripts/daemon/dispatcher-session-continuity.test.mjs +0 -365
  488. package/scripts/daemon/execution-ladder.test.mjs +0 -470
  489. package/scripts/daemon/goal-steward-cadence.test.mjs +0 -312
  490. package/scripts/daemon/inbox-deferral-session.test.mjs +0 -49
  491. package/scripts/daemon/inbox-deferral.test.mjs +0 -336
  492. package/scripts/daemon/inbox-wake.test.mjs +0 -199
  493. package/scripts/daemon/integration.test.mjs +0 -149
  494. package/scripts/daemon/lib/self-echo.test.mjs +0 -153
  495. package/scripts/daemon/lib/session-router.test.mjs +0 -554
  496. package/scripts/daemon/prompt-builder-preamble.test.mjs +0 -210
  497. package/scripts/daemon/prompt-builder.test.mjs +0 -556
  498. package/scripts/daemon/responder-cost.test.mjs +0 -68
  499. package/scripts/daemon/responder-history.test.mjs +0 -221
  500. package/scripts/daemon/sdk-version.test.mjs +0 -31
  501. package/scripts/daemon/session-lock.test.mjs +0 -252
  502. package/scripts/daemon/session-outcomes.test.mjs +0 -533
  503. package/scripts/daemon/typing-registry.test.mjs +0 -102
  504. package/scripts/hooks/pre-send-audit.test.mjs +0 -354
  505. package/scripts/huddle/huddle-prompt.test.mjs +0 -176
  506. package/scripts/local-triggers/autoupdate.test.mjs +0 -518
  507. package/scripts/local-triggers/generate-plists.test.mjs +0 -456
  508. package/scripts/media-generation/brand-clause.test.mjs +0 -135
  509. package/scripts/org/send-orgmail.first-contact.test.mjs +0 -102
  510. package/scripts/poller/inbox-privilege-injection.test.mjs +0 -167
  511. package/scripts/poller/inbox-scan-poller.test.mjs +0 -295
  512. package/scripts/poller/lib/cloud-relay-dedup.test.mjs +0 -133
  513. package/scripts/poller/slack-socket-mode.test.mjs +0 -805
  514. package/scripts/poller-launchd/install.test.mjs +0 -243
  515. package/scripts/restore-from-backup.test.mjs +0 -181
  516. package/scripts/session/feed.test.mjs +0 -196
  517. package/scripts/session/supervisor-sh.test.mjs +0 -218
  518. package/scripts/session/supervisor.test.mjs +0 -482
  519. package/scripts/setup/configure-macos.test.mjs +0 -306
  520. package/scripts/setup/gen-subagent-manifest.test.mjs +0 -124
  521. package/scripts/setup/generate-agent-package-json.test.mjs +0 -143
  522. package/scripts/setup/generate-capability.test.mjs +0 -134
  523. package/scripts/setup/init-agent.test.mjs +0 -370
  524. package/scripts/setup/init-skill-marketplace.test.mjs +0 -193
  525. package/scripts/vendor/sync-skill-packs.test.mjs +0 -103
  526. package/scripts/watchdog/memory-watchdog.test.mjs +0 -64
@@ -200,14 +200,25 @@ export function buildIdentityClaudeMd(agentJson, o = {}) {
200
200
  * Default command is the bare `cohort-mcp` (the global npm install); a caller
201
201
  * that knows it is not on PATH passes `{command, args}` (e.g. the current node
202
202
  * + this package's bin/cohort-mcp.mjs) so the server still starts. Pure.
203
+ *
204
+ * `inheritEnv` (CF-22, W5-B): the Cohort Engine gives no MCP server a
205
+ * credential its entry does not name, so the entry names the org credential
206
+ * cohort-mcp resolves from the environment (lib/org/client.mjs, both brands —
207
+ * bin/cohort-mcp.mjs bridges NEOLITH_* at boot). An enrolled seat's cohort-mcp
208
+ * reads its token from config/org.yaml first; the list keeps an env-only seat
209
+ * working. The claude binary tolerates the key; `claude mcp add-json` drops it,
210
+ * so global-setup's verify step falls back to the JSON merge, which keeps it.
203
211
  * @param {string} agentRoot @param {{command?:string, args?:string[]}} [o]
204
212
  */
205
213
  export function cohortMcpEntry(agentRoot, o = {}) {
206
214
  const command = o.command ? String(o.command) : "cohort-mcp";
207
215
  const args = Array.isArray(o.args) ? o.args.map(String) : [];
208
- return { command, args, env: { COHORT_AGENT_ROOT: String(agentRoot || "") } };
216
+ return { command, args, env: { COHORT_AGENT_ROOT: String(agentRoot || "") }, inheritEnv: [...COHORT_MCP_INHERIT_ENV] };
209
217
  }
210
218
 
219
+ /** The org credential names cohort-mcp's entry inherits (= lib/engine/context/child-env.mjs ORG_CREDENTIAL_ENV). */
220
+ export const COHORT_MCP_INHERIT_ENV = Object.freeze(["COHORT_API_TOKEN", "COHORT_TOKEN", "COHORT_API_KEY", "NEOLITH_API_TOKEN", "NEOLITH_TOKEN", "NEOLITH_API_KEY"]);
221
+
211
222
  function clone(v) { return JSON.parse(JSON.stringify(v)); }
212
223
  function isObj(v) { return v && typeof v === "object" && !Array.isArray(v); }
213
224
 
@@ -305,6 +316,57 @@ export function mergeSessionSettings(settings, o = {}) {
305
316
  return { settings: out, changes };
306
317
  }
307
318
 
319
+ // ---------------------------------------------------------------------------
320
+ // State-YAML gates (2026-09): the two hooks that keep an agent's state/queues,
321
+ // state/dashboards and state/indexes YAML parseable and un-clobbered. They live
322
+ // in the SEAT's .claude/settings.json (commands are seat-relative and only make
323
+ // sense inside an agent repo), never in ~/.claude. A hand edit to that file does
324
+ // not survive a fresh checkout (the scaffold copy is what `create` ships) or an
325
+ // upgrade (settings.json is not on the upgrade path), so the scaffold carries
326
+ // them for new seats and `global-setup` — which `upgrade` runs as a post-step —
327
+ // merges them onto existing ones with this helper. Pure; I/O is in bin/maestro.
328
+ // ---------------------------------------------------------------------------
329
+
330
+ /** The two hook groups, in the exact shape the scaffold .claude/settings.json carries. */
331
+ export const STATE_YAML_GATE_HOOKS = Object.freeze([
332
+ Object.freeze({
333
+ event: "PreToolUse",
334
+ matcher: "Write|Edit",
335
+ marker: "scripts/hooks/pre-write-yaml-validate.mjs",
336
+ command: "node ./scripts/hooks/pre-write-yaml-validate.mjs",
337
+ }),
338
+ Object.freeze({
339
+ event: "PostToolUse",
340
+ matcher: "Bash|Write|Edit",
341
+ marker: "scripts/hooks/validate-state-yaml.sh",
342
+ command: "./scripts/hooks/validate-state-yaml.sh",
343
+ }),
344
+ ]);
345
+
346
+ /**
347
+ * Merge the state-YAML gate hook groups into a seat's settings.json object, once.
348
+ * Presence is judged by the script marker anywhere under the event (any matcher),
349
+ * so an operator who already wired the script by hand is left alone. Existing
350
+ * groups, permissions and every other key are preserved. Idempotent. Pure.
351
+ *
352
+ * @param {object} settings parsed .claude/settings.json ({} / null when absent)
353
+ * @returns {{ settings: object, added: string[] }} added = "PreToolUse:pre-write-yaml-validate" ...
354
+ */
355
+ export function mergeStateYamlGateHooks(settings) {
356
+ const out = isObj(settings) ? clone(settings) : {};
357
+ const added = [];
358
+ if (!isObj(out.hooks)) out.hooks = {};
359
+ for (const g of STATE_YAML_GATE_HOOKS) {
360
+ if (!Array.isArray(out.hooks[g.event])) out.hooks[g.event] = [];
361
+ const present = out.hooks[g.event].some((grp) =>
362
+ grp && Array.isArray(grp.hooks) && grp.hooks.some((h) => h && typeof h.command === "string" && h.command.includes(g.marker)));
363
+ if (present) continue;
364
+ out.hooks[g.event].push({ matcher: g.matcher, hooks: [{ type: "command", command: g.command }] });
365
+ added.push(`${g.event}:${basename(g.marker).replace(/\.(mjs|sh)$/, "")}`);
366
+ }
367
+ return { settings: out, added };
368
+ }
369
+
308
370
  /** Build the machine pointer object. */
309
371
  export function buildPointer(repoRoot, agentName, nowMs) {
310
372
  return {
@@ -23,10 +23,128 @@ import {
23
23
  readFileSync, unlinkSync, readdirSync, mkdirSync, existsSync,
24
24
  } from "node:fs";
25
25
  import { join, basename } from "node:path";
26
+ import { execFileSync } from "node:child_process";
26
27
  import { writeJsonAtomic } from "../fs-atomic.mjs";
27
28
 
29
+ // ---------------------------------------------------------------------------
30
+ // Whose session is this? (the hook's owner, not the hook)
31
+ // ---------------------------------------------------------------------------
32
+
33
+ /**
34
+ * THE BUG THIS EXISTS FOR. `register()` defaults `pid` to `process.pid`, and
35
+ * the caller is a Claude Code HOOK (scripts/collective/hook-runner.mjs) — a
36
+ * node process that exits the moment it has written the record. `list()` then
37
+ * probes that pid, finds it dead, and reaps the record. So every interactive
38
+ * session on every machine registered itself and was deleted on the very next
39
+ * read: `who.mjs` said "No active Claude Code sessions" on a box with two of
40
+ * them open, and the "N other sessions active" digest line never fired. The
41
+ * presence registry was structurally empty from the day it shipped.
42
+ *
43
+ * The owner is up the process tree: hook → (a shell, sometimes) → the Claude
44
+ * Code / engine session that ran the hook. {@link owningSession} walks
45
+ * parents until it meets one, bounded, and reads its `--name` when it has one
46
+ * (a front door is `claude --name <first>-main`, a peer `--name <first>-<slug>`).
47
+ *
48
+ * `owningSession` is pure over an injected `readParent`; {@link readParentOf}
49
+ * is the `ps` reader. Fail-open: no owner found ⇒ `{pid:null}`, and `list()`
50
+ * treats an unknown pid as alive (liveness then rests on the heartbeat alone).
51
+ */
52
+
53
+ /** Commands that ARE a session: Claude Code, an engine front door/peer, an engine run. */
54
+ const OWNER_RE = /(^|\/|\s)claude(\s|$)|(^|\s)cohort-session:\S|\/lib\/engine\/cli\.mjs\s+(run|session)(\s|$)/;
55
+ /** Claude Code's own infrastructure processes are never a session's owner. */
56
+ const NOT_OWNER_RE = /\bbg-pty-host\b|\bbg-spare\b|(^|\s)claude\s+daemon\s+run\b/;
57
+
58
+ /** `--name X` off a command line, or "". */
59
+ export function sessionNameOf(command) {
60
+ const m = String(command || "").match(/(?:^|\s)--name(?:=|\s+)(\S+)/);
61
+ return m ? m[1] : "";
62
+ }
63
+
64
+ /**
65
+ * Walk parents from `startPid` to the session that owns it. PURE over
66
+ * `readParent(pid) → {ppid, command, elapsedSec?} | null`.
67
+ *
68
+ * `startedAt` is the OWNER PROCESS's start (its `ps etime`), when the reader
69
+ * supplies one: that is when the session opened, which is what a "since T"
70
+ * about it should mean — not the moment a hook last happened to write the
71
+ * record. `null` when the reader has no elapsed time.
72
+ *
73
+ * @param {number} startPid
74
+ * @param {(pid:number) => ({ppid:number, command:string, elapsedSec?:number}|null)} readParent
75
+ * @param {{maxHops?:number, now?:number}} [opts]
76
+ * @returns {{pid:number|null, name:string, command:string, startedAt:string|null}}
77
+ */
78
+ export function owningSession(startPid, readParent, opts = {}) {
79
+ const maxHops = Number.isFinite(opts.maxHops) ? opts.maxHops : 8;
80
+ const now = Number.isFinite(opts.now) ? opts.now : Date.now();
81
+ let pid = Number(startPid);
82
+ for (let hop = 0; hop < maxHops && Number.isFinite(pid) && pid > 1; hop++) {
83
+ let row = null;
84
+ try { row = readParent(pid); } catch { row = null; }
85
+ if (!row) break;
86
+ const cmd = String(row.command || "");
87
+ if (OWNER_RE.test(cmd) && !NOT_OWNER_RE.test(cmd)) {
88
+ const el = Number(row.elapsedSec);
89
+ const startedAt = Number.isFinite(el) && el >= 0 ? new Date(now - el * 1000).toISOString() : null;
90
+ return { pid, name: sessionNameOf(cmd), command: cmd.slice(0, 200), startedAt };
91
+ }
92
+ const ppid = Number(row.ppid);
93
+ if (!Number.isFinite(ppid) || ppid === pid) break;
94
+ pid = ppid;
95
+ }
96
+ return { pid: null, name: "", command: "", startedAt: null };
97
+ }
98
+
99
+ /**
100
+ * `ps etime` → seconds. The format is `[[dd-]hh:]mm:ss`; anything else is null.
101
+ * Exported for its test.
102
+ */
103
+ export function parseElapsed(etime) {
104
+ const m = String(etime || "").trim().match(/^(?:(\d+)-)?(?:(\d+):)?(\d{1,2}):(\d{2})$/);
105
+ if (!m) return null;
106
+ const [, d, h, mi, s] = m;
107
+ return (Number(d || 0) * 86400) + (Number(h || 0) * 3600) + (Number(mi) * 60) + Number(s);
108
+ }
109
+
110
+ /** `ps` reader for {@link owningSession}: `{ppid, command, elapsedSec}` of one pid, or null. */
111
+ export function readParentOf(pid) {
112
+ try {
113
+ const out = execFileSync("/bin/ps", ["-o", "ppid=,etime=,command=", "-p", String(pid)], { encoding: "utf8", timeout: 2000 });
114
+ const line = out.split("\n").map((l) => l.trim()).find(Boolean);
115
+ if (!line) return null;
116
+ const m = line.match(/^(\d+)\s+(\S+)\s+(.*)$/);
117
+ if (!m) return null;
118
+ const elapsedSec = parseElapsed(m[2]);
119
+ return { ppid: Number(m[1]), command: m[3], elapsedSec: elapsedSec === null ? undefined : elapsedSec };
120
+ } catch {
121
+ return null;
122
+ }
123
+ }
124
+
125
+ /**
126
+ * The session that owns the CURRENT process (a hook). Starts at the parent —
127
+ * the hook itself is never the owner.
128
+ * @param {number} [fromPid] defaults to process.ppid
129
+ */
130
+ export function resolveOwner(fromPid = process.ppid) {
131
+ return owningSession(fromPid, readParentOf);
132
+ }
133
+
28
134
  const PRESENCE_REL = "state/collective/presence";
29
135
  const DEFAULT_STALE_SEC = 300;
136
+ /**
137
+ * A record whose owner pid is KNOWN and ALIVE is not reaped for a stale
138
+ * heartbeat: the beat is a PostToolUse hook, so an open-but-idle interactive
139
+ * session stops beating after its last tool call and would otherwise vanish
140
+ * from every count five minutes later (and come back, re-registered, with a
141
+ * `startedAt` of "whenever the next beat happened"). Liveness for such a record
142
+ * is the pid probe. This cap is the one bound on that: a pid can be reused
143
+ * after a reboot, so a record that has not beaten in this long is reaped even
144
+ * if something answers at its pid. Two days is longer than any session a hook
145
+ * beats for and shorter than the gap in which a reused pid stays plausible.
146
+ */
147
+ export const DEFAULT_IDLE_CAP_SEC = 48 * 3600;
30
148
 
31
149
  function presenceDir(agentRoot) {
32
150
  return join(agentRoot, PRESENCE_REL);
@@ -65,16 +183,26 @@ export function register(agentRoot, info = {}) {
65
183
  const path = join(dir, `${safeId(info.sessionId)}.json`);
66
184
  try {
67
185
  mkdirSync(dir, { recursive: true });
68
- let startedAt = nowIso(info.nowMs);
186
+ // startedAt, in order of trust: the prior record's (a re-register keeps
187
+ // the original), then the OWNER PROCESS's start when the caller resolved
188
+ // one (a hook re-registering a session that lost its record still says
189
+ // when the session opened, not when this write happened), then now.
190
+ let startedAt = null;
69
191
  try {
70
192
  if (existsSync(path)) {
71
193
  const prev = JSON.parse(readFileSync(path, "utf8"));
72
194
  if (prev && prev.startedAt) startedAt = prev.startedAt;
73
195
  }
74
196
  } catch { /* ignore prior */ }
197
+ if (!startedAt && typeof info.startedAt === "string" && Number.isFinite(Date.parse(info.startedAt))) startedAt = info.startedAt;
198
+ if (!startedAt) startedAt = nowIso(info.nowMs);
75
199
  const rec = {
76
200
  sessionId: String(info.sessionId),
77
- pid: Number.isFinite(info.pid) ? info.pid : process.pid,
201
+ // `null` is an honest "unknown owner" (the walk found nothing): list()
202
+ // then judges liveness by the heartbeat alone. Absent still means "me",
203
+ // which is right for a session registering itself, wrong for a hook —
204
+ // hooks pass what resolveOwner() found.
205
+ pid: info.pid === null ? null : Number.isFinite(info.pid) ? info.pid : process.pid,
78
206
  cwd: info.cwd || "",
79
207
  repo: info.repo || (info.cwd ? basename(info.cwd) : ""),
80
208
  agentName: info.agentName || "",
@@ -125,7 +253,14 @@ export function deregister(agentRoot, sessionId) {
125
253
  * entries as a side effect. Newest-heartbeat first.
126
254
  *
127
255
  * @param {string} agentRoot
128
- * @param {object} [opts] - { staleSec, nowMs, reap=true }
256
+ * THE RULE. A record with a KNOWN pid lives exactly as long as that pid does
257
+ * (bounded by `idleCapSec`, see {@link DEFAULT_IDLE_CAP_SEC}); a record with
258
+ * an UNKNOWN pid (`null` — the owner walk found nothing) lives as long as its
259
+ * heartbeat is within `staleSec`. The heartbeat is a tool-use hook, so it is
260
+ * evidence of activity, not of existence — and "is this session open" is the
261
+ * existence question.
262
+ *
263
+ * @param {object} [opts] - { staleSec, idleCapSec, nowMs, reap=true }
129
264
  * @returns {object[]}
130
265
  */
131
266
  export function list(agentRoot, opts = {}) {
@@ -133,6 +268,7 @@ export function list(agentRoot, opts = {}) {
133
268
  const dir = presenceDir(agentRoot);
134
269
  if (!existsSync(dir)) return [];
135
270
  const staleMs = (Number.isFinite(opts.staleSec) ? opts.staleSec : DEFAULT_STALE_SEC) * 1000;
271
+ const idleCapMs = (Number.isFinite(opts.idleCapSec) ? opts.idleCapSec : DEFAULT_IDLE_CAP_SEC) * 1000;
136
272
  const nowMs = Number.isFinite(opts.nowMs) ? opts.nowMs : Date.now();
137
273
  const reap = opts.reap !== false;
138
274
  let files;
@@ -152,8 +288,9 @@ export function list(agentRoot, opts = {}) {
152
288
  continue;
153
289
  }
154
290
  const age = nowMs - Date.parse(rec.lastBeat || rec.startedAt || 0);
155
- const stale = !Number.isFinite(age) || age > staleMs;
156
- const dead = !isProcessRunning(rec.pid);
291
+ const pidKnown = Number.isFinite(rec.pid) && rec.pid > 0;
292
+ const dead = pidKnown && !isProcessRunning(rec.pid);
293
+ const stale = !Number.isFinite(age) || (pidKnown ? age > idleCapMs : age > staleMs);
157
294
  if (stale || dead) {
158
295
  if (reap) { try { unlinkSync(path); } catch { /* */ } }
159
296
  continue;