@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
@@ -0,0 +1,45 @@
1
+ /**
2
+ * lib/engine/tools/schedule-wakeup.mjs — ScheduleWakeup {delaySeconds, prompt, reason}.
3
+ *
4
+ * Asks the session to start a turn of its own later (session-runtime/wakeup.mjs):
5
+ * the delay is clamped to 60–3600 seconds, a newer wakeup replaces the pending
6
+ * one, and the pending wakeup is kept in the transcript so a resumed session
7
+ * arms it again. Exists only in a `cohort session` (`sessionOnly`); a mutating
8
+ * tool to the permission engine.
9
+ *
10
+ * @module lib/engine/tools/schedule-wakeup
11
+ */
12
+
13
+ import { WAKEUP_MAX_SECONDS, WAKEUP_MIN_SECONDS } from "../session-runtime/wakeup.mjs";
14
+
15
+ /**
16
+ * @param {{scheduler:ReturnType<typeof import('../session-runtime/wakeup.mjs').createWakeupScheduler>}} o
17
+ */
18
+ export function createScheduleWakeupTool({ scheduler }) {
19
+ return {
20
+ name: "ScheduleWakeup",
21
+ readOnly: false,
22
+ sessionOnly: true,
23
+ description:
24
+ `Have this session start again by itself after a delay, with the given prompt as its message. The delay is between ${WAKEUP_MIN_SECONDS} and ${WAKEUP_MAX_SECONDS} seconds (values outside are moved to the nearest limit). ` +
25
+ "Only one wakeup is pending at a time: scheduling another replaces it. Messages and events that arrive earlier are handled as usual; the wakeup still comes. Use it to check back on something or to resume idle work.",
26
+ input_schema: {
27
+ type: "object",
28
+ properties: {
29
+ delaySeconds: { type: "number", description: `Seconds from now (${WAKEUP_MIN_SECONDS}–${WAKEUP_MAX_SECONDS}).` },
30
+ prompt: { type: "string", description: "The message this session receives when it wakes." },
31
+ reason: { type: "string", description: "Why, in a few words (shown when it fires)." },
32
+ },
33
+ required: ["delaySeconds", "prompt", "reason"],
34
+ },
35
+ /** @param {{delaySeconds:number, prompt:string, reason:string}} input */
36
+ async run(input) {
37
+ if (input.prompt.trim() === "") return { ok: false, error: "The prompt is empty." };
38
+ const w = scheduler.schedule({ delaySeconds: input.delaySeconds, prompt: input.prompt, reason: input.reason });
39
+ const notes = [];
40
+ if (w.clamped) notes.push(`the delay was set to ${w.seconds} s (allowed: ${WAKEUP_MIN_SECONDS}–${WAKEUP_MAX_SECONDS})`);
41
+ if (w.replaced) notes.push(`it replaces the wakeup that was due at ${new Date(w.replaced.dueAt).toISOString()}`);
42
+ return { ok: true, content: `Wakeup scheduled in ${w.seconds} s (at ${new Date(w.dueAt).toISOString()}).${notes.length ? ` Note: ${notes.join("; ")}.` : ""}` };
43
+ },
44
+ };
45
+ }
@@ -0,0 +1,144 @@
1
+ /**
2
+ * lib/engine/tools/schema.mjs — tool-input validation and repair (pure).
3
+ *
4
+ * A small JSON-Schema subset: exactly what the built-in tool schemas use
5
+ * (object / string / number / integer / boolean / array, `enum`, `required`,
6
+ * `minimum`/`maximum`). It is not a general validator and does not pretend to
7
+ * be one; adding a heavier dependency for it is out of bounds (maestro is
8
+ * zero-dep by architecture).
9
+ *
10
+ * Open-weight models get tool arguments nearly right more often than exactly
11
+ * right, so validation COERCES the harmless cases before it refuses:
12
+ * · a numeric string for a number/integer ("40" → 40)
13
+ * · "true"/"false" for a boolean
14
+ * Unknown properties are dropped, not refused — refusing them costs a turn and
15
+ * teaches nothing.
16
+ *
17
+ * `repairToolJson` is the other half: argument text that did not parse as JSON
18
+ * gets one conservative repair pass (code fences, trailing commas, unbalanced
19
+ * closing brackets) before the loop reports the error back to the model.
20
+ *
21
+ * @module lib/engine/tools/schema
22
+ */
23
+
24
+ /**
25
+ * @param {any} schema an object schema
26
+ * @param {unknown} input
27
+ * @returns {{ ok:true, value: Record<string, unknown> } | { ok:false, error: string }}
28
+ */
29
+ export function validateInput(schema, input) {
30
+ if (!input || typeof input !== "object" || Array.isArray(input)) {
31
+ return { ok: false, error: "input must be a JSON object" };
32
+ }
33
+ const props = schema?.properties || {};
34
+ const out = {};
35
+ const problems = [];
36
+ for (const key of schema?.required || []) {
37
+ if (/** @type any */ (input)[key] === undefined || /** @type any */ (input)[key] === null) {
38
+ problems.push(`missing required parameter "${key}"`);
39
+ }
40
+ }
41
+ for (const [key, raw] of Object.entries(input)) {
42
+ const spec = props[key];
43
+ if (!spec || raw === undefined || raw === null) continue;
44
+ const r = coerce(spec, raw);
45
+ if (!r.ok) problems.push(`parameter "${key}" ${r.error}`);
46
+ else out[key] = r.value;
47
+ }
48
+ if (problems.length > 0) return { ok: false, error: problems.join("; ") };
49
+ return { ok: true, value: out };
50
+ }
51
+
52
+ /** @param {any} spec @param {unknown} v @returns {{ok:true, value:unknown}|{ok:false, error:string}} */
53
+ function coerce(spec, v) {
54
+ switch (spec.type) {
55
+ case "string":
56
+ if (typeof v !== "string") return { ok: false, error: "must be a string" };
57
+ if (Array.isArray(spec.enum) && !spec.enum.includes(v)) {
58
+ return { ok: false, error: `must be one of ${spec.enum.map((e) => JSON.stringify(e)).join(", ")}` };
59
+ }
60
+ return { ok: true, value: v };
61
+ case "number":
62
+ case "integer": {
63
+ const n = typeof v === "number" ? v : typeof v === "string" && v.trim() !== "" ? Number(v) : NaN;
64
+ if (!Number.isFinite(n)) return { ok: false, error: `must be a ${spec.type}` };
65
+ if (spec.type === "integer" && !Number.isInteger(n)) return { ok: false, error: "must be an integer" };
66
+ if (typeof spec.minimum === "number" && n < spec.minimum) return { ok: false, error: `must be ≥ ${spec.minimum}` };
67
+ if (typeof spec.maximum === "number" && n > spec.maximum) return { ok: false, error: `must be ≤ ${spec.maximum}` };
68
+ return { ok: true, value: n };
69
+ }
70
+ case "boolean":
71
+ if (typeof v === "boolean") return { ok: true, value: v };
72
+ if (v === "true" || v === "false") return { ok: true, value: v === "true" };
73
+ return { ok: false, error: "must be a boolean" };
74
+ case "array":
75
+ if (!Array.isArray(v)) return { ok: false, error: "must be an array" };
76
+ return { ok: true, value: v };
77
+ case "object":
78
+ if (!v || typeof v !== "object" || Array.isArray(v)) return { ok: false, error: "must be an object" };
79
+ return { ok: true, value: v };
80
+ default:
81
+ return { ok: true, value: v };
82
+ }
83
+ }
84
+
85
+ /**
86
+ * One conservative repair pass over tool-argument text that failed to parse.
87
+ * Never guesses at content — only at framing.
88
+ *
89
+ * @param {string} raw
90
+ * @returns {{ ok:true, value: Record<string, unknown> } | { ok:false, error: string }}
91
+ */
92
+ export function repairToolJson(raw) {
93
+ if (typeof raw !== "string" || raw.trim() === "") return { ok: false, error: "empty tool arguments" };
94
+ let s = raw.trim();
95
+ // A fenced block: ```json { … } ```
96
+ const fence = s.match(/^```[a-zA-Z]*\s*([\s\S]*?)\s*```$/);
97
+ if (fence) s = fence[1].trim();
98
+ // Leading prose before the object ("Here are the args: {…}").
99
+ const first = s.indexOf("{");
100
+ if (first > 0) s = s.slice(first);
101
+ // Trailing commas before a closer.
102
+ s = s.replace(/,\s*([}\]])/g, "$1");
103
+ const attempt = tryObject(s);
104
+ if (attempt) return { ok: true, value: attempt };
105
+ // Unbalanced closers (a truncated stream tail), outside strings only.
106
+ const closed = closeBrackets(s);
107
+ if (closed !== null) {
108
+ const again = tryObject(closed);
109
+ if (again) return { ok: true, value: again };
110
+ }
111
+ return { ok: false, error: "tool arguments are not valid JSON and could not be repaired" };
112
+ }
113
+
114
+ /** @param {string} s */
115
+ function tryObject(s) {
116
+ try {
117
+ const v = JSON.parse(s);
118
+ return v && typeof v === "object" && !Array.isArray(v) ? v : null;
119
+ } catch {
120
+ return null; // not parseable yet; the caller tries the next repair
121
+ }
122
+ }
123
+
124
+ /** @param {string} s @returns {string|null} */
125
+ function closeBrackets(s) {
126
+ const stack = [];
127
+ let inString = false;
128
+ let escaped = false;
129
+ for (const ch of s) {
130
+ if (inString) {
131
+ if (escaped) escaped = false;
132
+ else if (ch === "\\") escaped = true;
133
+ else if (ch === '"') inString = false;
134
+ continue;
135
+ }
136
+ if (ch === '"') inString = true;
137
+ else if (ch === "{" || ch === "[") stack.push(ch === "{" ? "}" : "]");
138
+ else if (ch === "}" || ch === "]") {
139
+ if (stack.pop() !== ch) return null;
140
+ }
141
+ }
142
+ if (inString || stack.length === 0) return null;
143
+ return s + stack.reverse().join("");
144
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * lib/engine/tools/send-message.mjs — SendMessage {to, message, summary?}.
3
+ *
4
+ * Local peer messaging for `cohort session` (W4-A1): the message goes to the
5
+ * named session's control socket (session-runtime/registry.mjs finds it) and
6
+ * arrives there as a user turn tagged "[from <this session>] …" — at once when
7
+ * the recipient is idle ("delivered"), after its current work otherwise
8
+ * ("queued"). A name with no live session is "not_found".
9
+ *
10
+ * This is NOT an outbound org send: nothing leaves the machine, so the
11
+ * in-process send gate (hooks.mjs, `messaging_send` & co.) does not screen it.
12
+ * It is a mutating tool to the permission engine (an allow rule, bypass, or an
13
+ * interactive approval), and it exists only in a session, never in a subagent
14
+ * (`sessionOnly`).
15
+ *
16
+ * @module lib/engine/tools/send-message
17
+ */
18
+
19
+ import { isValidSessionName } from "../session-runtime/registry.mjs";
20
+ import { sendControl } from "../session-runtime/socket.mjs";
21
+
22
+ export const MAX_MESSAGE_CHARS = 100_000;
23
+
24
+ /**
25
+ * @param {{selfName:string, registry:{get:(name:string)=>any, list:()=>any[]}, send?:typeof sendControl}} o
26
+ */
27
+ export function createSendMessageTool({ selfName, registry, send = sendControl }) {
28
+ const liveNames = () =>
29
+ registry
30
+ .list()
31
+ .map((e) => e.name)
32
+ .filter((n) => n !== selfName);
33
+ return {
34
+ name: "SendMessage",
35
+ readOnly: false,
36
+ sessionOnly: true,
37
+ description:
38
+ "Send a message to another agent session running on this machine, addressed by its session name (ListAgents shows who is running). " +
39
+ "The recipient gets it as a new message that starts with [from <your session name>]; if it is in the middle of work, the message waits until that work ends. " +
40
+ "The answer is one of: delivered (it is being worked on now), queued (it will be worked on next), not_found (no session by that name is running). " +
41
+ "Replies, if any, arrive later as messages from that session. Nothing is sent outside this machine.",
42
+ input_schema: {
43
+ type: "object",
44
+ properties: {
45
+ to: { type: "string", description: "The recipient's session name." },
46
+ message: { type: "string", description: "The full message. The recipient sees only this, so include what it needs." },
47
+ summary: { type: "string", description: "A few words describing the message, shown in the recipient's terminal." },
48
+ },
49
+ required: ["to", "message"],
50
+ },
51
+ /** @param {{to:string, message:string, summary?:string}} input */
52
+ async run(input) {
53
+ const to = input.to.trim();
54
+ if (!isValidSessionName(to)) return { ok: false, error: `"${input.to}" is not a session name.` };
55
+ if (to === selfName) return { ok: false, error: "That is this session's own name; a message to yourself is not sent." };
56
+ if (input.message.trim() === "") return { ok: false, error: "The message is empty." };
57
+ if (input.message.length > MAX_MESSAGE_CHARS) return { ok: false, error: `The message is longer than ${MAX_MESSAGE_CHARS} characters; shorten it or point to a file.` };
58
+ const notFound = () => {
59
+ const live = liveNames();
60
+ return { ok: /** @type {false} */ (false), error: `not_found: no session named "${to}" is running on this machine. Running: ${live.join(", ") || "none"}.` };
61
+ };
62
+ const entry = registry.get(to);
63
+ if (!entry) return notFound();
64
+ const r = await send(entry.socketPath, { type: "user", message: input.message, from: selfName, ...(input.summary ? { summary: input.summary } : {}) });
65
+ if (!r.ok) {
66
+ if (r.error.code === "not_running") return notFound();
67
+ return { ok: false, error: `The message to ${to} may not have arrived: ${r.error.message}.` };
68
+ }
69
+ const status = r.response?.status;
70
+ if (r.response?.ok && (status === "delivered" || status === "queued")) {
71
+ return { ok: true, content: JSON.stringify({ status, to }) };
72
+ }
73
+ if (status === "ended") return notFound();
74
+ return { ok: false, error: `${to} did not accept the message: ${r.response?.error ?? "no reason given"}.` };
75
+ },
76
+ };
77
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * lib/engine/tools/session.mjs — the tools that hold per-run state.
3
+ *
4
+ * The registry (tools/index.mjs) holds stateless built-ins. These are created
5
+ * per run, because they share state with it:
6
+ *
7
+ * NotebookEdit stateless, listed here so the registry's
8
+ * order (a prompt-cache prefix) stays put
9
+ * TodoWrite the run's task list
10
+ * Task, Agent, TaskOutput, TaskStop subagents (agents/runtime.mjs)
11
+ * BashOutput, KillShell background shells (bash-background.mjs)
12
+ *
13
+ * and the registry's Bash is replaced by the session Bash (persistent working
14
+ * directory, run_in_background). Order in the tool list: built-ins, Skill,
15
+ * then these in the order above, then MCP tools.
16
+ *
17
+ * `--tools` selection: a session tool is exposed when named; BashOutput and
18
+ * KillShell come with Bash, TaskOutput and TaskStop with Task or Agent.
19
+ *
20
+ * @module lib/engine/tools/session
21
+ */
22
+
23
+ import { createBashOutputTool, createKillShellTool, createSessionBash, createShellManager } from "./bash-background.mjs";
24
+ import { notebookEditTool } from "./notebook-edit.mjs";
25
+ import { createTodoWriteTool } from "./todo.mjs";
26
+
27
+ export const SESSION_TOOL_NAMES = Object.freeze(["NotebookEdit", "TodoWrite", "Task", "Agent", "TaskOutput", "TaskStop", "BashOutput", "KillShell", "Workflow", "WorkflowStatus"]);
28
+
29
+ /**
30
+ * Which session tools a `--tools` list exposes (null = every one). Pure.
31
+ * @param {string[]|null} names
32
+ * @returns {Set<string>|null}
33
+ */
34
+ export function wantedSessionTools(names) {
35
+ if (names == null) return null;
36
+ const set = new Set(names.filter((n) => SESSION_TOOL_NAMES.includes(n)));
37
+ if (names.includes("Bash")) {
38
+ set.add("BashOutput");
39
+ set.add("KillShell");
40
+ }
41
+ if (set.has("Task") || set.has("Agent")) {
42
+ set.add("TaskOutput");
43
+ set.add("TaskStop");
44
+ }
45
+ if (set.has("Workflow")) set.add("WorkflowStatus");
46
+ return set;
47
+ }
48
+
49
+ /**
50
+ * @param {{cwd:string, env:NodeJS.ProcessEnv, spillDir:string, todoState:ReturnType<typeof import('./todo.mjs').createTodoState>,
51
+ * onShellExit?:(summary:any)=>void, envPassthrough?:string[]}} o `onShellExit`: a background shell ended on its own (CF-50);
52
+ * `envPassthrough`: extended-shape secrets background shells keep (CF-22)
53
+ */
54
+ export function createSessionToolkit({ cwd, env, spillDir, todoState, onShellExit, envPassthrough = [] }) {
55
+ const shells = createShellManager({ spillDir, env, onExit: onShellExit, passthrough: envPassthrough });
56
+ const bash = createSessionBash({ shells, projectCwd: cwd });
57
+ return {
58
+ shells,
59
+ bash,
60
+ /** The registry's tools with Bash replaced by the session Bash, order kept. @param {any[]} tools */
61
+ withSessionBash: (tools) => tools.map((t) => (t.name === "Bash" ? bash : t)),
62
+ /**
63
+ * @param {{wanted:Set<string>|null, agentTools:any[], workflowTools?:any[]}} p workflowTools: Workflow, WorkflowStatus (W4-A2), appended
64
+ */
65
+ tools: ({ wanted, agentTools, workflowTools = [] }) =>
66
+ [notebookEditTool, createTodoWriteTool(todoState), ...agentTools, createBashOutputTool(shells), createKillShellTool(shells), ...workflowTools].filter((t) => wanted === null || wanted.has(t.name)),
67
+ /** Kill every background shell. */
68
+ dispose: () => shells.killAll(),
69
+ };
70
+ }
@@ -0,0 +1,144 @@
1
+ /**
2
+ * lib/engine/tools/todo.mjs — the TodoWrite tool and the run's task list.
3
+ *
4
+ * Input contract: `{ todos: [{ content, status: pending|in_progress|completed, activeForm? }] }`.
5
+ * Each call replaces the whole list.
6
+ *
7
+ * Validation refuses what cannot be a list (a non-array, an item without
8
+ * content, an unknown status). Several items in progress at once is allowed
9
+ * but answered with a warning: one item in progress keeps the list honest.
10
+ *
11
+ * The list lives in the run's state: the CLI appends every change to the
12
+ * session transcript as `{type:'todos', sessionId, ts, todos}` (so `--resume`
13
+ * restores it), reports it in stream-json (`cohort_todos`) and in the result
14
+ * (`cohort.todos`). A subagent gets its own list, which is not persisted.
15
+ *
16
+ * `validateTodos` and `todosFromRecords` are pure.
17
+ *
18
+ * @module lib/engine/tools/todo
19
+ */
20
+
21
+ import { readFileSync } from "node:fs";
22
+ import { parseTranscript } from "../session/store.mjs";
23
+
24
+ export const TODO_STATUSES = Object.freeze(["pending", "in_progress", "completed"]);
25
+
26
+ /**
27
+ * @typedef {{content:string, status:'pending'|'in_progress'|'completed', activeForm?:string}} Todo
28
+ */
29
+
30
+ /**
31
+ * @param {unknown} raw
32
+ * @returns {{ok:true, todos:Todo[], warnings:string[]}|{ok:false, error:string}}
33
+ */
34
+ export function validateTodos(raw) {
35
+ if (!Array.isArray(raw)) return { ok: false, error: "todos must be an array" };
36
+ /** @type {Todo[]} */
37
+ const todos = [];
38
+ const problems = [];
39
+ raw.forEach((item, i) => {
40
+ if (!item || typeof item !== "object" || Array.isArray(item)) {
41
+ problems.push(`todos[${i}] must be an object`);
42
+ return;
43
+ }
44
+ const content = typeof item.content === "string" ? item.content.trim() : "";
45
+ if (content === "") problems.push(`todos[${i}].content must be a non-empty string`);
46
+ if (!TODO_STATUSES.includes(item.status)) problems.push(`todos[${i}].status must be one of ${TODO_STATUSES.join(", ")}`);
47
+ if (item.activeForm !== undefined && typeof item.activeForm !== "string") problems.push(`todos[${i}].activeForm must be a string`);
48
+ /** @type {Todo} */
49
+ const todo = { content, status: item.status };
50
+ if (typeof item.activeForm === "string" && item.activeForm.trim() !== "") todo.activeForm = item.activeForm.trim();
51
+ todos.push(todo);
52
+ });
53
+ if (problems.length > 0) return { ok: false, error: problems.join("; ") };
54
+ const inProgress = todos.filter((t) => t.status === "in_progress").length;
55
+ const warnings = inProgress > 1 ? [`${inProgress} items are in_progress; keep exactly one item in progress and finish it before starting the next.`] : [];
56
+ return { ok: true, todos, warnings };
57
+ }
58
+
59
+ /** The most recent list recorded in a transcript's records. @param {any[]} records @returns {Todo[]} */
60
+ export function todosFromRecords(records) {
61
+ for (let i = records.length - 1; i >= 0; i--) {
62
+ const r = records[i];
63
+ if (r?.type === "todos" && Array.isArray(r.todos)) {
64
+ const v = validateTodos(r.todos);
65
+ return v.ok ? v.todos : [];
66
+ }
67
+ }
68
+ return [];
69
+ }
70
+
71
+ /** The list a resumed session had. Never throws. @param {string} transcriptPath */
72
+ export function loadSessionTodos(transcriptPath) {
73
+ try {
74
+ return todosFromRecords(parseTranscript(readFileSync(transcriptPath, "utf8")).records);
75
+ } catch {
76
+ return []; // no transcript yet, or unreadable: start with an empty list
77
+ }
78
+ }
79
+
80
+ /**
81
+ * @param {{initial?:Todo[], onChange?:(todos:Todo[])=>void}} [o]
82
+ */
83
+ export function createTodoState({ initial = [], onChange = () => {} } = {}) {
84
+ let todos = [...initial];
85
+ return {
86
+ get todos() {
87
+ return todos;
88
+ },
89
+ /** @param {Todo[]} next */
90
+ set(next) {
91
+ todos = next;
92
+ onChange(next);
93
+ },
94
+ };
95
+ }
96
+
97
+ /** @param {Todo[]} todos */
98
+ function summary(todos) {
99
+ const n = (/** @type string */ s) => todos.filter((t) => t.status === s).length;
100
+ return `${todos.length} item${todos.length === 1 ? "" : "s"}: ${n("completed")} completed, ${n("in_progress")} in progress, ${n("pending")} pending`;
101
+ }
102
+
103
+ /**
104
+ * @param {ReturnType<typeof createTodoState>} state
105
+ * @returns {import('./index.mjs').Tool & {forChild:()=>any}}
106
+ */
107
+ export function createTodoWriteTool(state) {
108
+ return {
109
+ name: "TodoWrite",
110
+ // Changes nothing outside this run's own state.
111
+ readOnly: true,
112
+ description:
113
+ "Keep a short task list for multi-step work, so progress stays visible. Each call sends the whole list and replaces the previous one. " +
114
+ "Mark an item in_progress before starting it and completed as soon as it is done; keep one item in progress at a time. " +
115
+ "`activeForm` is an optional present-tense phrasing of the item (\"Running the tests\").",
116
+ input_schema: {
117
+ type: "object",
118
+ properties: {
119
+ todos: {
120
+ type: "array",
121
+ description: "The complete task list.",
122
+ items: {
123
+ type: "object",
124
+ properties: {
125
+ content: { type: "string", description: "The task, in imperative form." },
126
+ status: { type: "string", enum: [...TODO_STATUSES] },
127
+ activeForm: { type: "string", description: "The task in present-continuous form." },
128
+ },
129
+ required: ["content", "status"],
130
+ },
131
+ },
132
+ },
133
+ required: ["todos"],
134
+ },
135
+ async run(input) {
136
+ const v = validateTodos(input.todos);
137
+ if (!v.ok) return { ok: false, error: `The task list was not saved: ${v.error}.` };
138
+ state.set(v.todos);
139
+ const warn = v.warnings.length ? `\n\nWarning: ${v.warnings.join(" ")}` : "";
140
+ return { ok: true, content: `Task list saved (${summary(v.todos)}).${warn}` };
141
+ },
142
+ forChild: () => createTodoWriteTool(createTodoState()),
143
+ };
144
+ }