@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
@@ -0,0 +1,138 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { readFileSync, statSync } from 'node:fs';
3
+ import { documentLines } from '../document-lines.js';
4
+ import { COMMENT_QUOTE_MAX, ReviewOperationError, } from './types.js';
5
+ const REVIEW_SOURCE_MAX_BYTES = 2 * 1024 * 1024;
6
+ function sourceUnreadable(file, phase, reason) {
7
+ throw new ReviewOperationError('source_unreadable', 'review source is unreadable', { phase, reason, file });
8
+ }
9
+ /** Read the reviewed document once, preserving the exact bytes used for its digest. */
10
+ export function readReviewSource(file, phase = 'submit') {
11
+ let size;
12
+ try {
13
+ const stat = statSync(file);
14
+ if (!stat.isFile())
15
+ return sourceUnreadable(file, phase, 'unreadable');
16
+ size = stat.size;
17
+ }
18
+ catch (error) {
19
+ return sourceUnreadable(file, phase, error.code === 'ENOENT' ? 'missing' : 'unreadable');
20
+ }
21
+ if (size > REVIEW_SOURCE_MAX_BYTES)
22
+ return sourceUnreadable(file, phase, 'too_large');
23
+ let bytes;
24
+ try {
25
+ bytes = readFileSync(file);
26
+ }
27
+ catch (error) {
28
+ return sourceUnreadable(file, phase, error.code === 'ENOENT' ? 'missing' : 'unreadable');
29
+ }
30
+ if (bytes.length > REVIEW_SOURCE_MAX_BYTES)
31
+ return sourceUnreadable(file, phase, 'too_large');
32
+ if (bytes.includes(0))
33
+ return sourceUnreadable(file, phase, 'binary');
34
+ let text;
35
+ try {
36
+ text = new TextDecoder('utf-8', { fatal: true }).decode(bytes);
37
+ }
38
+ catch {
39
+ return sourceUnreadable(file, phase, 'binary');
40
+ }
41
+ return {
42
+ bytes,
43
+ sha256: createHash('sha256').update(bytes).digest('hex'),
44
+ lines: documentLines(text),
45
+ };
46
+ }
47
+ function requireDocumentSource(review, expected_doc_hash) {
48
+ const source = readReviewSource(review.file, 'comment');
49
+ if (source.sha256 !== expected_doc_hash) {
50
+ throw new ReviewOperationError('stale_document', 'review document changed since it was rendered', {
51
+ reason: 'document_changed',
52
+ expected_sha256: expected_doc_hash,
53
+ actual_sha256: source.sha256,
54
+ });
55
+ }
56
+ if (source.sha256 !== review.anchor_base_sha256) {
57
+ throw new ReviewOperationError('stale_document', 'review comment ranges have not been remapped to the current document', {
58
+ reason: 'anchor_base_stale',
59
+ expected_sha256: review.anchor_base_sha256,
60
+ actual_sha256: source.sha256,
61
+ });
62
+ }
63
+ return source;
64
+ }
65
+ /** Require both the caller's rendered source and the persisted range coordinate base to be current. */
66
+ export function requireCurrentDocument(review, expected_doc_hash) {
67
+ return requireDocumentSource(review, expected_doc_hash);
68
+ }
69
+ /** Verify a create-time source precondition from the review row held by the caller's canvas write transaction. */
70
+ export function requireCurrentDocumentLocked(db, review_id, expected_doc_hash) {
71
+ const review = db.prepare('SELECT file, anchor_base_sha256 FROM reviews WHERE review_id = ?').get(review_id);
72
+ if (review === undefined) {
73
+ throw new ReviewOperationError('unknown_review', 'review was not found', { received: review_id });
74
+ }
75
+ return requireDocumentSource(review, expected_doc_hash);
76
+ }
77
+ /** Reject malformed ranges rather than normalizing them into a different anchor. */
78
+ export function validateAnchorShape(anchor) {
79
+ const violations = [];
80
+ const hasStart = anchor.col_start !== undefined;
81
+ const hasEnd = anchor.col_end !== undefined;
82
+ if (!Number.isSafeInteger(anchor.line) || anchor.line < 1)
83
+ violations.push('line must be a positive integer');
84
+ if (!Number.isSafeInteger(anchor.end_line) || anchor.end_line < 1)
85
+ violations.push('end_line must be a positive integer');
86
+ if (Number.isSafeInteger(anchor.line) && Number.isSafeInteger(anchor.end_line) && anchor.end_line < anchor.line) {
87
+ violations.push('end_line must be greater than or equal to line');
88
+ }
89
+ if (hasStart !== hasEnd)
90
+ violations.push('col_start and col_end must be provided together');
91
+ if (hasStart && (!Number.isSafeInteger(anchor.col_start) || anchor.col_start < 0))
92
+ violations.push('col_start must be a non-negative integer');
93
+ if (hasEnd && (!Number.isSafeInteger(anchor.col_end) || anchor.col_end < 0))
94
+ violations.push('col_end must be a non-negative integer');
95
+ if (hasStart
96
+ && hasEnd
97
+ && anchor.line === anchor.end_line
98
+ && Number.isSafeInteger(anchor.col_start)
99
+ && Number.isSafeInteger(anchor.col_end)
100
+ && anchor.col_end <= anchor.col_start) {
101
+ violations.push('col_end must be greater than col_start for a single-line anchor');
102
+ }
103
+ if (anchor.quote !== undefined && typeof anchor.quote !== 'string')
104
+ violations.push('quote must be a string');
105
+ if (typeof anchor.quote === 'string' && Buffer.byteLength(anchor.quote, 'utf8') > COMMENT_QUOTE_MAX) {
106
+ violations.push(`quote exceeds ${COMMENT_QUOTE_MAX} bytes`);
107
+ }
108
+ if (violations.length > 0) {
109
+ throw new ReviewOperationError('invalid_anchor', 'comment anchor is invalid', { violations });
110
+ }
111
+ }
112
+ function isUtf8Boundary(bytes, offset) {
113
+ return offset === bytes.length || (bytes[offset] & 0xc0) !== 0x80;
114
+ }
115
+ /** Capture the full source range used to render an anchor, without silently clamping it. */
116
+ export function captureAnchorText(lines, anchor) {
117
+ validateAnchorShape(anchor);
118
+ if (anchor.line > lines.length || anchor.end_line > lines.length) {
119
+ throw new ReviewOperationError('anchor_out_of_range', 'comment anchor exceeds the reviewed document', {
120
+ line_count: lines.length,
121
+ requested: { line: anchor.line, end_line: anchor.end_line },
122
+ });
123
+ }
124
+ if (anchor.col_start !== undefined && anchor.col_end !== undefined) {
125
+ const startLine = Buffer.from(lines[anchor.line - 1], 'utf8');
126
+ const endLine = Buffer.from(lines[anchor.end_line - 1], 'utf8');
127
+ if (anchor.col_start > startLine.length
128
+ || anchor.col_end > endLine.length
129
+ || !isUtf8Boundary(startLine, anchor.col_start)
130
+ || !isUtf8Boundary(endLine, anchor.col_end)) {
131
+ throw new ReviewOperationError('anchor_out_of_range', 'comment anchor columns exceed the reviewed document', {
132
+ line_count: lines.length,
133
+ requested: { line: anchor.line, end_line: anchor.end_line },
134
+ });
135
+ }
136
+ }
137
+ return lines.slice(anchor.line - 1, anchor.end_line).join('\n');
138
+ }
@@ -0,0 +1,13 @@
1
+ import type { FeedbackComment } from '../human/types.js';
2
+ import type { ReviewRecord, ReviewResult } from './types.js';
3
+ export interface ReviewCommentSnapshot {
4
+ comments: FeedbackComment[];
5
+ total: number;
6
+ unresolved: number;
7
+ }
8
+ /** Build the immutable approval result from the review row and one comment snapshot. */
9
+ export declare function buildReviewResult(review: ReviewRecord, snapshot: ReviewCommentSnapshot, approvedSha256: string, at: string): ReviewResult;
10
+ /** Project one immutable result to ticket/output or inline result surfaces. */
11
+ export declare function projectReviewResult(review: ReviewRecord, result: ReviewResult): {
12
+ destinations: string[];
13
+ };
@@ -0,0 +1,58 @@
1
+ import { join } from 'node:path';
2
+ import { atomicWriteJson, publishJsonExclusive, responsePath } from '../human/convention.js';
3
+ import { ticketDir } from '../human/root.js';
4
+ import { reviewResultPath } from '../canvas/paths.js';
5
+ /** Build the immutable approval result from the review row and one comment snapshot. */
6
+ export function buildReviewResult(review, snapshot, approvedSha256, at) {
7
+ return {
8
+ schema: 'humanloop.review-response/v1',
9
+ review_id: review.review_id,
10
+ file: review.file,
11
+ submitted: true,
12
+ approved: true,
13
+ changed: approvedSha256 !== review.baseline_sha256,
14
+ baseline_sha256: review.baseline_sha256,
15
+ approved_sha256: approvedSha256,
16
+ companion_node_id: review.companion_node_id,
17
+ comments_total: snapshot.total,
18
+ comments_unresolved: snapshot.unresolved,
19
+ comments: snapshot.comments,
20
+ submittedAt: at,
21
+ savedAt: at,
22
+ };
23
+ }
24
+ /** Project one immutable result to ticket/output or inline result surfaces. */
25
+ export function projectReviewResult(review, result) {
26
+ const destinations = [];
27
+ if (review.origin_kind === 'ticket') {
28
+ if (review.bridge_node_id === null)
29
+ throw new Error(`ticket review has no bridge node: ${review.review_id}`);
30
+ const dir = ticketDir(review.bridge_node_id);
31
+ const feedback = {
32
+ file: result.file,
33
+ submitted: result.submitted,
34
+ approved: result.approved,
35
+ comments: result.comments,
36
+ submittedAt: result.submittedAt,
37
+ savedAt: result.savedAt,
38
+ reviewId: result.review_id,
39
+ changed: result.changed,
40
+ baselineSha256: result.baseline_sha256,
41
+ approvedSha256: result.approved_sha256,
42
+ companionNodeId: result.companion_node_id,
43
+ commentsTotal: result.comments_total,
44
+ commentsUnresolved: result.comments_unresolved,
45
+ };
46
+ const response = responsePath(dir);
47
+ publishJsonExclusive(response, { schema: 'humanloop.review-response/v1', kind: 'review', result: feedback, completedAt: result.submittedAt });
48
+ destinations.push(response);
49
+ const output = review.output_path ?? join(dir, 'feedback.json');
50
+ atomicWriteJson(output, feedback);
51
+ destinations.push(output);
52
+ return { destinations };
53
+ }
54
+ const output = reviewResultPath(review.review_id);
55
+ atomicWriteJson(output, result);
56
+ destinations.push(output);
57
+ return { destinations };
58
+ }
@@ -0,0 +1,19 @@
1
+ import type { NodeStatus } from '../canvas/types.js';
2
+ import { type ReviewRecord } from './types.js';
3
+ /**
4
+ * Finish a committed binding without ever recapturing its origin. Each step is
5
+ * safe to repeat after a crash; review.json is the final visibility switch.
6
+ */
7
+ export declare function realizeReview(reviewId: string): Promise<ReviewRecord>;
8
+ /**
9
+ * Reopen one already-open companion when needed. The surface only attaches to
10
+ * the returned node; it never owns a second revive path.
11
+ */
12
+ export declare function openExistingReview(reviewId: string): Promise<{
13
+ review: ReviewRecord;
14
+ disposition: 'terminal';
15
+ } | {
16
+ review: ReviewRecord;
17
+ companion_status: NodeStatus;
18
+ disposition: 'open';
19
+ }>;
@@ -0,0 +1,217 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { basename, join } from 'node:path';
3
+ import { getNode, subscribersOf, subscribe } from '../canvas/index.js';
4
+ import { withCanvasWrite } from '../canvas/db.js';
5
+ import { atomicWriteJson } from '../human/convention.js';
6
+ import { ticketDir } from '../human/root.js';
7
+ import { submitReview } from '../human/tickets.js';
8
+ import { isBrokerLive } from '../runtime/model-swap.js';
9
+ import { NodeIdConflictError, spawnNode } from '../runtime/nodes.js';
10
+ import { transition } from '../runtime/lifecycle.js';
11
+ import { reviveNode } from '../runtime/revive.js';
12
+ import { awaitCompanionBind, spawnReviewCompanion } from './companion.js';
13
+ import { signalReviewActivity } from './signal.js';
14
+ import { bindBridgeNode, getReview, openReviewRow } from './store.js';
15
+ import { COMPANION_BIND_DEADLINE_MS, ReviewOperationError } from './types.js';
16
+ function unknownReview(reviewId) {
17
+ return new ReviewOperationError('unknown_review', 'review was not found', { received: reviewId });
18
+ }
19
+ function originMissing(originNodeId) {
20
+ return new ReviewOperationError('review_origin_missing', 'review origin was not found', {
21
+ origin_node_id: originNodeId,
22
+ reason: 'origin_missing',
23
+ });
24
+ }
25
+ function companionUnbound(reviewId, waitedMs) {
26
+ return new ReviewOperationError('review_companion_unbound', 'review companion did not bind in time', {
27
+ review_id: reviewId,
28
+ waited_ms: waitedMs,
29
+ });
30
+ }
31
+ function bridgeUnsubscribed(review, bridgeNodeId) {
32
+ return new ReviewOperationError('review_bridge_unsubscribed', 'review bridge is not subscribed to its origin', {
33
+ review_id: review.review_id,
34
+ bridge_node_id: bridgeNodeId,
35
+ origin_node_id: review.origin_node_id,
36
+ });
37
+ }
38
+ /** Cancel only a binding whose immutable branch has disappeared. No later pass may recapture it. */
39
+ function cancelLostBinding(review) {
40
+ const canceled = withCanvasWrite((db) => db.prepare(`
41
+ UPDATE reviews
42
+ SET state = 'canceled', canceled_at = ?, cancel_reason = 'branch_lost', cancel_actor = NULL
43
+ WHERE review_id = ? AND state = 'binding'
44
+ `).run(new Date().toISOString(), review.review_id).changes > 0);
45
+ if (canceled)
46
+ signalReviewActivity();
47
+ }
48
+ function requireReview(reviewId) {
49
+ const review = getReview(reviewId);
50
+ if (review === null)
51
+ throw unknownReview(reviewId);
52
+ return review;
53
+ }
54
+ function requireOrigin(review) {
55
+ const origin = getNode(review.origin_node_id);
56
+ if (origin === null)
57
+ throw originMissing(review.origin_node_id);
58
+ return origin;
59
+ }
60
+ function spawnCompanion(review) {
61
+ if (getNode(review.companion_node_id) !== null)
62
+ return;
63
+ const origin = requireOrigin(review);
64
+ try {
65
+ spawnReviewCompanion({
66
+ nodeId: review.companion_node_id,
67
+ reviewId: review.review_id,
68
+ originNodeId: review.origin_node_id,
69
+ branchFile: review.branch_file,
70
+ targetFile: review.file,
71
+ modelSpec: review.model_spec,
72
+ cwd: origin.cwd,
73
+ profileId: origin.profile_id ?? null,
74
+ });
75
+ }
76
+ catch (error) {
77
+ if (!(error instanceof NodeIdConflictError))
78
+ throw error;
79
+ }
80
+ }
81
+ function cancelInvisibleBridge(nodeId) {
82
+ // A bridge which lost bindBridgeNode() was never exposed, so canceling it
83
+ // cannot produce a final report or notify the origin.
84
+ const node = getNode(nodeId);
85
+ if (node !== null && node.status !== 'done' && node.status !== 'canceled')
86
+ transition(nodeId, 'cancel');
87
+ }
88
+ /** Assert the only route a bridge has back to its origin before ticket exposure. */
89
+ function ensureBridgeSubscription(review, bridgeNodeId) {
90
+ const hasActiveOrigin = () => subscribersOf(bridgeNodeId).some((subscriber) => subscriber.node_id === review.origin_node_id && subscriber.active);
91
+ if (!hasActiveOrigin())
92
+ subscribe(review.origin_node_id, bridgeNodeId, true);
93
+ if (!hasActiveOrigin())
94
+ throw bridgeUnsubscribed(review, bridgeNodeId);
95
+ }
96
+ /** R2: create/bind the ticket bridge and prove its active origin subscription. */
97
+ function realizeBridge(review) {
98
+ if (review.origin_kind !== 'ticket')
99
+ return review;
100
+ let current = review;
101
+ if (current.bridge_node_id === null) {
102
+ const origin = requireOrigin(current);
103
+ const bridge = spawnNode({
104
+ kind: 'human',
105
+ lifecycle: 'terminal',
106
+ parent: current.origin_node_id,
107
+ cwd: origin.cwd,
108
+ profile_id: origin.profile_id ?? null,
109
+ name: 'human-review',
110
+ });
111
+ atomicWriteJson(join(ticketDir(bridge.node_id), 'run.json'), { mode: 'review' });
112
+ if (!bindBridgeNode(current.review_id, bridge.node_id)) {
113
+ cancelInvisibleBridge(bridge.node_id);
114
+ current = requireReview(current.review_id);
115
+ }
116
+ else {
117
+ current = requireReview(current.review_id);
118
+ }
119
+ }
120
+ if (current.bridge_node_id === null)
121
+ throw bridgeUnsubscribed(current, '');
122
+ ensureBridgeSubscription(current, current.bridge_node_id);
123
+ return current;
124
+ }
125
+ async function awaitAttachableCompanion(nodeId, deadlineMs) {
126
+ const deadline = Date.now() + deadlineMs;
127
+ for (;;) {
128
+ const companion = getNode(nodeId);
129
+ if (companion !== null && isBrokerLive(companion))
130
+ return true;
131
+ const remaining = deadline - Date.now();
132
+ if (remaining <= 0)
133
+ return false;
134
+ await new Promise((resolveWait) => setTimeout(resolveWait, Math.min(100, remaining)));
135
+ }
136
+ }
137
+ function publishTicket(review) {
138
+ if (review.bridge_node_id === null)
139
+ throw bridgeUnsubscribed(review, '');
140
+ if (review.subtitle === null)
141
+ throw new Error(`ticket review has no subtitle: ${review.review_id}`);
142
+ submitReview({
143
+ dir: ticketDir(review.bridge_node_id),
144
+ review: {
145
+ file: review.file,
146
+ ...(review.output_path !== null ? { output: review.output_path } : {}),
147
+ title: basename(review.file),
148
+ subtitle: review.subtitle,
149
+ source: { nodeId: review.origin_node_id },
150
+ blockedSince: review.created,
151
+ },
152
+ });
153
+ }
154
+ /**
155
+ * Finish a committed binding without ever recapturing its origin. Each step is
156
+ * safe to repeat after a crash; review.json is the final visibility switch.
157
+ */
158
+ export async function realizeReview(reviewId) {
159
+ let review = requireReview(reviewId);
160
+ if (review.state !== 'binding')
161
+ return review;
162
+ if (!existsSync(review.branch_file)) {
163
+ cancelLostBinding(review);
164
+ throw new ReviewOperationError('branch_lost', 'review branch was lost before realization', { review_id: review.review_id });
165
+ }
166
+ // R1: creation consumes only the persisted coordinates and preallocated id.
167
+ spawnCompanion(review);
168
+ // R2: ticket bridge birth and route assertion precede all public exposure.
169
+ review = realizeBridge(requireReview(reviewId));
170
+ if (review.state !== 'binding')
171
+ return review;
172
+ // R3: only realization owns this initial launch/retry.
173
+ const companion = getNode(review.companion_node_id);
174
+ if (companion === null)
175
+ throw companionUnbound(review.review_id, 0);
176
+ if (companion.pi_session_id === null)
177
+ reviveNode(companion.node_id, { resume: false });
178
+ // R4: session_start is the durable bind boundary.
179
+ if (!await awaitCompanionBind(review.companion_node_id, COMPANION_BIND_DEADLINE_MS)) {
180
+ throw companionUnbound(review.review_id, COMPANION_BIND_DEADLINE_MS);
181
+ }
182
+ // R5: SQLite visibility commits before the ticket projection. A missing
183
+ // projection is deliberately under-exposed and recovered by the startup sweep.
184
+ if (openReviewRow(review.review_id, new Date().toISOString())) {
185
+ signalReviewActivity();
186
+ if (review.origin_kind === 'ticket')
187
+ publishTicket(review);
188
+ }
189
+ return requireReview(reviewId);
190
+ }
191
+ /**
192
+ * Reopen one already-open companion when needed. The surface only attaches to
193
+ * the returned node; it never owns a second revive path.
194
+ */
195
+ export async function openExistingReview(reviewId) {
196
+ const review = requireReview(reviewId);
197
+ if (review.state === 'binding')
198
+ throw unknownReview(reviewId);
199
+ if (review.state !== 'open')
200
+ return { review, disposition: 'terminal' };
201
+ const companion = getNode(review.companion_node_id);
202
+ if (companion === null)
203
+ throw companionUnbound(review.review_id, 0);
204
+ if (!isBrokerLive(companion))
205
+ reviveNode(companion.node_id, { resume: true });
206
+ if (!await awaitAttachableCompanion(review.companion_node_id, COMPANION_BIND_DEADLINE_MS)) {
207
+ throw companionUnbound(review.review_id, COMPANION_BIND_DEADLINE_MS);
208
+ }
209
+ const attachable = getNode(review.companion_node_id);
210
+ if (attachable === null)
211
+ throw companionUnbound(review.review_id, 0);
212
+ return {
213
+ review: requireReview(reviewId),
214
+ companion_status: attachable.status,
215
+ disposition: 'open',
216
+ };
217
+ }
@@ -0,0 +1,24 @@
1
+ import { type CommentAnchor, type CommentAuthorKind } from './types.js';
2
+ export interface RangeBatchEntry {
3
+ comment_id: string;
4
+ anchor?: CommentAnchor;
5
+ detached?: true;
6
+ }
7
+ interface Actor {
8
+ kind: CommentAuthorKind;
9
+ node_id: string | null;
10
+ }
11
+ /** Atomically replace every live review coordinate with ranges expressed against the current source. */
12
+ export declare function applyRangeBatch(args: {
13
+ review_id: string;
14
+ from_doc_hash: string;
15
+ to_doc_hash: string;
16
+ entries: RangeBatchEntry[];
17
+ actor: Actor;
18
+ operation_id: string;
19
+ }): {
20
+ updated: number;
21
+ detached: number;
22
+ anchor_base_sha256: string;
23
+ };
24
+ export {};
@@ -0,0 +1,156 @@
1
+ import { withCanvasWrite } from '../canvas/db.js';
2
+ import { nowIso } from '../fs-utils.js';
3
+ import { signalReviewActivity } from './signal.js';
4
+ import { replaceAnchorBaseLocked } from './store.js';
5
+ import { ReviewOperationError, } from './types.js';
6
+ import { captureAnchorText, readReviewSource, validateAnchorShape } from './document.js';
7
+ function anchorFromRow(row) {
8
+ return {
9
+ line: row.anchor_line,
10
+ end_line: row.anchor_end_line,
11
+ ...(row.anchor_col_start === null ? {} : { col_start: row.anchor_col_start }),
12
+ ...(row.anchor_col_end === null ? {} : { col_end: row.anchor_col_end }),
13
+ ...(row.anchor_quote === null ? {} : { quote: row.anchor_quote }),
14
+ };
15
+ }
16
+ function rangeBatchStale(reason, expected, actual, comment_id) {
17
+ throw new ReviewOperationError('range_batch_stale', 'review comment ranges are stale', {
18
+ reason,
19
+ expected,
20
+ actual,
21
+ ...(comment_id === undefined ? {} : { comment_id }),
22
+ });
23
+ }
24
+ function validateEntry(entry) {
25
+ if (typeof entry.comment_id !== 'string' || entry.comment_id === '') {
26
+ throw new ReviewOperationError('invalid_anchor', 'comment anchor is invalid', {
27
+ violations: ['comment_id must be a non-empty string'],
28
+ });
29
+ }
30
+ const hasAnchor = entry.anchor !== undefined;
31
+ const detached = entry.detached === true;
32
+ if (hasAnchor === detached) {
33
+ throw new ReviewOperationError('invalid_anchor', 'comment anchor is invalid', {
34
+ violations: ['each range batch entry must provide exactly one of anchor or detached'],
35
+ });
36
+ }
37
+ if (hasAnchor)
38
+ validateAnchorShape(entry.anchor);
39
+ }
40
+ function appendReanchorEvent(db, args) {
41
+ db.prepare(`
42
+ INSERT INTO review_comment_events
43
+ (review_id, comment_id, kind, state_change, revision_after, actor_kind, actor_node_id, at, operation_id, payload)
44
+ SELECT review_id, ?, 'reanchored', 1, ?, ?, ?, ?, ?, ?
45
+ FROM review_comments WHERE comment_id = ?
46
+ `).run(args.row.comment_id, args.row.revision + 1, args.actor.kind, args.actor.node_id, args.at, args.operation_id, JSON.stringify({
47
+ previous_anchor: args.previous_anchor,
48
+ anchor: args.anchor,
49
+ previous_anchor_state: args.previous_anchor_state,
50
+ anchor_state: args.anchor_state,
51
+ previous_doc_hash: args.previous_doc_hash,
52
+ doc_hash: args.doc_hash,
53
+ line_text: args.line_text,
54
+ }), args.row.comment_id);
55
+ }
56
+ /** Atomically replace every live review coordinate with ranges expressed against the current source. */
57
+ export function applyRangeBatch(args) {
58
+ const result = withCanvasWrite((db) => {
59
+ const review = db.prepare('SELECT file, anchor_base_sha256 FROM reviews WHERE review_id = ?').get(args.review_id);
60
+ if (review === undefined) {
61
+ throw new ReviewOperationError('unknown_review', 'review was not found', { received: args.review_id });
62
+ }
63
+ const source = readReviewSource(review.file, 'comment');
64
+ if (source.sha256 !== args.to_doc_hash)
65
+ rangeBatchStale('to_doc_hash', args.to_doc_hash, source.sha256);
66
+ if (review.anchor_base_sha256 !== args.from_doc_hash) {
67
+ rangeBatchStale('from_doc_hash', review.anchor_base_sha256, args.from_doc_hash);
68
+ }
69
+ const comments = db.prepare(`
70
+ SELECT comment_id, status, revision, text, anchor_line, anchor_end_line,
71
+ anchor_col_start, anchor_col_end, anchor_quote, anchor_line_text,
72
+ anchor_doc_hash, anchor_state
73
+ FROM review_comments
74
+ WHERE review_id = ? AND status != 'deleted'
75
+ ORDER BY created_seq
76
+ `).all(args.review_id);
77
+ for (const comment of comments) {
78
+ if (comment.anchor_doc_hash !== args.from_doc_hash) {
79
+ rangeBatchStale('comment_hash', args.from_doc_hash, comment.anchor_doc_hash, comment.comment_id);
80
+ }
81
+ }
82
+ const entries = new Map();
83
+ const extra = [];
84
+ for (const entry of args.entries) {
85
+ validateEntry(entry);
86
+ if (entries.has(entry.comment_id)) {
87
+ extra.push(entry.comment_id);
88
+ }
89
+ else {
90
+ entries.set(entry.comment_id, entry);
91
+ }
92
+ }
93
+ const commentIds = new Set(comments.map((comment) => comment.comment_id));
94
+ for (const commentId of entries.keys()) {
95
+ if (!commentIds.has(commentId))
96
+ extra.push(commentId);
97
+ }
98
+ const missing = comments.filter((comment) => !entries.has(comment.comment_id)).map((comment) => comment.comment_id);
99
+ if (missing.length > 0 || extra.length > 0) {
100
+ throw new ReviewOperationError('range_batch_incomplete', 'range batch must cover every non-deleted comment exactly once', {
101
+ missing,
102
+ extra,
103
+ });
104
+ }
105
+ let updated = 0;
106
+ let detached = 0;
107
+ const at = nowIso();
108
+ for (const comment of comments) {
109
+ const entry = entries.get(comment.comment_id);
110
+ const previousAnchor = anchorFromRow(comment);
111
+ const nextAnchor = entry.detached === true ? previousAnchor : entry.anchor;
112
+ const nextState = entry.detached === true ? 'detached' : 'live';
113
+ const nextLineText = entry.detached === true
114
+ ? comment.anchor_line_text
115
+ : captureAnchorText(source.lines, nextAnchor);
116
+ const changed = comment.anchor_state !== nextState
117
+ || comment.anchor_doc_hash !== source.sha256
118
+ || comment.anchor_line !== nextAnchor.line
119
+ || comment.anchor_end_line !== nextAnchor.end_line
120
+ || comment.anchor_col_start !== (nextAnchor.col_start ?? null)
121
+ || comment.anchor_col_end !== (nextAnchor.col_end ?? null)
122
+ || comment.anchor_quote !== (nextAnchor.quote ?? null)
123
+ || comment.anchor_line_text !== nextLineText;
124
+ if (!changed)
125
+ continue;
126
+ const revision = comment.revision + 1;
127
+ db.prepare(`
128
+ UPDATE review_comments
129
+ SET revision = ?, anchor_line = ?, anchor_end_line = ?, anchor_col_start = ?, anchor_col_end = ?,
130
+ anchor_quote = ?, anchor_line_text = ?, anchor_doc_hash = ?, anchor_state = ?,
131
+ updated_by_kind = ?, updated_by_node_id = ?, updated_at = ?
132
+ WHERE comment_id = ?
133
+ `).run(revision, nextAnchor.line, nextAnchor.end_line, nextAnchor.col_start ?? null, nextAnchor.col_end ?? null, nextAnchor.quote ?? null, nextLineText, source.sha256, nextState, args.actor.kind, args.actor.node_id, at, comment.comment_id);
134
+ appendReanchorEvent(db, {
135
+ row: comment,
136
+ actor: args.actor,
137
+ operation_id: args.operation_id,
138
+ at,
139
+ previous_anchor: previousAnchor,
140
+ anchor: nextAnchor,
141
+ previous_anchor_state: comment.anchor_state,
142
+ anchor_state: nextState,
143
+ previous_doc_hash: comment.anchor_doc_hash,
144
+ doc_hash: source.sha256,
145
+ line_text: nextLineText,
146
+ });
147
+ updated += 1;
148
+ if (nextState === 'detached')
149
+ detached += 1;
150
+ }
151
+ replaceAnchorBaseLocked(db, args.review_id, source.bytes, source.sha256);
152
+ return { updated, detached, anchor_base_sha256: source.sha256 };
153
+ });
154
+ signalReviewActivity();
155
+ return result;
156
+ }
@@ -0,0 +1,5 @@
1
+ /** Signal one durable review mutation to every open viewer without watching every review directory. */
2
+ export declare function reviewActivityPath(): string;
3
+ /** Signal one review mutation (comment create/edit/resolve/delete/reanchor/delivery) to every open review surface. */
4
+ export declare function signalReviewActivity(): void;
5
+ export declare function watchReviewActivity(onChange: () => void): () => void;
@@ -0,0 +1,31 @@
1
+ import { basename, dirname, join } from 'node:path';
2
+ import { mkdirSync, watch, writeFileSync } from 'node:fs';
3
+ import { crtrHome } from '../canvas/paths.js';
4
+ /** Signal one durable review mutation to every open viewer without watching every review directory. */
5
+ export function reviewActivityPath() {
6
+ return join(crtrHome(), 'signals', 'review-activity');
7
+ }
8
+ /** Signal one review mutation (comment create/edit/resolve/delete/reanchor/delivery) to every open review surface. */
9
+ export function signalReviewActivity() {
10
+ try {
11
+ mkdirSync(dirname(reviewActivityPath()), { recursive: true, mode: 0o700 });
12
+ writeFileSync(reviewActivityPath(), `${Date.now()}\n`, { mode: 0o600 });
13
+ }
14
+ catch {
15
+ // A later review read is still authoritative.
16
+ }
17
+ }
18
+ export function watchReviewActivity(onChange) {
19
+ const activityPath = reviewActivityPath();
20
+ try {
21
+ mkdirSync(dirname(activityPath), { recursive: true, mode: 0o700 });
22
+ const watcher = watch(dirname(activityPath), (_event, filename) => {
23
+ if (String(filename) === basename(activityPath))
24
+ onChange();
25
+ });
26
+ return () => watcher.close();
27
+ }
28
+ catch {
29
+ return () => { };
30
+ }
31
+ }