@north-light/crouter 0.3.173 → 0.3.175

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 (674) hide show
  1. package/bin/runtime-selector.mjs +8 -1
  2. package/dist/api/__tests__/{client.test.js → serial/client.test.js} +2 -135
  3. package/dist/api/client.d.ts +12 -21
  4. package/dist/api/client.js +22 -31
  5. package/dist/api/dto/broker-ops.d.ts +158 -0
  6. package/dist/api/dto/broker-ops.js +6 -0
  7. package/dist/api/dto/crons.d.ts +6 -0
  8. package/dist/api/dto/human.d.ts +7 -1
  9. package/dist/api/dto/inbox.d.ts +1 -71
  10. package/dist/api/dto/inbox.js +1 -9
  11. package/dist/api/dto/nodes.d.ts +6 -7
  12. package/dist/api/dto/reviews.d.ts +1 -11
  13. package/dist/api/routes.d.ts +7 -4
  14. package/dist/api/routes.js +8 -5
  15. package/dist/build-root.js +3 -3
  16. package/dist/builtin-memory/04-base-worker.md +1 -4
  17. package/dist/builtin-memory/05-kinds/advisor/00-base.md +1 -5
  18. package/dist/builtin-memory/05-kinds/advisor/01-orchestrator.md +2 -7
  19. package/dist/builtin-memory/05-kinds/design/00-base.md +1 -6
  20. package/dist/builtin-memory/05-kinds/developer/00-base.md +1 -6
  21. package/dist/builtin-memory/05-kinds/explore/00-base.md +1 -4
  22. package/dist/builtin-memory/05-kinds/explore/01-orchestrator.md +1 -2
  23. package/dist/builtin-memory/05-kinds/general/00-base.md +1 -3
  24. package/dist/builtin-memory/05-kinds/plan/00-base.md +1 -6
  25. package/dist/builtin-memory/05-kinds/plan/reviewers/00-base.md +1 -2
  26. package/dist/builtin-memory/05-kinds/plan/reviewers/architecture-fit.md +1 -3
  27. package/dist/builtin-memory/05-kinds/plan/reviewers/code-smells.md +1 -3
  28. package/dist/builtin-memory/05-kinds/plan/reviewers/pattern-consistency.md +1 -3
  29. package/dist/builtin-memory/05-kinds/plan/reviewers/requirements-coverage.md +1 -3
  30. package/dist/builtin-memory/05-kinds/plan/reviewers/security.md +1 -4
  31. package/dist/builtin-memory/05-kinds/review/companion/00-base.md +2 -3
  32. package/dist/builtin-memory/05-kinds/spec/00-base.md +1 -2
  33. package/dist/builtin-memory/advisor/council.md +1 -6
  34. package/dist/builtin-memory/design.md +2 -7
  35. package/dist/builtin-memory/development.md +9 -14
  36. package/dist/builtin-memory/internal/marketplaces.md +2 -6
  37. package/dist/builtin-memory/internal/nodes-and-canvas.md +1 -1
  38. package/dist/builtin-memory/internal/plugins.md +2 -8
  39. package/dist/builtin-memory/internal/storage-tiers.md +1 -2
  40. package/dist/builtin-memory/testing.md +41 -0
  41. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/{provider-rotation.test.ts → serial/provider-rotation.test.ts} +27 -179
  42. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/claude-plugin-commands.ts +5 -15
  43. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/frontmatter-rules/index.ts +1 -28
  44. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +43 -88
  45. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +7 -1
  46. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +9 -1
  47. package/dist/cli.js +2 -2
  48. package/dist/clients/attach/__tests__/attach-remote-readonly.test.js +37 -131
  49. package/dist/clients/attach/__tests__/chat-view-snapshot-ordering.test.js +71 -152
  50. package/dist/clients/attach/__tests__/context-message.test.js +16 -174
  51. package/dist/clients/attach/__tests__/crtr-output.test.js +36 -2069
  52. package/dist/clients/attach/__tests__/diagram.test.js +2 -2
  53. package/dist/clients/attach/__tests__/edit-diff.test.js +0 -4
  54. package/dist/clients/attach/__tests__/file-review-focus.test.js +2 -1
  55. package/dist/clients/attach/__tests__/group-activity.test.js +40 -229
  56. package/dist/clients/attach/__tests__/oauth-dialog-lifecycle.test.js +26 -76
  57. package/dist/clients/attach/__tests__/prompt-preview.test.js +4 -70
  58. package/dist/clients/attach/__tests__/ref-autocomplete.test.js +11 -125
  59. package/dist/clients/attach/__tests__/titled-editor-preview.test.js +14 -205
  60. package/dist/clients/attach/command.d.ts +2 -1
  61. package/dist/clients/attach/command.js +81 -76
  62. package/dist/clients/attach/input/capabilities.d.ts +27 -0
  63. package/dist/clients/attach/input/capabilities.js +96 -0
  64. package/dist/clients/attach/input/controller.d.ts +20 -182
  65. package/dist/clients/attach/input/controller.js +128 -537
  66. package/dist/clients/attach/input/overlay-owner.d.ts +41 -0
  67. package/dist/clients/attach/input/overlay-owner.js +199 -0
  68. package/dist/clients/attach/overlays/file-review.d.ts +0 -4
  69. package/dist/clients/attach/overlays/file-review.js +13 -5
  70. package/dist/clients/attach/overlays/graph.js +2 -10
  71. package/dist/clients/attach/render/chat-view.d.ts +4 -3
  72. package/dist/clients/attach/render/chat-view.js +39 -33
  73. package/dist/clients/attach/render/group-activity.d.ts +1 -0
  74. package/dist/clients/attach/render/group-activity.js +20 -13
  75. package/dist/clients/attach/render/group-recap.js +12 -3
  76. package/dist/clients/attach/session/connection.js +9 -15
  77. package/dist/clients/attach/session/editor-inventory.d.ts +6 -5
  78. package/dist/clients/attach/session/editor-inventory.js +7 -8
  79. package/dist/clients/attach/session/file-links.d.ts +13 -0
  80. package/dist/clients/attach/session/file-links.js +107 -0
  81. package/dist/clients/attach/session/frame.d.ts +3 -0
  82. package/dist/clients/attach/session/frame.js +10 -1
  83. package/dist/clients/attach/session/frames.js +2 -3
  84. package/dist/clients/attach/session/input-wiring.d.ts +4 -1
  85. package/dist/clients/attach/session/input-wiring.js +15 -14
  86. package/dist/clients/attach/session/mouse.d.ts +6 -1
  87. package/dist/clients/attach/session/mouse.js +8 -5
  88. package/dist/clients/attach/session/profile-files.js +18 -12
  89. package/dist/clients/attach/session/reconnect.js +1 -1
  90. package/dist/clients/attach/session/state-sync.js +1 -2
  91. package/dist/clients/attach/session/surface-host.d.ts +3 -2
  92. package/dist/clients/attach/session/surface-host.js +4 -4
  93. package/dist/clients/attach/slash/dispatch.d.ts +8 -31
  94. package/dist/clients/attach/slash/dispatch.js +34 -81
  95. package/dist/clients/attach/viewer.js +820 -860
  96. package/dist/clients/conversation/projection.d.ts +79 -0
  97. package/dist/clients/conversation/projection.js +341 -0
  98. package/dist/clients/inbox/__tests__/serial/inbox-controller.test.js +159 -0
  99. package/dist/clients/inbox/__tests__/serial/mount-panel.test.js +151 -0
  100. package/dist/clients/inbox/controller.d.ts +0 -4
  101. package/dist/clients/inbox/controller.js +0 -2
  102. package/dist/clients/inbox/review/__tests__/review-core.test.js +4 -293
  103. package/dist/clients/inbox/review/__tests__/teardown-notice.test.js +100 -0
  104. package/dist/clients/inbox/review/comments-client.d.ts +3 -2
  105. package/dist/clients/inbox/review/comments-client.js +12 -12
  106. package/dist/clients/inbox/review/companion-pane.d.ts +30 -0
  107. package/dist/clients/inbox/review/companion-pane.js +86 -0
  108. package/dist/clients/inbox/review/document-surface.d.ts +182 -0
  109. package/dist/clients/inbox/review/{document-region.js → document-surface.js} +252 -48
  110. package/dist/clients/inbox/review/frame.js +5 -4
  111. package/dist/clients/inbox/review/launch.d.ts +8 -8
  112. package/dist/clients/inbox/review/launch.js +52 -92
  113. package/dist/clients/inbox/review/nvim.d.ts +2 -0
  114. package/dist/clients/inbox/review/nvim.js +14 -12
  115. package/dist/clients/inbox/review/review-client.d.ts +12 -8
  116. package/dist/clients/inbox/review/review-client.js +46 -35
  117. package/dist/clients/inbox/review-adapter.d.ts +3 -10
  118. package/dist/clients/inbox/review-adapter.js +27 -19
  119. package/dist/clients/inbox/surface.d.ts +0 -4
  120. package/dist/clients/inbox/surface.js +0 -2
  121. package/dist/clients/surfaces/host.d.ts +11 -3
  122. package/dist/commands/__tests__/api-client-cold-start-diagnostic.test.js +7 -34
  123. package/dist/commands/__tests__/human.test.js +11 -322
  124. package/dist/commands/api-client.d.ts +5 -5
  125. package/dist/commands/api-client.js +6 -85
  126. package/dist/commands/canvas-browse.js +3 -29
  127. package/dist/commands/canvas-config.js +1 -1
  128. package/dist/commands/canvas-use.js +3 -3
  129. package/dist/commands/canvas.js +1 -1
  130. package/dist/commands/cron.js +5 -1
  131. package/dist/commands/human/queue.d.ts +0 -4
  132. package/dist/commands/human/queue.js +7 -40
  133. package/dist/commands/memory/list.d.ts +14 -0
  134. package/dist/commands/memory/list.js +35 -31
  135. package/dist/commands/memory/read.d.ts +7 -0
  136. package/dist/commands/memory/read.js +14 -8
  137. package/dist/commands/memory/write.js +13 -12
  138. package/dist/commands/node/bash.d.ts +1 -0
  139. package/dist/commands/node/bash.js +198 -0
  140. package/dist/commands/node/create.d.ts +23 -0
  141. package/dist/commands/node/create.js +207 -0
  142. package/dist/commands/node/inspect.d.ts +2 -0
  143. package/dist/commands/node/inspect.js +302 -0
  144. package/dist/commands/node/lifecycle.d.ts +4 -0
  145. package/dist/commands/node/lifecycle.js +415 -0
  146. package/dist/commands/node/message.d.ts +1 -0
  147. package/dist/commands/node/message.js +164 -0
  148. package/dist/commands/node/pyramid.d.ts +2 -0
  149. package/dist/commands/node/pyramid.js +79 -0
  150. package/dist/commands/node/subscription.d.ts +1 -0
  151. package/dist/commands/node/subscription.js +82 -0
  152. package/dist/commands/node/wait.d.ts +1 -0
  153. package/dist/commands/node/wait.js +159 -0
  154. package/dist/commands/node-lifecycle-revive.js +10 -57
  155. package/dist/commands/node.d.ts +0 -42
  156. package/dist/commands/node.js +11 -2065
  157. package/dist/commands/search.js +1 -1
  158. package/dist/commands/surface/node/cycle.d.ts +1 -0
  159. package/dist/commands/surface/node/cycle.js +83 -0
  160. package/dist/commands/surface/node/focus.d.ts +1 -0
  161. package/dist/commands/surface/node/focus.js +67 -0
  162. package/dist/commands/surface/node/id.d.ts +1 -0
  163. package/dist/commands/surface/node/id.js +38 -0
  164. package/dist/commands/surface/node/pin.d.ts +1 -0
  165. package/dist/commands/surface/node/pin.js +42 -0
  166. package/dist/commands/surface/node/placement.d.ts +46 -0
  167. package/dist/commands/surface/node/placement.js +124 -0
  168. package/dist/commands/surface-node.js +4 -1
  169. package/dist/commands/surface-reload.js +1 -2
  170. package/dist/commands/surface-tmux-spread.js +1 -1
  171. package/dist/commands/surface.js +5 -20
  172. package/dist/commands/sys/__tests__/setup-core.test.js +18 -271
  173. package/dist/commands/sys/config.js +6 -6
  174. package/dist/commands/sys/logs.js +1 -1
  175. package/dist/commands/sys/panels/broker-limits-panel.d.ts +17 -0
  176. package/dist/commands/sys/panels/broker-limits-panel.js +104 -0
  177. package/dist/commands/sys/panels/dependencies-panel.d.ts +14 -0
  178. package/dist/commands/sys/panels/dependencies-panel.js +80 -0
  179. package/dist/commands/sys/panels/keybindings-panel.d.ts +56 -0
  180. package/dist/commands/sys/panels/keybindings-panel.js +703 -0
  181. package/dist/commands/sys/panels/keys-panel.d.ts +16 -0
  182. package/dist/commands/sys/panels/keys-panel.js +60 -0
  183. package/dist/commands/sys/panels/models-panel.d.ts +77 -0
  184. package/dist/commands/sys/panels/models-panel.js +647 -0
  185. package/dist/commands/sys/panels/panel.d.ts +45 -0
  186. package/dist/commands/sys/panels/panel.js +137 -0
  187. package/dist/commands/sys/panels/plugins-panel.d.ts +16 -0
  188. package/dist/commands/sys/panels/plugins-panel.js +80 -0
  189. package/dist/commands/sys/panels/prefs-panel.d.ts +21 -0
  190. package/dist/commands/sys/panels/prefs-panel.js +85 -0
  191. package/dist/commands/sys/panels/provider-panel.d.ts +62 -0
  192. package/dist/commands/sys/panels/provider-panel.js +538 -0
  193. package/dist/commands/sys/settings-shell.d.ts +55 -0
  194. package/dist/commands/sys/settings-shell.js +400 -0
  195. package/dist/commands/sys/settings.d.ts +2 -0
  196. package/dist/commands/sys/settings.js +31 -0
  197. package/dist/commands/sys/setup-core.d.ts +3 -33
  198. package/dist/commands/sys/setup-core.js +27 -78
  199. package/dist/commands/sys/setup-settings-runner.d.ts +24 -0
  200. package/dist/commands/sys/setup-settings-runner.js +63 -0
  201. package/dist/commands/sys/setup-wizard.d.ts +26 -125
  202. package/dist/commands/sys/setup-wizard.js +209 -2473
  203. package/dist/commands/sys/setup.js +5 -7
  204. package/dist/commands/sys.js +3 -2
  205. package/dist/core/__tests__/broker-extension-canvas-db-boundary.test.js +50 -0
  206. package/dist/core/__tests__/broker-launch-candidates.test.js +20 -9
  207. package/dist/core/__tests__/broker-preflight.test.js +1 -1
  208. package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +108 -27
  209. package/dist/core/__tests__/canvas-inbox-watcher.test.js +14 -0
  210. package/dist/core/__tests__/canvas.test.js +2 -36
  211. package/dist/core/__tests__/close.test.js +14 -3
  212. package/dist/core/__tests__/context-intro.test.js +32 -143
  213. package/dist/core/__tests__/cron-broker-capacity.test.js +22 -3
  214. package/dist/core/__tests__/daemon-boot.test.js +2 -20
  215. package/dist/core/__tests__/daemon-wedge.test.js +1 -1
  216. package/dist/core/__tests__/dead-node-policy-table.test.js +1 -18
  217. package/dist/core/__tests__/fixtures/fake-engine.d.ts +1 -1
  218. package/dist/core/__tests__/fixtures/fake-engine.js +2 -2
  219. package/dist/core/__tests__/fixtures/fake-pi-host.js +2 -2
  220. package/dist/core/__tests__/focuses.test.js +4 -98
  221. package/dist/core/__tests__/helpers/broker-clients.d.ts +0 -3
  222. package/dist/core/__tests__/helpers/broker-clients.js +0 -14
  223. package/dist/core/__tests__/helpers/harness.d.ts +0 -8
  224. package/dist/core/__tests__/helpers/harness.js +7 -87
  225. package/dist/core/__tests__/helpers/viewer-panes.js +1 -1
  226. package/dist/core/__tests__/inline-memory-refs.test.js +5 -134
  227. package/dist/core/__tests__/kickoff.test.js +1 -1
  228. package/dist/core/__tests__/memory-resolver-precedence.test.js +29 -1
  229. package/dist/core/__tests__/migration.test.js +35 -48
  230. package/dist/core/__tests__/on-read-dedup-resume.test.js +22 -12
  231. package/dist/core/__tests__/passive-subscription.test.js +26 -49
  232. package/dist/core/__tests__/preview-registry-sync.test.js +1 -2
  233. package/dist/core/__tests__/prune.test.js +94 -1
  234. package/dist/core/__tests__/review-model-floor.test.js +0 -32
  235. package/dist/core/__tests__/seam/broker-attach-multiclient.test.js +255 -0
  236. package/dist/core/__tests__/{full → seam}/broker-attach-stream.test.js +39 -7
  237. package/dist/core/__tests__/{full → seam}/broker-crash-teardown.test.js +30 -29
  238. package/dist/core/__tests__/{full → seam}/broker-pane-resolution.test.js +54 -5
  239. package/dist/core/__tests__/{full → seam}/broker-provider-retry.test.js +134 -82
  240. package/dist/core/__tests__/{full → seam}/broker-whip-interrupt.test.js +15 -4
  241. package/dist/core/__tests__/{full/idle-release-settlement.test.js → seam/dormancy-release.test.js} +69 -17
  242. package/dist/core/__tests__/{full/yield-message-transaction.test.js → seam/yield-refresh-transaction.test.js} +31 -9
  243. package/dist/core/__tests__/secrets.test.js +1 -7
  244. package/dist/core/__tests__/{broker-fork-seam.test.js → serial/broker-fork-seam.test.js} +58 -7
  245. package/dist/core/__tests__/{broker-sdk-wiring.test.js → serial/broker-sdk-wiring.test.js} +18 -162
  246. package/dist/core/__tests__/{broker-snapshot-history.test.js → serial/broker-snapshot-history.test.js} +9 -9
  247. package/dist/core/__tests__/{command-plugins.test.js → serial/command-plugins.test.js} +105 -15
  248. package/dist/core/__tests__/{deferred-no-wake.test.js → serial/deferred-no-wake.test.js} +23 -20
  249. package/dist/core/__tests__/{flagship-lifecycle.test.js → serial/flagship-lifecycle.test.js} +7 -7
  250. package/dist/core/__tests__/{host-teardown-process-group.test.js → serial/host-teardown-process-group.test.js} +6 -6
  251. package/dist/core/__tests__/{human-deliver-e2e.test.js → serial/human-deliver-e2e.test.js} +12 -12
  252. package/dist/core/__tests__/{live-mutation.test.js → serial/live-mutation.test.js} +5 -5
  253. package/dist/core/__tests__/{refresh-stall-recycle.test.js → serial/refresh-stall-recycle.test.js} +5 -14
  254. package/dist/core/__tests__/{revive.test.js → serial/revive.test.js} +15 -100
  255. package/dist/core/__tests__/{spawn-root.test.js → serial/spawn-root.test.js} +32 -15
  256. package/dist/core/__tests__/serial/subscription-delivery.test.js +126 -0
  257. package/dist/core/__tests__/serial/tmux-surface.test.js +256 -0
  258. package/dist/core/__tests__/{worktree.test.js → serial/worktree.test.js} +15 -15
  259. package/dist/core/__tests__/session-model.test.js +0 -18
  260. package/dist/core/__tests__/spawn-worktree-id-conflict.test.js +9 -0
  261. package/dist/core/__tests__/watchdog-abort-arms-retry.test.js +50 -0
  262. package/dist/core/broker-client/__tests__/transport-relay.test.js +15 -16
  263. package/dist/core/broker-client/__tests__/transport-socket.test.js +28 -0
  264. package/dist/core/broker-client/client.d.ts +2 -2
  265. package/dist/core/broker-client/client.js +4 -7
  266. package/dist/core/broker-client/transport-relay.d.ts +13 -19
  267. package/dist/core/broker-client/transport-relay.js +19 -34
  268. package/dist/core/broker-client/transport-socket.d.ts +2 -2
  269. package/dist/core/broker-client/transport-socket.js +9 -2
  270. package/dist/core/broker-client/transport.d.ts +7 -3
  271. package/dist/core/broker-client/transport.js +4 -4
  272. package/dist/core/canvas/__tests__/attention.test.js +1 -18
  273. package/dist/core/canvas/__tests__/remote-canvas-source.test.js +7 -64
  274. package/dist/core/canvas/__tests__/remote-transport.test.js +2 -3
  275. package/dist/core/canvas/__tests__/render-remote.test.js +7 -52
  276. package/dist/core/canvas/browse/app.d.ts +2 -17
  277. package/dist/core/canvas/browse/app.js +16 -88
  278. package/dist/core/canvas/browse/model.d.ts +0 -4
  279. package/dist/core/canvas/browse/model.js +1 -12
  280. package/dist/core/canvas/browse/render.js +6 -3
  281. package/dist/core/canvas/crons.d.ts +8 -5
  282. package/dist/core/canvas/crons.js +7 -6
  283. package/dist/core/canvas/extensions.d.ts +7 -3
  284. package/dist/core/canvas/extensions.js +16 -4
  285. package/dist/core/canvas/nav-model.d.ts +0 -4
  286. package/dist/core/canvas/nav-model.js +2 -14
  287. package/dist/core/canvas/node-order.d.ts +3 -0
  288. package/dist/core/canvas/node-order.js +12 -0
  289. package/dist/core/canvas/remote-canvas-source.d.ts +3 -6
  290. package/dist/core/canvas/remote-canvas-source.js +8 -15
  291. package/dist/core/command.js +11 -4
  292. package/dist/core/config.js +3 -1
  293. package/dist/core/events/envelope.js +1 -1
  294. package/dist/core/events/read.js +7 -21
  295. package/dist/core/events/source.d.ts +0 -2
  296. package/dist/core/events/source.js +0 -16
  297. package/dist/core/events/types.d.ts +1 -1
  298. package/dist/core/fault-classifier.d.ts +1 -1
  299. package/dist/core/feed/passive.d.ts +0 -11
  300. package/dist/core/feed/passive.js +1 -62
  301. package/dist/core/human/__tests__/{inbox-core.test.js → serial/inbox-core.test.js} +6 -6
  302. package/dist/core/keybindings/__tests__/resolve.test.js +1 -96
  303. package/dist/core/keybindings/background-bash.js +3 -4
  304. package/dist/core/keybindings/catalog.js +1 -1
  305. package/dist/core/memory/inline-ref-grammar.js +3 -3
  306. package/dist/core/memory-resolver.d.ts +11 -25
  307. package/dist/core/memory-resolver.js +51 -33
  308. package/dist/core/model-routes.d.ts +15 -0
  309. package/dist/core/model-routes.js +12 -0
  310. package/dist/core/pool-credential-store.js +1 -1
  311. package/dist/core/preview-registry.js +8 -9
  312. package/dist/core/profiles/__tests__/root-profile.test.js +4 -14
  313. package/dist/core/review/realize.d.ts +0 -13
  314. package/dist/core/review/realize.js +0 -40
  315. package/dist/core/review/types.d.ts +1 -1
  316. package/dist/core/runtime/__tests__/engine-route.test.js +2 -2
  317. package/dist/core/runtime/bearings.js +1 -11
  318. package/dist/core/runtime/broker/client-registry.d.ts +33 -0
  319. package/dist/core/runtime/broker/client-registry.js +164 -0
  320. package/dist/core/runtime/broker/daemon-ops.d.ts +19 -0
  321. package/dist/core/runtime/broker/daemon-ops.js +49 -0
  322. package/dist/core/runtime/broker/event-projection.d.ts +42 -0
  323. package/dist/core/runtime/broker/event-projection.js +178 -0
  324. package/dist/core/runtime/broker/fault-retry.d.ts +39 -0
  325. package/dist/core/runtime/broker/fault-retry.js +277 -0
  326. package/dist/core/runtime/broker/frame-dispatch.d.ts +238 -0
  327. package/dist/core/runtime/broker/frame-dispatch.js +1537 -0
  328. package/dist/core/runtime/broker/inbox.d.ts +22 -0
  329. package/dist/core/runtime/broker/inbox.js +167 -0
  330. package/dist/core/runtime/broker/passive.d.ts +16 -0
  331. package/dist/core/runtime/broker/passive.js +44 -0
  332. package/dist/core/runtime/broker/read-ops.d.ts +23 -0
  333. package/dist/core/runtime/broker/read-ops.js +150 -0
  334. package/dist/core/runtime/broker/rebind.d.ts +74 -0
  335. package/dist/core/runtime/broker/rebind.js +211 -0
  336. package/dist/core/runtime/broker/tool-groups.d.ts +39 -0
  337. package/dist/core/runtime/broker/tool-groups.js +244 -0
  338. package/dist/core/runtime/broker-extension-render.d.ts +11 -0
  339. package/dist/core/runtime/broker-extension-render.js +181 -0
  340. package/dist/core/runtime/broker-persona-guidance.d.ts +13 -0
  341. package/dist/core/runtime/broker-persona-guidance.js +58 -0
  342. package/dist/core/runtime/broker-protocol.d.ts +1 -1
  343. package/dist/core/runtime/broker-protocol.js +1 -1
  344. package/dist/core/runtime/broker.d.ts +5 -196
  345. package/dist/core/runtime/broker.js +276 -3059
  346. package/dist/core/runtime/canvas-extensions.js +11 -2
  347. package/dist/core/runtime/fault.js +1 -1
  348. package/dist/core/runtime/model-selection.d.ts +9 -0
  349. package/dist/core/runtime/model-selection.js +12 -0
  350. package/dist/core/runtime/model-swap.d.ts +3 -3
  351. package/dist/core/runtime/model-swap.js +33 -25
  352. package/dist/core/runtime/placement-tmux.d.ts +3 -14
  353. package/dist/core/runtime/placement-tmux.js +11 -80
  354. package/dist/core/runtime/placement.d.ts +4 -2
  355. package/dist/core/runtime/placement.js +7 -12
  356. package/dist/core/runtime/promote.d.ts +14 -1
  357. package/dist/core/runtime/promote.js +1 -1
  358. package/dist/core/runtime/reset.d.ts +1 -1
  359. package/dist/core/runtime/roadmap.js +1 -1
  360. package/dist/core/runtime/spawn.d.ts +8 -0
  361. package/dist/core/runtime/spawn.js +36 -6
  362. package/dist/core/runtime/stop-guard.d.ts +4 -3
  363. package/dist/core/runtime/stop-guard.js +36 -18
  364. package/dist/core/runtime/stop-signals.d.ts +4 -1
  365. package/dist/core/runtime/stream-watchdog.js +4 -5
  366. package/dist/core/runtime/structured-output.d.ts +15 -2
  367. package/dist/core/runtime/structured-output.js +35 -12
  368. package/dist/core/runtime/tmux-bindings.d.ts +25 -0
  369. package/dist/core/runtime/tmux-bindings.js +682 -0
  370. package/dist/core/runtime/tmux-chrome.d.ts +2 -1
  371. package/dist/core/runtime/tmux-chrome.js +3 -2
  372. package/dist/core/runtime/{tmux.d.ts → tmux-driver.d.ts} +18 -26
  373. package/dist/core/runtime/tmux-driver.js +598 -0
  374. package/dist/core/runtime/view-socket.d.ts +14 -0
  375. package/dist/core/runtime/view-socket.js +83 -0
  376. package/dist/core/session-model/session-state.d.ts +1 -2
  377. package/dist/core/session-model/session-state.js +5 -8
  378. package/dist/core/spawn.d.ts +1 -1
  379. package/dist/core/spawn.js +2 -2
  380. package/dist/core/substrate/gate.d.ts +1 -1
  381. package/dist/core/substrate/on-read-pure.d.ts +7 -0
  382. package/dist/core/substrate/on-read-pure.js +272 -0
  383. package/dist/core/termrender/termrender.js +32 -2
  384. package/dist/core/user-settings.d.ts +23 -1
  385. package/dist/core/user-settings.js +18 -1
  386. package/dist/daemon/api/__tests__/node-model-validation.test.js +134 -0
  387. package/dist/daemon/api/__tests__/review-terminal-conflict.test.js +98 -0
  388. package/dist/daemon/api/__tests__/{full → seam}/api-server.test.js +6 -48
  389. package/dist/daemon/api/__tests__/{full → seam}/b10-attach-modelauth.test.js +17 -44
  390. package/dist/daemon/api/__tests__/{full/push-parity.test.js → seam/leaf-api-parity.test.js} +61 -42
  391. package/dist/daemon/api/bridge.js +40 -81
  392. package/dist/daemon/api/handlers/broker-ops.d.ts +2 -0
  393. package/dist/daemon/api/handlers/broker-ops.js +383 -0
  394. package/dist/daemon/api/handlers/crons.js +9 -4
  395. package/dist/daemon/api/handlers/files.js +6 -7
  396. package/dist/daemon/api/handlers/inbox.js +2 -340
  397. package/dist/daemon/api/handlers/nodes.d.ts +10 -1
  398. package/dist/daemon/api/handlers/nodes.js +74 -39
  399. package/dist/daemon/api/handlers/review-comments.js +5 -1
  400. package/dist/daemon/api/handlers/reviews.js +0 -19
  401. package/dist/daemon/api/map.js +25 -4
  402. package/dist/daemon/api/router.js +2 -3
  403. package/dist/daemon/api/server.js +5 -7
  404. package/dist/daemon/cron-run.d.ts +4 -0
  405. package/dist/daemon/cron-run.js +30 -13
  406. package/dist/daemon/crtrd.d.ts +3 -58
  407. package/dist/daemon/crtrd.js +28 -1244
  408. package/dist/daemon/manage.js +0 -1
  409. package/dist/daemon/reconcilers/broker-supervision.d.ts +50 -0
  410. package/dist/daemon/reconcilers/broker-supervision.js +549 -0
  411. package/dist/daemon/reconcilers/cron-lane.d.ts +10 -0
  412. package/dist/daemon/reconcilers/cron-lane.js +32 -0
  413. package/dist/daemon/reconcilers/dormant-inbox.d.ts +18 -0
  414. package/dist/daemon/reconcilers/dormant-inbox.js +143 -0
  415. package/dist/daemon/reconcilers/storage-maintenance.d.ts +22 -0
  416. package/dist/daemon/reconcilers/storage-maintenance.js +213 -0
  417. package/dist/daemon/review/finish.js +2 -5
  418. package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +34 -53
  419. package/dist/pi-extensions/__tests__/canvas-recap.test.js +16 -113
  420. package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +72 -99
  421. package/dist/pi-extensions/__tests__/canvas-stophook-context-nudge.test.js +29 -12
  422. package/dist/pi-extensions/broker-local.d.ts +20 -0
  423. package/dist/pi-extensions/broker-local.js +89 -0
  424. package/dist/pi-extensions/canvas-context-intro.d.ts +3 -3
  425. package/dist/pi-extensions/canvas-context-intro.js +22 -11
  426. package/dist/pi-extensions/canvas-doc-substrate.js +17 -34
  427. package/dist/pi-extensions/canvas-goal-capture.js +7 -12
  428. package/dist/pi-extensions/canvas-inbox-watcher.js +124 -139
  429. package/dist/pi-extensions/canvas-passive-context.d.ts +3 -3
  430. package/dist/pi-extensions/canvas-passive-context.js +22 -18
  431. package/dist/pi-extensions/canvas-recap.d.ts +2 -2
  432. package/dist/pi-extensions/canvas-recap.js +48 -21
  433. package/dist/pi-extensions/canvas-review-boundary.js +10 -4
  434. package/dist/pi-extensions/canvas-stophook.d.ts +0 -5
  435. package/dist/pi-extensions/canvas-stophook.js +149 -431
  436. package/dist/pi-extensions/canvas-structured-output.js +52 -30
  437. package/dist/pi-extensions/envelope-merge.d.ts +13 -0
  438. package/dist/pi-extensions/envelope-merge.js +29 -0
  439. package/dist/shared/working-activity.d.ts +3 -2
  440. package/dist/shared/working-activity.js +3 -2
  441. package/dist/types.d.ts +6 -5
  442. package/dist/types.js +2 -2
  443. package/docs/compat/hearth-crtr-v1.md +4 -12
  444. package/docs/compat/hearth-crtr-v2.md +3 -11
  445. package/docs/compat/hearth-crtr-v3.md +4 -12
  446. package/docs/compat/hearth-crtr-v4.md +3 -11
  447. package/docs/compat/hearth-crtr-v5.md +3 -11
  448. package/docs/public-api.md +3 -7
  449. package/package.json +7 -49
  450. package/runtime.lock.json +2766 -14533
  451. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/claude-plugin-commands.test.ts +0 -49
  452. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/memory-slash-commands.test.ts +0 -194
  453. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/statusline.test.ts +0 -68
  454. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/frontmatter-rules/test.ts +0 -119
  455. package/dist/clients/attach/__tests__/attach-chrome-remote.test.js +0 -439
  456. package/dist/clients/attach/__tests__/attach-keybindings.test.js +0 -170
  457. package/dist/clients/attach/__tests__/crtr-output-coverage.test.js +0 -57
  458. package/dist/clients/attach/__tests__/editor-frame-title.test.js +0 -19
  459. package/dist/clients/attach/__tests__/input-controller-extension-command.test.js +0 -32
  460. package/dist/clients/attach/__tests__/markdown-headings.test.js +0 -35
  461. package/dist/clients/attach/__tests__/session-identity-refresh.test.js +0 -32
  462. package/dist/clients/conversation/conversation.d.ts +0 -65
  463. package/dist/clients/conversation/conversation.js +0 -318
  464. package/dist/clients/conversation/frames.d.ts +0 -30
  465. package/dist/clients/conversation/frames.js +0 -52
  466. package/dist/clients/conversation/region.d.ts +0 -51
  467. package/dist/clients/conversation/region.js +0 -265
  468. package/dist/clients/inbox/__tests__/inbox-controller.test.js +0 -247
  469. package/dist/clients/inbox/__tests__/mount-panel.test.js +0 -776
  470. package/dist/clients/inbox/review/__tests__/remap.test.js +0 -196
  471. package/dist/clients/inbox/review/columns.d.ts +0 -22
  472. package/dist/clients/inbox/review/columns.js +0 -40
  473. package/dist/clients/inbox/review/composition.d.ts +0 -60
  474. package/dist/clients/inbox/review/composition.js +0 -278
  475. package/dist/clients/inbox/review/document-region.d.ts +0 -120
  476. package/dist/clients/surfaces/region.d.ts +0 -16
  477. package/dist/clients/web/__tests__/push-engine.test.js +0 -158
  478. package/dist/clients/web/__tests__/push-registry.test.d.ts +0 -1
  479. package/dist/clients/web/__tests__/push-registry.test.js +0 -177
  480. package/dist/clients/web/dev-server.d.ts +0 -7
  481. package/dist/clients/web/dev-server.js +0 -46
  482. package/dist/clients/web/events.d.ts +0 -19
  483. package/dist/clients/web/events.js +0 -163
  484. package/dist/clients/web/push-engine.d.ts +0 -44
  485. package/dist/clients/web/push-engine.js +0 -125
  486. package/dist/clients/web/push-registry.d.ts +0 -85
  487. package/dist/clients/web/push-registry.js +0 -373
  488. package/dist/clients/web/server.d.ts +0 -19
  489. package/dist/clients/web/server.js +0 -661
  490. package/dist/clients/web/web-client/shared/protocol.d.ts +0 -214
  491. package/dist/clients/web/web-client/shared/protocol.js +0 -8
  492. package/dist/clients/web/web-cmd.d.ts +0 -2
  493. package/dist/clients/web/web-cmd.js +0 -141
  494. package/dist/commands/__tests__/api-canvas-source.test.d.ts +0 -1
  495. package/dist/commands/__tests__/api-canvas-source.test.js +0 -37
  496. package/dist/commands/__tests__/canvas-config.test.d.ts +0 -1
  497. package/dist/commands/__tests__/canvas-config.test.js +0 -107
  498. package/dist/commands/__tests__/node-no-kickoff-flag.test.d.ts +0 -1
  499. package/dist/commands/__tests__/node-no-kickoff-flag.test.js +0 -73
  500. package/dist/commands/__tests__/node-worktree-flag.test.d.ts +0 -1
  501. package/dist/commands/__tests__/node-worktree-flag.test.js +0 -49
  502. package/dist/commands/__tests__/search-contents.test.d.ts +0 -1
  503. package/dist/commands/__tests__/search-contents.test.js +0 -52
  504. package/dist/commands/memory/__tests__/lint.test.d.ts +0 -1
  505. package/dist/commands/memory/__tests__/lint.test.js +0 -15
  506. package/dist/commands/sys/__tests__/config-broker-thresholds.test.d.ts +0 -1
  507. package/dist/commands/sys/__tests__/config-broker-thresholds.test.js +0 -95
  508. package/dist/commands/sys/__tests__/config-keybindings.test.d.ts +0 -1
  509. package/dist/commands/sys/__tests__/config-keybindings.test.js +0 -55
  510. package/dist/commands/sys/__tests__/helpers/setup-runtime.d.ts +0 -5
  511. package/dist/commands/sys/__tests__/helpers/setup-runtime.js +0 -19
  512. package/dist/commands/sys/__tests__/setup-front-door.test.d.ts +0 -1
  513. package/dist/commands/sys/__tests__/setup-front-door.test.js +0 -102
  514. package/dist/commands/sys/__tests__/setup-models.test.d.ts +0 -1
  515. package/dist/commands/sys/__tests__/setup-models.test.js +0 -205
  516. package/dist/commands/sys/__tests__/setup-providers-viewport.test.d.ts +0 -1
  517. package/dist/commands/sys/__tests__/setup-providers-viewport.test.js +0 -155
  518. package/dist/commands/sys/__tests__/sync-deps.test.d.ts +0 -1
  519. package/dist/commands/sys/__tests__/sync-deps.test.js +0 -179
  520. package/dist/commands/sys/__tests__/sync-import.test.d.ts +0 -1
  521. package/dist/commands/sys/__tests__/sync-import.test.js +0 -315
  522. package/dist/core/__tests__/base-worker-prompt.test.d.ts +0 -1
  523. package/dist/core/__tests__/base-worker-prompt.test.js +0 -57
  524. package/dist/core/__tests__/command-plugins-surfaces.test.d.ts +0 -1
  525. package/dist/core/__tests__/command-plugins-surfaces.test.js +0 -359
  526. package/dist/core/__tests__/connection-reconnect-resume.test.d.ts +0 -1
  527. package/dist/core/__tests__/connection-reconnect-resume.test.js +0 -36
  528. package/dist/core/__tests__/finalization-migration.test.d.ts +0 -1
  529. package/dist/core/__tests__/finalization-migration.test.js +0 -38
  530. package/dist/core/__tests__/fixtures/extra-extension.d.ts +0 -1
  531. package/dist/core/__tests__/fixtures/extra-extension.js +0 -3
  532. package/dist/core/__tests__/fixtures/spawn-proof-target.test.d.ts +0 -1
  533. package/dist/core/__tests__/fixtures/spawn-proof-target.test.js +0 -15
  534. package/dist/core/__tests__/fork.test.d.ts +0 -1
  535. package/dist/core/__tests__/fork.test.js +0 -108
  536. package/dist/core/__tests__/full/broker-attach-limits.test.d.ts +0 -1
  537. package/dist/core/__tests__/full/broker-attach-limits.test.js +0 -147
  538. package/dist/core/__tests__/full/broker-deliver-ack.test.d.ts +0 -1
  539. package/dist/core/__tests__/full/broker-deliver-ack.test.js +0 -73
  540. package/dist/core/__tests__/full/broker-dialogs.test.d.ts +0 -1
  541. package/dist/core/__tests__/full/broker-dialogs.test.js +0 -180
  542. package/dist/core/__tests__/full/broker-display-snapshot.test.d.ts +0 -1
  543. package/dist/core/__tests__/full/broker-display-snapshot.test.js +0 -75
  544. package/dist/core/__tests__/full/broker-dormant-wake.test.d.ts +0 -1
  545. package/dist/core/__tests__/full/broker-dormant-wake.test.js +0 -130
  546. package/dist/core/__tests__/full/broker-model-changed-broadcast.test.d.ts +0 -1
  547. package/dist/core/__tests__/full/broker-model-changed-broadcast.test.js +0 -57
  548. package/dist/core/__tests__/full/broker-navigate-tree-rewelcome.test.d.ts +0 -1
  549. package/dist/core/__tests__/full/broker-navigate-tree-rewelcome.test.js +0 -80
  550. package/dist/core/__tests__/full/cascade-close.test.d.ts +0 -1
  551. package/dist/core/__tests__/full/cascade-close.test.js +0 -240
  552. package/dist/core/__tests__/full/detach-focus.test.d.ts +0 -1
  553. package/dist/core/__tests__/full/detach-focus.test.js +0 -126
  554. package/dist/core/__tests__/full/idle-release-settlement.test.d.ts +0 -1
  555. package/dist/core/__tests__/full/issue-146-yield-spawn-focus.test.d.ts +0 -1
  556. package/dist/core/__tests__/full/issue-146-yield-spawn-focus.test.js +0 -107
  557. package/dist/core/__tests__/full/park-clock-release.test.d.ts +0 -1
  558. package/dist/core/__tests__/full/park-clock-release.test.js +0 -56
  559. package/dist/core/__tests__/full/yield-message-transaction.test.d.ts +0 -1
  560. package/dist/core/__tests__/memory-index-render.test.d.ts +0 -1
  561. package/dist/core/__tests__/memory-index-render.test.js +0 -60
  562. package/dist/core/__tests__/model-routes.test.d.ts +0 -1
  563. package/dist/core/__tests__/model-routes.test.js +0 -41
  564. package/dist/core/__tests__/on-read-identity.test.d.ts +0 -1
  565. package/dist/core/__tests__/on-read-identity.test.js +0 -61
  566. package/dist/core/__tests__/phase4-review-migration.test.d.ts +0 -1
  567. package/dist/core/__tests__/phase4-review-migration.test.js +0 -100
  568. package/dist/core/__tests__/prune-to-limit.test.d.ts +0 -1
  569. package/dist/core/__tests__/prune-to-limit.test.js +0 -126
  570. package/dist/core/__tests__/reset.test.d.ts +0 -1
  571. package/dist/core/__tests__/reset.test.js +0 -69
  572. package/dist/core/__tests__/spawn-no-kickoff.test.d.ts +0 -1
  573. package/dist/core/__tests__/spawn-no-kickoff.test.js +0 -115
  574. package/dist/core/__tests__/steer-note.test.d.ts +0 -1
  575. package/dist/core/__tests__/steer-note.test.js +0 -41
  576. package/dist/core/__tests__/subcommand-tier.test.d.ts +0 -1
  577. package/dist/core/__tests__/subcommand-tier.test.js +0 -99
  578. package/dist/core/__tests__/subscription-delivery.test.js +0 -211
  579. package/dist/core/__tests__/test-runner-cheap-sharding.test.d.ts +0 -1
  580. package/dist/core/__tests__/test-runner-cheap-sharding.test.js +0 -97
  581. package/dist/core/__tests__/tmux-surface.test.js +0 -484
  582. package/dist/core/__tests__/wake-bearings.test.d.ts +0 -1
  583. package/dist/core/__tests__/wake-bearings.test.js +0 -102
  584. package/dist/core/canvas/__tests__/remote-event-stream.test.d.ts +0 -1
  585. package/dist/core/canvas/__tests__/remote-event-stream.test.js +0 -146
  586. package/dist/core/canvas/__tests__/source-resolve.test.d.ts +0 -1
  587. package/dist/core/canvas/__tests__/source-resolve.test.js +0 -77
  588. package/dist/core/canvas/browse/__tests__/model.test.d.ts +0 -1
  589. package/dist/core/canvas/browse/__tests__/model.test.js +0 -290
  590. package/dist/core/canvas/browse/__tests__/rebuild-coalescer.test.d.ts +0 -1
  591. package/dist/core/canvas/browse/__tests__/rebuild-coalescer.test.js +0 -134
  592. package/dist/core/canvas/browse/__tests__/render.test.d.ts +0 -1
  593. package/dist/core/canvas/browse/__tests__/render.test.js +0 -111
  594. package/dist/core/canvas/browse/rebuild-coalescer.d.ts +0 -7
  595. package/dist/core/canvas/browse/rebuild-coalescer.js +0 -61
  596. package/dist/core/canvas/remote-event-stream.d.ts +0 -24
  597. package/dist/core/canvas/remote-event-stream.js +0 -94
  598. package/dist/core/feed/__tests__/coalesce-human.test.d.ts +0 -1
  599. package/dist/core/feed/__tests__/coalesce-human.test.js +0 -36
  600. package/dist/core/feed/__tests__/empty-node-guard.test.d.ts +0 -1
  601. package/dist/core/feed/__tests__/empty-node-guard.test.js +0 -45
  602. package/dist/core/host-exports/__tests__/export-prunes-boot-skill.test.d.ts +0 -1
  603. package/dist/core/host-exports/__tests__/export-prunes-boot-skill.test.js +0 -84
  604. package/dist/core/human/__tests__/visible-paths.test.d.ts +0 -1
  605. package/dist/core/human/__tests__/visible-paths.test.js +0 -28
  606. package/dist/core/keybindings/__tests__/inbox-affordance.test.d.ts +0 -1
  607. package/dist/core/keybindings/__tests__/inbox-affordance.test.js +0 -38
  608. package/dist/core/review/__tests__/project.test.d.ts +0 -1
  609. package/dist/core/review/__tests__/project.test.js +0 -71
  610. package/dist/core/runtime/__tests__/command-expansion.test.d.ts +0 -1
  611. package/dist/core/runtime/__tests__/command-expansion.test.js +0 -25
  612. package/dist/core/runtime/__tests__/session-visibility.test.d.ts +0 -1
  613. package/dist/core/runtime/__tests__/session-visibility.test.js +0 -119
  614. package/dist/core/runtime/__tests__/spawn-at-given-id.test.d.ts +0 -1
  615. package/dist/core/runtime/__tests__/spawn-at-given-id.test.js +0 -34
  616. package/dist/core/runtime/tmux.js +0 -1314
  617. package/dist/daemon/api/__tests__/full/push-parity.test.d.ts +0 -1
  618. package/dist/daemon/api/__tests__/full/worktree-parity.test.d.ts +0 -1
  619. package/dist/daemon/api/__tests__/full/worktree-parity.test.js +0 -160
  620. package/dist/daemon/api/__tests__/inbox.test.d.ts +0 -1
  621. package/dist/daemon/api/__tests__/inbox.test.js +0 -489
  622. package/dist/web-client/apple-touch-icon.png +0 -0
  623. package/dist/web-client/assets/fragment-mono-latin-400-normal-BYwT3kSJ.woff +0 -0
  624. package/dist/web-client/assets/fragment-mono-latin-400-normal-yxdJ5AmL.woff2 +0 -0
  625. package/dist/web-client/assets/fragment-mono-latin-ext-400-normal-BbKYyvR9.woff2 +0 -0
  626. package/dist/web-client/assets/fragment-mono-latin-ext-400-normal-CT4YFKeK.woff +0 -0
  627. package/dist/web-client/assets/index-CCPaLeW7.css +0 -2
  628. package/dist/web-client/assets/index-kyOWsnZK.js +0 -86
  629. package/dist/web-client/assets/martian-mono-cyrillic-wght-normal-B84CD5C_.woff2 +0 -0
  630. package/dist/web-client/assets/martian-mono-latin-ext-wght-normal-DlL6xMw5.woff2 +0 -0
  631. package/dist/web-client/assets/martian-mono-latin-wght-normal-5W32yIyr.woff2 +0 -0
  632. package/dist/web-client/assets/workbox-window.prod.es5-Bd17z0YL.js +0 -2
  633. package/dist/web-client/icon-192.png +0 -0
  634. package/dist/web-client/icon-512.png +0 -0
  635. package/dist/web-client/icon-maskable-512.png +0 -0
  636. package/dist/web-client/index.html +0 -17
  637. package/dist/web-client/manifest.webmanifest +0 -1
  638. package/dist/web-client/sw.js +0 -1
  639. /package/dist/api/__tests__/{client.test.d.ts → serial/client.test.d.ts} +0 -0
  640. /package/dist/clients/inbox/__tests__/{inbox-controller.test.d.ts → serial/inbox-controller.test.d.ts} +0 -0
  641. /package/dist/clients/inbox/__tests__/{mount-panel.test.d.ts → serial/mount-panel.test.d.ts} +0 -0
  642. /package/dist/clients/{attach/__tests__/attach-chrome-remote.test.d.ts → inbox/review/__tests__/teardown-notice.test.d.ts} +0 -0
  643. /package/dist/{clients/attach/__tests__/attach-keybindings.test.d.ts → core/__tests__/broker-extension-canvas-db-boundary.test.d.ts} +0 -0
  644. /package/dist/{clients/attach/__tests__/crtr-output-coverage.test.d.ts → core/__tests__/seam/broker-attach-multiclient.test.d.ts} +0 -0
  645. /package/dist/core/__tests__/{full → seam}/broker-attach-stream.test.d.ts +0 -0
  646. /package/dist/core/__tests__/{full → seam}/broker-crash-teardown.test.d.ts +0 -0
  647. /package/dist/core/__tests__/{full → seam}/broker-pane-resolution.test.d.ts +0 -0
  648. /package/dist/core/__tests__/{full → seam}/broker-provider-retry.test.d.ts +0 -0
  649. /package/dist/core/__tests__/{full → seam}/broker-whip-interrupt.test.d.ts +0 -0
  650. /package/dist/{clients/attach/__tests__/editor-frame-title.test.d.ts → core/__tests__/seam/dormancy-release.test.d.ts} +0 -0
  651. /package/dist/{clients/attach/__tests__/input-controller-extension-command.test.d.ts → core/__tests__/seam/yield-refresh-transaction.test.d.ts} +0 -0
  652. /package/dist/core/__tests__/{broker-fork-seam.test.d.ts → serial/broker-fork-seam.test.d.ts} +0 -0
  653. /package/dist/core/__tests__/{broker-sdk-wiring.test.d.ts → serial/broker-sdk-wiring.test.d.ts} +0 -0
  654. /package/dist/core/__tests__/{broker-snapshot-history.test.d.ts → serial/broker-snapshot-history.test.d.ts} +0 -0
  655. /package/dist/core/__tests__/{command-plugins.test.d.ts → serial/command-plugins.test.d.ts} +0 -0
  656. /package/dist/core/__tests__/{deferred-no-wake.test.d.ts → serial/deferred-no-wake.test.d.ts} +0 -0
  657. /package/dist/core/__tests__/{flagship-lifecycle.test.d.ts → serial/flagship-lifecycle.test.d.ts} +0 -0
  658. /package/dist/core/__tests__/{host-teardown-process-group.test.d.ts → serial/host-teardown-process-group.test.d.ts} +0 -0
  659. /package/dist/core/__tests__/{human-deliver-e2e.test.d.ts → serial/human-deliver-e2e.test.d.ts} +0 -0
  660. /package/dist/core/__tests__/{live-mutation.test.d.ts → serial/live-mutation.test.d.ts} +0 -0
  661. /package/dist/core/__tests__/{refresh-stall-recycle.test.d.ts → serial/refresh-stall-recycle.test.d.ts} +0 -0
  662. /package/dist/core/__tests__/{revive.test.d.ts → serial/revive.test.d.ts} +0 -0
  663. /package/dist/core/__tests__/{spawn-root.test.d.ts → serial/spawn-root.test.d.ts} +0 -0
  664. /package/dist/core/__tests__/{subscription-delivery.test.d.ts → serial/subscription-delivery.test.d.ts} +0 -0
  665. /package/dist/core/__tests__/{tmux-surface.test.d.ts → serial/tmux-surface.test.d.ts} +0 -0
  666. /package/dist/core/__tests__/{worktree.test.d.ts → serial/worktree.test.d.ts} +0 -0
  667. /package/dist/{clients/attach/__tests__/markdown-headings.test.d.ts → core/__tests__/watchdog-abort-arms-retry.test.d.ts} +0 -0
  668. /package/dist/{clients/attach/__tests__/session-identity-refresh.test.d.ts → core/broker-client/__tests__/transport-socket.test.d.ts} +0 -0
  669. /package/dist/core/human/__tests__/{inbox-core.test.d.ts → serial/inbox-core.test.d.ts} +0 -0
  670. /package/dist/{clients/inbox/review/__tests__/remap.test.d.ts → daemon/api/__tests__/node-model-validation.test.d.ts} +0 -0
  671. /package/dist/{clients/surfaces/region.js → daemon/api/__tests__/review-terminal-conflict.test.d.ts} +0 -0
  672. /package/dist/daemon/api/__tests__/{full → seam}/api-server.test.d.ts +0 -0
  673. /package/dist/daemon/api/__tests__/{full → seam}/b10-attach-modelauth.test.d.ts +0 -0
  674. /package/dist/{clients/web/__tests__/push-engine.test.d.ts → daemon/api/__tests__/seam/leaf-api-parity.test.d.ts} +0 -0
@@ -1,489 +0,0 @@
1
- // Run with: node --import tsx/esm --test src/daemon/api/__tests__/inbox.test.ts
2
- //
3
- // Integration coverage for crtrd's `/v1/human/inbox` routes
4
- // (inbox-contract.md §A, `handlers/inbox.ts`). Exercises the full leaf → typed
5
- // `CrtrClient` → in-process `createApiServer()` → handler → ticket store —
6
- // never a mock client or pre-parsed handler call. Tickets live directly under
7
- // the derived `ticketsRoot()` and use the same public store APIs as production.
8
- // A raw hand-written `deck.json` is used only for states that submission cannot
9
- // produce: an unresolved `bodyPath`, a malformed stored deck, or a raced final.
10
- import { test, describe, before, after, beforeEach } from 'node:test';
11
- import assert from 'node:assert/strict';
12
- import { spawn } from 'node:child_process';
13
- import { mkdtempSync, mkdirSync, rmSync, writeFileSync, readFileSync, existsSync, realpathSync, } from 'node:fs';
14
- import { tmpdir } from 'node:os';
15
- import { join } from 'node:path';
16
- import { createHash } from 'node:crypto';
17
- import { claimTicket, releaseClaim } from '../../../core/human/claim.js';
18
- import { deckPath, responsePath } from '../../../core/human/convention.js';
19
- import { notifyDeck } from '../../../core/human/deck-factories.js';
20
- import { ticketsRoot } from '../../../core/human/root.js';
21
- import { submitDeck, submitReview } from '../../../core/human/tickets.js';
22
- import { createApiServer } from '../server.js';
23
- import { apiSocketPath } from '../../../core/canvas/paths.js';
24
- import { CrtrClient, ApiError } from '../../../api/index.js';
25
- const TICKET_ID_PATTERN = /^[a-f0-9]{64}$/;
26
- // ---------------------------------------------------------------------------
27
- // Harness — one server for the whole file; a fresh derived tickets root per
28
- // test.
29
- // ---------------------------------------------------------------------------
30
- let crtrHome;
31
- let priorCrtrHome;
32
- let server;
33
- let client;
34
- async function waitForSocket(path) {
35
- for (let i = 0; i < 200; i++) {
36
- if (existsSync(path))
37
- return;
38
- await new Promise((r) => setTimeout(r, 25));
39
- }
40
- throw new Error(`api socket never bound at ${path}`);
41
- }
42
- before(async () => {
43
- priorCrtrHome = process.env['CRTR_HOME'];
44
- crtrHome = mkdtempSync(join(tmpdir(), 'crtr-inbox-home-'));
45
- process.env['CRTR_HOME'] = crtrHome;
46
- server = createApiServer();
47
- await waitForSocket(apiSocketPath());
48
- client = new CrtrClient({ socketPath: apiSocketPath(), autostart: false });
49
- });
50
- after(async () => {
51
- await server?.close();
52
- rmSync(crtrHome, { recursive: true, force: true });
53
- if (priorCrtrHome !== undefined)
54
- process.env['CRTR_HOME'] = priorCrtrHome;
55
- else
56
- delete process.env['CRTR_HOME'];
57
- });
58
- beforeEach(() => {
59
- rmSync(ticketsRoot(), { recursive: true, force: true });
60
- mkdirSync(ticketsRoot(), { recursive: true, mode: 0o700 });
61
- });
62
- // ---------------------------------------------------------------------------
63
- // Fixture helpers
64
- // ---------------------------------------------------------------------------
65
- function freshRoot(..._unused) {
66
- mkdirSync(ticketsRoot(), { recursive: true, mode: 0o700 });
67
- return realpathSync(ticketsRoot());
68
- }
69
- function deckJson(blockedSince, extra = {}) {
70
- return {
71
- title: 'Ship it?',
72
- source: { blockedSince },
73
- interactions: [{ id: 'q', title: 'Ship it?', subtitle: 'Approve shipping now so the release can proceed.', options: [{ id: 'yes', label: 'Yes' }, { id: 'no', label: 'No' }] }],
74
- ...extra,
75
- };
76
- }
77
- /** Create a real pending deck ticket through the public submission API — the
78
- * normal fixture path for any test that just needs a valid, answerable
79
- * ticket on disk. */
80
- function submitTestDeck(root, id, blockedSince, extra = {}) {
81
- return submitDeck({ dir: join(root, id), deck: deckJson(blockedSince, extra) }).dir;
82
- }
83
- /** Create a real pending review ticket through the public submission API. */
84
- function submitTestReview(root, id, blockedSince) {
85
- const srcDir = mkdtempSync(join(tmpdir(), 'crtr-inbox-review-src-'));
86
- const file = join(srcDir, 'plan.md');
87
- writeFileSync(file, '# Plan\n\nDo the thing.\n');
88
- return submitReview({ dir: join(root, id), review: { title: 'Review the plan', subtitle: 'Review the plan now so implementation can begin with confidence.', source: {}, file, blockedSince } }).dir;
89
- }
90
- /** RAW fixture writer — bypasses `submitDeck`'s own submit-time validation
91
- * and body-path resolution entirely. Used ONLY to construct on-disk states
92
- * a real submitter cannot produce: an unresolved `bodyPath`, a tampered
93
- * stored deck, or an adversarial root-replacement target. */
94
- function writeRawDeckTicket(root, id, deck) {
95
- const dir = join(root, id);
96
- mkdirSync(dir, { recursive: true, mode: 0o700 });
97
- writeFileSync(deckPath(dir), JSON.stringify(deck));
98
- return dir;
99
- }
100
- function ticketIdFor(root, basename) {
101
- return createHash('sha256').update(`${root}\0${basename}`, 'utf8').digest('hex');
102
- }
103
- async function expectApiError(promise, status, code) {
104
- try {
105
- await promise;
106
- }
107
- catch (err) {
108
- assert.ok(err instanceof ApiError, `expected ApiError, got ${String(err)}`);
109
- assert.equal(err.status, status, `status: ${err.message}`);
110
- assert.equal(err.code, code, `code: ${err.message}`);
111
- return err;
112
- }
113
- throw new Error('expected the promise to reject');
114
- }
115
- // ---------------------------------------------------------------------------
116
- // Wire-shape assertions — a closed allowlist per DTO plus a recursive
117
- // forbidden-content walk (host paths, claim/dir/file/output/bodyPath keys).
118
- // ---------------------------------------------------------------------------
119
- function assertKeys(obj, allowed, label) {
120
- for (const key of Object.keys(obj)) {
121
- assert.ok(allowed.includes(key), `unexpected field "${key}" in ${label}: ${JSON.stringify(obj)}`);
122
- }
123
- }
124
- function assertSourceShape(source) {
125
- assert.equal(typeof source, 'object');
126
- assertKeys(source, ['sessionName', 'askedBy', 'blockedSince', 'nodeId'], 'DeckSourceDTO');
127
- }
128
- function assertTicketSummaryShape(ticket) {
129
- assert.match(ticket.ticket_id, TICKET_ID_PATTERN);
130
- assert.ok(ticket.kind === 'deck' || ticket.kind === 'review');
131
- const allowed = ticket.kind === 'deck'
132
- ? ['ticket_id', 'kind', 'title', 'subtitle', 'blocked_since', 'source', 'interaction_kind']
133
- : ['ticket_id', 'kind', 'title', 'subtitle', 'blocked_since', 'source'];
134
- assertKeys(ticket, allowed, `InboxTicketSummaryDTO(${ticket.kind})`);
135
- assertSourceShape(ticket.source);
136
- }
137
- function assertInboxListShape(body) {
138
- assertKeys(body, ['tickets'], 'InboxListDTO');
139
- for (const ticket of body.tickets)
140
- assertTicketSummaryShape(ticket);
141
- }
142
- function assertInteractionShape(interaction) {
143
- assertKeys(interaction, ['id', 'title', 'subtitle', 'body', 'options', 'multiSelect', 'allowFreetext', 'freetextLabel', 'kind', 'preAnswered'], 'InteractionDTO');
144
- for (const option of interaction.options) {
145
- assertKeys(option, ['id', 'label', 'description'], 'InteractionOptionDTO');
146
- }
147
- if (interaction.preAnswered !== undefined) {
148
- assertKeys(interaction.preAnswered, ['selectedOptionId', 'selectedOptionIds', 'freetext', 'label'], 'InteractionPreAnswerDTO');
149
- }
150
- }
151
- function assertDeckShape(deck) {
152
- assertKeys(deck, ['title', 'source', 'interactions'], 'DeckDTO');
153
- if (deck.source !== undefined)
154
- assertSourceShape(deck.source);
155
- for (const interaction of deck.interactions)
156
- assertInteractionShape(interaction);
157
- }
158
- function assertInboxDeckShape(body) {
159
- assertKeys(body, ['ticket_id', 'kind', 'deck'], 'InboxDeckDTO');
160
- assert.equal(body.kind, 'deck');
161
- assert.match(body.ticket_id, TICKET_ID_PATTERN);
162
- assertDeckShape(body.deck);
163
- }
164
- function assertDeckResultShape(result) {
165
- assertKeys(result, ['schema', 'kind', 'responses', 'summary', 'completedAt'], 'DeckTicketResultDTO');
166
- assert.equal(result.schema, 'humanloop.response/v2');
167
- assert.equal(result.kind, 'deck');
168
- for (const response of result.responses) {
169
- assertKeys(response, ['id', 'selectedOptionId', 'selectedOptionIds', 'freetext', 'optionComments'], 'InteractionResponseDTO');
170
- }
171
- }
172
- function assertCanceledResultShape(result) {
173
- assertKeys(result, ['schema', 'kind', 'canceledAt', 'reason', 'actor'], 'CanceledTicketResultDTO');
174
- assert.equal(result.schema, 'humanloop.cancel/v1');
175
- assert.equal(result.kind, 'canceled');
176
- }
177
- const FORBIDDEN_KEYS = new Set(['dir', 'file', 'output', 'claim', 'bodyPath']);
178
- /** Recursively walk a parsed JSON value, failing on any forbidden key name or
179
- * any string value containing one of the given forbidden substrings (host
180
- * paths, in practice). Used against full success AND error bodies. */
181
- function assertNoForbiddenContent(value, forbidden, path = '$') {
182
- if (Array.isArray(value)) {
183
- value.forEach((item, i) => assertNoForbiddenContent(item, forbidden, `${path}[${i}]`));
184
- return;
185
- }
186
- if (value !== null && typeof value === 'object') {
187
- for (const [key, val] of Object.entries(value)) {
188
- assert.ok(!FORBIDDEN_KEYS.has(key), `forbidden key "${key}" present at ${path}.${key}`);
189
- assertNoForbiddenContent(val, forbidden, `${path}.${key}`);
190
- }
191
- return;
192
- }
193
- if (typeof value === 'string') {
194
- for (const needle of forbidden) {
195
- assert.ok(!value.includes(needle), `forbidden content "${needle}" leaked at ${path}: ${JSON.stringify(value)}`);
196
- }
197
- }
198
- }
199
- // ---------------------------------------------------------------------------
200
- // GET /v1/human/inbox — enumerate
201
- // ---------------------------------------------------------------------------
202
- describe('GET /v1/human/inbox — enumerate', () => {
203
- test('mixed deck + review tickets preserve canonical-root hash ids while sorting newest first', async () => {
204
- const root = freshRoot('crouter');
205
- // This is the pre-cutover opaque-id formula: canonical root + NUL + ticket
206
- // basename. The single derived root retains that canonical root, so an id
207
- // a client computed before root registration disappeared still resolves.
208
- const olderDeckId = ticketIdFor(root, 'older-deck');
209
- submitTestDeck(root, 'older-deck', '2026-01-01T00:00:00.000Z');
210
- submitTestReview(root, 'newer-review', '2026-01-02T00:00:00.000Z');
211
- const list = await client.listHumanInbox();
212
- assertInboxListShape(list);
213
- assertNoForbiddenContent(list, [root]);
214
- assert.equal(list.tickets.length, 2);
215
- assert.equal(list.tickets[0].kind, 'review', 'newer blocked_since sorts first');
216
- assert.equal(list.tickets[1].kind, 'deck');
217
- assert.equal(list.tickets[0].ticket_id, ticketIdFor(root, 'newer-review'));
218
- assert.equal(list.tickets[1].ticket_id, olderDeckId);
219
- assert.equal((await client.getHumanInboxDeck(olderDeckId)).deck.title, 'Ship it?');
220
- });
221
- test('sorts by parsed instant across differing UTC offsets, not lexical string order', async () => {
222
- const root = freshRoot('crouter');
223
- // '+14:00' is lexically GREATER than 'Z' at the date digit that differs,
224
- // but the instant it names is two hours EARLIER than the 'Z' ticket
225
- // below (2026-01-01T10:00:00Z vs. 2026-01-01T12:00:00Z). A lexical
226
- // descending sort would (wrongly) place this ticket first.
227
- submitTestDeck(root, 'lexically-later-but-earlier-instant', '2026-01-02T00:00:00+14:00');
228
- submitTestDeck(root, 'lexically-earlier-but-later-instant', '2026-01-01T12:00:00Z');
229
- const list = await client.listHumanInbox();
230
- assert.equal(list.tickets.length, 2);
231
- assert.equal(list.tickets[0].blocked_since, '2026-01-01T12:00:00Z', 'the chronologically newer instant sorts first regardless of lexical string order');
232
- assert.equal(list.tickets[0].ticket_id, ticketIdFor(root, 'lexically-earlier-but-later-instant'));
233
- assert.equal(list.tickets[1].ticket_id, ticketIdFor(root, 'lexically-later-but-earlier-instant'));
234
- });
235
- });
236
- // ---------------------------------------------------------------------------
237
- // GET /v1/human/inbox/:ticket_id — read a pending deck
238
- // ---------------------------------------------------------------------------
239
- describe('GET /v1/human/inbox/:ticket_id', () => {
240
- test('inlines bodyPath at read time; no bodyPath key on the wire', async () => {
241
- // RAW fixture: `submitDeck` resolves `bodyPath` to inline `body` at
242
- // submit time, so a ticket with a still-unresolved `bodyPath` on disk
243
- // (the state crtrd's own `parseDeck` read-time resolution exists to
244
- // handle) cannot be produced through the public submission API.
245
- const root = freshRoot('crouter');
246
- const dir = join(root, 'body-deck');
247
- mkdirSync(dir, { recursive: true, mode: 0o700 });
248
- writeFileSync(join(dir, 'body.md'), 'Full markdown body.\n');
249
- writeFileSync(deckPath(dir), JSON.stringify({
250
- title: 'Ship it?',
251
- source: { blockedSince: '2026-01-01T00:00:00.000Z' },
252
- interactions: [{ id: 'q', title: 'Ship it?', subtitle: 'Approve shipping now so the release can proceed.', bodyPath: 'body.md', options: [{ id: 'yes', label: 'Yes' }] }],
253
- }));
254
- const id = ticketIdFor(root, 'body-deck');
255
- const deckDto = await client.getHumanInboxDeck(id);
256
- assertInboxDeckShape(deckDto);
257
- assertNoForbiddenContent(deckDto, [root, dir]);
258
- assert.equal(deckDto.deck.interactions[0].body, 'Full markdown body.\n');
259
- });
260
- test('404 ticket_not_found for a syntactically valid but unknown ticket id', async () => {
261
- await expectApiError(client.getHumanInboxDeck('a'.repeat(64)), 404, 'ticket_not_found');
262
- });
263
- test('409 ticket_kind_unsupported for a review ticket', async () => {
264
- const root = freshRoot('crouter');
265
- submitTestReview(root, 'rev', '2026-01-01T00:00:00.000Z');
266
- await expectApiError(client.getHumanInboxDeck(ticketIdFor(root, 'rev')), 409, 'ticket_kind_unsupported');
267
- });
268
- test('malformed stored deck sanitizes to 500 internal: no bodyPath value or host path in error.message', async () => {
269
- // RAW fixture: simulates a ticket mutated on disk after enumeration (or a
270
- // corrupted store) — a state `submitDeck` cannot produce, since it
271
- // validates and resolves bodyPath before ever writing deck.json. This
272
- // reproduces the review's exact repro shape: a bodyPath that escapes the
273
- // ticket directory to an existing absolute host file.
274
- const root = freshRoot('crouter');
275
- const dir = writeRawDeckTicket(root, 'malformed', deckJson('2026-01-01T00:00:00.000Z', {
276
- interactions: [{ id: 'q', title: 'Ship it?', subtitle: 'Approve shipping now so the release can proceed.', bodyPath: process.execPath, options: [{ id: 'yes', label: 'Yes' }] }],
277
- }));
278
- const id = ticketIdFor(root, 'malformed');
279
- const err = await expectApiError(client.getHumanInboxDeck(id), 500, 'internal');
280
- assert.ok(!err.message.includes(process.execPath), `error.message leaked a host path: ${err.message}`);
281
- assert.ok(!err.message.includes(dir), `error.message leaked the ticket directory: ${err.message}`);
282
- assert.ok(!err.message.toLowerCase().includes('bodypath'), `error.message named bodyPath: ${err.message}`);
283
- });
284
- });
285
- // ---------------------------------------------------------------------------
286
- // POST /v1/human/inbox/:ticket_id/respond
287
- // ---------------------------------------------------------------------------
288
- describe('POST /v1/human/inbox/:ticket_id/respond', () => {
289
- test('persists the canonical humanloop.response/v2 result unchanged and matches stored response.json exactly', async () => {
290
- const root = freshRoot('crouter', true);
291
- const dir = submitTestDeck(root, 'ask', '2026-01-01T00:00:00.000Z');
292
- const id = ticketIdFor(root, 'ask');
293
- const result = await client.respondHumanInboxDeck(id, { responses: [{ id: 'q', selectedOptionId: 'yes' }] });
294
- assertDeckResultShape(result);
295
- assertNoForbiddenContent(result, [root, dir]);
296
- assert.deepEqual(result.responses, [{ id: 'q', selectedOptionId: 'yes' }]);
297
- const stored = JSON.parse(readFileSync(responsePath(dir), 'utf8'));
298
- assert.deepEqual(stored, result, 'the wire result is the stored humanloop response exactly, unreconstructed');
299
- assert.ok(existsSync(responsePath(dir)), 'the canonical response remains the only ticket receipt');
300
- });
301
- test('ordinary notify acknowledgement — no special endpoint/shape', async () => {
302
- const root = freshRoot('crouter');
303
- const id_ = 'notify';
304
- submitDeck({ dir: join(root, id_), deck: notifyDeck('Build done', { subtitle: 'The build completed successfully, so the change is ready to review.' }) });
305
- const id = ticketIdFor(root, 'notify');
306
- const result = await client.respondHumanInboxDeck(id, { responses: [{ id: 'notify', selectedOptionId: 'ok' }] });
307
- assert.equal(result.kind, 'deck');
308
- assert.deepEqual(result.responses, [{ id: 'notify', selectedOptionId: 'ok' }]);
309
- });
310
- test('409 ticket_kind_unsupported for a review ticket', async () => {
311
- const root = freshRoot('crouter');
312
- submitTestReview(root, 'rev', '2026-01-01T00:00:00.000Z');
313
- await expectApiError(client.respondHumanInboxDeck(ticketIdFor(root, 'rev'), { responses: [] }), 409, 'ticket_kind_unsupported');
314
- });
315
- test('409 ticket_already_resolved on a second respond after the first succeeds', async () => {
316
- const root = freshRoot('crouter');
317
- submitTestDeck(root, 'ask', '2026-01-01T00:00:00.000Z');
318
- const id = ticketIdFor(root, 'ask');
319
- await client.respondHumanInboxDeck(id, { responses: [{ id: 'q', selectedOptionId: 'yes' }] });
320
- await expectApiError(client.respondHumanInboxDeck(id, { responses: [{ id: 'q', selectedOptionId: 'no' }] }), 409, 'ticket_already_resolved');
321
- });
322
- test('a live foreign claim is taken over by the daemon finish path', async () => {
323
- const root = freshRoot('crouter');
324
- const dir = submitTestDeck(root, 'ask', '2026-01-01T00:00:00.000Z');
325
- const id = ticketIdFor(root, 'ask');
326
- const foreignClaim = claimTicket(dir, { host: 'someone-else' });
327
- assert.ok(foreignClaim !== null, 'foreign claim acquired');
328
- const result = await client.respondHumanInboxDeck(id, { responses: [{ id: 'q', selectedOptionId: 'yes' }] });
329
- assertDeckResultShape(result);
330
- assert.equal(releaseClaim(dir, foreignClaim.token), false, 'the takeover released the replaced claim');
331
- });
332
- test('external cancel/resolve race: a resolution that lands between the precheck and claimTicket() reports ticket_already_resolved, never ticket_unavailable', async () => {
333
- const root = freshRoot('crouter');
334
- const dir = submitTestDeck(root, 'race', '2026-01-01T00:00:00.000Z');
335
- const id = ticketIdFor(root, 'race');
336
- // A real separate OS process holds the SAME ticket lock humanloop's
337
- // `claimTicket` contends for (`<dir>/.ticket-lock`, matching the marker
338
- // scheme in humanloop's `convention.ts`). It signals once it holds the
339
- // lock, then after a short real delay writes `response.json` directly
340
- // (simulating an external actor's cancel/respond winning the race) and
341
- // releases. Because the holder grabs the lock BEFORE this test sends the
342
- // respond request, the precheck inside the handler is guaranteed to see
343
- // the ticket still pending, and `claimTicket()`'s own lock acquisition is
344
- // guaranteed to block until the holder resolves the ticket and releases
345
- // — deterministically reproducing the exact interleaving the fix must
346
- // handle, without relying on a microsecond-scale timing coincidence.
347
- const script = `
348
- const { mkdirSync, writeFileSync, rmSync } = require('node:fs');
349
- const dir = process.env.RACE_TICKET_DIR;
350
- const lockPath = dir + '/.ticket-lock';
351
- mkdirSync(lockPath, { mode: 0o700 });
352
- writeFileSync(lockPath + '/owner.race-holder', '', { flag: 'wx', mode: 0o600 });
353
- process.stdout.write('holding\\n');
354
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 300);
355
- writeFileSync(dir + '/response.json', JSON.stringify({
356
- schema: 'humanloop.cancel/v1', kind: 'canceled',
357
- canceledAt: new Date().toISOString(), reason: 'external race', actor: 'external',
358
- }) + '\\n', { mode: 0o600 });
359
- rmSync(lockPath, { recursive: true, force: true });
360
- process.stdout.write('resolved\\n');
361
- `;
362
- const holder = spawn(process.execPath, ['-e', script], {
363
- env: { ...process.env, RACE_TICKET_DIR: dir },
364
- stdio: ['ignore', 'pipe', 'pipe'],
365
- });
366
- let stdout = '';
367
- let stderr = '';
368
- holder.stdout.setEncoding('utf8');
369
- holder.stderr.setEncoding('utf8');
370
- holder.stdout.on('data', (chunk) => {
371
- stdout += chunk;
372
- });
373
- holder.stderr.on('data', (chunk) => {
374
- stderr += chunk;
375
- });
376
- const exited = new Promise((resolve) => holder.once('close', resolve));
377
- try {
378
- await new Promise((resolve, reject) => {
379
- const timer = setTimeout(() => reject(new Error(`race holder never signaled "holding": stdout=${stdout} stderr=${stderr}`)), 5000);
380
- const check = () => {
381
- if (stdout.includes('holding')) {
382
- clearTimeout(timer);
383
- resolve();
384
- }
385
- };
386
- holder.stdout.on('data', check);
387
- holder.once('error', (err) => {
388
- clearTimeout(timer);
389
- reject(err);
390
- });
391
- check();
392
- });
393
- await expectApiError(client.respondHumanInboxDeck(id, { responses: [{ id: 'q', selectedOptionId: 'yes' }] }), 409, 'ticket_already_resolved');
394
- }
395
- finally {
396
- await exited;
397
- }
398
- assert.ok(stdout.includes('resolved'), `race holder did not complete its resolve+release: stdout=${stdout} stderr=${stderr}`);
399
- });
400
- test('500 internal for a finalize failure that is not a response-shape rejection — sanitized, not misclassified as 400', async () => {
401
- const root = freshRoot('crouter');
402
- const dir = submitTestDeck(root, 'ask', '2026-01-01T00:00:00.000Z');
403
- const id = ticketIdFor(root, 'ask');
404
- // RAW tamper: rewrite the persisted deck so its own re-read inside
405
- // `finalizeDeck` fails deck-schema validation (interactions.min(1)) —
406
- // this is a storage-layer failure, not a response-shape rejection, and
407
- // `submitDeck` could never have written it in the first place.
408
- writeFileSync(deckPath(dir), JSON.stringify({ title: 'Ship it?', interactions: [] }));
409
- const err = await expectApiError(client.respondHumanInboxDeck(id, { responses: [{ id: 'q', selectedOptionId: 'yes' }] }), 500, 'internal');
410
- assert.equal(err.message, 'the ticket could not be finalized');
411
- assert.ok(!err.message.includes(dir), `error.message leaked the ticket directory: ${err.message}`);
412
- });
413
- test('400 invalid_ticket_response releases the claim so a following valid respond succeeds', async () => {
414
- const root = freshRoot('crouter');
415
- submitTestDeck(root, 'ask', '2026-01-01T00:00:00.000Z');
416
- const id = ticketIdFor(root, 'ask');
417
- const err = await expectApiError(client.respondHumanInboxDeck(id, { responses: [{ id: 'nonexistent', freetext: 'x' }] }), 400, 'invalid_ticket_response');
418
- assert.match(err.message, /response does not match a unique deck interaction: nonexistent/);
419
- const result = await client.respondHumanInboxDeck(id, { responses: [{ id: 'q', selectedOptionId: 'yes' }] });
420
- assert.equal(result.kind, 'deck', 'the claim was released, so a valid respond now succeeds');
421
- });
422
- test('400 invalid_request for a malformed body', async () => {
423
- const root = freshRoot('crouter');
424
- submitTestDeck(root, 'ask', '2026-01-01T00:00:00.000Z');
425
- const id = ticketIdFor(root, 'ask');
426
- const path = `/v1/human/inbox/${id}/respond`;
427
- await expectApiError(client.request('POST', path, { responses: [], extra: true }), 400, 'invalid_request');
428
- await expectApiError(client.request('POST', path, { responses: 'nope' }), 400, 'invalid_request');
429
- });
430
- });
431
- // ---------------------------------------------------------------------------
432
- // POST /v1/human/inbox/:ticket_id/cancel
433
- // ---------------------------------------------------------------------------
434
- describe('POST /v1/human/inbox/:ticket_id/cancel', () => {
435
- test('default reason and hardcoded human actor match stored response.json', async () => {
436
- const root = freshRoot('crouter', true);
437
- const dir = submitTestDeck(root, 'ask', '2026-01-01T00:00:00.000Z');
438
- const id = ticketIdFor(root, 'ask');
439
- const result = await client.cancelHumanInboxTicket(id);
440
- assertCanceledResultShape(result);
441
- assertNoForbiddenContent(result, [root, dir]);
442
- assert.equal(result.reason, 'Canceled from the inbox.');
443
- assert.equal(result.actor, 'human');
444
- const stored = JSON.parse(readFileSync(responsePath(dir), 'utf8'));
445
- assert.deepEqual(stored, result, 'the wire result is the stored humanloop cancellation exactly, unreconstructed');
446
- assert.ok(existsSync(responsePath(dir)), 'the canonical cancellation remains the only ticket receipt');
447
- });
448
- test('custom reason is honored; actor is always human regardless of any client-supplied field', async () => {
449
- const root = freshRoot('crouter');
450
- submitTestDeck(root, 'ask', '2026-01-01T00:00:00.000Z');
451
- const id = ticketIdFor(root, 'ask');
452
- const result = await client.cancelHumanInboxTicket(id, { reason: 'changed my mind' });
453
- assert.equal(result.reason, 'changed my mind');
454
- assert.equal(result.actor, 'human');
455
- });
456
- test('recordless review cancellation returns the stored humanloop.cancel/v1 result', async () => {
457
- const root = freshRoot('crouter');
458
- const dir = submitTestReview(root, 'rev', '2026-01-01T00:00:00.000Z');
459
- const result = await client.cancelHumanInboxTicket(ticketIdFor(root, 'rev'));
460
- assertCanceledResultShape(result);
461
- assert.deepEqual(JSON.parse(readFileSync(responsePath(dir), 'utf8')), result);
462
- });
463
- test('409 ticket_already_resolved after the ticket was already resolved via respond', async () => {
464
- const root = freshRoot('crouter');
465
- submitTestDeck(root, 'ask', '2026-01-01T00:00:00.000Z');
466
- const id = ticketIdFor(root, 'ask');
467
- await client.respondHumanInboxDeck(id, { responses: [{ id: 'q', selectedOptionId: 'yes' }] });
468
- await expectApiError(client.cancelHumanInboxTicket(id), 409, 'ticket_already_resolved');
469
- });
470
- test('400 invalid_request for an empty (post-trim) reason and for a >1000-char reason', async () => {
471
- const root = freshRoot('crouter');
472
- submitTestDeck(root, 'ask', '2026-01-01T00:00:00.000Z');
473
- const id = ticketIdFor(root, 'ask');
474
- await expectApiError(client.cancelHumanInboxTicket(id, { reason: ' ' }), 400, 'invalid_request');
475
- await expectApiError(client.cancelHumanInboxTicket(id, { reason: 'x'.repeat(1001) }), 400, 'invalid_request');
476
- });
477
- });
478
- // ---------------------------------------------------------------------------
479
- // Malformed ticket id syntax
480
- // ---------------------------------------------------------------------------
481
- describe('malformed ticket id syntax', () => {
482
- test('400 invalid_request on GET/respond/cancel for a non-hex or wrong-length id', async () => {
483
- for (const bad of ['not-hex-not-64-chars', 'a'.repeat(63), 'g'.repeat(64)]) {
484
- await expectApiError(client.request('GET', `/v1/human/inbox/${bad}`), 400, 'invalid_request');
485
- await expectApiError(client.request('POST', `/v1/human/inbox/${bad}/respond`, { responses: [] }), 400, 'invalid_request');
486
- await expectApiError(client.request('POST', `/v1/human/inbox/${bad}/cancel`, {}), 400, 'invalid_request');
487
- }
488
- });
489
- });