@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
@@ -1,8 +1,10 @@
1
+ import { envNodeId, envProfileId } from '../shared/env.js';
1
2
  import { dirname, join, relative, sep } from 'node:path';
2
3
  import { readdirSync } from 'node:fs';
3
4
  import { CRTR_DIR_NAME } from '../types.js';
4
5
  import { pathExists, readText, realpathOrSelf, walkFiles } from './fs-utils.js';
5
6
  import { parseFrontmatterGeneric } from './frontmatter.js';
7
+ import { interpolateNodePaths } from './canvas/paths.js';
6
8
  import { listInstalledPlugins, listInstalledPluginsInRoot } from './resolver.js';
7
9
  import { notFound, usage } from './errors.js';
8
10
  import { warn } from './output.js';
@@ -19,18 +21,16 @@ import { associateRepository } from './memory/repository-association.js';
19
21
  function ambientTarget() {
20
22
  return {
21
23
  cwd: process.cwd(),
22
- profileId: process.env['CRTR_PROFILE_ID'] || null,
23
- nodeId: process.env['CRTR_NODE_ID'] || null,
24
+ profileId: envProfileId() || null,
25
+ nodeId: envNodeId() || null,
24
26
  };
25
27
  }
26
28
  /** The selected profile id from the process env, or '' when none. */
27
29
  function selectedProfileId() {
28
30
  return ambientTarget().profileId ?? '';
29
31
  }
30
- // ---------------------------------------------------------------------------
31
32
  // Store discovery: which physical stores a target sees, in what order, and
32
33
  // whether each one's namespace lets it mount.
33
- // ---------------------------------------------------------------------------
34
34
  /** The memory scopes in resolution precedence: node > project stack > profile >
35
35
  * user > builtin. Node is included only when the target names one and ranks
36
36
  * NEAREST — a node doc overrides any wider scope. Project is included only when
@@ -291,10 +291,8 @@ export function nativeMemoryStoresInPrecedence(scope, includeDescendants = false
291
291
  .filter((store) => store.mountStatus === 'ready' && store.scope !== 'builtin' && store.plugin === undefined)
292
292
  .map((store) => ({ scope: store.scope, memoryDir: store.storeRoot, store }));
293
293
  }
294
- // ---------------------------------------------------------------------------
295
294
  // Document loading: one store's markdown files become canonically-identified
296
295
  // documents.
297
- // ---------------------------------------------------------------------------
298
296
  function firstLine(e) {
299
297
  return (e instanceof Error ? e.message : String(e)).split('\n')[0] ?? '';
300
298
  }
@@ -375,9 +373,7 @@ export function loadStoreMemoryDocs(store, quiet = false) {
375
373
  export function listPluginMemoryDocs(plugin, scope, quiet = false, projectMemory = NEUTRAL_PROJECT_MEMORY) {
376
374
  return loadStoreMemoryDocs(pluginStoreDescriptor(plugin, scope, projectMemory), quiet);
377
375
  }
378
- // ---------------------------------------------------------------------------
379
376
  // Views: exact canonical resolution over a loaded corpus.
380
- // ---------------------------------------------------------------------------
381
377
  /** The canonical form of a query: empty segments dropped. An `NN-` ordering
382
378
  * prefix is physical-path-only and is NOT stripped here — a canonical name
383
379
  * never carries one, so `04-base` is simply no document's address. */
@@ -462,9 +458,7 @@ export function loadMemoryTargetView(target, opts = {}) {
462
458
  export function loadMemoryStoreView(store, quiet = false) {
463
459
  return { store, ...makeView([store], loadStoreMemoryDocs(store, quiet)) };
464
460
  }
465
- // ---------------------------------------------------------------------------
466
461
  // Corpus listing.
467
- // ---------------------------------------------------------------------------
468
462
  /** All project-scoped docs visible from an explicit node workspace/profile — the
469
463
  * project-only slice of a `MemoryTarget` view, used where only workspace docs
470
464
  * are wanted (a workspace-open render). For a full-precedence target-addressed
@@ -507,6 +501,16 @@ export function createMemoryDocSnapshot(target = ambientTarget()) {
507
501
  export function resolveMemoryDocs(names) {
508
502
  return createMemoryDocSnapshot().resolve(names);
509
503
  }
504
+ /** Load the body at an already-resolved memory path with the same frontmatter
505
+ * stripping and in-node path interpolation as `memory read`. Callers that
506
+ * already have the resolved path (such as slash-command discovery) avoid
507
+ * re-walking every memory store for each document. */
508
+ export function readMemoryDocContent(path, includeFrontmatter = false) {
509
+ const raw = readText(path);
510
+ const content = includeFrontmatter ? raw : parseFrontmatterGeneric(raw).body;
511
+ const nodeId = envNodeId();
512
+ return nodeId ? interpolateNodePaths(content, nodeId) : content;
513
+ }
510
514
  /** The address winner for an exact canonical name in the ambient target. */
511
515
  export function resolveMemoryDoc(rawName, opts = {}) {
512
516
  return resolveMemoryDocForTarget(rawName, ambientTarget(), opts);
@@ -541,10 +545,8 @@ export function resolveMemoryCandidatesForTarget(rawName, target, opts = {}) {
541
545
  export function resolveMemoryDocInStore(rawName, store, opts = {}) {
542
546
  return loadMemoryStoreView(store, true).resolve(rawName, opts);
543
547
  }
544
- // ---------------------------------------------------------------------------
545
548
  // Physical sidecar lookup: the revision log mirrors the doc tree, so it is
546
549
  // addressed by PHYSICAL path segments, not by canonical identity.
547
- // ---------------------------------------------------------------------------
548
550
  /** Find the direct child of `dir` — an `ext` file (matched on name minus
549
551
  * extension) or a directory — whose NORMALIZED display name equals
550
552
  * `segment`. An exact literal match (no prefix to strip) wins over a
@@ -16,6 +16,8 @@ export interface PaginateOpts<T> {
16
16
  /** Returns the stable sort key for an item. Must match the sort order of the
17
17
  * input array — ascending by this key. */
18
18
  keyOf: (item: T) => string;
19
+ /** Compares sort keys when their order is not raw code-unit order. */
20
+ compareKeys?: (left: string, right: string) => number;
19
21
  /** 'count' → compute and return `total`; 'omit' → return `null`. */
20
22
  total: 'count' | 'omit';
21
23
  }
@@ -11,9 +11,7 @@
11
11
  // item after it is returned correctly).
12
12
  import { CrtrError } from './errors.js';
13
13
  import { ExitCode } from '../types.js';
14
- // ---------------------------------------------------------------------------
15
14
  // Cursor encoding / decoding
16
- // ---------------------------------------------------------------------------
17
15
  /** Encodes a stable sort key into an opaque base64url cursor token. */
18
16
  export function encodeCursor(key) {
19
17
  const json = JSON.stringify({ k: key });
@@ -69,9 +67,10 @@ export function paginate(allItemsSortedAscByKey, params, opts) {
69
67
  let startIndex = 0;
70
68
  if (params.cursor !== undefined) {
71
69
  const cursorKey = decodeCursor(params.cursor); // throws CrtrError on bad cursor
70
+ const compareKeys = opts.compareKeys ?? ((left, right) => left < right ? -1 : left > right ? 1 : 0);
72
71
  // Find the first item whose key is strictly greater than cursorKey.
73
72
  // Linear scan; callers with large datasets should pre-filter upstream.
74
- startIndex = allItemsSortedAscByKey.findIndex((item) => opts.keyOf(item) > cursorKey);
73
+ startIndex = allItemsSortedAscByKey.findIndex((item) => compareKeys(opts.keyOf(item), cursorKey) > 0);
75
74
  if (startIndex === -1) {
76
75
  // All items are at or before the cursor key — list is exhausted.
77
76
  startIndex = allItemsSortedAscByKey.length;
@@ -14,6 +14,9 @@ export declare const HELP_ICON: string;
14
14
  * paints lives in one file: terminal for `bash`, file-text for `read`, floppy for
15
15
  * `write` (content committed to disk), pencil-square for `edit`. */
16
16
  export declare const TOOL_ICON: Readonly<Record<'bash' | 'read' | 'write' | 'edit', string>>;
17
+ /** Per-leaf overrides, keyed by full leaf help-name path (e.g. "memory
18
+ * lint"), same shape as SUPPRESS_OUTPUT_LEAVES. */
19
+ export declare const LEAF_ICON_CP: Record<string, number>;
17
20
  /** Resolve the Nerd Font glyph for a command's argv tokens: the longest
18
21
  * matching LEAF_ICON_CP prefix, else the tokens[0] family glyph, else the
19
22
  * generic ◆ fallback for an unmapped family. */
@@ -29,7 +32,18 @@ export interface CrtrPreview {
29
32
  jsonl?: Record<string, unknown>[];
30
33
  error?: Record<string, unknown>;
31
34
  }
35
+ type SummaryInput = {
36
+ result?: Record<string, unknown>;
37
+ jsonl?: Record<string, unknown>[];
38
+ error?: Record<string, unknown>;
39
+ };
40
+ type Summarizer = (input: SummaryInput, tokens: readonly string[]) => SummaryLine[] | null;
41
+ export declare const SUMMARIZERS: ReadonlyArray<{
42
+ path: string;
43
+ run: Summarizer;
44
+ }>;
32
45
  /** Summarize an exact CLI result record. The recorded path is authoritative:
33
46
  * shell tokenization is used only for an invocation's static icon/help chrome,
34
47
  * never to identify or decode the returned leaf data. */
35
48
  export declare function summarizePath(preview: CrtrPreview, tokens?: readonly string[]): SummaryLine[] | null;
49
+ export {};
@@ -42,12 +42,10 @@ export function suppressOutputForPath(tokens) {
42
42
  }
43
43
  return false;
44
44
  }
45
- // ---------------------------------------------------------------------------
46
45
  // Icon registry (rule 12, inline-ui-placement: real Nerd Font glyphs, not the
47
46
  // generic ◆ diamond). Every leaf's glyph is looked up first in LEAF_ICON_CP
48
47
  // (longest-prefix match, same shape as SUPPRESS_OUTPUT_LEAVES); an unmapped
49
48
  // leaf falls back to its root family's glyph in FAMILY_ICON_CP.
50
- // ---------------------------------------------------------------------------
51
49
  /** question-circle — the header glyph for a `-h`/`--help` invocation,
52
50
  * regardless of which command's help was requested. */
53
51
  const HELP_ICON_CP = 0xf059;
@@ -83,7 +81,7 @@ const FAMILY_ICON_CP = {
83
81
  };
84
82
  /** Per-leaf overrides, keyed by full leaf help-name path (e.g. "memory
85
83
  * lint"), same shape as SUPPRESS_OUTPUT_LEAVES. */
86
- const LEAF_ICON_CP = {
84
+ export const LEAF_ICON_CP = {
87
85
  'canvas dashboard': 0xf0e4,
88
86
  'canvas snapshot': 0xf030,
89
87
  'canvas browse': 0xf14e,
@@ -345,7 +343,7 @@ function summarizeProfileList(input) {
345
343
  ...profiles.slice(0, 5).map((profile) => ({ text: `${stringField(profile, 'name')} · ${numberField(profile, 'projects_count')} projects`, tone: 'muted' })),
346
344
  ];
347
345
  }
348
- const SUMMARIZERS = [
346
+ export const SUMMARIZERS = [
349
347
  { path: 'memory read', run: summarizeMemoryRead },
350
348
  { path: 'memory list', run: summarizeArray('docs') },
351
349
  { path: 'memory find', run: summarizeArray('hits', 'hits') },
@@ -7,6 +7,7 @@
7
7
  // into the command text — the executed script has to stay exactly what the
8
8
  // agent wrote, because `cmd.sh`, job inspection, and the doc substrate's
9
9
  // `command` surfaces all read that text as the agent's own words.
10
+ import { envNodeId } from '../shared/env.js';
10
11
  import { tmpdir } from 'node:os';
11
12
  import { join } from 'node:path';
12
13
  export const PREVIEW_RESULT_PATH_ENV = 'CRTR_PREVIEW_RESULT_PATH';
@@ -16,7 +17,7 @@ export const PREVIEW_RESULT_PATH_ENV = 'CRTR_PREVIEW_RESULT_PATH';
16
17
  * Node id and pid are equal across both sides of the transport (they run in
17
18
  * the same broker process) without either side sharing state. */
18
19
  function brokerToken() {
19
- return `${encodeURIComponent(process.env['CRTR_NODE_ID'] ?? 'no-node')}-${process.pid}`;
20
+ return `${encodeURIComponent(envNodeId() ?? 'no-node')}-${process.pid}`;
20
21
  }
21
22
  /** Deterministic from the tool call id, so the spawning side and the result
22
23
  * side derive the same path without sharing state. */
@@ -24,12 +24,44 @@ export interface ProfileEntry {
24
24
  profileId: string;
25
25
  manifest: ProfileManifest;
26
26
  }
27
- /** Every profile with a readable manifest, in directory-listing order.
28
- * Enumerates ONLY immediate children of `profilesRoot()` — never recurses,
29
- * never trusts a caller-supplied id. A dir whose name doesn't match the
30
- * generated id shape, or whose `profile.json` is missing/corrupt, is skipped
31
- * rather than crashing the whole listing. */
27
+ export interface ProfileEnumeration {
28
+ /** Every profile with a readable manifest, in directory-listing order. */
29
+ entries: ProfileEntry[];
30
+ /** Ids whose directory is present under the profiles root but whose manifest
31
+ * did not read — corrupt JSON, a pre-object project shape, an unreadable or
32
+ * missing `profile.json`. These are NOT deleted profiles, and a caller that
33
+ * cannot tell the two apart silently substitutes another identity. */
34
+ unreadableIds: string[];
35
+ }
36
+ /** Every profile with a readable manifest, in directory-listing order. A
37
+ * profile whose manifest is missing or corrupt is skipped rather than
38
+ * crashing the whole listing — `lookupProfile` is how a caller that must not
39
+ * confuse "skipped" with "gone" asks. */
32
40
  export declare function listProfiles(): ProfileEntry[];
41
+ /** What a stored profile id resolves to — including the case every
42
+ * null-returning resolver here erases: the profile is THERE and could not be
43
+ * read. A caller that treats that as "deleted" substitutes another identity
44
+ * (root) for the one its work was pinned to. */
45
+ export type ProfileLookup = {
46
+ kind: 'resolved';
47
+ entry: ProfileEntry;
48
+ }
49
+ /** Nothing on this host answers to the id or name — a deleted profile. */
50
+ | {
51
+ kind: 'absent';
52
+ }
53
+ /** The id's directory is present; its manifest did not read. */
54
+ | {
55
+ kind: 'unreadable';
56
+ profileId: string;
57
+ manifestPath: string;
58
+ };
59
+ /** Exact resolution (generated id, else a unique manifest `name`) that reports
60
+ * WHY it failed. Only an ID can come back `unreadable`: a name lives inside
61
+ * the manifest that could not be read, so an unreadable profile has no name to
62
+ * match on. Throws on an ambiguous name, exactly as `loadProfileManifest`
63
+ * does. */
64
+ export declare function lookupProfile(profileIdOrName: string): ProfileLookup;
33
65
  /** Read one exact profile id without falling back to manifest-name matching.
34
66
  * Used at persistence gates that already hold a resolved durable identity. */
35
67
  export declare function loadExactProfileManifest(profileId: string): ProfileEntry;
@@ -15,10 +15,8 @@ import { ensureDir, nowIso } from '../fs-utils.js';
15
15
  import { usage, notFound, ambiguous, general } from '../errors.js';
16
16
  import { withExclusiveDirectoryLock } from '../exclusive-lock.js';
17
17
  import { fuzzyMatchProfile } from './fuzzy-match.js';
18
- // ---------------------------------------------------------------------------
19
18
  // Path safety — the only code in the tree that builds a path under the
20
19
  // profiles root. Every other module reaches profile paths through this file.
21
- // ---------------------------------------------------------------------------
22
20
  /** `<slug>-<shortid>`: slug is lower-case, non-alphanumeric collapsed to `-`,
23
21
  * trimmed to 40 chars (defaulting to `profile`); shortid is 8 lowercase hex
24
22
  * chars. The char class alone rules out `/`, `\`, `.`, and `..` segments —
@@ -71,11 +69,9 @@ export function profileManifestPath(profileId) {
71
69
  export function profileMemoryDir(profileId) {
72
70
  return join(profileRoot(profileId), 'memory');
73
71
  }
74
- // ---------------------------------------------------------------------------
75
72
  // Enumeration + resolution — the ONLY functions that turn a CLI operand into
76
73
  // a profile id. Both walk immediate children of the canonical root; neither
77
74
  // ever joins an unvalidated operand into a path.
78
- // ---------------------------------------------------------------------------
79
75
  /** A profile's `home` DEFAULTS to its first project dir. Normalizing here (the
80
76
  * single read path) rather than at write time means a manifest written before
81
77
  * homes existed, and one whose explicit home was later removed from purview,
@@ -175,16 +171,15 @@ export function profileHome(profileId) {
175
171
  return null;
176
172
  }
177
173
  }
178
- /** Every profile with a readable manifest, in directory-listing order.
179
- * Enumerates ONLY immediate children of `profilesRoot()` — never recurses,
180
- * never trusts a caller-supplied id. A dir whose name doesn't match the
181
- * generated id shape, or whose `profile.json` is missing/corrupt, is skipped
182
- * rather than crashing the whole listing. */
183
- export function listProfiles() {
174
+ /** The one walk of the profiles root: immediate children only — never
175
+ * recurses, never trusts a caller-supplied id. A dir whose name doesn't match
176
+ * the generated id shape is not a profile at all and is ignored. */
177
+ function enumerateProfiles() {
184
178
  const root = profilesRoot();
185
179
  if (!existsSync(root))
186
- return [];
187
- const out = [];
180
+ return { entries: [], unreadableIds: [] };
181
+ const entries = [];
182
+ const unreadableIds = [];
188
183
  for (const dirent of readdirSync(root, { withFileTypes: true })) {
189
184
  if (!dirent.isDirectory())
190
185
  continue;
@@ -193,10 +188,37 @@ export function listProfiles() {
193
188
  continue;
194
189
  const manifest = readManifestFile(join(root, profileId, 'profile.json'), profileId);
195
190
  if (manifest === null)
196
- continue;
197
- out.push({ profileId, manifest });
191
+ unreadableIds.push(profileId);
192
+ else
193
+ entries.push({ profileId, manifest });
198
194
  }
199
- return out;
195
+ return { entries, unreadableIds };
196
+ }
197
+ /** Every profile with a readable manifest, in directory-listing order. A
198
+ * profile whose manifest is missing or corrupt is skipped rather than
199
+ * crashing the whole listing — `lookupProfile` is how a caller that must not
200
+ * confuse "skipped" with "gone" asks. */
201
+ export function listProfiles() {
202
+ return enumerateProfiles().entries;
203
+ }
204
+ /** Exact resolution (generated id, else a unique manifest `name`) that reports
205
+ * WHY it failed. Only an ID can come back `unreadable`: a name lives inside
206
+ * the manifest that could not be read, so an unreadable profile has no name to
207
+ * match on. Throws on an ambiguous name, exactly as `loadProfileManifest`
208
+ * does. */
209
+ export function lookupProfile(profileIdOrName) {
210
+ const { entries, unreadableIds } = enumerateProfiles();
211
+ const exact = findExactProfile(entries, profileIdOrName);
212
+ if (exact !== null)
213
+ return { kind: 'resolved', entry: exact };
214
+ if (unreadableIds.includes(profileIdOrName)) {
215
+ return {
216
+ kind: 'unreadable',
217
+ profileId: profileIdOrName,
218
+ manifestPath: join(profilesRoot(), profileIdOrName, 'profile.json'),
219
+ };
220
+ }
221
+ return { kind: 'absent' };
200
222
  }
201
223
  // Three resolvers, in descending tolerance. `resolveProfileOperand` is what a
202
224
  // command leaf calls for a value the USER TYPED: exact id, exact name, then a
@@ -255,11 +277,13 @@ function profileNotFound(all, profileIdOrName) {
255
277
  });
256
278
  }
257
279
  export function loadProfileManifest(profileIdOrName) {
258
- const all = listProfiles();
259
- const exact = findExactProfile(all, profileIdOrName);
260
- if (exact !== null)
261
- return exact;
262
- throw profileNotFound(all, profileIdOrName);
280
+ const lookup = lookupProfile(profileIdOrName);
281
+ if (lookup.kind === 'resolved')
282
+ return lookup.entry;
283
+ // Unreadable and absent both answer not-found here: this is the tolerant
284
+ // resolver every runtime consumer already handles that way. A caller that
285
+ // must NOT substitute another identity calls `lookupProfile` directly.
286
+ throw profileNotFound(listProfiles(), profileIdOrName);
263
287
  }
264
288
  /** Resolve a profile the USER TYPED — a `--profile` flag or a `<profile>`
265
289
  * operand. Exact id or name wins outright; only when nothing matches exactly
@@ -316,10 +340,8 @@ function writeManifestAtomic(path, manifest) {
316
340
  writeFileSync(tmp, JSON.stringify(manifest, null, 2) + '\n', 'utf8');
317
341
  renameSync(tmp, path);
318
342
  }
319
- // ---------------------------------------------------------------------------
320
343
  // Project dirs — resolved to absolute real paths, must exist + be a directory
321
344
  // on add/create, deduped by real path, stored in manifest order.
322
- // ---------------------------------------------------------------------------
323
345
  function resolveExistingProjectDir(dir) {
324
346
  const abs = resolvePath(dir);
325
347
  if (!existsSync(abs) || !statSync(abs).isDirectory()) {
@@ -364,11 +386,9 @@ function generateProfileId(name) {
364
386
  }
365
387
  throw general(`failed to generate a unique profile id for "${name}" after 20 attempts`);
366
388
  }
367
- // ---------------------------------------------------------------------------
368
389
  // Mutations — all under withProfileManifestLock, all atomic writes, all call
369
390
  // resetScopeCache() when they can change project dirs or the profile name
370
391
  // (never for a bare last-used-at touch).
371
- // ---------------------------------------------------------------------------
372
392
  export function ensureRootProfile() {
373
393
  return withProfileManifestLock(ROOT_PROFILE_ID, () => {
374
394
  const manifestPath = profileManifestPath(ROOT_PROFILE_ID);
@@ -120,7 +120,6 @@ export function selectProfileForCwdReadOnly(cwd) {
120
120
  function isInteractive() {
121
121
  return Boolean(process.stdin.isTTY) && Boolean(process.stdout.isTTY);
122
122
  }
123
- // ---------------------------------------------------------------------------
124
123
  // Pre-boot prompt presentation. These prompts print to the raw controlling
125
124
  // terminal BEFORE pi takes it over (not the attach viewer), so styling is
126
125
  // plain ANSI via the house `stdoutColor` helper (NO_COLOR / non-TTY aware) and
@@ -129,7 +128,6 @@ function isInteractive() {
129
128
  // dim subtitle, then accent-keyed option rows. We deliberately do NOT echo the
130
129
  // user's own cwd back at them (they know where they are) — the only paths
131
130
  // worth showing are OTHER dirs: which project root a covering profile owns.
132
- // ---------------------------------------------------------------------------
133
131
  const dim = stdoutColor.dim;
134
132
  const bold = stdoutColor.bold;
135
133
  const accent = stdoutColor.cyan;
@@ -310,7 +308,6 @@ function runMenu(bindings, title, subtitle, rows, opts = {}) {
310
308
  draw();
311
309
  });
312
310
  }
313
- // ---------------------------------------------------------------------------
314
311
  // The profile manager. `v` on either selection gate opens the Profiles page of
315
312
  // `crtr sys settings` in a tmux popup: this menu is a CHOOSER filtered to cwd,
316
313
  // while that page is the whole map — every profile, every directory each one
@@ -318,7 +315,6 @@ function runMenu(bindings, title, subtitle, rows, opts = {}) {
318
315
  // torn down (runMenu cleans up before resolving an action), and the caller then
319
316
  // re-runs the whole selector, so a directory just adopted or a default just
320
317
  // pinned decides the menu you come back to.
321
- // ---------------------------------------------------------------------------
322
318
  /** Open the profiles page in a tmux popup and block until it closes. False when
323
319
  * there is no tmux to host it — possible only on the `crtr node new` path, as
324
320
  * the front door requires tmux before it ever selects — after printing the
@@ -1,31 +1,12 @@
1
- import { basename, dirname, join } from 'node:path';
2
- import { mkdirSync, watch, writeFileSync } from 'node:fs';
3
- import { crtrHome } from '../canvas/paths.js';
1
+ import { activityPath, signalActivity, watchActivity } from '../activity-signal.js';
4
2
  /** Signal one durable review mutation to every open viewer without watching every review directory. */
5
3
  export function reviewActivityPath() {
6
- return join(crtrHome(), 'signals', 'review-activity');
4
+ return activityPath('review-activity');
7
5
  }
8
6
  /** Signal one review mutation (comment create/edit/resolve/delete/reanchor/delivery) to every open review surface. */
9
7
  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
- }
8
+ signalActivity('review-activity');
17
9
  }
18
10
  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
- }
11
+ return watchActivity('review-activity', onChange);
31
12
  }
@@ -1,13 +1,13 @@
1
1
  /**
2
2
  * Terminal-review ticket visibility filter.
3
- * Hides canonical review tickets by state; passes pre-cutover tickets unchanged.
3
+ * Hides review tickets by canonical state.
4
4
  * This module never imports canvas DB directly; daemon call sites apply the filter.
5
5
  */
6
6
  import type { TicketSummary } from '../human/types.js';
7
7
  /**
8
8
  * Determines if a review ticket should be visible based on its canonical review state.
9
- * A review ticket is hidden when a matching canonical row exists and its state is
10
- * 'binding', 'approved', or 'canceled'. Pre-cutover tickets (no canonical row) remain visible.
9
+ * Only an open row is visible: 'binding', 'approved', and 'canceled' are hidden, and so is a
10
+ * ticket with no row at all — a row is committed before its ticket is ever published.
11
11
  */
12
12
  export declare function isReviewTicketVisible(bridgeNodeId: string): boolean;
13
13
  /**
@@ -1,21 +1,16 @@
1
1
  /**
2
2
  * Terminal-review ticket visibility filter.
3
- * Hides canonical review tickets by state; passes pre-cutover tickets unchanged.
3
+ * Hides review tickets by canonical state.
4
4
  * This module never imports canvas DB directly; daemon call sites apply the filter.
5
5
  */
6
6
  import { getReviewByBridge } from './store.js';
7
7
  /**
8
8
  * Determines if a review ticket should be visible based on its canonical review state.
9
- * A review ticket is hidden when a matching canonical row exists and its state is
10
- * 'binding', 'approved', or 'canceled'. Pre-cutover tickets (no canonical row) remain visible.
9
+ * Only an open row is visible: 'binding', 'approved', and 'canceled' are hidden, and so is a
10
+ * ticket with no row at all — a row is committed before its ticket is ever published.
11
11
  */
12
12
  export function isReviewTicketVisible(bridgeNodeId) {
13
- const review = getReviewByBridge(bridgeNodeId);
14
- // Pre-cutover ticket with no record: visible
15
- if (review === null)
16
- return true;
17
- // Canonical review exists: hide terminal states, show open
18
- return review.state === 'open';
13
+ return getReviewByBridge(bridgeNodeId)?.state === 'open';
19
14
  }
20
15
  /**
21
16
  * Filters a ticket summary array to remove hidden terminal-review tickets.
@@ -9,7 +9,7 @@
9
9
  // session). An idle follow_up must run as a prompt.
10
10
  import { test } from 'node:test';
11
11
  import assert from 'node:assert/strict';
12
- import { resolveEngineRoute } from '../broker/frame-dispatch.js';
12
+ import { resolveEngineRoute } from '../broker/engine-routing.js';
13
13
  const IMAGES = [{ type: 'image', data: 'aGk=', mimeType: 'image/png' }];
14
14
  test('m-C: follow_up on an IDLE session escalates to prompt (no streamingBehavior)', () => {
15
15
  const route = resolveEngineRoute({ type: 'follow_up' }, false);
@@ -102,12 +102,10 @@ test('B: configured model-provider keys cross by exact name', () => {
102
102
  assert.equal(env.ANTHROPIC_API_KEY, 'fake-anthropic');
103
103
  assert.equal(env.OPENAI_API_KEY, 'fake-openai');
104
104
  });
105
- // ---------------------------------------------------------------------------
106
105
  // A2 — proxy userinfo guard (Major 1 fix): must reject username-only,
107
106
  // password-only, and scheme-less userinfo, for BOTH uppercase and lowercase
108
107
  // var names; must keep a plain proxy URL, for both cases; NO_PROXY always
109
108
  // keeps.
110
- // ---------------------------------------------------------------------------
111
109
  test('A2: a credentialed proxy URL is dropped, a plain one crosses (uppercase)', () => {
112
110
  const env = base(LEAK_CLASS_HOST_ENV);
113
111
  assert.equal(env.HTTP_PROXY, undefined, 'userinfo-bearing proxy must not cross');
@@ -142,10 +140,8 @@ test('A2: a non-http(s) scheme or hostless value is dropped, never admitted', ()
142
140
  const env = base({ HTTP_PROXY: 'not a url at all' });
143
141
  assert.equal(env.HTTP_PROXY, undefined);
144
142
  });
145
- // ---------------------------------------------------------------------------
146
143
  // buildBrokerEnv: overlay + drop the leak class through the real process.env
147
144
  // path.
148
- // ---------------------------------------------------------------------------
149
145
  test('buildBrokerEnv overlays inv.env + FRONT_DOOR_ENV on top of the sanitized base, and drops the leak class', () => {
150
146
  const inv = {
151
147
  argv: [],
@@ -170,9 +166,7 @@ test('buildBrokerEnv overlays inv.env + FRONT_DOOR_ENV on top of the sanitized b
170
166
  delete process.env.GIT_ASKPASS;
171
167
  }
172
168
  });
173
- // ---------------------------------------------------------------------------
174
169
  // C — explicit spawnEnv.allow escape hatch: literal names + globs.
175
- // ---------------------------------------------------------------------------
176
170
  test('C: an explicit spawnEnv.allow literal name admits a var that would otherwise be dropped', () => {
177
171
  mkdirSync(join(isolatedHome, '.crouter'), { recursive: true });
178
172
  writeFileSync(join(isolatedHome, '.crouter', 'config.json'), JSON.stringify({ spawnEnv: { allow: ['GOOGLE_GENERATIVE_AI_API_KEY'] } }));
@@ -198,10 +192,8 @@ test('C (glob, Minor fix): a bare "*" (inherit-all) is rejected loud at config r
198
192
  resetScopeCache();
199
193
  assert.throws(() => readMergedLaunchConfig(isolatedCwd, null), /matches every env var name/, 'a bare "*" must fail loud at config read, per cto-rejects-fallback-hedges — never silently literal, never silently dropped');
200
194
  });
201
- // ---------------------------------------------------------------------------
202
195
  // Major 2 fix — spawnEnv.allow resolves from the TARGET's own cwd/profile,
203
196
  // never the calling/launcher process's ambient scope.
204
- // ---------------------------------------------------------------------------
205
197
  test('target-scope: spawnEnv.allow is read from the TARGET cwd, not process.cwd() (the ambient/launcher cwd)', () => {
206
198
  const launcherCwd = realpathSync(mkdtempSync(join(tmpdir(), 'crtr-spawn-env-launcher-')));
207
199
  const targetCwd = realpathSync(mkdtempSync(join(tmpdir(), 'crtr-spawn-env-target-')));
@@ -0,0 +1,56 @@
1
+ import { type NamedNode } from '../canvas/labels.js';
2
+ export declare function escapeAttribute(value: string): string;
3
+ /** The `<project_context>` block — the `<environment>` snapshot only: a
4
+ * host/runtime line (platform, hostname, node version, tmux-or-not — the
5
+ * headless engine's own process env, which its bash tools inherit), the cwd
6
+ * dir listing, and git status/worktrees. Project guidance lives in the
7
+ * document substrate, so this wrapper stays environment-only. It always emits
8
+ * because the environment snapshot is always present. */
9
+ export declare function buildProjectContextBlock(cwd: string): string;
10
+ /** The context-directory note — names the path INLINE (no XML attribute, so it
11
+ * never scopes a sibling block) and frames the dir as durable, shared scratch.
12
+ * Present for every node, rendered as the `## Your context directory`
13
+ * subsection of <crtr-bearings>. */
14
+ export declare function contextDirNote(nodeId: string): string;
15
+ /** The profile-purview note — rendered ONLY when the profile resolves to a
16
+ * manifest that names at least one project dir. Phrased as PURVIEW, not a cwd
17
+ * replacement: the node's cwd is still its one working dir (bash/edits run
18
+ * there), the profile just widens what it may ALSO read/act on. Omitted
19
+ * entirely for a profile with no project purview (including the stable root
20
+ * profile) or a deleted/unreadable manifest — never throws. This absolute-path
21
+ * disclosure is intentional (spec-approved): the agent must know its full
22
+ * purview, not a redacted count. */
23
+ export declare function profileNote(cwd: string, profileId: string | null | undefined): string;
24
+ /** Orchestrator-only framing: a resident orchestrator survives refresh cycles,
25
+ * so its context dir is also where a future cycle of itself resumes the work.
26
+ * Used inside the bearings block AND in the promotion guidance dump, so a
27
+ * promoted node gets the same note a born-orchestrator gets. */
28
+ export declare function orchestratorContextNote(nodeId: string): string;
29
+ export interface RelativeLabelNode extends NamedNode {
30
+ node_id: string;
31
+ kind: string;
32
+ status: string;
33
+ name: string;
34
+ description?: string;
35
+ icon?: string;
36
+ }
37
+ /** One relative's line in the map: `<id> — <kind> · <status>`, plus the node's
38
+ * task description when it carries one (orientation: what that relative is for). */
39
+ export declare function relativeLabel(node: RelativeLabelNode): string;
40
+ /** The graph mini-map: the ancestry trunk (root first), then this node marked
41
+ * `● you`, then its immediate children. Returns '' for a lone root with no
42
+ * children (nothing to map). */
43
+ export declare function renderGraphMap(nodeId: string, kind: string, mode: string, trunk: readonly RelativeLabelNode[], children: readonly RelativeLabelNode[]): string;
44
+ /** The identity assertion injected at session start. A fork names its source
45
+ * while treating the copied first-person narrative as reference. The caller
46
+ * resolves `sourceLabel`: the bare `forkFrom` id when the source lookup
47
+ * fails/is unsafe, else `${forkFrom} ("${fullName(src)}")`. */
48
+ export declare function buildIdentityAssertion(nodeId: string, kind: string, mode: string, forkFrom: string | null | undefined, sourceLabel: string): string;
49
+ export interface ManagedWorktreeLike {
50
+ state: string;
51
+ path: string;
52
+ branch: string;
53
+ base_ref: string;
54
+ base_sha: string;
55
+ }
56
+ export declare function worktreeNote(worktree: ManagedWorktreeLike | null | undefined): string;