@north-light/crouter 0.3.233 → 0.3.235

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 (411) hide show
  1. package/dist/api/client.js +16 -15
  2. package/dist/api/dto/nodes.d.ts +11 -0
  3. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/integration/provider-rotation.test.ts +1 -1
  4. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/claude-plugin-commands.ts +14 -17
  5. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/crtr-commands/index.ts +7 -11
  6. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/frontmatter-rules/index.ts +8 -8
  7. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +10 -11
  8. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/pi-shell-runner.ts +2 -2
  9. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +3 -3
  10. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +7 -7
  11. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +8 -8
  12. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/statusline.ts +6 -6
  13. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/sysprompt-window.ts +3 -3
  14. package/dist/builtin-pi-packages/pi-crtr-extensions/lib/subscription-state.ts +1 -1
  15. package/dist/clients/attach/chrome/ticket-panel.js +15 -2
  16. package/dist/clients/attach/overlays/graph.js +0 -3
  17. package/dist/clients/attach/overlays/mcp.js +1 -2
  18. package/dist/clients/attach/render/chat-view.js +0 -12
  19. package/dist/clients/attach/render/crtr-output.js +0 -2
  20. package/dist/clients/attach/render/tool-calls.js +0 -6
  21. package/dist/clients/attach/slash/dispatch.js +8 -11
  22. package/dist/clients/attach/viewer.js +535 -537
  23. package/dist/clients/conversation/projection.js +0 -2
  24. package/dist/clients/inbox/controller.js +22 -3
  25. package/dist/clients/inbox/review/companion-pane.js +0 -4
  26. package/dist/clients/inbox/review-adapter.js +0 -7
  27. package/dist/clients/inbox/tui/slots.d.ts +3 -3
  28. package/dist/clients/inbox/tui/types.d.ts +2 -2
  29. package/dist/commands/api-client.js +0 -8
  30. package/dist/commands/attention.js +1 -9
  31. package/dist/commands/canvas-browse.js +2 -1
  32. package/dist/commands/canvas-config.js +0 -10
  33. package/dist/commands/canvas-history.js +2 -1
  34. package/dist/commands/cron.js +10 -23
  35. package/dist/commands/dashboard.js +0 -2
  36. package/dist/commands/human/feedback.js +2 -1
  37. package/dist/commands/human/prompts.js +2 -5
  38. package/dist/commands/human/queue.js +2 -7
  39. package/dist/commands/human/request.js +0 -14
  40. package/dist/commands/human/review.js +2 -1
  41. package/dist/commands/human.js +1 -1
  42. package/dist/commands/memory/__tests__/command-selector-and-mutation-guards.test.js +4 -4
  43. package/dist/commands/memory/__tests__/memory-list-and-lint-ignore.test.d.ts +1 -0
  44. package/dist/commands/memory/__tests__/memory-list-and-lint-ignore.test.js +96 -0
  45. package/dist/commands/memory/delete.js +1 -1
  46. package/dist/commands/memory/edit.js +6 -3
  47. package/dist/commands/memory/find.js +2 -2
  48. package/dist/commands/memory/lint.js +2 -1
  49. package/dist/commands/memory/list.d.ts +4 -0
  50. package/dist/commands/memory/list.js +20 -14
  51. package/dist/commands/memory/move.js +1 -1
  52. package/dist/commands/memory/read.d.ts +2 -7
  53. package/dist/commands/memory/read.js +5 -16
  54. package/dist/commands/memory/shared.d.ts +1 -0
  55. package/dist/commands/memory/shared.js +17 -8
  56. package/dist/commands/memory/write.js +8 -4
  57. package/dist/commands/node/bash.js +2 -1
  58. package/dist/commands/node/create.js +2 -1
  59. package/dist/commands/node/inspect.js +11 -10
  60. package/dist/commands/node/lifecycle.js +5 -13
  61. package/dist/commands/node/message.js +11 -6
  62. package/dist/commands/node/subscription.js +7 -2
  63. package/dist/commands/node/wait.js +9 -4
  64. package/dist/commands/node-context.js +2 -1
  65. package/dist/commands/node-lifecycle-revive.js +0 -2
  66. package/dist/commands/node-worktree.js +2 -1
  67. package/dist/commands/pkg/browse/actions.d.ts +1 -1
  68. package/dist/commands/pkg/browse/actions.js +4 -4
  69. package/dist/commands/pkg/browse/catalog.d.ts +1 -1
  70. package/dist/commands/pkg/browse/catalog.js +15 -15
  71. package/dist/commands/pkg/browse/command-view.d.ts +1 -1
  72. package/dist/commands/pkg/browse/command-view.js +3 -3
  73. package/dist/commands/pkg/browse/detail-panel.d.ts +1 -1
  74. package/dist/commands/pkg/browse/detail-panel.js +8 -8
  75. package/dist/commands/pkg/browse/doc-view.d.ts +1 -1
  76. package/dist/commands/pkg/browse/doc-view.js +3 -3
  77. package/dist/commands/pkg/browse/list-panel.d.ts +1 -1
  78. package/dist/commands/pkg/browse/list-panel.js +3 -3
  79. package/dist/commands/pkg/browse/model.d.ts +3 -3
  80. package/dist/commands/pkg/browse/shell.d.ts +2 -2
  81. package/dist/commands/pkg/browse/shell.js +13 -13
  82. package/dist/commands/pkg/plugin-manage.js +3 -2
  83. package/dist/commands/pkg/shared.js +0 -2
  84. package/dist/commands/pkg.js +1 -0
  85. package/dist/commands/profile/delete.js +2 -1
  86. package/dist/commands/profile/rename.js +1 -1
  87. package/dist/commands/push.js +2 -3
  88. package/dist/commands/revive.js +0 -2
  89. package/dist/commands/surface/node/placement.js +2 -1
  90. package/dist/commands/surface-edit.js +2 -1
  91. package/dist/commands/sys/config.js +1 -4
  92. package/dist/commands/sys/daemon.js +0 -10
  93. package/dist/commands/sys/doctor.js +3 -2
  94. package/dist/commands/sys/panels/broker-limits-panel.d.ts +2 -2
  95. package/dist/commands/sys/panels/broker-limits-panel.js +4 -4
  96. package/dist/commands/sys/panels/dependencies-panel.d.ts +2 -2
  97. package/dist/commands/sys/panels/dependencies-panel.js +2 -2
  98. package/dist/commands/sys/panels/keybindings-panel.d.ts +1 -1
  99. package/dist/commands/sys/panels/keybindings-panel.js +5 -5
  100. package/dist/commands/sys/panels/models-panel.d.ts +4 -4
  101. package/dist/commands/sys/panels/models-panel.js +11 -10
  102. package/dist/commands/sys/panels/plugins-panel.d.ts +2 -2
  103. package/dist/commands/sys/panels/plugins-panel.js +2 -2
  104. package/dist/commands/sys/panels/prefs-panel.d.ts +2 -2
  105. package/dist/commands/sys/panels/prefs-panel.js +4 -4
  106. package/dist/commands/sys/panels/profiles-panel.d.ts +1 -1
  107. package/dist/commands/sys/panels/profiles-panel.js +14 -13
  108. package/dist/commands/sys/panels/provider-panel.d.ts +3 -3
  109. package/dist/commands/sys/panels/provider-panel.js +8 -8
  110. package/dist/commands/sys/settings-shell.d.ts +3 -3
  111. package/dist/commands/sys/settings-shell.js +15 -15
  112. package/dist/commands/sys/setup-settings-runner.d.ts +3 -3
  113. package/dist/commands/sys/setup-settings-runner.js +5 -5
  114. package/dist/commands/sys/setup-wizard.d.ts +4 -4
  115. package/dist/commands/sys/setup-wizard.js +8 -8
  116. package/dist/commands/sys/support/prepare.js +4 -4
  117. package/dist/commands/sys/support/submit.js +2 -2
  118. package/dist/commands/sys/sync-deps.js +0 -2
  119. package/dist/commands/sys/sync-project-guidance.js +2 -1
  120. package/dist/commands/sys/sysprompt.js +2 -1
  121. package/dist/commands/sys.js +1 -0
  122. package/dist/core/__tests__/boot.test.js +0 -8
  123. package/dist/core/__tests__/broker-extension-canvas-db-boundary.test.js +23 -1
  124. package/dist/core/__tests__/canvas-db-boundary.test.d.ts +1 -0
  125. package/dist/core/__tests__/canvas-db-boundary.test.js +46 -0
  126. package/dist/core/__tests__/canvas.test.js +1 -1
  127. package/dist/core/__tests__/child-death-wake.test.js +0 -2
  128. package/dist/core/__tests__/cli-help-contract.test.d.ts +1 -0
  129. package/dist/core/__tests__/cli-help-contract.test.js +150 -0
  130. package/dist/core/__tests__/cron-armed-context-failure.test.d.ts +1 -0
  131. package/dist/core/__tests__/cron-armed-context-failure.test.js +145 -0
  132. package/dist/core/__tests__/daemon-wedge.test.js +0 -4
  133. package/dist/core/__tests__/dead-node-policy-table.test.js +0 -4
  134. package/dist/core/__tests__/fixtures/fake-engine.js +4 -14
  135. package/dist/core/__tests__/fixtures/fake-pi-host.js +5 -5
  136. package/dist/core/__tests__/focuses.test.js +0 -6
  137. package/dist/core/__tests__/fs-utils.test.d.ts +1 -0
  138. package/dist/core/__tests__/fs-utils.test.js +50 -0
  139. package/dist/core/__tests__/helpers/harness.js +1 -1
  140. package/dist/core/__tests__/inline-memory-refs.test.js +0 -2
  141. package/dist/core/__tests__/integration/broker-fork-seam.test.js +0 -2
  142. package/dist/core/__tests__/integration/broker-sdk-wiring.test.js +3 -16
  143. package/dist/core/__tests__/integration/command-plugins.test.js +21 -24
  144. package/dist/core/__tests__/integration/deferred-no-wake.test.js +0 -6
  145. package/dist/core/__tests__/integration/flagship-lifecycle.test.js +0 -26
  146. package/dist/core/__tests__/integration/live-mutation-verbs.test.js +0 -4
  147. package/dist/core/__tests__/integration/live-mutation.test.js +1 -5
  148. package/dist/core/__tests__/integration/refresh-stall-recycle.test.js +1 -5
  149. package/dist/core/__tests__/integration/revive.test.js +0 -12
  150. package/dist/core/__tests__/integration/subscription-delivery.test.js +0 -6
  151. package/dist/core/__tests__/integration/tmux-surface.test.js +24 -8
  152. package/dist/core/__tests__/integration/worktree-reap.test.js +0 -6
  153. package/dist/core/__tests__/lifecycle.test.js +0 -18
  154. package/dist/core/__tests__/memory-resolver-precedence.test.js +1 -1
  155. package/dist/core/__tests__/migration.test.js +2 -2
  156. package/dist/core/__tests__/on-read-dedup-resume.test.js +3 -3
  157. package/dist/core/__tests__/pid-identity-match.test.js +1 -1
  158. package/dist/core/__tests__/plugin-kinds.test.js +1 -1
  159. package/dist/core/__tests__/plugin-page-components.test.js +1 -1
  160. package/dist/core/__tests__/preview-registry-sync.test.js +42 -1
  161. package/dist/core/__tests__/respawn-throttle.test.js +0 -4
  162. package/dist/core/__tests__/revive-all.test.js +5 -5
  163. package/dist/core/__tests__/revive-parked-fresh.test.js +62 -11
  164. package/dist/core/__tests__/seam/broker-attach-multiclient.test.js +0 -8
  165. package/dist/core/__tests__/seam/broker-attach-stream.test.js +0 -8
  166. package/dist/core/__tests__/seam/broker-crash-teardown.test.js +0 -2
  167. package/dist/core/__tests__/tmux-driver-boundary.test.d.ts +1 -0
  168. package/dist/core/__tests__/tmux-driver-boundary.test.js +50 -0
  169. package/dist/core/__tests__/watchdog-abort-arms-retry.test.js +11 -6
  170. package/dist/core/activity-signal.d.ts +7 -0
  171. package/dist/core/activity-signal.js +38 -0
  172. package/dist/core/auto-update.js +2 -1
  173. package/dist/core/bootstrap.js +2 -1
  174. package/dist/core/canvas/__tests__/remote-canvas-source.test.js +1 -1
  175. package/dist/core/canvas/__tests__/remote-transport.test.js +1 -1
  176. package/dist/core/canvas/__tests__/render-remote.test.js +1 -1
  177. package/dist/core/canvas/browse/app.js +3 -2
  178. package/dist/core/canvas/browse/model.d.ts +1 -1
  179. package/dist/core/canvas/browse/model.js +0 -4
  180. package/dist/core/canvas/canvas.d.ts +22 -0
  181. package/dist/core/canvas/canvas.js +61 -19
  182. package/dist/core/canvas/crons.js +0 -6
  183. package/dist/core/canvas/db.d.ts +14 -20
  184. package/dist/core/canvas/db.js +30 -1332
  185. package/dist/core/canvas/focuses.js +0 -4
  186. package/dist/core/canvas/history.js +2 -1
  187. package/dist/core/canvas/meta-file.js +18 -27
  188. package/dist/core/canvas/migrations.d.ts +20 -0
  189. package/dist/core/canvas/migrations.js +1339 -0
  190. package/dist/core/canvas/nav-model.js +1 -11
  191. package/dist/core/canvas/nav-render.js +0 -8
  192. package/dist/core/canvas/remote-canvas-source.d.ts +8 -0
  193. package/dist/core/canvas/remote-canvas-source.js +9 -3
  194. package/dist/core/canvas/render-source.js +31 -13
  195. package/dist/core/command-manifests/manifest.js +0 -10
  196. package/dist/core/command-manifests/registry.d.ts +2 -0
  197. package/dist/core/command-manifests/schema.js +0 -4
  198. package/dist/core/command-plugins/compose.js +4 -2
  199. package/dist/core/command-plugins/discovery.d.ts +1 -0
  200. package/dist/core/command-plugins/discovery.js +1 -1
  201. package/dist/core/command-plugins/transport/http-invoke.js +0 -14
  202. package/dist/core/command.js +2 -6
  203. package/dist/core/config.js +2 -1
  204. package/dist/core/fault-classifier.js +8 -0
  205. package/dist/core/feed/inbox.d.ts +10 -0
  206. package/dist/core/feed/inbox.js +18 -12
  207. package/dist/core/fs-utils.d.ts +9 -2
  208. package/dist/core/fs-utils.js +7 -15
  209. package/dist/core/help.js +0 -8
  210. package/dist/core/host-exports/export.d.ts +2 -2
  211. package/dist/core/host-exports/export.js +9 -12
  212. package/dist/core/human/answer.d.ts +2 -2
  213. package/dist/core/human/convention.d.ts +2 -5
  214. package/dist/core/human/convention.js +3 -13
  215. package/dist/core/human/page-schema.d.ts +2 -2
  216. package/dist/core/human/page-schema.js +2 -2
  217. package/dist/core/human/root.js +6 -23
  218. package/dist/core/human/scan.js +24 -7
  219. package/dist/core/human/tickets.d.ts +4 -1
  220. package/dist/core/human/tickets.js +20 -6
  221. package/dist/core/io.js +0 -12
  222. package/dist/core/memory/identity.js +0 -4
  223. package/dist/core/memory/inline-ref-inventory.js +3 -2
  224. package/dist/core/memory/lint.js +3 -6
  225. package/dist/core/memory-resolver.d.ts +5 -0
  226. package/dist/core/memory-resolver.js +14 -12
  227. package/dist/core/pagination.d.ts +2 -0
  228. package/dist/core/pagination.js +2 -3
  229. package/dist/core/preview-registry.d.ts +14 -0
  230. package/dist/core/preview-registry.js +2 -4
  231. package/dist/core/preview-result-path.js +2 -1
  232. package/dist/core/profiles/manifest.d.ts +37 -5
  233. package/dist/core/profiles/manifest.js +44 -24
  234. package/dist/core/profiles/select.js +0 -4
  235. package/dist/core/review/signal.js +4 -23
  236. package/dist/core/review/ticket-filter.d.ts +3 -3
  237. package/dist/core/review/ticket-filter.js +4 -9
  238. package/dist/core/runtime/__tests__/engine-route.test.js +1 -1
  239. package/dist/core/runtime/__tests__/spawn-env.test.js +0 -8
  240. package/dist/core/runtime/bearings-render.d.ts +56 -0
  241. package/dist/core/runtime/bearings-render.js +241 -0
  242. package/dist/core/runtime/bearings.d.ts +2 -17
  243. package/dist/core/runtime/bearings.js +12 -198
  244. package/dist/core/runtime/broker/auth-reload.d.ts +39 -0
  245. package/dist/core/runtime/broker/auth-reload.js +149 -0
  246. package/dist/core/runtime/broker/engine-drive.d.ts +26 -0
  247. package/dist/core/runtime/broker/engine-drive.js +257 -0
  248. package/dist/core/runtime/broker/engine-routing.d.ts +143 -0
  249. package/dist/core/runtime/broker/engine-routing.js +211 -0
  250. package/dist/core/runtime/broker/event-projection.js +3 -2
  251. package/dist/core/runtime/broker/fault-retry.js +11 -4
  252. package/dist/core/runtime/broker/frame-client.d.ts +38 -0
  253. package/dist/core/runtime/broker/frame-client.js +129 -0
  254. package/dist/core/runtime/broker/frame-dispatch.d.ts +12 -189
  255. package/dist/core/runtime/broker/frame-dispatch.js +89 -1476
  256. package/dist/core/runtime/broker/frame-engine-control.d.ts +20 -0
  257. package/dist/core/runtime/broker/frame-engine-control.js +231 -0
  258. package/dist/core/runtime/broker/frame-memory-refs.d.ts +19 -0
  259. package/dist/core/runtime/broker/frame-memory-refs.js +41 -0
  260. package/dist/core/runtime/broker/frame-model-query.d.ts +13 -0
  261. package/dist/core/runtime/broker/frame-model-query.js +69 -0
  262. package/dist/core/runtime/broker/frame-read.d.ts +19 -0
  263. package/dist/core/runtime/broker/frame-read.js +116 -0
  264. package/dist/core/runtime/broker/frame-replies.d.ts +24 -0
  265. package/dist/core/runtime/broker/frame-replies.js +64 -0
  266. package/dist/core/runtime/broker/frame-session.d.ts +34 -0
  267. package/dist/core/runtime/broker/frame-session.js +203 -0
  268. package/dist/core/runtime/broker/rebind.js +50 -0
  269. package/dist/core/runtime/broker/turn-ignition.d.ts +42 -0
  270. package/dist/core/runtime/broker/turn-ignition.js +74 -0
  271. package/dist/core/runtime/broker-extension-render.js +9 -85
  272. package/dist/core/runtime/broker-protocol.js +0 -4
  273. package/dist/core/runtime/broker-sdk.js +2 -1
  274. package/dist/core/runtime/broker.d.ts +1 -1
  275. package/dist/core/runtime/broker.js +5 -23
  276. package/dist/core/runtime/fault.js +3 -2
  277. package/dist/core/runtime/headless-pi.js +3 -2
  278. package/dist/core/runtime/host.js +2 -3
  279. package/dist/core/runtime/kickoff.js +0 -4
  280. package/dist/core/runtime/launch-target.js +2 -1
  281. package/dist/core/runtime/launch.d.ts +2 -2
  282. package/dist/core/runtime/launch.js +20 -10
  283. package/dist/core/runtime/lifecycle.js +17 -26
  284. package/dist/core/runtime/managed-provider-cooling.d.ts +1 -1
  285. package/dist/core/runtime/naming.js +0 -2
  286. package/dist/core/runtime/nodes.js +2 -1
  287. package/dist/core/runtime/persona.js +0 -6
  288. package/dist/core/runtime/placement-tmux.js +2 -5
  289. package/dist/core/runtime/placement.js +4 -11
  290. package/dist/core/runtime/recap.js +4 -3
  291. package/dist/core/runtime/reopen.js +4 -7
  292. package/dist/core/runtime/reset.js +0 -4
  293. package/dist/core/runtime/resume-root.js +0 -2
  294. package/dist/core/runtime/revive.js +17 -13
  295. package/dist/core/runtime/session-list-cache.js +2 -2
  296. package/dist/core/runtime/shell-expansion.js +0 -2
  297. package/dist/core/runtime/spawn.js +0 -2
  298. package/dist/core/runtime/stop-guard.js +28 -16
  299. package/dist/core/runtime/stream-watchdog.d.ts +0 -1
  300. package/dist/core/runtime/stream-watchdog.js +13 -18
  301. package/dist/core/runtime/tmux-bindings.js +0 -2
  302. package/dist/core/runtime/tmux-driver.d.ts +12 -0
  303. package/dist/core/runtime/tmux-driver.js +18 -13
  304. package/dist/core/runtime/warm-pool.js +0 -4
  305. package/dist/core/scope.js +4 -3
  306. package/dist/core/spawn.js +2 -3
  307. package/dist/core/subscription-state.js +10 -17
  308. package/dist/core/substrate/__tests__/surface-match-pre-command.test.js +3 -3
  309. package/dist/core/substrate/frontmatter-validation.d.ts +1 -0
  310. package/dist/core/substrate/frontmatter-validation.js +1 -1
  311. package/dist/core/substrate/render.js +0 -12
  312. package/dist/core/substrate/schema.js +0 -12
  313. package/dist/core/substrate/surface-match.js +0 -2
  314. package/dist/core/termrender/display.js +2 -1
  315. package/dist/core/tui/page-host.d.ts +3 -3
  316. package/dist/core/tui/page-host.js +4 -4
  317. package/dist/core/tui/panel.js +3 -3
  318. package/dist/core/wake.js +0 -12
  319. package/dist/core/worktree.js +0 -2
  320. package/dist/daemon/__tests__/reconciler-signature.test.d.ts +1 -0
  321. package/dist/daemon/__tests__/reconciler-signature.test.js +36 -0
  322. package/dist/daemon/api/__tests__/error-seam.test.js +27 -0
  323. package/dist/daemon/api/__tests__/seam/api-server.test.js +2 -16
  324. package/dist/daemon/api/__tests__/seam/b10-attach-modelauth.test.js +3 -11
  325. package/dist/daemon/api/__tests__/seam/leaf-api-parity.test.js +0 -6
  326. package/dist/daemon/api/handlers/attach.js +2 -1
  327. package/dist/daemon/api/handlers/broker-ops.js +3 -6
  328. package/dist/daemon/api/handlers/canvas.js +12 -18
  329. package/dist/daemon/api/handlers/feedback-comments.js +0 -8
  330. package/dist/daemon/api/handlers/health.js +3 -2
  331. package/dist/daemon/api/handlers/human-requests.js +0 -10
  332. package/dist/daemon/api/handlers/human.js +1 -5
  333. package/dist/daemon/api/handlers/inbox.js +0 -18
  334. package/dist/daemon/api/handlers/memory.js +1 -1
  335. package/dist/daemon/api/handlers/messages.js +16 -20
  336. package/dist/daemon/api/handlers/nodes.js +23 -22
  337. package/dist/daemon/api/handlers/reports.js +1 -1
  338. package/dist/daemon/api/handlers/reviews.js +1 -6
  339. package/dist/daemon/api/handlers/worktree.js +6 -22
  340. package/dist/daemon/api/map.d.ts +4 -2
  341. package/dist/daemon/api/map.js +22 -8
  342. package/dist/daemon/api/router.js +0 -4
  343. package/dist/daemon/api/server.js +2 -5
  344. package/dist/daemon/boot-migrations.d.ts +14 -0
  345. package/dist/daemon/boot-migrations.js +31 -0
  346. package/dist/daemon/cron/armed-context.d.ts +21 -0
  347. package/dist/daemon/cron/armed-context.js +100 -0
  348. package/dist/daemon/cron/capture.d.ts +20 -0
  349. package/dist/daemon/cron/capture.js +45 -0
  350. package/dist/daemon/cron/disposition.d.ts +19 -0
  351. package/dist/daemon/cron/disposition.js +52 -0
  352. package/dist/daemon/cron/escalation.d.ts +12 -0
  353. package/dist/daemon/cron/escalation.js +124 -0
  354. package/dist/daemon/cron/held.d.ts +4 -0
  355. package/dist/daemon/cron/held.js +20 -0
  356. package/dist/daemon/cron/lease-recovery.d.ts +3 -0
  357. package/dist/daemon/cron/lease-recovery.js +119 -0
  358. package/dist/daemon/cron/live-runs.d.ts +11 -0
  359. package/dist/daemon/cron/live-runs.js +23 -0
  360. package/dist/daemon/cron/passes.d.ts +11 -0
  361. package/dist/daemon/cron/passes.js +78 -0
  362. package/dist/daemon/cron/settle.d.ts +28 -0
  363. package/dist/daemon/cron/settle.js +37 -0
  364. package/dist/daemon/cron/sinks.d.ts +2 -0
  365. package/dist/daemon/cron/sinks.js +100 -0
  366. package/dist/daemon/cron-run.d.ts +5 -41
  367. package/dist/daemon/cron-run.js +193 -882
  368. package/dist/daemon/crtrd-cli.js +2 -1
  369. package/dist/daemon/crtrd.js +22 -22
  370. package/dist/daemon/fleet.js +0 -4
  371. package/dist/daemon/human/deliver-action.js +2 -1
  372. package/dist/daemon/human/sweep.js +15 -4
  373. package/dist/daemon/manage.d.ts +2 -2
  374. package/dist/daemon/manage.js +4 -13
  375. package/dist/daemon/pidfile.js +2 -2
  376. package/dist/daemon/reconcilers/broker-supervision.js +27 -30
  377. package/dist/daemon/reconcilers/dormant-inbox.d.ts +1 -1
  378. package/dist/daemon/reconcilers/dormant-inbox.js +2 -1
  379. package/dist/daemon/reconcilers/live-obligation.js +2 -2
  380. package/dist/daemon/reconcilers/pending-review-submit.d.ts +1 -1
  381. package/dist/daemon/reconcilers/pending-review-submit.js +2 -1
  382. package/dist/daemon/reconcilers/storage-maintenance.d.ts +2 -1
  383. package/dist/daemon/reconcilers/storage-maintenance.js +2 -1
  384. package/dist/daemon/review/companion.js +2 -2
  385. package/dist/daemon/review/sweep.js +8 -3
  386. package/dist/migrations/001-surfaces-frontmatter.js +0 -6
  387. package/dist/migrations/corpus.js +2 -5
  388. package/dist/migrations/registry.js +15 -2
  389. package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +0 -4
  390. package/dist/pi-extensions/broker-local.js +2 -1
  391. package/dist/pi-extensions/canvas-bash-valve.js +7 -20
  392. package/dist/pi-extensions/canvas-context-intro.js +2 -7
  393. package/dist/pi-extensions/canvas-doc-substrate.js +2 -3
  394. package/dist/pi-extensions/canvas-goal-capture.js +2 -1
  395. package/dist/pi-extensions/canvas-inbox-watcher.js +5 -12
  396. package/dist/pi-extensions/canvas-passive-context.js +2 -5
  397. package/dist/pi-extensions/canvas-preview-result.js +2 -1
  398. package/dist/pi-extensions/canvas-prompt-scrub.js +2 -1
  399. package/dist/pi-extensions/canvas-recap.js +3 -16
  400. package/dist/pi-extensions/canvas-review-boundary.js +2 -5
  401. package/dist/pi-extensions/canvas-stophook.js +2 -26
  402. package/dist/pi-extensions/canvas-structured-output.js +2 -1
  403. package/dist/pi-extensions/canvas-tool-guide.js +2 -1
  404. package/dist/shared/__tests__/env-boundary.test.d.ts +1 -0
  405. package/dist/shared/__tests__/env-boundary.test.js +93 -0
  406. package/dist/shared/env.d.ts +68 -0
  407. package/dist/shared/env.js +171 -0
  408. package/package.json +3 -1
  409. package/runtime.lock.json +2 -2
  410. package/dist/core/canvas/render.d.ts +0 -47
  411. package/dist/core/canvas/render.js +0 -245
@@ -5,8 +5,15 @@ export declare function realpathOrSelf(p: string): string;
5
5
  export declare function expandTilde(p: string, homeDir?: string): string;
6
6
  export declare function ensureDir(dir: string): void;
7
7
  export declare function writeJson(path: string, data: unknown): void;
8
- export declare function atomicWriteJson(path: string, data: unknown): void;
9
- export declare function atomicWriteText(path: string, content: string | Uint8Array): void;
8
+ export interface AtomicWriteOptions {
9
+ /** Force this mode regardless of any existing file's mode. Omitted: preserve the existing file's mode, or default a new file to `0o600`. */
10
+ mode?: number;
11
+ }
12
+ export declare function atomicWriteJson(path: string, data: unknown, opts?: AtomicWriteOptions): void;
13
+ /** Replace one file's contents in a single rename, so a concurrent reader — the
14
+ * tmux inbox watcher, or crtrd serving a ticket read — sees either the old
15
+ * bytes or the new ones and never a truncated file mid-write. */
16
+ export declare function atomicWriteText(path: string, content: string | Uint8Array, opts?: AtomicWriteOptions): void;
10
17
  export declare function readJson<T = unknown>(path: string): T;
11
18
  export declare function readJsonIfExists<T = unknown>(path: string): T | null;
12
19
  /** Missing or corrupt reads as absent, so a crash artifact never throws. */
@@ -26,24 +26,16 @@ export function writeJson(path, data) {
26
26
  ensureDir(dirname(path));
27
27
  writeFileSync(path, JSON.stringify(data, null, 2) + '\n', 'utf8');
28
28
  }
29
- export function atomicWriteJson(path, data) {
30
- ensureDir(dirname(path));
31
- const temp = `${path}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`;
32
- const mode = existsSync(path) ? statSync(path).mode & 0o777 : 0o600;
33
- try {
34
- writeFileSync(temp, JSON.stringify(data, null, 2) + '\n', { encoding: 'utf8', mode });
35
- chmodSync(temp, mode);
36
- renameSync(temp, path);
37
- }
38
- finally {
39
- if (existsSync(temp))
40
- rmSync(temp, { force: true });
41
- }
29
+ export function atomicWriteJson(path, data, opts) {
30
+ atomicWriteText(path, JSON.stringify(data, null, 2) + '\n', opts);
42
31
  }
43
- export function atomicWriteText(path, content) {
32
+ /** Replace one file's contents in a single rename, so a concurrent reader — the
33
+ * tmux inbox watcher, or crtrd serving a ticket read — sees either the old
34
+ * bytes or the new ones and never a truncated file mid-write. */
35
+ export function atomicWriteText(path, content, opts) {
44
36
  ensureDir(dirname(path));
45
37
  const temp = `${path}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`;
46
- const mode = existsSync(path) ? statSync(path).mode & 0o777 : 0o600;
38
+ const mode = opts?.mode ?? (existsSync(path) ? statSync(path).mode & 0o777 : 0o600);
47
39
  try {
48
40
  writeFileSync(temp, content, { mode });
49
41
  chmodSync(temp, mode);
package/dist/core/help.js CHANGED
@@ -2,9 +2,7 @@
2
2
  // Pure functions — no side effects, no commander, no process.exit.
3
3
  // Rendering matches reference.md shapes exactly:
4
4
  // root L11-32, branch L43-58, leaf L65-189.
5
- // ---------------------------------------------------------------------------
6
5
  // Internal helpers
7
- // ---------------------------------------------------------------------------
8
6
  /** Build a self-named runtime-state element: `<tag attr="v">body</tag>`. The
9
7
  * subtree that owns the state authors it through this, so the tag name and any
10
8
  * scalar metadata (e.g. a count) travel with the data and render identically
@@ -42,9 +40,7 @@ function maxLen(names) {
42
40
  function pad(s, width) {
43
41
  return s + ' '.repeat(Math.max(0, width - s.length));
44
42
  }
45
- // ---------------------------------------------------------------------------
46
43
  // renderRoot
47
- // ---------------------------------------------------------------------------
48
44
  const IO_CONTRACT = 'I/O contract: flags and positional args on input; stdout is agent-ready markdown/XML you\n' +
49
45
  'act on directly — read it as a continuation of your prompt, don\'t parse it as data.\n' +
50
46
  'Exit 0 on success, non-zero on failure. Schemas appear at leaf -h; a leaf may route an uncommon flag to focused --flag -h guidance.';
@@ -132,9 +128,7 @@ export function renderRoot(h) {
132
128
  lines.push(CAPABILITY_DISCOVERY);
133
129
  return lines.join('\n');
134
130
  }
135
- // ---------------------------------------------------------------------------
136
131
  // renderBranch
137
- // ---------------------------------------------------------------------------
138
132
  /** Escape a value for a rendered XML attribute. Output is light XML around
139
133
  * markdown read as prose by a model, not parsed — so we only guard the
140
134
  * double-quote that would visually break the attribute, swapping it for a
@@ -170,9 +164,7 @@ export function renderBranch(h) {
170
164
  lines.push('</command>');
171
165
  return lines.join('\n');
172
166
  }
173
- // ---------------------------------------------------------------------------
174
167
  // renderLeafArgv
175
- // ---------------------------------------------------------------------------
176
168
  /** Build the display label for a param entry (left column). */
177
169
  function paramLabel(p) {
178
170
  if (p.kind === 'positional')
@@ -20,8 +20,8 @@ import type { RootDef } from '../command.js';
20
20
  export declare function writeIfOurs(path: string, content: string, markerPrefix: string): void;
21
21
  /** Render and write every built-in export artifact to every present host, while
22
22
  * pruning legacy generated artifacts that should no longer be surfaced.
23
- * Best-effort: swallows errors (debug-logged) so a provisioning fault never
24
- * breaks an unrelated `crtr` command. Kill switch: `CRTR_NO_EXPORTS=1`. */
23
+ * Best-effort: reports errors without breaking an unrelated `crtr` command.
24
+ * Kill switch: `CRTR_NO_EXPORTS=1`. */
25
25
  export declare function pruneLegacyExports(argv?: string[]): void;
26
26
  /** Write every built-in export artifact to every present host. Split from the
27
27
  * prune above because the prune must run BEFORE the front door boots (stale
@@ -16,6 +16,7 @@ import { writeFileSync } from 'node:fs';
16
16
  import { homedir } from 'node:os';
17
17
  import { dirname, join } from 'node:path';
18
18
  import { ensureDir, pathExists, readText, removePath } from '../fs-utils.js';
19
+ import { envNoExports } from '../../shared/env.js';
19
20
  import { builtinExportPairs, legacyExportArtifacts } from './builtins.js';
20
21
  const SKIP_SUBCOMMANDS = new Set([
21
22
  'help',
@@ -73,11 +74,11 @@ function removeIfOurs(path, target, markerPrefix) {
73
74
  }
74
75
  /** Render and write every built-in export artifact to every present host, while
75
76
  * pruning legacy generated artifacts that should no longer be surfaced.
76
- * Best-effort: swallows errors (debug-logged) so a provisioning fault never
77
- * breaks an unrelated `crtr` command. Kill switch: `CRTR_NO_EXPORTS=1`. */
77
+ * Best-effort: reports errors without breaking an unrelated `crtr` command.
78
+ * Kill switch: `CRTR_NO_EXPORTS=1`. */
78
79
  export function pruneLegacyExports(argv = process.argv) {
79
80
  try {
80
- if (process.env.CRTR_NO_EXPORTS === '1')
81
+ if (envNoExports())
81
82
  return;
82
83
  if (shouldSkipAllForArgv(argv))
83
84
  return;
@@ -90,10 +91,8 @@ export function pruneLegacyExports(argv = process.argv) {
90
91
  }
91
92
  }
92
93
  catch (e) {
93
- if (process.env.CRTR_DEBUG === '1') {
94
- const msg = e instanceof Error ? e.message : String(e);
95
- process.stderr.write(`crtr: exports prune error: ${msg}\n`);
96
- }
94
+ const msg = e instanceof Error ? e.message : String(e);
95
+ process.stderr.write(`crtr: exports prune error: ${msg}\n`);
97
96
  }
98
97
  }
99
98
  /** Write every built-in export artifact to every present host. Split from the
@@ -103,7 +102,7 @@ export function pruneLegacyExports(argv = process.argv) {
103
102
  * for building the full command tree just to reach a no-op. */
104
103
  export function provisionExports(root, argv = process.argv) {
105
104
  try {
106
- if (process.env.CRTR_NO_EXPORTS === '1')
105
+ if (envNoExports())
107
106
  return;
108
107
  if (shouldSkipAllForArgv(argv))
109
108
  return;
@@ -119,9 +118,7 @@ export function provisionExports(root, argv = process.argv) {
119
118
  }
120
119
  }
121
120
  catch (e) {
122
- if (process.env.CRTR_DEBUG === '1') {
123
- const msg = e instanceof Error ? e.message : String(e);
124
- process.stderr.write(`crtr: exports error: ${msg}\n`);
125
- }
121
+ const msg = e instanceof Error ? e.message : String(e);
122
+ process.stderr.write(`crtr: exports error: ${msg}\n`);
126
123
  }
127
124
  }
@@ -1,4 +1,4 @@
1
- import { type CommentAnchor, type PageResponses, type PageSlot } from './page-schema.js';
1
+ import { type PageCommentAnchor, type PageResponses, type PageSlot } from './page-schema.js';
2
2
  export type PageAnswer = {
3
3
  title: string;
4
4
  subtitle?: string;
@@ -34,7 +34,7 @@ export type PageAnswerGroup = {
34
34
  }[];
35
35
  };
36
36
  /** Stored responses predating the `whole` anchor's removal still carry it; answers keep rendering them. */
37
- export type LegacyCommentAnchor = CommentAnchor | {
37
+ export type LegacyCommentAnchor = PageCommentAnchor | {
38
38
  kind: 'whole';
39
39
  };
40
40
  export type PageAnswerComment = {
@@ -1,3 +1,5 @@
1
+ import { atomicWriteJson, atomicWriteText } from '../fs-utils.js';
2
+ export { atomicWriteJson, atomicWriteText };
1
3
  /** The authored page source path, derived from the persisted dialect. */
2
4
  export declare function pagePath(dir: string, dialect?: 'jsx' | 'html'): string;
3
5
  /** The compiled module the Gateway evaluates for JSX pages. */
@@ -24,11 +26,6 @@ export declare function isResolved(dir: string): boolean;
24
26
  export declare function isClaimed(dir: string): boolean;
25
27
  /** Resolve and verify a ticket directory without a registry. */
26
28
  export declare function requireTicket(dir: string): string;
27
- /** Replace one file's contents in a single rename, so a concurrent reader —
28
- * the tmux inbox watcher, or crtrd serving a ticket read — sees either the old
29
- * bytes or the new ones and never a truncated file mid-write. */
30
- export declare function atomicWriteText(path: string, contents: string): void;
31
- export declare function atomicWriteJson(path: string, value: unknown): void;
32
29
  /** Publish one immutable JSON record without replacing an existing winner. */
33
30
  export declare function publishJsonExclusive(path: string, value: unknown): boolean;
34
31
  export declare function clearProgress(dir: string): void;
@@ -1,5 +1,7 @@
1
- import { existsSync, linkSync, mkdirSync, readFileSync, realpathSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
1
+ import { existsSync, linkSync, mkdirSync, readFileSync, realpathSync, unlinkSync, writeFileSync } from 'node:fs';
2
2
  import { dirname } from 'node:path';
3
+ import { atomicWriteJson, atomicWriteText } from '../fs-utils.js';
4
+ export { atomicWriteJson, atomicWriteText };
3
5
  /** The authored page source path, derived from the persisted dialect. */
4
6
  export function pagePath(dir, dialect = 'jsx') { return `${dir}/page.${dialect === 'jsx' ? 'tsx' : 'html'}`; }
5
7
  /** The compiled module the Gateway evaluates for JSX pages. */
@@ -51,18 +53,6 @@ export function requireTicket(dir) {
51
53
  }
52
54
  return canonicalDir;
53
55
  }
54
- /** Replace one file's contents in a single rename, so a concurrent reader —
55
- * the tmux inbox watcher, or crtrd serving a ticket read — sees either the old
56
- * bytes or the new ones and never a truncated file mid-write. */
57
- export function atomicWriteText(path, contents) {
58
- const tmp = `${path}.${process.pid}.${Math.random().toString(16).slice(2)}.tmp`;
59
- mkdirSync(dirname(path), { recursive: true });
60
- writeFileSync(tmp, contents, { mode: 0o600 });
61
- renameSync(tmp, path);
62
- }
63
- export function atomicWriteJson(path, value) {
64
- atomicWriteText(path, `${JSON.stringify(value, null, 2)}\n`);
65
- }
66
56
  /** Publish one immutable JSON record without replacing an existing winner. */
67
57
  export function publishJsonExclusive(path, value) {
68
58
  const tmp = `${path}.${process.pid}.${Math.random().toString(16).slice(2)}.tmp`;
@@ -2,14 +2,14 @@ import { z } from 'zod';
2
2
  import { BUILTIN_PAGE_KINDS } from './page-catalog.js';
3
3
  import type { ProductPageComponents } from '../../types.js';
4
4
  export declare const PAGE_SLOT_ID_PATTERN: RegExp;
5
- export declare const commentAnchorSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
5
+ export declare const pageCommentAnchorSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
6
6
  kind: z.ZodLiteral<"option">;
7
7
  optionId: z.ZodString;
8
8
  }, z.core.$strict>, z.ZodObject<{
9
9
  kind: z.ZodLiteral<"row">;
10
10
  rowId: z.ZodString;
11
11
  }, z.core.$strict>], "kind">;
12
- export type CommentAnchor = z.infer<typeof commentAnchorSchema>;
12
+ export type PageCommentAnchor = z.infer<typeof pageCommentAnchorSchema>;
13
13
  export declare const commentSchema: z.ZodObject<{
14
14
  id: z.ZodString;
15
15
  anchor: z.ZodDiscriminatedUnion<[z.ZodObject<{
@@ -17,11 +17,11 @@ function uniqueIds(items, ctx, label) {
17
17
  // one of a question's options, or a table row. Feedback on rendered page content — including
18
18
  // the question text itself — is a ticket-level feedback comment (feedback.ts), not part of
19
19
  // any response.
20
- export const commentAnchorSchema = z.discriminatedUnion('kind', [
20
+ export const pageCommentAnchorSchema = z.discriminatedUnion('kind', [
21
21
  z.object({ kind: z.literal('option'), optionId: itemIdSchema }).strict(),
22
22
  z.object({ kind: z.literal('row'), rowId: itemIdSchema }).strict(),
23
23
  ]);
24
- export const commentSchema = z.object({ id: z.string().min(1), anchor: commentAnchorSchema, text: z.string() }).strict();
24
+ export const commentSchema = z.object({ id: z.string().min(1), anchor: pageCommentAnchorSchema, text: z.string() }).strict();
25
25
  export const optionSchema = z.object({ id: itemIdSchema, label: z.string().min(1), description: z.string().optional(), recommended: z.boolean().optional() }).strict();
26
26
  export const optionsConfigSchema = z.object({
27
27
  label: z.string().optional(),
@@ -1,7 +1,7 @@
1
- import { mkdirSync, realpathSync, watch, writeFileSync } from 'node:fs';
1
+ import { realpathSync } from 'node:fs';
2
2
  import { createHash } from 'node:crypto';
3
- import { basename, dirname, join } from 'node:path';
4
- import { crtrHome, nodeDir, nodesRoot } from '../canvas/paths.js';
3
+ import { nodeDir, nodesRoot } from '../canvas/paths.js';
4
+ import { activityPath, signalActivity, watchActivity } from '../activity-signal.js';
5
5
  export function ticketsRoot() {
6
6
  return nodesRoot();
7
7
  }
@@ -17,29 +17,12 @@ export function ticketDir(ticketId) {
17
17
  return nodeDir(ticketId);
18
18
  }
19
19
  export function inboxActivityPath() {
20
- return join(crtrHome(), 'signals', 'inbox-activity');
20
+ return activityPath('inbox-activity');
21
21
  }
22
22
  /** Signal one durable ticket mutation to every open inbox without watching every ticket directory. */
23
23
  export function signalInboxActivity() {
24
- try {
25
- mkdirSync(dirname(inboxActivityPath()), { recursive: true, mode: 0o700 });
26
- writeFileSync(inboxActivityPath(), `${Date.now()}\n`, { mode: 0o600 });
27
- }
28
- catch {
29
- // A later inbox scan is still authoritative.
30
- }
24
+ signalActivity('inbox-activity');
31
25
  }
32
26
  export function watchInboxActivity(onChange) {
33
- const activityPath = inboxActivityPath();
34
- try {
35
- mkdirSync(dirname(activityPath), { recursive: true, mode: 0o700 });
36
- const watcher = watch(dirname(activityPath), (_event, filename) => {
37
- if (String(filename) === basename(activityPath))
38
- onChange();
39
- });
40
- return () => watcher.close();
41
- }
42
- catch {
43
- return () => { };
44
- }
27
+ return watchActivity('inbox-activity', onChange);
45
28
  }
@@ -7,6 +7,10 @@ import { pageAwaitsResponse, pageOverviewActions } from './page-schema.js';
7
7
  import { parsePage } from './page.js';
8
8
  import { pageTicketState, readTicketResult } from './tickets.js';
9
9
  import { ticketsRoot } from './root.js';
10
+ import { emitEvent } from '../events/emit.js';
11
+ function errorText(error) {
12
+ return error instanceof Error ? error.message : String(error);
13
+ }
10
14
  function claimSummary(dir) {
11
15
  const claim = readJsonOrNull(claimPath(dir));
12
16
  if (claim === null || typeof claim.host !== 'string' || typeof claim.claimedAt !== 'string' || typeof claim.heartbeatAt !== 'string')
@@ -94,7 +98,14 @@ export function scanInbox() {
94
98
  }
95
99
  if (isResolved(dir))
96
100
  continue;
97
- const item = reviewSummary(dir, basename(dir)) ?? pageSummary(dir, basename(dir));
101
+ let item;
102
+ try {
103
+ item = reviewSummary(dir, basename(dir)) ?? pageSummary(dir, basename(dir));
104
+ }
105
+ catch (error) {
106
+ emitEvent({ level: 'warn', event: 'human.scan.ticket_unreadable', fields: { ticket_id: basename(dir), error: errorText(error) } });
107
+ continue;
108
+ }
98
109
  if (item !== null && (item.kind !== 'page' || item.inbox))
99
110
  items.push(item);
100
111
  }
@@ -124,13 +135,19 @@ export function scanPageHistory(nodeId) {
124
135
  catch {
125
136
  continue;
126
137
  }
127
- const summary = pageSummary(dir, basename(dir));
128
- if (summary === null)
129
- continue;
130
- if (nodeId !== undefined && summary.source.nodeId !== nodeId)
138
+ try {
139
+ const summary = pageSummary(dir, basename(dir));
140
+ if (summary === null)
141
+ continue;
142
+ if (nodeId !== undefined && summary.source.nodeId !== nodeId)
143
+ continue;
144
+ const result = summary.state === 'resolved' ? readTicketResult(dir) : null;
145
+ items.push({ ...summary, ...(result?.kind === 'page' ? { answerDigest: result.summary.replace(/\s+/g, ' ').trim() } : {}) });
146
+ }
147
+ catch (error) {
148
+ emitEvent({ level: 'warn', event: 'human.scan.ticket_unreadable', fields: { ticket_id: basename(dir), error: errorText(error) } });
131
149
  continue;
132
- const result = summary.state === 'resolved' ? readTicketResult(dir) : null;
133
- items.push({ ...summary, ...(result?.kind === 'page' ? { answerDigest: result.summary.replace(/\s+/g, ' ').trim() } : {}) });
150
+ }
134
151
  }
135
152
  return items.sort((a, b) => Date.parse(a.emittedAt) - Date.parse(b.emittedAt) || a.id.localeCompare(b.id));
136
153
  }
@@ -1,7 +1,10 @@
1
1
  import type { ReviewDescriptor, TicketResult } from './types.js';
2
2
  import type { ProductPageComponents } from '../../types.js';
3
3
  import { type PageDelivery, type PageManifest, type PageResponses, type PageSource } from './page-schema.js';
4
- /** Strict decoder for the only canonical final marker. */
4
+ /** Strict decoder for the only canonical final marker. Only a missing file reads
5
+ * as absent — a corrupt (unparseable) response is a real anomaly and throws,
6
+ * since silently treating it as "no result yet" would mask data loss instead
7
+ * of surfacing it to whichever caller owns the failure boundary. */
5
8
  export declare function readTicketResult(dir: string): TicketResult | null;
6
9
  export interface SubmitPageOptions {
7
10
  dir: string;
@@ -27,14 +27,20 @@ const pageResultSchema = z.object({ schema: z.literal('crtr.page-response/v1'),
27
27
  const pageProgressSchema = z.object({ kind: z.literal('page'), responses: z.record(z.string(), slotResponseSchema), savedAt: iso }).strict();
28
28
  const reviewResultSchema = z.object({ schema: z.literal('humanloop.review-response/v1'), kind: z.literal('review'), result: feedbackSchema, completedAt: iso }).strict();
29
29
  const canceledResultSchema = z.object({ schema: z.literal('humanloop.cancel/v1'), kind: z.literal('canceled'), canceledAt: iso, reason: z.string().optional(), actor: z.string().optional(), disposition: z.enum(['canceled', 'dismissed']).optional() }).strict();
30
- /** Strict decoder for the only canonical final marker. */
30
+ /** Strict decoder for the only canonical final marker. Only a missing file reads
31
+ * as absent — a corrupt (unparseable) response is a real anomaly and throws,
32
+ * since silently treating it as "no result yet" would mask data loss instead
33
+ * of surfacing it to whichever caller owns the failure boundary. */
31
34
  export function readTicketResult(dir) {
35
+ const path = responsePath(dir);
36
+ if (!existsSync(path))
37
+ return null;
32
38
  let raw;
33
39
  try {
34
- raw = JSON.parse(readFileSync(responsePath(dir), 'utf8'));
40
+ raw = JSON.parse(readFileSync(path, 'utf8'));
35
41
  }
36
- catch {
37
- return null;
42
+ catch (error) {
43
+ throw new Error(`corrupt ticket result: ${path}`, { cause: error });
38
44
  }
39
45
  const page = pageResultSchema.safeParse(raw);
40
46
  if (page.success)
@@ -246,7 +252,10 @@ function completePageLocked(dir, responses, claimToken, opts) {
246
252
  const result = { schema: 'crtr.page-response/v1', kind: 'page', responses: parsedResponses, summary: buildPageSummary(manifest, parsedResponses), completedAt: opts.completedAt ?? new Date().toISOString(), ...(opts.actor === undefined ? {} : { actor: opts.actor }) };
247
253
  const won = exclusiveResult(dir, result);
248
254
  clearOwnedWork(dir, claimToken);
249
- return { won, result: won ? result : readTicketResult(dir) ?? result };
255
+ // Lost the race: the winner's result must already be on disk, so a null read
256
+ // here is an anomaly worth surfacing rather than silently falling back to
257
+ // this loser's own uncommitted result.
258
+ return { won, result: won ? result : (readTicketResult(dir) ?? (() => { throw new Error(`lost the completion race but no winning result is on disk: ${dir}`); })()) };
250
259
  }
251
260
  export function completePage(dir, responses, claimToken, opts = {}) {
252
261
  const ticket = requireTicket(dir);
@@ -288,7 +297,12 @@ export function cancelTicket(dir, opts = {}) {
288
297
  requireCancelablePage(ticket);
289
298
  const result = { schema: 'humanloop.cancel/v1', kind: 'canceled', canceledAt: new Date().toISOString(), ...(opts.reason === undefined ? {} : { reason: opts.reason }), ...(opts.actor === undefined ? {} : { actor: opts.actor }), ...(opts.disposition === undefined ? {} : { disposition: opts.disposition }) };
290
299
  const won = exclusiveResult(ticket, result);
291
- return { status: won ? 'canceled' : 'already_resolved', result: won ? result : readTicketResult(ticket) ?? result };
300
+ // Same anomaly as completePageLocked: losing the race with no winning
301
+ // result on disk means the ticket state is broken, not just contended.
302
+ return {
303
+ status: won ? 'canceled' : 'already_resolved',
304
+ result: won ? result : (readTicketResult(ticket) ?? (() => { throw new Error(`lost the cancellation race but no winning result is on disk: ${ticket}`); })()),
305
+ };
292
306
  });
293
307
  if (canceled.status === 'canceled')
294
308
  signalInboxActivity();
package/dist/core/io.js CHANGED
@@ -9,9 +9,7 @@ import { ExitCode } from '../types.js';
9
9
  import { PREVIEW_RESULT_PATH_ENV } from './preview-result-path.js';
10
10
  import { renderError } from './render.js';
11
11
  import { ApiError } from '../api/index.js';
12
- // ---------------------------------------------------------------------------
13
12
  // output mode — prose (default) vs raw JSON (--json global, for tooling)
14
- // ---------------------------------------------------------------------------
15
13
  let jsonOutput = false;
16
14
  let preview;
17
15
  /** A preview is bounded UI metadata, never a bulk-data channel. One
@@ -98,9 +96,7 @@ export class InputError extends CrtrError {
98
96
  this.payload = payload;
99
97
  }
100
98
  }
101
- // ---------------------------------------------------------------------------
102
99
  // stdin
103
- // ---------------------------------------------------------------------------
104
100
  /** Read raw stdin to EOF. Returns empty string when stdin is a TTY (no pipe).
105
101
  * Called by the argv parser for leaves declaring a `stdin` parameter. */
106
102
  export async function readStdinRaw() {
@@ -148,9 +144,7 @@ export async function peekStdinRaw(timeoutMs = 200) {
148
144
  process.stdin.resume();
149
145
  });
150
146
  }
151
- // ---------------------------------------------------------------------------
152
147
  // stdout — the result, nothing else
153
- // ---------------------------------------------------------------------------
154
148
  /** Raw-JSON mirror of a single-shot response (the `--json` escape hatch). The
155
149
  * default path renders the result as prose instead — see render.ts. */
156
150
  export function emit(obj) {
@@ -192,15 +186,11 @@ export function writeStdout(s) {
192
186
  }
193
187
  });
194
188
  }
195
- // ---------------------------------------------------------------------------
196
189
  // stderr — diagnostics the agent MAY capture, never the result
197
- // ---------------------------------------------------------------------------
198
190
  export function diag(message) {
199
191
  process.stderr.write(message + '\n');
200
192
  }
201
- // ---------------------------------------------------------------------------
202
193
  // errors
203
- // ---------------------------------------------------------------------------
204
194
  function payloadOf(e) {
205
195
  if (e instanceof InputError)
206
196
  return e.payload;
@@ -259,7 +249,6 @@ function jsonSafeResult(result) {
259
249
  message: 'The completed primary result could not be represented as JSON.',
260
250
  };
261
251
  }
262
- // ---------------------------------------------------------------------------
263
252
  // ApiError → clean CLI error — the ONE shared translation (plan C-5)
264
253
  //
265
254
  // A daemon `ApiError` is NOT a `CrtrError`, so left unhandled it falls through
@@ -268,7 +257,6 @@ function jsonSafeResult(result) {
268
257
  // error the pre-API leaves surfaced, so every re-plumbed verb shares one
269
258
  // contract instead of re-implementing the mapping per command. io.ts importing
270
259
  // `ApiError` from `src/api` is canvas-clean (src/api is Node-built-ins only).
271
- // ---------------------------------------------------------------------------
272
260
  /** Map a daemon HTTP status to the stable CLI error code the pre-API leaves
273
261
  * used, so agent-facing error contracts survive the re-plumb. Used only as the
274
262
  * fallback when the server did NOT round-trip a full structured payload. */
@@ -4,12 +4,10 @@
4
4
  // (a project's declaration, an installed plugin's mount prefix, or empty), and
5
5
  // its canonical name — the only address resolution accepts — is namespace ⊕
6
6
  // local. Pure and side-effect free.
7
- // ---------------------------------------------------------------------------
8
7
  // Segment normalization — the optional `NN-` ordering pin. A physical path may
9
8
  // carry a two-digit prefix on any segment (`00-runtime-base/`, `01-spine/`)
10
9
  // purely to pin structural order against natural sort. The prefix is NEVER part
11
10
  // of identity: only the physical path keeps it.
12
- // ---------------------------------------------------------------------------
13
11
  const NUMERIC_PREFIX_RE = /^\d{2}-/;
14
12
  const SEGMENT_RE = /^[A-Za-z0-9._-]+$/;
15
13
  /** The filename (and path segment) whose document fronts its directory. */
@@ -85,9 +83,7 @@ export function physicalRelativePathFor(localName, representation) {
85
83
  }
86
84
  return representation === 'directory-index' ? `${local}/${INDEX_SEGMENT}.md` : `${local}.md`;
87
85
  }
88
- // ---------------------------------------------------------------------------
89
86
  // Local name and canonical composition.
90
- // ---------------------------------------------------------------------------
91
87
  /** A document's explicit frontmatter `name` as a local name — normalized with
92
88
  * one trailing `INDEX` collapsed — or `null` when it carries none. */
93
89
  export function explicitLocalName(fm) {
@@ -1,6 +1,7 @@
1
1
  // Authoritative broker-side inventory for inline memory references. It uses one
2
2
  // target view, exact canonical winners, and the canonical tree for directory
3
3
  // browse names; gates, rungs, and slash flags do not affect referenceability.
4
+ import { envNodeId, envProfileId } from '../../shared/env.js';
4
5
  import { loadMemoryTargetView } from '../memory-resolver.js';
5
6
  import { buildMemoryTree } from './tree.js';
6
7
  import { memoryExtensionEffectiveCatalog, projectEffectiveMemoryExtensions } from './extensions.js';
@@ -21,8 +22,8 @@ const SCOPE_SORT_RANK = {
21
22
  export function buildRefInventory() {
22
23
  const target = {
23
24
  cwd: process.cwd(),
24
- profileId: process.env['CRTR_PROFILE_ID'] || null,
25
- nodeId: process.env['CRTR_NODE_ID'] || null,
25
+ profileId: envProfileId() || null,
26
+ nodeId: envNodeId() || null,
26
27
  };
27
28
  const view = loadMemoryTargetView(target, { quiet: true });
28
29
  const winners = new Map();
@@ -10,6 +10,7 @@
10
10
  // a non-fatal authoring smell, `notice` is inspection data (duplicate stores are
11
11
  // legitimate, so equal-canonical candidates across stores are reported, never
12
12
  // failed).
13
+ import { envNodeId, envProfileId } from '../../shared/env.js';
13
14
  import { basename, join, matchesGlob, relative, sep } from 'node:path';
14
15
  import { pathExists, readText, realpathOrSelf, walkFiles } from '../fs-utils.js';
15
16
  import { parseFrontmatterGeneric } from '../frontmatter.js';
@@ -51,7 +52,6 @@ function makeResult(findings, checked, documents, storesLinted) {
51
52
  notices: findings.filter((f) => f.severity === 'notice'),
52
53
  };
53
54
  }
54
- // ---------------------------------------------------------------------------
55
55
  // Body-length caps, measured in WORDS (frontmatter excluded). Words, not lines:
56
56
  // house style writes each paragraph as ONE logical line and lets the editor
57
57
  // soft-wrap, so a line count measures wrapping style rather than context cost.
@@ -68,7 +68,6 @@ function makeResult(findings, checked, documents, storesLinted) {
68
68
  // composes into an agent's persona — are structurally exempt: an agent reads its
69
69
  // whole persona by construction. Suppression elsewhere is deliberate and
70
70
  // per-doc: `lint-ignore: length`, surfaced ONLY by the finding itself.
71
- // ---------------------------------------------------------------------------
72
71
  const BOOT_CONTENT_MAX_WORDS = 1000;
73
72
  const EVENT_CONTENT_MAX_WORDS = 1000;
74
73
  const BOOT_PREVIEW_MAX_WORDS = 3000;
@@ -116,9 +115,7 @@ export function lintShortPreviewBody(fm, body) {
116
115
  const noun = previewEvents.length === 1 ? 'entry delivers' : 'entries deliver';
117
116
  return `body is ${words} words but the ${previewEvents.join('/')} ${noun} \`preview\`; use \`at: content\` so agents receive the whole rule without a separate memory read`;
118
117
  }
119
- // ---------------------------------------------------------------------------
120
118
  // Reference resolution: what a `[[link]]` or a `memory-read` route may name.
121
- // ---------------------------------------------------------------------------
122
119
  /** Every canonical name a reference may target in a corpus: each document's
123
120
  * exact identity plus every canonical directory, because reading a directory
124
121
  * answers with its own document and its listing. Derived from the canonical
@@ -455,8 +452,8 @@ function lintMemoryReadEntry(entry, doc, fm, resolvable, mounted, add) {
455
452
  function ambientTarget() {
456
453
  return {
457
454
  cwd: process.cwd(),
458
- profileId: process.env['CRTR_PROFILE_ID'] || null,
459
- nodeId: process.env['CRTR_NODE_ID'] || null,
455
+ profileId: envProfileId() || null,
456
+ nodeId: envNodeId() || null,
460
457
  };
461
458
  }
462
459
  /** Lint every store a target sees, then the diagnostics only the merged view
@@ -242,6 +242,11 @@ export declare function createMemoryDocSnapshot(target?: MemoryTarget): MemoryDo
242
242
  /** Resolve several canonical names over one memory-source snapshot. Failed
243
243
  * names are omitted so callers can preserve their per-document error behavior. */
244
244
  export declare function resolveMemoryDocs(names: readonly string[]): Map<string, MemoryDoc>;
245
+ /** Load the body at an already-resolved memory path with the same frontmatter
246
+ * stripping and in-node path interpolation as `memory read`. Callers that
247
+ * already have the resolved path (such as slash-command discovery) avoid
248
+ * re-walking every memory store for each document. */
249
+ export declare function readMemoryDocContent(path: string, includeFrontmatter?: boolean): string;
245
250
  /** The address winner for an exact canonical name in the ambient target. */
246
251
  export declare function resolveMemoryDoc(rawName: string, opts?: MemoryResolutionOpts): MemoryDoc;
247
252
  /** Resolve a memory document as ANOTHER node would see it — the same precedence