@north-light/crouter 0.3.170 → 0.3.172

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 (670) hide show
  1. package/dist/api/client.d.ts +37 -11
  2. package/dist/api/client.js +84 -15
  3. package/dist/api/dto/human.d.ts +53 -39
  4. package/dist/api/dto/human.js +2 -3
  5. package/dist/api/dto/inbox.d.ts +0 -1
  6. package/dist/api/dto/nodes.d.ts +12 -0
  7. package/dist/api/dto/review-comments.d.ts +137 -0
  8. package/dist/api/dto/review-comments.js +5 -0
  9. package/dist/api/dto/reviews.d.ts +109 -0
  10. package/dist/api/dto/reviews.js +5 -0
  11. package/dist/api/index.d.ts +2 -0
  12. package/dist/api/index.js +2 -0
  13. package/dist/api/routes.d.ts +17 -4
  14. package/dist/api/routes.js +21 -5
  15. package/dist/build-root.d.ts +1 -16
  16. package/dist/build-root.js +1 -25
  17. package/dist/builtin-memory/00-runtime-base.md +7 -5
  18. package/dist/builtin-memory/04-base-worker.md +1 -1
  19. package/dist/builtin-memory/04-orchestration-kernel.md +7 -7
  20. package/dist/builtin-memory/05-kinds/design/00-base.md +1 -1
  21. package/dist/builtin-memory/05-kinds/design/01-orchestrator.md +1 -1
  22. package/dist/builtin-memory/05-kinds/developer/00-base.md +1 -1
  23. package/dist/builtin-memory/05-kinds/explore/00-base.md +1 -1
  24. package/dist/builtin-memory/05-kinds/plan/00-base.md +1 -1
  25. package/dist/builtin-memory/05-kinds/plan/01-orchestrator.md +1 -1
  26. package/dist/builtin-memory/05-kinds/review/00-base.md +1 -1
  27. package/dist/builtin-memory/05-kinds/review/companion/00-base.md +20 -0
  28. package/dist/builtin-memory/05-kinds/spec/00-base.md +3 -7
  29. package/dist/builtin-memory/05-kinds/spec/01-orchestrator.md +3 -3
  30. package/dist/builtin-memory/05-kinds/spec/requirements.md +3 -3
  31. package/dist/builtin-memory/design.md +2 -2
  32. package/dist/builtin-memory/insights/capture.md +50 -0
  33. package/dist/builtin-memory/insights/init.md +45 -0
  34. package/dist/builtin-memory/insights/listen.md +9 -0
  35. package/dist/builtin-memory/internal/agent-shaping.md +2 -2
  36. package/dist/builtin-memory/internal/nodes-and-canvas.md +1 -1
  37. package/dist/builtin-memory/internal/plugins.md +12 -11
  38. package/dist/builtin-memory/internal/storage-tiers.md +2 -2
  39. package/dist/builtin-memory/{planning.md → plan/roadmap.md} +3 -3
  40. package/dist/builtin-memory/plan.md +19 -0
  41. package/dist/builtin-memory/spec/guide.md +38 -0
  42. package/dist/builtin-memory/spec/requirements.md +31 -0
  43. package/dist/builtin-memory/spec/roadmap.md +35 -0
  44. package/dist/builtin-memory/spec.md +10 -86
  45. package/dist/builtin-pi-packages/pi-crtr-extensions/README.md +3 -4
  46. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/provider-rotation.test.ts +49 -5
  47. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +56 -0
  48. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +920 -0
  49. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +221 -156
  50. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/statusline.ts +4 -5
  51. package/dist/cli.js +2 -6
  52. package/dist/clients/attach/__tests__/attach-chrome-remote.test.js +7 -35
  53. package/dist/clients/attach/__tests__/attach-keybindings.test.js +32 -16
  54. package/dist/clients/attach/__tests__/context-message.test.js +142 -6
  55. package/dist/clients/attach/__tests__/crtr-output.test.js +29 -45
  56. package/dist/clients/attach/__tests__/diagram.test.js +1 -1
  57. package/dist/clients/attach/__tests__/file-review-focus.test.js +48 -0
  58. package/dist/clients/attach/__tests__/group-activity.test.js +266 -0
  59. package/dist/clients/attach/__tests__/input-controller-extension-command.test.js +32 -0
  60. package/dist/clients/attach/__tests__/session-identity-refresh.test.js +32 -0
  61. package/dist/clients/attach/__tests__/titled-editor-preview.test.js +1 -2
  62. package/dist/clients/attach/chrome/header.d.ts +10 -0
  63. package/dist/clients/attach/chrome/header.js +30 -0
  64. package/dist/clients/attach/chrome/recap.d.ts +7 -0
  65. package/dist/clients/attach/chrome/recap.js +19 -0
  66. package/dist/clients/attach/chrome/status-line.d.ts +3 -0
  67. package/dist/clients/attach/chrome/status-line.js +5 -0
  68. package/dist/clients/attach/chrome/widgets.js +11 -21
  69. package/dist/clients/attach/command.js +11 -7
  70. package/dist/clients/attach/config.d.ts +4 -0
  71. package/dist/clients/attach/config.js +3 -0
  72. package/dist/clients/attach/input/controller.d.ts +19 -0
  73. package/dist/clients/attach/input/controller.js +62 -28
  74. package/dist/clients/attach/input/titled-editor.d.ts +0 -4
  75. package/dist/clients/attach/input/titled-editor.js +2 -10
  76. package/dist/clients/attach/overlays/file-review.d.ts +9 -5
  77. package/dist/clients/attach/overlays/file-review.js +9 -70
  78. package/dist/clients/attach/overlays/help.d.ts +33 -0
  79. package/dist/clients/attach/overlays/help.js +204 -0
  80. package/dist/clients/attach/render/activity-slot.d.ts +4 -0
  81. package/dist/clients/attach/render/activity-slot.js +13 -0
  82. package/dist/clients/attach/render/chat-view.d.ts +117 -12
  83. package/dist/clients/attach/render/chat-view.js +604 -135
  84. package/dist/clients/attach/render/context-message.d.ts +2 -1
  85. package/dist/clients/attach/render/context-message.js +17 -13
  86. package/dist/clients/attach/render/crtr-output.js +1 -3
  87. package/dist/clients/attach/render/diagram.js +2 -2
  88. package/dist/clients/attach/render/edit-diff.d.ts +4 -1
  89. package/dist/clients/attach/render/edit-diff.js +7 -11
  90. package/dist/clients/attach/render/group-activity.d.ts +32 -0
  91. package/dist/clients/attach/render/group-activity.js +206 -0
  92. package/dist/clients/attach/render/group-recap.d.ts +10 -0
  93. package/dist/clients/attach/render/group-recap.js +93 -0
  94. package/dist/clients/attach/render/html-markdown.d.ts +17 -0
  95. package/dist/clients/attach/render/html-markdown.js +228 -0
  96. package/dist/clients/attach/render/measured-container.d.ts +96 -0
  97. package/dist/clients/attach/render/measured-container.js +322 -0
  98. package/dist/clients/attach/render/scroll-animation.d.ts +8 -0
  99. package/dist/clients/attach/render/scroll-animation.js +80 -0
  100. package/dist/clients/attach/render/tool-calls.js +6 -6
  101. package/dist/clients/attach/render/transcript-copy.d.ts +9 -0
  102. package/dist/clients/attach/render/transcript-copy.js +81 -0
  103. package/dist/clients/attach/render/viewport.d.ts +29 -0
  104. package/dist/clients/attach/render/viewport.js +159 -0
  105. package/dist/clients/attach/session/bindings.d.ts +10 -7
  106. package/dist/clients/attach/session/bindings.js +23 -8
  107. package/dist/clients/attach/session/connection.js +4 -5
  108. package/dist/clients/attach/session/context.d.ts +6 -4
  109. package/dist/clients/attach/session/frame.d.ts +34 -0
  110. package/dist/clients/attach/session/frame.js +176 -0
  111. package/dist/clients/attach/session/frames.d.ts +4 -1
  112. package/dist/clients/attach/session/frames.js +14 -1
  113. package/dist/clients/attach/session/identity.js +4 -3
  114. package/dist/clients/attach/session/input-wiring.d.ts +14 -4
  115. package/dist/clients/attach/session/input-wiring.js +15 -13
  116. package/dist/clients/attach/session/invariant.d.ts +9 -0
  117. package/dist/clients/attach/session/invariant.js +15 -0
  118. package/dist/clients/attach/session/keys.d.ts +10 -2
  119. package/dist/clients/attach/session/keys.js +13 -5
  120. package/dist/clients/attach/session/layout.d.ts +10 -8
  121. package/dist/clients/attach/session/layout.js +33 -34
  122. package/dist/clients/attach/session/mouse.d.ts +18 -0
  123. package/dist/clients/attach/session/mouse.js +64 -0
  124. package/dist/clients/attach/session/pane-tag.d.ts +3 -0
  125. package/dist/clients/attach/session/pane-tag.js +51 -8
  126. package/dist/clients/attach/session/reconnect.d.ts +3 -0
  127. package/dist/clients/attach/session/reconnect.js +42 -5
  128. package/dist/clients/attach/session/surface-frame.d.ts +20 -0
  129. package/dist/clients/attach/session/surface-frame.js +73 -0
  130. package/dist/clients/attach/session/surface-host.d.ts +64 -0
  131. package/dist/clients/attach/session/surface-host.js +255 -0
  132. package/dist/clients/attach/session/whip.d.ts +17 -0
  133. package/dist/clients/attach/session/whip.js +334 -0
  134. package/dist/clients/attach/slash/dispatch.d.ts +13 -1
  135. package/dist/clients/attach/slash/dispatch.js +51 -29
  136. package/dist/clients/attach/viewer.js +907 -819
  137. package/dist/clients/conversation/conversation.d.ts +65 -0
  138. package/dist/clients/conversation/conversation.js +318 -0
  139. package/dist/clients/conversation/frames.d.ts +30 -0
  140. package/dist/clients/conversation/frames.js +52 -0
  141. package/dist/clients/conversation/region.d.ts +51 -0
  142. package/dist/clients/conversation/region.js +265 -0
  143. package/dist/clients/conversation/submit.d.ts +38 -0
  144. package/dist/clients/conversation/submit.js +51 -0
  145. package/dist/clients/inbox/__tests__/inbox-controller.test.js +247 -0
  146. package/dist/clients/inbox/__tests__/mount-panel.test.js +776 -0
  147. package/dist/clients/inbox/controller.d.ts +79 -0
  148. package/dist/clients/inbox/controller.js +448 -0
  149. package/dist/clients/inbox/deck-adapter.d.ts +29 -0
  150. package/dist/clients/inbox/deck-adapter.js +62 -0
  151. package/dist/clients/inbox/layout.d.ts +8 -0
  152. package/dist/clients/inbox/layout.js +9 -0
  153. package/dist/clients/inbox/resolve.d.ts +10 -0
  154. package/dist/clients/inbox/resolve.js +34 -0
  155. package/dist/clients/inbox/review/__tests__/editor-roundtrip.test.js +29 -0
  156. package/dist/clients/inbox/review/__tests__/remap.test.js +196 -0
  157. package/dist/clients/inbox/review/__tests__/review-core.test.js +341 -0
  158. package/dist/clients/inbox/review/anchors.d.ts +41 -0
  159. package/dist/clients/inbox/review/anchors.js +89 -0
  160. package/dist/clients/inbox/review/columns.d.ts +22 -0
  161. package/dist/clients/inbox/review/columns.js +40 -0
  162. package/dist/clients/inbox/review/comments-client.d.ts +66 -0
  163. package/dist/clients/inbox/review/comments-client.js +98 -0
  164. package/dist/clients/inbox/review/composition.d.ts +60 -0
  165. package/dist/clients/inbox/review/composition.js +278 -0
  166. package/dist/clients/inbox/review/document-region.d.ts +120 -0
  167. package/dist/clients/inbox/review/document-region.js +902 -0
  168. package/dist/clients/inbox/review/frame.d.ts +45 -0
  169. package/dist/clients/inbox/review/frame.js +215 -0
  170. package/dist/clients/inbox/review/keys.d.ts +67 -0
  171. package/dist/clients/inbox/review/keys.js +130 -0
  172. package/dist/clients/inbox/review/launch.d.ts +10 -0
  173. package/dist/clients/inbox/review/launch.js +129 -0
  174. package/dist/clients/inbox/review/live-source.d.ts +24 -0
  175. package/dist/clients/inbox/review/live-source.js +160 -0
  176. package/dist/clients/inbox/review/nvim.d.ts +13 -0
  177. package/dist/clients/inbox/review/nvim.js +42 -0
  178. package/dist/clients/inbox/review/remap.d.ts +51 -0
  179. package/dist/clients/inbox/review/remap.js +157 -0
  180. package/dist/clients/inbox/review/review-client.d.ts +30 -0
  181. package/dist/clients/inbox/review/review-client.js +107 -0
  182. package/dist/clients/inbox/review/roundtrip.d.ts +5 -0
  183. package/dist/clients/inbox/review/roundtrip.js +36 -0
  184. package/dist/clients/inbox/review/state.d.ts +143 -0
  185. package/dist/clients/inbox/review/state.js +282 -0
  186. package/dist/clients/inbox/review-adapter.d.ts +29 -0
  187. package/dist/clients/inbox/review-adapter.js +92 -0
  188. package/dist/clients/inbox/review-visibility.d.ts +5 -0
  189. package/dist/clients/inbox/review-visibility.js +19 -0
  190. package/dist/clients/inbox/surface.d.ts +19 -0
  191. package/dist/clients/inbox/surface.js +128 -0
  192. package/dist/clients/inbox/tui/ansi.d.ts +66 -0
  193. package/dist/clients/inbox/tui/ansi.js +317 -0
  194. package/dist/clients/inbox/tui/clipboard.d.ts +2 -0
  195. package/dist/clients/inbox/tui/clipboard.js +26 -0
  196. package/dist/clients/inbox/tui/input.d.ts +7 -0
  197. package/dist/clients/inbox/tui/input.js +653 -0
  198. package/dist/clients/inbox/tui/keys.d.ts +28 -0
  199. package/dist/clients/inbox/tui/keys.js +111 -0
  200. package/dist/clients/inbox/tui/panel.d.ts +2 -0
  201. package/dist/clients/inbox/tui/panel.js +217 -0
  202. package/dist/clients/inbox/tui/render.d.ts +37 -0
  203. package/dist/clients/inbox/tui/render.js +476 -0
  204. package/dist/clients/inbox/tui/types.d.ts +57 -0
  205. package/dist/clients/inbox/tui/types.js +1 -0
  206. package/dist/clients/inbox/tui.d.ts +6 -0
  207. package/dist/clients/inbox/tui.js +74 -0
  208. package/dist/clients/surfaces/host.d.ts +66 -0
  209. package/dist/clients/surfaces/host.js +1 -0
  210. package/dist/clients/surfaces/region.d.ts +16 -0
  211. package/dist/clients/surfaces/region.js +1 -0
  212. package/dist/clients/web/__tests__/push-engine.test.js +14 -158
  213. package/dist/clients/web/dev-server.d.ts +3 -3
  214. package/dist/clients/web/dev-server.js +9 -23
  215. package/dist/clients/web/events.js +4 -6
  216. package/dist/clients/web/push-engine.d.ts +2 -66
  217. package/dist/clients/web/push-engine.js +30 -161
  218. package/dist/clients/web/push-registry.d.ts +0 -4
  219. package/dist/clients/web/push-registry.js +2 -7
  220. package/dist/clients/web/server.js +3 -35
  221. package/dist/clients/web/web-client/shared/protocol.d.ts +0 -108
  222. package/dist/clients/web/web-cmd.js +2 -2
  223. package/dist/commands/__tests__/human.test.js +27 -15
  224. package/dist/commands/api-client.js +3 -3
  225. package/dist/commands/canvas-browse.js +1 -1
  226. package/dist/commands/canvas-issue.js +1 -1
  227. package/dist/commands/canvas-use.js +1 -1
  228. package/dist/commands/human/doc.d.ts +2 -0
  229. package/dist/commands/human/doc.js +98 -0
  230. package/dist/commands/human/inbox.d.ts +2 -0
  231. package/dist/commands/human/inbox.js +101 -0
  232. package/dist/commands/human/prompts.d.ts +2 -3
  233. package/dist/commands/human/prompts.js +27 -180
  234. package/dist/commands/human/queue.d.ts +1 -5
  235. package/dist/commands/human/queue.js +54 -216
  236. package/dist/commands/human/review.d.ts +2 -0
  237. package/dist/commands/human/review.js +298 -0
  238. package/dist/commands/human/shared.d.ts +2 -69
  239. package/dist/commands/human/shared.js +1 -181
  240. package/dist/commands/human.js +15 -20
  241. package/dist/commands/memory/__tests__/lint.test.d.ts +1 -0
  242. package/dist/commands/memory/__tests__/lint.test.js +15 -0
  243. package/dist/commands/memory/delete.js +2 -2
  244. package/dist/commands/memory/lint.d.ts +1 -0
  245. package/dist/commands/memory/lint.js +21 -3
  246. package/dist/commands/memory/read.js +4 -4
  247. package/dist/commands/node.js +8 -7
  248. package/dist/commands/pkg/plugin-manage.js +143 -68
  249. package/dist/commands/surface-inspect.js +2 -2
  250. package/dist/commands/surface.js +6 -12
  251. package/dist/commands/sys/__tests__/setup-core.test.js +9 -5
  252. package/dist/commands/sys/__tests__/setup-front-door.test.js +7 -42
  253. package/dist/commands/sys/config.js +29 -3
  254. package/dist/commands/sys/feedback.js +1 -1
  255. package/dist/commands/sys/setup-core.d.ts +12 -11
  256. package/dist/commands/sys/setup-core.js +55 -46
  257. package/dist/commands/sys/setup-wizard.d.ts +4 -1
  258. package/dist/commands/sys/setup-wizard.js +50 -9
  259. package/dist/commands/sys/sync-deps.js +1 -1
  260. package/dist/commands/sys.js +4 -6
  261. package/dist/core/__tests__/broker-sdk-wiring.test.js +6 -1
  262. package/dist/core/__tests__/broker-snapshot-history.test.js +9 -1
  263. package/dist/core/__tests__/dead-node-policy-table.test.js +3 -4
  264. package/dist/core/__tests__/fixtures/fake-engine.d.ts +8 -5
  265. package/dist/core/__tests__/fixtures/fake-engine.js +28 -2
  266. package/dist/core/__tests__/full/broker-provider-retry.test.js +87 -2
  267. package/dist/core/__tests__/full/broker-whip-interrupt.test.d.ts +1 -0
  268. package/dist/core/__tests__/full/broker-whip-interrupt.test.js +39 -0
  269. package/dist/core/__tests__/full/detach-focus.test.js +1 -1
  270. package/dist/core/__tests__/human-deliver-e2e.test.js +64 -64
  271. package/dist/core/__tests__/human-deliver.test.js +71 -345
  272. package/dist/core/__tests__/inline-memory-refs.test.js +2 -12
  273. package/dist/core/__tests__/memory-resolver-precedence.test.js +12 -15
  274. package/dist/core/__tests__/migration.test.js +10 -0
  275. package/dist/core/__tests__/model-pin-durability.test.js +14 -0
  276. package/dist/core/__tests__/phase4-review-migration.test.d.ts +1 -0
  277. package/dist/core/__tests__/phase4-review-migration.test.js +100 -0
  278. package/dist/core/__tests__/phase4-review-store.test.d.ts +1 -0
  279. package/dist/core/__tests__/phase4-review-store.test.js +143 -0
  280. package/dist/core/__tests__/remote-canvas-target.test.js +2 -2
  281. package/dist/core/__tests__/revive.test.js +22 -53
  282. package/dist/core/__tests__/session-cycles.test.js +48 -18
  283. package/dist/core/__tests__/spawn-no-kickoff.test.js +8 -12
  284. package/dist/core/__tests__/stop-guard.test.js +16 -1
  285. package/dist/core/__tests__/tmux-surface.test.js +21 -25
  286. package/dist/core/asset-root.d.ts +7 -0
  287. package/dist/core/asset-root.js +18 -0
  288. package/dist/core/broker-client/client.d.ts +0 -7
  289. package/dist/core/broker-client/client.js +4 -10
  290. package/dist/core/broker-client/transport-relay.d.ts +3 -4
  291. package/dist/core/broker-client/transport-relay.js +3 -4
  292. package/dist/core/canvas/__tests__/attention.test.js +1 -1
  293. package/dist/core/canvas/__tests__/remote-canvas-source.test.js +1 -1
  294. package/dist/core/canvas/__tests__/remote-transport.test.d.ts +1 -0
  295. package/dist/core/{view/__tests__/transport-remote.test.js → canvas/__tests__/remote-transport.test.js} +2 -2
  296. package/dist/core/canvas/__tests__/render-remote.test.js +1 -1
  297. package/dist/core/canvas/attention.js +4 -4
  298. package/dist/core/canvas/canvas.d.ts +6 -11
  299. package/dist/core/canvas/canvas.js +11 -33
  300. package/dist/core/canvas/db.js +207 -38
  301. package/dist/core/canvas/extensions.d.ts +1 -1
  302. package/dist/core/canvas/extensions.js +8 -1
  303. package/dist/core/canvas/index.d.ts +0 -1
  304. package/dist/core/canvas/index.js +0 -1
  305. package/dist/core/canvas/nav-model.d.ts +1 -1
  306. package/dist/core/canvas/nav-model.js +1 -1
  307. package/dist/core/canvas/paths.d.ts +5 -0
  308. package/dist/core/canvas/paths.js +16 -0
  309. package/dist/core/canvas/remote-canvas-source.js +2 -2
  310. package/dist/core/canvas/remote-transport.d.ts +7 -0
  311. package/dist/core/{view/transport-remote.js → canvas/remote-transport.js} +3 -4
  312. package/dist/core/canvas/source.js +1 -1
  313. package/dist/core/canvas/status-glyph.js +4 -12
  314. package/dist/core/canvas/types.d.ts +10 -35
  315. package/dist/core/clipboard-text.d.ts +8 -0
  316. package/dist/core/clipboard-text.js +18 -0
  317. package/dist/core/command-manifests/registry.d.ts +1 -3
  318. package/dist/core/command-plugins/bundle.d.ts +29 -0
  319. package/dist/core/command-plugins/bundle.js +198 -0
  320. package/dist/core/command-plugins/discovery.d.ts +0 -8
  321. package/dist/core/command-plugins/discovery.js +2 -39
  322. package/dist/core/command-plugins/transport/http-fetch.d.ts +4 -30
  323. package/dist/core/command-plugins/transport/http-fetch.js +17 -95
  324. package/dist/core/command.d.ts +1 -13
  325. package/dist/core/command.js +1 -16
  326. package/dist/core/config.d.ts +4 -0
  327. package/dist/core/config.js +28 -3
  328. package/dist/core/document-lines.d.ts +4 -0
  329. package/dist/core/document-lines.js +6 -0
  330. package/dist/core/feed/feed.d.ts +2 -0
  331. package/dist/core/feed/feed.js +11 -7
  332. package/dist/core/human/__tests__/inbox-cancel-worker.d.ts +1 -0
  333. package/dist/core/human/__tests__/inbox-cancel-worker.js +7 -0
  334. package/dist/core/human/__tests__/inbox-claim-worker.d.ts +1 -0
  335. package/dist/core/human/__tests__/inbox-claim-worker.js +17 -0
  336. package/dist/core/human/__tests__/inbox-core.test.d.ts +1 -0
  337. package/dist/core/human/__tests__/inbox-core.test.js +144 -0
  338. package/dist/core/human/__tests__/inbox-stale-claim-worker.d.ts +1 -0
  339. package/dist/core/human/__tests__/inbox-stale-claim-worker.js +8 -0
  340. package/dist/core/human/__tests__/visible-paths.test.d.ts +1 -0
  341. package/dist/{clients/attach → core/human}/__tests__/visible-paths.test.js +9 -0
  342. package/dist/core/human/claim.d.ts +23 -0
  343. package/dist/core/human/claim.js +67 -0
  344. package/dist/core/human/convention.d.ts +30 -0
  345. package/dist/core/human/convention.js +202 -0
  346. package/dist/core/human/deck-factories.d.ts +8 -0
  347. package/dist/core/human/deck-factories.js +15 -0
  348. package/dist/core/human/deck-schema.d.ts +70 -0
  349. package/dist/core/human/deck-schema.js +92 -0
  350. package/dist/core/human/root.d.ts +7 -0
  351. package/dist/core/human/root.js +37 -0
  352. package/dist/core/human/scan.d.ts +3 -0
  353. package/dist/core/human/scan.js +76 -0
  354. package/dist/core/human/summary.d.ts +8 -0
  355. package/dist/core/human/summary.js +44 -0
  356. package/dist/core/human/tickets.d.ts +49 -0
  357. package/dist/core/human/tickets.js +190 -0
  358. package/dist/core/human/types.d.ts +136 -0
  359. package/dist/core/human/types.js +5 -0
  360. package/dist/{clients/attach → core/human}/visible-paths.d.ts +1 -1
  361. package/dist/{clients/attach → core/human}/visible-paths.js +3 -2
  362. package/dist/core/inspector/chrome.d.ts +6 -0
  363. package/dist/core/inspector/chrome.js +14 -0
  364. package/dist/core/inspector/contract.d.ts +105 -0
  365. package/dist/core/inspector/contract.js +1 -0
  366. package/dist/core/inspector/core.d.ts +4 -5
  367. package/dist/core/inspector/core.js +3 -5
  368. package/dist/core/inspector/host.d.ts +46 -0
  369. package/dist/core/{tui → inspector}/host.js +42 -208
  370. package/dist/core/inspector/text.d.ts +2 -2
  371. package/dist/core/inspector/transport.d.ts +6 -0
  372. package/dist/core/inspector/transport.js +36 -0
  373. package/dist/core/inspector/tui.d.ts +2 -2
  374. package/dist/core/inspector/tui.js +15 -15
  375. package/dist/core/keybindings/__tests__/inbox-affordance.test.js +11 -11
  376. package/dist/core/keybindings/__tests__/resolve.test.js +10 -19
  377. package/dist/core/keybindings/attach-control.d.ts +9 -3
  378. package/dist/core/keybindings/attach-control.js +3 -1
  379. package/dist/core/keybindings/catalog.d.ts +6 -5
  380. package/dist/core/keybindings/catalog.js +61 -133
  381. package/dist/core/keybindings/inbox.d.ts +5 -6
  382. package/dist/core/keybindings/inbox.js +9 -10
  383. package/dist/core/keybindings/index.d.ts +3 -3
  384. package/dist/core/keybindings/index.js +3 -3
  385. package/dist/core/keybindings/persistence.d.ts +5 -1
  386. package/dist/core/keybindings/persistence.js +24 -6
  387. package/dist/core/keybindings/resolve.d.ts +6 -0
  388. package/dist/core/keybindings/resolve.js +10 -3
  389. package/dist/core/memory/inline-ref-grammar.js +2 -3
  390. package/dist/core/memory-resolver.d.ts +27 -29
  391. package/dist/core/memory-resolver.js +45 -39
  392. package/dist/core/model-routes.d.ts +33 -0
  393. package/dist/core/model-routes.js +42 -3
  394. package/dist/core/preview-registry.js +17 -55
  395. package/dist/core/profiles/select.js +3 -3
  396. package/dist/core/{view/remote-canvas-target.d.ts → remote-canvas-target.d.ts} +2 -2
  397. package/dist/core/{view/remote-canvas-target.js → remote-canvas-target.js} +4 -4
  398. package/dist/core/review/__tests__/capture-origin.test.d.ts +1 -0
  399. package/dist/core/review/__tests__/capture-origin.test.js +139 -0
  400. package/dist/core/review/__tests__/comments.test.d.ts +1 -0
  401. package/dist/core/review/__tests__/comments.test.js +98 -0
  402. package/dist/core/review/__tests__/project.test.d.ts +1 -0
  403. package/dist/core/review/__tests__/project.test.js +71 -0
  404. package/dist/core/review/__tests__/remap.test.d.ts +1 -0
  405. package/dist/core/review/__tests__/remap.test.js +159 -0
  406. package/dist/core/review/__tests__/stage-identity.test.d.ts +1 -0
  407. package/dist/core/review/__tests__/stage-identity.test.js +140 -0
  408. package/dist/core/review/birth.d.ts +7 -0
  409. package/dist/core/review/birth.js +23 -0
  410. package/dist/core/review/comments.d.ts +69 -0
  411. package/dist/core/review/comments.js +295 -0
  412. package/dist/core/review/companion.d.ts +35 -0
  413. package/dist/core/review/companion.js +205 -0
  414. package/dist/core/review/document.d.ts +27 -0
  415. package/dist/core/review/document.js +138 -0
  416. package/dist/core/review/project.d.ts +13 -0
  417. package/dist/core/review/project.js +58 -0
  418. package/dist/core/review/realize.d.ts +19 -0
  419. package/dist/core/review/realize.js +217 -0
  420. package/dist/core/review/remap.d.ts +24 -0
  421. package/dist/core/review/remap.js +156 -0
  422. package/dist/core/review/signal.d.ts +5 -0
  423. package/dist/core/review/signal.js +31 -0
  424. package/dist/core/review/stage.d.ts +26 -0
  425. package/dist/core/review/stage.js +184 -0
  426. package/dist/core/review/store.d.ts +46 -0
  427. package/dist/core/review/store.js +163 -0
  428. package/dist/core/review/ticket-filter.d.ts +17 -0
  429. package/dist/core/review/ticket-filter.js +32 -0
  430. package/dist/core/review/types.d.ts +130 -0
  431. package/dist/core/review/types.js +23 -0
  432. package/dist/core/runtime/__tests__/session-visibility.test.d.ts +1 -0
  433. package/dist/core/runtime/__tests__/session-visibility.test.js +119 -0
  434. package/dist/core/runtime/bearings.d.ts +5 -17
  435. package/dist/core/runtime/bearings.js +88 -71
  436. package/dist/core/runtime/broker-protocol.d.ts +31 -8
  437. package/dist/core/runtime/broker.d.ts +15 -1
  438. package/dist/core/runtime/broker.js +573 -141
  439. package/dist/core/runtime/canvas-extensions.d.ts +4 -5
  440. package/dist/core/runtime/canvas-extensions.js +5 -5
  441. package/dist/core/runtime/front-door.js +4 -4
  442. package/dist/core/runtime/headless-pi.d.ts +24 -0
  443. package/dist/core/runtime/headless-pi.js +152 -0
  444. package/dist/core/runtime/kickoff.d.ts +1 -5
  445. package/dist/core/runtime/kickoff.js +1 -5
  446. package/dist/core/runtime/model-registry.d.ts +3 -0
  447. package/dist/core/runtime/model-registry.js +13 -0
  448. package/dist/core/runtime/naming-persist.d.ts +4 -0
  449. package/dist/core/runtime/naming-persist.js +27 -1
  450. package/dist/core/runtime/naming.d.ts +17 -12
  451. package/dist/core/runtime/naming.js +91 -140
  452. package/dist/core/runtime/node-read.js +19 -10
  453. package/dist/core/runtime/nodes.d.ts +9 -14
  454. package/dist/core/runtime/nodes.js +3 -53
  455. package/dist/core/runtime/package-health.d.ts +6 -21
  456. package/dist/core/runtime/package-health.js +12 -123
  457. package/dist/core/runtime/placement-tmux.d.ts +2 -1
  458. package/dist/core/runtime/placement-tmux.js +6 -4
  459. package/dist/core/runtime/placement.d.ts +1 -0
  460. package/dist/core/runtime/placement.js +4 -8
  461. package/dist/core/runtime/promote.js +3 -0
  462. package/dist/core/runtime/recap.d.ts +2 -7
  463. package/dist/core/runtime/recap.js +35 -69
  464. package/dist/core/runtime/revive.js +34 -77
  465. package/dist/core/runtime/session-cycles.d.ts +4 -0
  466. package/dist/core/runtime/session-cycles.js +14 -1
  467. package/dist/core/runtime/session-visibility.d.ts +37 -0
  468. package/dist/core/runtime/session-visibility.js +83 -0
  469. package/dist/core/runtime/spawn.js +10 -22
  470. package/dist/core/runtime/stop-guard.d.ts +1 -1
  471. package/dist/core/runtime/stop-guard.js +18 -13
  472. package/dist/core/runtime/surface-bg.js +1 -1
  473. package/dist/core/runtime/tmux-chrome.d.ts +1 -1
  474. package/dist/core/runtime/tmux-chrome.js +1 -1
  475. package/dist/core/runtime/tmux.d.ts +35 -7
  476. package/dist/core/runtime/tmux.js +439 -62
  477. package/dist/core/runtime/tool-group-summary.d.ts +19 -0
  478. package/dist/core/runtime/tool-group-summary.js +88 -0
  479. package/dist/core/scope.d.ts +2 -7
  480. package/dist/core/scope.js +9 -35
  481. package/dist/core/substrate/schema.d.ts +2 -2
  482. package/dist/core/termrender/code-doc.d.ts +12 -0
  483. package/dist/core/termrender/code-doc.js +92 -0
  484. package/dist/core/termrender/display.d.ts +12 -0
  485. package/dist/core/termrender/display.js +19 -0
  486. package/dist/core/termrender/termrender.d.ts +73 -0
  487. package/dist/core/termrender/termrender.js +795 -0
  488. package/dist/core/termrender/version.d.ts +1 -0
  489. package/dist/core/termrender/version.js +1 -0
  490. package/dist/core/tui/terminal.js +1 -2
  491. package/dist/core/user-settings.d.ts +71 -3
  492. package/dist/core/user-settings.js +83 -1
  493. package/dist/daemon/api/__tests__/inbox.test.js +44 -133
  494. package/dist/daemon/api/handlers/human.d.ts +0 -10
  495. package/dist/daemon/api/handlers/human.js +104 -702
  496. package/dist/daemon/api/handlers/inbox.js +113 -142
  497. package/dist/daemon/api/handlers/review-comments.d.ts +2 -0
  498. package/dist/daemon/api/handlers/review-comments.js +500 -0
  499. package/dist/daemon/api/handlers/reviews.d.ts +2 -0
  500. package/dist/daemon/api/handlers/reviews.js +287 -0
  501. package/dist/daemon/api/map.d.ts +22 -7
  502. package/dist/daemon/api/map.js +159 -11
  503. package/dist/daemon/api/server.js +4 -0
  504. package/dist/daemon/cron-run.js +6 -9
  505. package/dist/daemon/crtrd.js +17 -33
  506. package/dist/daemon/fleet.d.ts +1 -8
  507. package/dist/daemon/fleet.js +4 -30
  508. package/dist/daemon/human/finish.d.ts +14 -0
  509. package/dist/daemon/human/finish.js +115 -0
  510. package/dist/daemon/human/sweep.d.ts +6 -0
  511. package/dist/daemon/human/sweep.js +49 -0
  512. package/dist/daemon/messaging/node-message.d.ts +18 -0
  513. package/dist/daemon/messaging/node-message.js +70 -0
  514. package/dist/daemon/review/comment-notify.d.ts +26 -0
  515. package/dist/daemon/review/comment-notify.js +107 -0
  516. package/dist/daemon/review/deliver.d.ts +7 -0
  517. package/dist/daemon/review/deliver.js +56 -0
  518. package/dist/daemon/review/finish.d.ts +22 -0
  519. package/dist/daemon/review/finish.js +254 -0
  520. package/dist/daemon/review/sweep.d.ts +11 -0
  521. package/dist/daemon/review/sweep.js +120 -0
  522. package/dist/index.d.ts +2 -0
  523. package/dist/index.js +2 -2
  524. package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +42 -64
  525. package/dist/pi-extensions/__tests__/canvas-recap.test.d.ts +1 -0
  526. package/dist/pi-extensions/__tests__/canvas-recap.test.js +159 -0
  527. package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +16 -38
  528. package/dist/pi-extensions/__tests__/canvas-structured-output.test.d.ts +1 -0
  529. package/dist/pi-extensions/__tests__/canvas-structured-output.test.js +63 -0
  530. package/dist/pi-extensions/canvas-context-intro.d.ts +8 -11
  531. package/dist/pi-extensions/canvas-context-intro.js +50 -104
  532. package/dist/pi-extensions/canvas-recap.d.ts +15 -2
  533. package/dist/pi-extensions/canvas-recap.js +192 -69
  534. package/dist/pi-extensions/canvas-review-boundary.d.ts +59 -0
  535. package/dist/pi-extensions/canvas-review-boundary.js +81 -0
  536. package/dist/pi-extensions/canvas-stophook.d.ts +3 -3
  537. package/dist/pi-extensions/canvas-stophook.js +78 -128
  538. package/dist/pi-extensions/canvas-structured-output.js +52 -8
  539. package/dist/shared/generated-context.d.ts +29 -4
  540. package/dist/shared/generated-context.js +122 -7
  541. package/dist/shared/tool-groups.d.ts +23 -0
  542. package/dist/shared/tool-groups.js +60 -0
  543. package/dist/shared/working-activity.d.ts +8 -0
  544. package/dist/shared/working-activity.js +26 -0
  545. package/dist/types.d.ts +36 -2
  546. package/dist/types.js +51 -0
  547. package/dist/web-client/assets/index-CCPaLeW7.css +2 -0
  548. package/dist/web-client/assets/index-kyOWsnZK.js +86 -0
  549. package/dist/web-client/index.html +2 -2
  550. package/dist/web-client/sw.js +1 -1
  551. package/docs/compat/hearth-crtr-v4.md +2 -2
  552. package/docs/compat/hearth-crtr-v5.md +4 -2
  553. package/docs/public-api.md +3 -3
  554. package/package.json +21 -14
  555. package/runtime.lock.json +4661 -1453
  556. package/scripts/postinstall.mjs +15 -2
  557. package/dist/builtin-pi-packages/pi-mode-switch/README.md +0 -36
  558. package/dist/builtin-pi-packages/pi-mode-switch/bin/mode +0 -36
  559. package/dist/builtin-pi-packages/pi-mode-switch/extensions/index.ts +0 -449
  560. package/dist/builtin-pi-packages/pi-mode-switch/package.json +0 -14
  561. package/dist/builtin-pi-packages/pi-mode-switch/tsconfig.json +0 -9
  562. package/dist/builtin-views/_lib/states.mjs +0 -161
  563. package/dist/builtin-views/canvas/core.mjs +0 -657
  564. package/dist/builtin-views/canvas/text.mjs +0 -58
  565. package/dist/builtin-views/canvas/tui.mjs +0 -168
  566. package/dist/builtin-views/canvas/web.jsx +0 -121
  567. package/dist/builtin-views/chat/core.mjs +0 -684
  568. package/dist/builtin-views/chat/text.mjs +0 -101
  569. package/dist/builtin-views/chat/tui.mjs +0 -360
  570. package/dist/builtin-views/chat/web.jsx +0 -362
  571. package/dist/builtin-views/git-pr/core.mjs +0 -673
  572. package/dist/builtin-views/git-pr/text.mjs +0 -84
  573. package/dist/builtin-views/git-pr/tui.mjs +0 -301
  574. package/dist/builtin-views/git-pr/web.jsx +0 -216
  575. package/dist/builtin-views/inbox/_lib/render.mjs +0 -175
  576. package/dist/builtin-views/inbox/core.mjs +0 -1273
  577. package/dist/builtin-views/inbox/text.mjs +0 -73
  578. package/dist/builtin-views/inbox/tui.mjs +0 -314
  579. package/dist/builtin-views/inbox/web.jsx +0 -188
  580. package/dist/builtin-views/linkedin/core.mjs +0 -906
  581. package/dist/builtin-views/linkedin/text.mjs +0 -69
  582. package/dist/builtin-views/linkedin/tui.mjs +0 -413
  583. package/dist/builtin-views/linkedin/web.jsx +0 -206
  584. package/dist/builtin-views/prompt-review/core.mjs +0 -735
  585. package/dist/builtin-views/prompt-review/text.mjs +0 -15
  586. package/dist/builtin-views/prompt-review/tui.mjs +0 -196
  587. package/dist/builtin-views/prompt-review/web.jsx +0 -484
  588. package/dist/builtin-views/workspace-sidebar/__tests__/core.test.js +0 -50
  589. package/dist/builtin-views/workspace-sidebar/__tests__/core.test.ts +0 -53
  590. package/dist/builtin-views/workspace-sidebar/core.mjs +0 -670
  591. package/dist/builtin-views/workspace-sidebar/text.mjs +0 -53
  592. package/dist/builtin-views/workspace-sidebar/tui.mjs +0 -141
  593. package/dist/builtin-views/workspace-sidebar/web.jsx +0 -109
  594. package/dist/clients/attach/session/mode.d.ts +0 -25
  595. package/dist/clients/attach/session/mode.js +0 -97
  596. package/dist/commands/sys/promptstudio.d.ts +0 -2
  597. package/dist/commands/sys/promptstudio.js +0 -65
  598. package/dist/commands/view-cycle.d.ts +0 -2
  599. package/dist/commands/view-cycle.js +0 -130
  600. package/dist/commands/view-list.d.ts +0 -2
  601. package/dist/commands/view-list.js +0 -66
  602. package/dist/commands/view-new.d.ts +0 -2
  603. package/dist/commands/view-new.js +0 -74
  604. package/dist/commands/view-pick.d.ts +0 -6
  605. package/dist/commands/view-pick.js +0 -129
  606. package/dist/commands/view-run.d.ts +0 -2
  607. package/dist/commands/view-run.js +0 -214
  608. package/dist/commands/view.d.ts +0 -2
  609. package/dist/commands/view.js +0 -31
  610. package/dist/commands/workspace.d.ts +0 -2
  611. package/dist/commands/workspace.js +0 -165
  612. package/dist/core/__tests__/chat-view-reconnect.test.js +0 -105
  613. package/dist/core/__tests__/full/consult-startup-failure.test.js +0 -124
  614. package/dist/core/canvas/__tests__/human-work-outbox.test.js +0 -123
  615. package/dist/core/canvas/human-work-outbox.d.ts +0 -73
  616. package/dist/core/canvas/human-work-outbox.js +0 -261
  617. package/dist/core/command-plugins/store.d.ts +0 -16
  618. package/dist/core/command-plugins/store.js +0 -64
  619. package/dist/core/keybindings/__tests__/bespoke-consumers.test.js +0 -40
  620. package/dist/core/runtime/fault-recovery-nudge.d.ts +0 -3
  621. package/dist/core/runtime/fault-recovery-nudge.js +0 -3
  622. package/dist/core/tui/__tests__/host-keybindings.test.js +0 -113
  623. package/dist/core/tui/host.d.ts +0 -66
  624. package/dist/core/view/__tests__/transport-cache.test.js +0 -62
  625. package/dist/core/view/bridge.d.ts +0 -10
  626. package/dist/core/view/bridge.js +0 -31
  627. package/dist/core/view/chrome.d.ts +0 -9
  628. package/dist/core/view/chrome.js +0 -22
  629. package/dist/core/view/contract.d.ts +0 -205
  630. package/dist/core/view/contract.js +0 -23
  631. package/dist/core/view/loader.d.ts +0 -31
  632. package/dist/core/view/loader.js +0 -188
  633. package/dist/core/view/stream-local.d.ts +0 -3
  634. package/dist/core/view/stream-local.js +0 -231
  635. package/dist/core/view/transport-cache.d.ts +0 -8
  636. package/dist/core/view/transport-cache.js +0 -38
  637. package/dist/core/view/transport-local.d.ts +0 -7
  638. package/dist/core/view/transport-local.js +0 -83
  639. package/dist/core/view/transport-remote.d.ts +0 -2
  640. package/dist/core/view/transport.d.ts +0 -10
  641. package/dist/core/view/transport.js +0 -15
  642. package/dist/pi-extensions/naming-tool.d.ts +0 -24
  643. package/dist/pi-extensions/naming-tool.js +0 -67
  644. package/dist/prompts/view.d.ts +0 -7
  645. package/dist/prompts/view.js +0 -172
  646. package/dist/web/ViewChrome.d.ts +0 -7
  647. package/dist/web/ViewChrome.js +0 -28
  648. package/dist/web/ViewPane.d.ts +0 -39
  649. package/dist/web/ViewPane.js +0 -48
  650. package/dist/web/index.d.ts +0 -8
  651. package/dist/web/index.js +0 -22
  652. package/dist/web/runtime.d.ts +0 -39
  653. package/dist/web/runtime.js +0 -232
  654. package/dist/web/states.d.ts +0 -24
  655. package/dist/web/states.js +0 -24
  656. package/dist/web/transport-http.d.ts +0 -5
  657. package/dist/web/transport-http.js +0 -34
  658. package/dist/web/transport-stream.d.ts +0 -3
  659. package/dist/web/transport-stream.js +0 -204
  660. package/dist/web-client/assets/index-U_NZ66VE.js +0 -79
  661. package/dist/web-client/assets/index-aaeu4adv.css +0 -2
  662. /package/dist/{builtin-views/workspace-sidebar/__tests__/core.test.d.ts → clients/attach/__tests__/file-review-focus.test.d.ts} +0 -0
  663. /package/dist/clients/attach/__tests__/{visible-paths.test.d.ts → group-activity.test.d.ts} +0 -0
  664. /package/dist/{core/__tests__/chat-view-reconnect.test.d.ts → clients/attach/__tests__/input-controller-extension-command.test.d.ts} +0 -0
  665. /package/dist/{core/__tests__/full/consult-startup-failure.test.d.ts → clients/attach/__tests__/session-identity-refresh.test.d.ts} +0 -0
  666. /package/dist/{core/canvas/__tests__/human-work-outbox.test.d.ts → clients/inbox/__tests__/inbox-controller.test.d.ts} +0 -0
  667. /package/dist/{core/keybindings/__tests__/bespoke-consumers.test.d.ts → clients/inbox/__tests__/mount-panel.test.d.ts} +0 -0
  668. /package/dist/{core/tui/__tests__/host-keybindings.test.d.ts → clients/inbox/review/__tests__/editor-roundtrip.test.d.ts} +0 -0
  669. /package/dist/{core/view/__tests__/transport-cache.test.d.ts → clients/inbox/review/__tests__/remap.test.d.ts} +0 -0
  670. /package/dist/{core/view/__tests__/transport-remote.test.d.ts → clients/inbox/review/__tests__/review-core.test.d.ts} +0 -0
@@ -20,6 +20,7 @@ import { join } from 'node:path';
20
20
  import { buildLaunchSpec, isProviderPinnedModelToken, normalizeModel } from '../runtime/launch.js';
21
21
  import { defaultModelLaddersConfig } from '../../types.js';
22
22
  import { persistDormantModel } from '../runtime/model-swap.js';
23
+ import { promote } from '../runtime/promote.js';
23
24
  import { createNode, getNode } from '../canvas/canvas.js';
24
25
  import { closeDb } from '../canvas/db.js';
25
26
  const ladders = defaultModelLaddersConfig();
@@ -102,6 +103,19 @@ test('an explicit concrete pin survives a config/lifecycle rebuild whose portabl
102
103
  // Direction 2: a deliberately TIER-run (unpinned) node must NOT be wrongly
103
104
  // re-pinned when a subsequent rebuild/persist normalizes its override to the
104
105
  // concrete cell it happens to share bytes with.
106
+ test('promotion preserves the exact-model contract required to revive a private human-work fork', () => {
107
+ const exactModel = 'openai-codex/gpt-5.6-sol:max';
108
+ createNode(node('exact-node', {
109
+ model_override: exactModel,
110
+ launch: { extensions: [], env: {}, model: exactModel, modelProviderPinned: true, modelExact: true },
111
+ }));
112
+ promote('exact-node');
113
+ const after = getNode('exact-node');
114
+ assert.equal(after?.mode, 'orchestrator');
115
+ assert.equal(after?.launch?.model, exactModel);
116
+ assert.equal(after?.launch?.modelProviderPinned, true);
117
+ assert.equal(after?.launch?.modelExact, true, 'promotion must not create a launch recipe that reviveNode rejects');
118
+ });
105
119
  test('a deliberately tier-run node is not wrongly re-pinned when a rebuild or persist normalizes its override to the concrete cell', () => {
106
120
  // The buildLaunchSpec seam: an explicit `false` carried through a rebuild wins
107
121
  // over a concrete override that would otherwise read as pinned.
@@ -0,0 +1,100 @@
1
+ import { test, before, after } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { mkdtempSync, rmSync } from 'node:fs';
4
+ import { tmpdir } from 'node:os';
5
+ import { join } from 'node:path';
6
+ import { DatabaseSync } from 'node:sqlite';
7
+ import { closeDb, MIGRATIONS, openDb } from '../canvas/db.js';
8
+ import { canvasDbPath, ensureHome } from '../canvas/paths.js';
9
+ let home;
10
+ const legacyOutboxTable = ['consult', 'outbox'].join('_');
11
+ function userVersion(db) {
12
+ return db.prepare('PRAGMA user_version').get().user_version;
13
+ }
14
+ function names(db, type) {
15
+ return new Set(db.prepare('SELECT name FROM sqlite_master WHERE type = ?').all(type)
16
+ .map((row) => row.name));
17
+ }
18
+ function columns(db, table) {
19
+ return db.prepare(`PRAGMA table_info(${table})`).all().map((row) => row.name);
20
+ }
21
+ function migrateToVersion(db, target) {
22
+ for (let version = userVersion(db); version < target; version += 1) {
23
+ MIGRATIONS[version](db);
24
+ db.exec(`PRAGMA user_version = ${version + 1}`);
25
+ }
26
+ }
27
+ before(() => {
28
+ home = mkdtempSync(join(tmpdir(), 'crtr-phase4-review-migration-'));
29
+ process.env['CRTR_HOME'] = home;
30
+ });
31
+ after(() => {
32
+ closeDb();
33
+ rmSync(home, { recursive: true, force: true });
34
+ delete process.env['CRTR_HOME'];
35
+ });
36
+ test('v27/v28 review schema, v29 anchor state, and v30 outbox drop migrate fresh and from v28', () => {
37
+ const db = openDb();
38
+ assert.equal(userVersion(db), 30);
39
+ assert.equal(names(db, 'table').has(legacyOutboxTable), false);
40
+ const tables = names(db, 'table');
41
+ assert.deepEqual(['reviews', 'review_comments', 'review_comment_events'].every((name) => tables.has(name)), true);
42
+ assert.deepEqual(columns(db, 'reviews'), [
43
+ 'review_id', 'idempotency_key', 'origin_kind', 'origin_node_id', 'bridge_node_id',
44
+ 'companion_node_id', 'review_dir', 'file', 'origin_session_file', 'origin_session_id',
45
+ 'origin_leaf_id', 'model_spec', 'branch_file', 'baseline_sha256', 'baseline_bytes',
46
+ 'anchor_base_sha256', 'anchor_base_bytes', 'state', 'created', 'captured_at', 'opened_at',
47
+ 'output_path', 'subtitle', 'approved_at', 'approved_sha256', 'changed', 'comments_total',
48
+ 'comments_unresolved', 'result_json', 'canceled_at', 'cancel_reason', 'cancel_actor',
49
+ 'delivery_entry_id', 'delivered_at', 'delivery_error', 'projected_at', 'projection_error',
50
+ ]);
51
+ assert.deepEqual(columns(db, 'review_comments'), [
52
+ 'comment_id', 'review_id', 'status', 'revision', 'text', 'anchor_line', 'anchor_end_line',
53
+ 'anchor_col_start', 'anchor_col_end', 'anchor_quote', 'anchor_line_text', 'anchor_doc_hash',
54
+ 'author_kind', 'author_node_id', 'updated_by_kind', 'updated_by_node_id', 'created_at',
55
+ 'updated_at', 'resolved_at', 'deleted_at', 'created_seq', 'anchor_state',
56
+ ]);
57
+ assert.deepEqual(columns(db, 'review_comment_events'), [
58
+ 'seq', 'review_id', 'comment_id', 'kind', 'state_change', 'revision_after', 'actor_kind',
59
+ 'actor_node_id', 'at', 'operation_id', 'payload',
60
+ ]);
61
+ const indexes = names(db, 'index');
62
+ for (const index of [
63
+ 'idx_reviews_state', 'idx_reviews_origin', 'idx_reviews_file', 'idx_reviews_bridge',
64
+ 'idx_review_comments_review_line_seq', 'idx_review_comments_review_status',
65
+ 'idx_review_comment_events_review_seq', 'idx_review_comment_events_comment_seq',
66
+ ]) {
67
+ assert.equal(indexes.has(index), true, `missing ${index}`);
68
+ }
69
+ closeDb();
70
+ rmSync(home, { recursive: true, force: true });
71
+ ensureHome();
72
+ const raw = new DatabaseSync(canvasDbPath());
73
+ migrateToVersion(raw, 28);
74
+ assert.equal(userVersion(raw), 28);
75
+ raw.prepare(`
76
+ INSERT INTO review_comments
77
+ (comment_id, review_id, status, revision, text, anchor_line, anchor_end_line,
78
+ anchor_line_text, anchor_doc_hash, author_kind, updated_by_kind, created_at,
79
+ updated_at, created_seq)
80
+ VALUES ('v28-comment', 'v28-review', 'open', 1, 'existing', 1, 1,
81
+ 'source', 'hash', 'human', 'human', '2026-01-01T00:00:00.000Z',
82
+ '2026-01-01T00:00:00.000Z', 1)
83
+ `).run();
84
+ migrateToVersion(raw, 29);
85
+ assert.equal(userVersion(raw), 29);
86
+ raw.prepare(`
87
+ INSERT INTO ${legacyOutboxTable}
88
+ (node_id, ticket_node_id, request_id, work_kind, status, updated_at)
89
+ VALUES ('legacy-outbox-node', 'legacy-ticket-node', 'legacy-request', 'follow_up', 'pending', '2026-01-01T00:00:00.000Z')
90
+ `).run();
91
+ raw.close();
92
+ const migrated = openDb();
93
+ assert.equal(userVersion(migrated), 30);
94
+ assert.equal(names(migrated, 'table').has('reviews'), true);
95
+ assert.equal(names(migrated, 'table').has('review_comments'), true);
96
+ assert.equal(names(migrated, 'table').has('review_comment_events'), true);
97
+ assert.equal(migrated.prepare("SELECT anchor_state FROM review_comments WHERE comment_id = 'v28-comment'").get().anchor_state, 'live');
98
+ assert.throws(() => migrated.prepare("UPDATE review_comments SET anchor_state = 'invalid' WHERE comment_id = 'v28-comment'").run());
99
+ assert.equal(names(migrated, 'table').has(legacyOutboxTable), false);
100
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,143 @@
1
+ import { test, before, after, beforeEach } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { existsSync, mkdtempSync, rmSync } from 'node:fs';
4
+ import { tmpdir } from 'node:os';
5
+ import { join } from 'node:path';
6
+ import { closeDb, openDb } from '../canvas/db.js';
7
+ import { approveReviewLocked, bindBridgeNode, cancelReviewRow, commitBindingReview, getReview, getReviewByBridge, getReviewByCompanion, listReviews, markDelivered, markDeliveryError, markProjected, markProjectionError, openReviewRow, readAnchorBase, } from '../review/store.js';
8
+ let home;
9
+ function row(overrides = {}) {
10
+ return {
11
+ review_id: 'review-one',
12
+ idempotency_key: 'request-one',
13
+ origin_kind: 'inline',
14
+ origin_node_id: 'origin-one',
15
+ bridge_node_id: null,
16
+ companion_node_id: 'companion-one',
17
+ review_dir: '/tmp/review-one',
18
+ file: '/tmp/does-not-exist.txt',
19
+ origin_session_file: '/tmp/origin.jsonl',
20
+ origin_session_id: 'session-one',
21
+ origin_leaf_id: 'leaf-one',
22
+ model_spec: 'anthropic/claude:test',
23
+ branch_file: '/tmp/review-one/branch-point.jsonl',
24
+ baseline_sha256: 'baseline-hash',
25
+ baseline_bytes: 3,
26
+ anchor_base_sha256: 'anchor-hash',
27
+ anchor_base_bytes: Buffer.from('abc'),
28
+ state: 'binding',
29
+ created: '2026-01-01T00:00:00.000Z',
30
+ captured_at: '2026-01-01T00:00:00.000Z',
31
+ opened_at: null,
32
+ output_path: null,
33
+ subtitle: null,
34
+ approved_at: null,
35
+ approved_sha256: null,
36
+ changed: null,
37
+ comments_total: null,
38
+ comments_unresolved: null,
39
+ result_json: null,
40
+ canceled_at: null,
41
+ cancel_reason: null,
42
+ cancel_actor: null,
43
+ delivery_entry_id: null,
44
+ delivered_at: null,
45
+ delivery_error: null,
46
+ projected_at: null,
47
+ projection_error: null,
48
+ ...overrides,
49
+ };
50
+ }
51
+ before(() => {
52
+ home = mkdtempSync(join(tmpdir(), 'crtr-phase4-review-store-'));
53
+ process.env['CRTR_HOME'] = home;
54
+ });
55
+ beforeEach(() => {
56
+ closeDb();
57
+ rmSync(home, { recursive: true, force: true });
58
+ });
59
+ after(() => {
60
+ closeDb();
61
+ rmSync(home, { recursive: true, force: true });
62
+ delete process.env['CRTR_HOME'];
63
+ });
64
+ test('reviews DAO owns idempotent binding and conditional lifecycle writes', () => {
65
+ const first = row();
66
+ assert.equal(existsSync(first.review_dir), false, 'the birth commit does not create filesystem state');
67
+ assert.deepEqual(commitBindingReview(first), { review_id: 'review-one', won: true });
68
+ assert.deepEqual(commitBindingReview(row({ review_id: 'review-two', companion_node_id: 'companion-two' })), {
69
+ review_id: 'review-one',
70
+ won: false,
71
+ });
72
+ assert.equal(listReviews({}).length, 0, 'binding rows are hidden from ordinary review lists');
73
+ assert.equal(getReview(first.review_id)?.source_missing, true, 'source_missing is computed from the current filesystem');
74
+ assert.equal(getReviewByCompanion(first.companion_node_id), null);
75
+ assert.equal(bindBridgeNode(first.review_id, 'bridge-one'), true);
76
+ assert.equal(bindBridgeNode(first.review_id, 'bridge-two'), false);
77
+ assert.equal(getReviewByBridge('bridge-one'), null, 'binding rows are not exposed by bridge lookup');
78
+ assert.equal(openReviewRow(first.review_id, '2026-01-01T00:01:00.000Z'), true);
79
+ assert.equal(openReviewRow(first.review_id, '2026-01-01T00:02:00.000Z'), false);
80
+ assert.equal(getReviewByBridge('bridge-one')?.review_id, first.review_id);
81
+ const db = openDb();
82
+ assert.equal(approveReviewLocked(db, {
83
+ review_id: first.review_id,
84
+ approved_at: '2026-01-01T00:03:00.000Z',
85
+ approved_sha256: 'approved-hash',
86
+ changed: true,
87
+ comments_total: 2,
88
+ comments_unresolved: 1,
89
+ result_json: '{"approved":true}',
90
+ }), true);
91
+ assert.equal(approveReviewLocked(db, {
92
+ review_id: first.review_id,
93
+ approved_at: '2026-01-01T00:04:00.000Z',
94
+ approved_sha256: 'approved-hash-2',
95
+ changed: false,
96
+ comments_total: 0,
97
+ comments_unresolved: 0,
98
+ result_json: '{}',
99
+ }), false, 'a second approval cannot win the conditional write');
100
+ markProjectionError(first.review_id, 'temporary projection failure');
101
+ markProjected(first.review_id, '2026-01-01T00:05:00.000Z');
102
+ markDeliveryError(first.review_id, 'temporary delivery failure');
103
+ markDelivered(first.review_id, 'receipt-one', '2026-01-01T00:06:00.000Z');
104
+ const approved = getReview(first.review_id);
105
+ assert.equal(approved?.projection_error, null);
106
+ assert.equal(approved?.delivery_error, null);
107
+ assert.equal(approved?.delivered_at, '2026-01-01T00:06:00.000Z');
108
+ assert.deepEqual(readAnchorBase(first.review_id), { bytes: Buffer.from('abc'), sha256: 'anchor-hash' });
109
+ });
110
+ test('stale ledger errors cannot overwrite recorded success', () => {
111
+ const review = row({ review_id: 'review-ledgers', idempotency_key: 'request-ledgers', companion_node_id: 'companion-ledgers' });
112
+ commitBindingReview(review);
113
+ markProjected(review.review_id, '2026-01-01T00:05:00.000Z');
114
+ markProjectionError(review.review_id, 'stale projection failure');
115
+ markDelivered(review.review_id, 'receipt-ledgers', '2026-01-01T00:06:00.000Z');
116
+ markDeliveryError(review.review_id, 'stale delivery failure');
117
+ const recorded = getReview(review.review_id);
118
+ assert.equal(recorded?.projected_at, '2026-01-01T00:05:00.000Z');
119
+ assert.equal(recorded?.projection_error, null);
120
+ assert.equal(recorded?.delivered_at, '2026-01-01T00:06:00.000Z');
121
+ assert.equal(recorded?.delivery_error, null);
122
+ });
123
+ test('approval cannot win after cancellation and companion ids remain unique', () => {
124
+ const canceled = row({ review_id: 'review-canceled', idempotency_key: 'request-canceled', companion_node_id: 'companion-canceled' });
125
+ commitBindingReview(canceled);
126
+ assert.equal(openReviewRow(canceled.review_id, '2026-01-01T00:59:00.000Z'), true);
127
+ assert.equal(cancelReviewRow(canceled.review_id, { at: '2026-01-01T01:00:00.000Z', reason: 'not needed', actor: 'human' }), true);
128
+ assert.equal(approveReviewLocked(openDb(), {
129
+ review_id: canceled.review_id,
130
+ approved_at: '2026-01-01T01:01:00.000Z',
131
+ approved_sha256: 'hash',
132
+ changed: false,
133
+ comments_total: 0,
134
+ comments_unresolved: 0,
135
+ result_json: '{}',
136
+ }), false);
137
+ assert.throws(() => commitBindingReview(row({
138
+ review_id: 'review-duplicate-companion',
139
+ idempotency_key: 'request-duplicate-companion',
140
+ companion_node_id: 'companion-canceled',
141
+ state: 'binding',
142
+ })), /UNIQUE constraint failed: reviews\.companion_node_id/);
143
+ });
@@ -2,7 +2,7 @@
2
2
  // `remoteCanvas.targets.<name>` (`../config.ts`'s normalizeRemoteCanvasTarget,
3
3
  // exercised through the public readConfig/updateConfig surface — it isn't
4
4
  // exported directly) plus `resolveRemoteCanvasTarget`'s error paths
5
- // (`../view/remote-canvas-target.ts`). The one load-bearing invariant here:
5
+ // (`../remote-canvas-target.ts`). The one load-bearing invariant here:
6
6
  // an error message about a missing/unknown target must never leak a token
7
7
  // substring, even though it's guiding the agent toward the exact command
8
8
  // that would set one.
@@ -13,7 +13,7 @@ import { tmpdir } from 'node:os';
13
13
  import { join } from 'node:path';
14
14
  import { readConfig, updateConfig } from '../config.js';
15
15
  import { setRelayToken } from '../secrets.js';
16
- import { getRemoteCanvasTarget, resolveRemoteCanvasTarget } from '../view/remote-canvas-target.js';
16
+ import { getRemoteCanvasTarget, resolveRemoteCanvasTarget } from '../remote-canvas-target.js';
17
17
  let home;
18
18
  let realHome;
19
19
  before(() => {
@@ -33,16 +33,15 @@ import { tmpdir } from 'node:os';
33
33
  import { join, dirname } from 'node:path';
34
34
  import { fileURLToPath } from 'node:url';
35
35
  import { spawn, spawnSync } from 'node:child_process';
36
- import { createNode, getNode, subscribe, updateNode, clearPid } from '../canvas/canvas.js';
37
- import { armFollowUpWork } from '../canvas/human-work-outbox.js';
36
+ import { createNode, getNode, subscribe, updateNode } from '../canvas/canvas.js';
38
37
  import { armCron, hasPendingCancelOnWakeCron } from '../canvas/crons.js';
39
38
  import { readInboxSince } from '../feed/inbox.js';
40
39
  import { closeDb } from '../canvas/db.js';
41
40
  import { buildPiArgv, CANVAS_EXTENSIONS } from '../runtime/launch.js';
42
- import { CANVAS_INBOX_WATCHER_PATH } from '../runtime/canvas-extensions.js';
41
+ import { CANVAS_CONTEXT_INTRO_PATH, CANVAS_INBOX_WATCHER_PATH, CANVAS_REVIEW_BOUNDARY_PATH, CANVAS_STOPHOOK_PATH, } from '../runtime/canvas-extensions.js';
43
42
  import { headlessBrokerHost } from '../runtime/host.js';
44
43
  import { resumeArgs, reviveNode } from '../runtime/revive.js';
45
- import { REVIVE_KICKOFF_SENTINEL, writeGoal } from '../runtime/kickoff.js';
44
+ import { REVIVE_KICKOFF_SENTINEL, readGoal, writeGoal } from '../runtime/kickoff.js';
46
45
  import { seedRoadmap } from '../runtime/roadmap.js';
47
46
  import { InputError } from '../io.js';
48
47
  import { createHarness } from './helpers/harness.js';
@@ -193,6 +192,8 @@ test('buildPiArgv restores mandatory canvas extensions when persisted launch met
193
192
  assert.equal(seen.get(ext), 1, `mandatory extension present exactly once: ${ext}`);
194
193
  }
195
194
  assert.deepEqual(extensions.slice(0, required.size), mandatory, 'mandatory extensions are loaded first and once');
195
+ assert.equal(extensions.indexOf(CANVAS_STOPHOOK_PATH), extensions.indexOf(CANVAS_REVIEW_BOUNDARY_PATH) + 1, 'review boundary runs immediately before the stophook commits a companion bind');
196
+ assert.ok(extensions.indexOf(CANVAS_CONTEXT_INTRO_PATH) > extensions.indexOf(CANVAS_REVIEW_BOUNDARY_PATH), 'review boundary still precedes fork bearings');
196
197
  });
197
198
  test('buildPiArgv replaces the previous mandatory prefix with the current canvas hooks', () => {
198
199
  const historicalNames = [
@@ -424,66 +425,34 @@ test('reviveNode fresh-fallback clears the stale session identity (Major-3: no p
424
425
  await h.dispose();
425
426
  }
426
427
  });
427
- test('private fork revival uses immutable provenance once, then only its bound child session', () => {
428
+ test('an unbound ordinary fork replays its immutable source with and without its goal', () => {
428
429
  const originalLaunch = headlessBrokerHost.launch;
429
- // No harness here — bind a standalone in-process fleet so reviveNode's
430
- // boundFleet() resolves; the launch stub below registers with it exactly as
431
- // the real host does (registration is the launch seam's contract).
432
430
  const tf = bindTestFleet();
433
- const privateId = 'private-fork';
434
- const forkFile = join(home, 'private-source.jsonl');
435
- const childFile = join(home, 'private-child.jsonl');
436
- const exactModel = 'openai-codex/gpt-5.3-codex:xhigh';
437
- const kickoff = 'private immutable kickoff';
431
+ const forkFile = join(home, 'ordinary-fork-source.jsonl');
438
432
  const launches = [];
439
433
  try {
440
434
  mkdirSync(home, { recursive: true });
441
435
  writeFileSync(forkFile, '{}\n', 'utf8');
442
- createNode(node(privateId, {
443
- kind: 'general',
444
- cwd: home,
445
- fork_from: forkFile,
446
- human_work_target: { work_kind: 'follow_up', dir: '/ticket', request_id: 'request-1', root: '/root' },
447
- private_fork_phase: 'fork_pending',
448
- private_fork_recipe: { kickoff },
449
- launch: { model: exactModel, modelExact: true, tools: [], extensions: [], env: {} },
450
- }));
451
- // reviveNode's locked launch-gate (Major-3) requires a still-armed matching
452
- // human-work row before it will launch a private fork — arm one here to
453
- // mirror the real handleFollowUpStart/handleVisualStart claim it recheck.
454
- armFollowUpWork({ nodeId: privateId, dir: '/ticket', root: '/root', requestId: 'request-1' });
455
436
  headlessBrokerHost.launch = (nodeId, inv) => {
456
- launches.push({ argv: [...inv.argv], env: { ...inv.env } });
437
+ launches.push([...inv.argv]);
457
438
  const handle = { pid: disposableLivePid(), exited: new Promise(() => { }) };
458
- // Mirror the real host contract: the entry IS liveness (design D-1).
459
439
  tf.fleet.register(nodeId, handle);
460
440
  return handle;
461
441
  };
462
- reviveNode(privateId, { resume: false });
463
- const pending = launches[0];
464
- const forkIndex = pending.argv.indexOf('--fork');
465
- assert.equal(pending.argv[forkIndex + 1], forkFile, 'pending claim forks exactly from immutable provenance');
466
- assert.equal(pending.argv.at(-1), kickoff, 'pending claim uses its persisted kickoff');
467
- assert.equal(pending.argv[pending.argv.indexOf('--model') + 1], exactModel, 'pending claim preserves its exact model');
468
- reviveNode(privateId, { resume: false });
469
- assert.equal(launches.length, 1, 'live first launch (a live fleet entry) makes a concurrent revive a no-op');
470
- writeFileSync(childFile, '{}\n', 'utf8');
471
- // Model the first broker's death: its exit removes the fleet entry (and the
472
- // stophook clears the recorded pid) — the next revive may proceed.
473
- tf.fleet.forget(privateId);
474
- clearPid(privateId);
475
- updateNode(privateId, {
476
- pi_session_id: 'child-session',
477
- pi_session_file: childFile,
478
- private_fork_phase: 'session_started',
479
- });
480
- reviveNode(privateId, { resume: false });
481
- const started = launches[1];
482
- const sessionIndex = started.argv.indexOf('--session');
483
- assert.equal(started.argv[sessionIndex + 1], childFile, 'bound claim resumes only its writable child session');
484
- assert.equal(started.argv.includes('--fork'), false, 'bound claim never forks provenance again');
485
- assert.equal(started.argv.includes(kickoff), false, 'bound claim never replays its one-shot kickoff');
486
- assert.equal(started.argv.includes('--crtr-new-cycle'), false, 'bound claim ignores a no-resume request rather than cycling');
442
+ createNode(node('ordinary-fork-promptless', { cwd: home, fork_from: forkFile }));
443
+ reviveNode('ordinary-fork-promptless', { resume: true });
444
+ const promptless = launches[0];
445
+ const promptlessFork = promptless.indexOf('--fork');
446
+ assert.equal(promptless[promptlessFork + 1], forkFile, 'an unbound ordinary fork uses its immutable source');
447
+ assert.deepEqual(promptless, buildPiArgv(getNode('ordinary-fork-promptless'), { forkFrom: forkFile }).argv, 'a promptless fork has no trailing positional prompt');
448
+ createNode(node('ordinary-fork-goal', { cwd: home, fork_from: forkFile }));
449
+ writeGoal('ordinary-fork-goal', 'continue the forked task');
450
+ const goal = readGoal('ordinary-fork-goal');
451
+ reviveNode('ordinary-fork-goal', { resume: false });
452
+ const goalCarrying = launches[1];
453
+ const goalFork = goalCarrying.indexOf('--fork');
454
+ assert.equal(goalCarrying[goalFork + 1], forkFile, 'an unbound ordinary fork uses its immutable source regardless of requested resume mode');
455
+ assert.equal(goalCarrying.at(-1), goal, 'a goal-carrying fork preserves its exact goal as the trailing prompt');
487
456
  }
488
457
  finally {
489
458
  headlessBrokerHost.launch = originalLaunch;
@@ -19,6 +19,32 @@ function userMsg(id, parentId, text) {
19
19
  message: { role: 'user', content: text, timestamp: 0 },
20
20
  };
21
21
  }
22
+ function assistantMsg(id, parentId, content, stopReason, errorMessage) {
23
+ return {
24
+ type: 'message',
25
+ id,
26
+ parentId,
27
+ timestamp: new Date(0).toISOString(),
28
+ message: { role: 'assistant', content, stopReason, errorMessage, timestamp: 0 },
29
+ };
30
+ }
31
+ function cycleManager(entries, currentId) {
32
+ const byId = new Map(entries.map((entry) => [entry.id, entry]));
33
+ const branchFrom = (fromId) => {
34
+ const path = [];
35
+ let current = byId.get(fromId);
36
+ while (current) {
37
+ path.push(current);
38
+ current = current.parentId ? byId.get(current.parentId) : undefined;
39
+ }
40
+ return path.reverse();
41
+ };
42
+ return {
43
+ buildSessionContext: () => buildSessionContext(entries, currentId, byId),
44
+ getEntries: () => entries,
45
+ getBranch: (fromId) => branchFrom(fromId ?? currentId),
46
+ };
47
+ }
22
48
  test('cycleAwareMessages: a compacted prior cycle replays via buildSessionContext (one summary, no raw pre-compaction messages)', () => {
23
49
  // Cycle 0: m1 -> m2 -> [compaction c1, keeping from m2] -> m3 (cycle 0's leaf
24
50
  // at yield time). Cycle marker cy1 roots a FRESH branch (parentId: null, per
@@ -46,25 +72,9 @@ test('cycleAwareMessages: a compacted prior cycle replays via buildSessionContex
46
72
  };
47
73
  const m4 = userMsg('m4', 'cy1', 'cycle 1 live message');
48
74
  const entries = [m1, m2, c1, m3, cy1, m4];
49
- const byId = new Map(entries.map((e) => [e.id, e]));
50
- function branchFrom(fromId) {
51
- const path = [];
52
- let current = fromId ? byId.get(fromId) : undefined;
53
- while (current) {
54
- path.push(current);
55
- current = current.parentId ? byId.get(current.parentId) : undefined;
56
- }
57
- return path.reverse();
58
- }
59
75
  // Minimal fake mirroring the real SDK's SessionManager just enough to drive
60
- // cycleAwareMessages: buildSessionContext() (current leaf only, m4) plus the
61
- // tree accessors it reads (getEntries/getBranch).
62
- const sm = {
63
- buildSessionContext: () => buildSessionContext(entries, 'm4', byId),
64
- getEntries: () => entries,
65
- getBranch: (fromId) => branchFrom(fromId ?? 'm4'),
66
- };
67
- const out = cycleAwareMessages(sm);
76
+ // cycleAwareMessages: buildSessionContext() plus the tree accessors it reads.
77
+ const out = cycleAwareMessages(cycleManager(entries, 'm4'));
68
78
  const summaries = out.filter((m) => m.role === 'compactionSummary');
69
79
  assert.equal(summaries.length, 1, 'exactly one compactionSummary message from the replayed prior cycle');
70
80
  const texts = out.map((m) => (typeof m.content === 'string' ? m.content : ''));
@@ -75,3 +85,23 @@ test('cycleAwareMessages: a compacted prior cycle replays via buildSessionContex
75
85
  const dividers = out.filter((m) => m.customType === 'crtr-cycle-divider');
76
86
  assert.equal(dividers.length, 1, 'one divider between the replayed prior cycle and the live one');
77
87
  });
88
+ test('cycleAwareMessages: a yielded cycle keeps terminal assistant content without an abort notice', () => {
89
+ const m1 = userMsg('m1', null, 'yield after this turn');
90
+ const toolCall = { type: 'toolCall', id: 'yield-call', name: 'bash', arguments: { command: 'crtr node yield' } };
91
+ const m2 = assistantMsg('m2', 'm1', [toolCall], 'aborted', 'Request was aborted');
92
+ const cy1 = {
93
+ type: 'custom',
94
+ id: 'cy1',
95
+ parentId: null,
96
+ timestamp: new Date(1).toISOString(),
97
+ customType: CRTR_CYCLE_CUSTOM_TYPE,
98
+ data: { cycle: 2, fromLeaf: 'm2' },
99
+ };
100
+ const m3 = userMsg('m3', 'cy1', 'fresh cycle');
101
+ const out = cycleAwareMessages(cycleManager([m1, m2, cy1, m3], 'm3'));
102
+ const yieldedAssistant = out.find((message) => message.role === 'assistant');
103
+ assert.deepEqual(yieldedAssistant.content, [toolCall], 'the yielded turn remains available to transcript renderers');
104
+ assert.equal(yieldedAssistant.stopReason, undefined, 'yield is not presented as a failed assistant turn');
105
+ assert.equal(yieldedAssistant.errorMessage, undefined, 'the provider abort text is omitted with the yield boundary');
106
+ assert.equal(out.filter((message) => message.customType === 'crtr-cycle-divider').length, 1);
107
+ });
@@ -1,8 +1,7 @@
1
1
  // spawnChild({ prompt: '' }) — the no-kickoff path `node new --root --no-kickoff`
2
- // rides. Proves the mechanism the guest convo-node birth now relies on: an empty
3
- // prompt reaches buildPiArgv as `undefined`-equivalent (no positional pushed), so
4
- // the broker never drives a kickoff turn — the node comes up idle, goal-less, and
5
- // (when forking) without an identity-reassertion turn either.
2
+ // rides. An empty prompt reaches buildPiArgv as `undefined`-equivalent, so the
3
+ // broker comes up idle and goal-less. Fork identity is carried by the canonical
4
+ // session-start bearings message rather than by either kickoff shape.
6
5
  import { test, before, after, beforeEach } from 'node:test';
7
6
  import assert from 'node:assert/strict';
8
7
  import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
@@ -82,7 +81,7 @@ test('no-kickoff root: no positional prompt reaches pi argv, and no goal is pers
82
81
  rmSync(cwdWithKickoff, { recursive: true, force: true });
83
82
  }
84
83
  });
85
- test('no-kickoff + fork-from: the forked history rides through with NO identity-reassertion/kickoff turn', async () => {
84
+ test('fork-from keeps identity out of the task prompt with or without kickoff', async () => {
86
85
  const cwdNoKickoff = mkdtempSync(join(tmpdir(), 'crtr-no-kickoff-fork-cwd-'));
87
86
  const cwdWithKickoff = mkdtempSync(join(tmpdir(), 'crtr-kickoff-fork-cwd-'));
88
87
  const seedPathNoKickoff = join(cwdNoKickoff, 'seed.jsonl');
@@ -101,14 +100,11 @@ test('no-kickoff + fork-from: the forked history rides through with NO identity-
101
100
  assert.ok(argvNoKickoff.includes(seedPathNoKickoff), 'the resolved seed path is the no-kickoff fork source');
102
101
  assert.ok(argvWithKickoff.includes('--fork'), 'the real-kickoff fork source still rides through to pi');
103
102
  assert.ok(argvWithKickoff.includes(seedPathWithKickoff), 'the resolved seed path is the real-kickoff fork source');
104
- // The real-kickoff fork prepends an identity re-assertion ("you are a FORK
105
- // of ...") to its kickoff prompt (existing behavior, unchanged) — the
106
- // no-kickoff fork gets neither that block nor any prompt at all, since no
107
- // turn ever runs at spawn to carry it.
103
+ // A real kickoff carries exactly the user's task. The session-start
104
+ // bearings message owns fork identity for both prompt shapes.
108
105
  const trailingWithKickoff = argvWithKickoff[argvWithKickoff.length - 1];
109
- assert.match(trailingWithKickoff, /a FORK of/, 'the identity re-assertion still leads a real fork kickoff');
110
- assert.match(trailingWithKickoff, /continue please$/, 'the real prompt still trails the identity block');
111
- assert.notEqual(argvNoKickoff[argvNoKickoff.length - 1], trailingWithKickoff, 'the no-kickoff fork carries no identity/kickoff turn at all');
106
+ assert.equal(trailingWithKickoff, 'continue please', 'the real fork kickoff is the unmodified user task');
107
+ assert.notEqual(argvNoKickoff[argvNoKickoff.length - 1], trailingWithKickoff, 'the no-kickoff fork carries no task prompt');
112
108
  }
113
109
  finally {
114
110
  cap.restore();
@@ -5,11 +5,13 @@
5
5
  // final, so it may go dormant regardless of whether it has a parent.
6
6
  import { test, before, after, beforeEach } from 'node:test';
7
7
  import assert from 'node:assert/strict';
8
- import { mkdtempSync, rmSync } from 'node:fs';
8
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
9
9
  import { tmpdir } from 'node:os';
10
10
  import { join } from 'node:path';
11
11
  import { createNode, deliverAndSettleMessageWait, getNode, subscribe, setMessageWait, setStatus } from '../canvas/canvas.js';
12
12
  import { closeDb, openDb } from '../canvas/db.js';
13
+ import { contextDir } from '../canvas/paths.js';
14
+ import { bashJobPaths } from '../bash-jobs.js';
13
15
  import { evaluateStop } from '../runtime/stop-guard.js';
14
16
  import { writeOutputSchema } from '../runtime/structured-output.js';
15
17
  let home;
@@ -67,6 +69,19 @@ test('a TERMINAL node with nothing live to await and no final pushed is reprompt
67
69
  assert.equal(d.action, 'reprompt');
68
70
  assert.equal(d.reason, 'stalled');
69
71
  });
72
+ test('a TERMINAL node awaiting a background bash job may dorm until its completion message', () => {
73
+ createNode(node('bashWait', { parent: 'mgr', lifecycle: 'terminal' }));
74
+ const paths = bashJobPaths(contextDir('bashWait'), `${Date.now()}-fixture`);
75
+ mkdirSync(paths.dir, { recursive: true });
76
+ writeFileSync(paths.cmdSh, 'long-running command');
77
+ writeFileSync(paths.jobLog, '');
78
+ writeFileSync(paths.jobBg, '');
79
+ assert.deepEqual(evaluateStop('bashWait', noSignals), { action: 'allow', reason: 'awaiting' });
80
+ writeFileSync(paths.jobExit, '0');
81
+ const completed = evaluateStop('bashWait', noSignals);
82
+ assert.equal(completed.action, 'reprompt');
83
+ assert.equal(completed.reason, 'stalled');
84
+ });
70
85
  test('only an explicit live controller wait permits dormancy; ancestry alone does not', () => {
71
86
  createNode(node('parent', { lifecycle: 'terminal', status: 'active' }));
72
87
  createNode(node('child', { parent: 'parent' }));