@north-light/crouter 0.3.233 → 0.3.234

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 (379) hide show
  1. package/dist/api/client.js +16 -15
  2. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/integration/provider-rotation.test.ts +1 -1
  3. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/claude-plugin-commands.ts +14 -17
  4. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/crtr-commands/index.ts +7 -11
  5. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/frontmatter-rules/index.ts +8 -8
  6. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +10 -11
  7. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/pi-shell-runner.ts +2 -2
  8. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +3 -3
  9. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +7 -7
  10. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +8 -8
  11. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/statusline.ts +6 -6
  12. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/sysprompt-window.ts +3 -3
  13. package/dist/builtin-pi-packages/pi-crtr-extensions/lib/subscription-state.ts +1 -1
  14. package/dist/clients/attach/chrome/ticket-panel.js +15 -2
  15. package/dist/clients/attach/overlays/graph.js +0 -3
  16. package/dist/clients/attach/overlays/mcp.js +1 -2
  17. package/dist/clients/attach/render/chat-view.js +0 -12
  18. package/dist/clients/attach/render/crtr-output.js +0 -2
  19. package/dist/clients/attach/render/tool-calls.js +0 -6
  20. package/dist/clients/attach/slash/dispatch.js +8 -11
  21. package/dist/clients/attach/viewer.js +532 -534
  22. package/dist/clients/conversation/projection.js +0 -2
  23. package/dist/clients/inbox/controller.js +22 -3
  24. package/dist/clients/inbox/review/companion-pane.js +0 -4
  25. package/dist/clients/inbox/review-adapter.js +0 -7
  26. package/dist/clients/inbox/tui/slots.d.ts +3 -3
  27. package/dist/clients/inbox/tui/types.d.ts +2 -2
  28. package/dist/commands/api-client.js +0 -8
  29. package/dist/commands/attention.js +0 -8
  30. package/dist/commands/canvas-browse.js +2 -1
  31. package/dist/commands/canvas-config.js +0 -10
  32. package/dist/commands/canvas-history.js +2 -1
  33. package/dist/commands/cron.js +5 -18
  34. package/dist/commands/dashboard.js +0 -2
  35. package/dist/commands/human/feedback.js +2 -1
  36. package/dist/commands/human/prompts.js +2 -5
  37. package/dist/commands/human/queue.js +2 -7
  38. package/dist/commands/human/request.js +0 -14
  39. package/dist/commands/human/review.js +2 -1
  40. package/dist/commands/memory/__tests__/command-selector-and-mutation-guards.test.js +4 -4
  41. package/dist/commands/memory/find.js +2 -2
  42. package/dist/commands/memory/lint.js +2 -1
  43. package/dist/commands/memory/read.d.ts +2 -7
  44. package/dist/commands/memory/read.js +5 -16
  45. package/dist/commands/memory/shared.js +4 -7
  46. package/dist/commands/memory/write.js +4 -3
  47. package/dist/commands/node/bash.js +2 -1
  48. package/dist/commands/node/create.js +2 -1
  49. package/dist/commands/node/inspect.js +0 -6
  50. package/dist/commands/node/lifecycle.js +4 -13
  51. package/dist/commands/node/message.js +3 -2
  52. package/dist/commands/node/subscription.js +2 -1
  53. package/dist/commands/node/wait.js +4 -3
  54. package/dist/commands/node-context.js +2 -1
  55. package/dist/commands/node-lifecycle-revive.js +0 -2
  56. package/dist/commands/node-worktree.js +2 -1
  57. package/dist/commands/pkg/browse/actions.d.ts +1 -1
  58. package/dist/commands/pkg/browse/actions.js +4 -4
  59. package/dist/commands/pkg/browse/catalog.d.ts +1 -1
  60. package/dist/commands/pkg/browse/catalog.js +15 -15
  61. package/dist/commands/pkg/browse/command-view.d.ts +1 -1
  62. package/dist/commands/pkg/browse/command-view.js +3 -3
  63. package/dist/commands/pkg/browse/detail-panel.d.ts +1 -1
  64. package/dist/commands/pkg/browse/detail-panel.js +8 -8
  65. package/dist/commands/pkg/browse/doc-view.d.ts +1 -1
  66. package/dist/commands/pkg/browse/doc-view.js +3 -3
  67. package/dist/commands/pkg/browse/list-panel.d.ts +1 -1
  68. package/dist/commands/pkg/browse/list-panel.js +3 -3
  69. package/dist/commands/pkg/browse/model.d.ts +3 -3
  70. package/dist/commands/pkg/browse/shell.d.ts +2 -2
  71. package/dist/commands/pkg/browse/shell.js +13 -13
  72. package/dist/commands/pkg/plugin-manage.js +2 -1
  73. package/dist/commands/pkg/shared.js +0 -2
  74. package/dist/commands/profile/delete.js +2 -1
  75. package/dist/commands/push.js +2 -3
  76. package/dist/commands/revive.js +0 -2
  77. package/dist/commands/surface/node/placement.js +2 -1
  78. package/dist/commands/surface-edit.js +2 -1
  79. package/dist/commands/sys/config.js +0 -4
  80. package/dist/commands/sys/daemon.js +0 -10
  81. package/dist/commands/sys/doctor.js +3 -2
  82. package/dist/commands/sys/panels/broker-limits-panel.d.ts +2 -2
  83. package/dist/commands/sys/panels/broker-limits-panel.js +4 -4
  84. package/dist/commands/sys/panels/dependencies-panel.d.ts +2 -2
  85. package/dist/commands/sys/panels/dependencies-panel.js +2 -2
  86. package/dist/commands/sys/panels/keybindings-panel.d.ts +1 -1
  87. package/dist/commands/sys/panels/keybindings-panel.js +5 -5
  88. package/dist/commands/sys/panels/models-panel.d.ts +4 -4
  89. package/dist/commands/sys/panels/models-panel.js +11 -10
  90. package/dist/commands/sys/panels/plugins-panel.d.ts +2 -2
  91. package/dist/commands/sys/panels/plugins-panel.js +2 -2
  92. package/dist/commands/sys/panels/prefs-panel.d.ts +2 -2
  93. package/dist/commands/sys/panels/prefs-panel.js +4 -4
  94. package/dist/commands/sys/panels/profiles-panel.d.ts +1 -1
  95. package/dist/commands/sys/panels/profiles-panel.js +14 -13
  96. package/dist/commands/sys/panels/provider-panel.d.ts +3 -3
  97. package/dist/commands/sys/panels/provider-panel.js +8 -8
  98. package/dist/commands/sys/settings-shell.d.ts +3 -3
  99. package/dist/commands/sys/settings-shell.js +15 -15
  100. package/dist/commands/sys/setup-settings-runner.d.ts +3 -3
  101. package/dist/commands/sys/setup-settings-runner.js +5 -5
  102. package/dist/commands/sys/setup-wizard.d.ts +4 -4
  103. package/dist/commands/sys/setup-wizard.js +8 -8
  104. package/dist/commands/sys/support/prepare.js +4 -4
  105. package/dist/commands/sys/support/submit.js +2 -2
  106. package/dist/commands/sys/sync-deps.js +0 -2
  107. package/dist/commands/sys/sync-project-guidance.js +2 -1
  108. package/dist/commands/sys/sysprompt.js +2 -1
  109. package/dist/core/__tests__/boot.test.js +0 -8
  110. package/dist/core/__tests__/broker-extension-canvas-db-boundary.test.js +23 -1
  111. package/dist/core/__tests__/canvas-db-boundary.test.d.ts +1 -0
  112. package/dist/core/__tests__/canvas-db-boundary.test.js +46 -0
  113. package/dist/core/__tests__/canvas.test.js +1 -1
  114. package/dist/core/__tests__/child-death-wake.test.js +0 -2
  115. package/dist/core/__tests__/cron-armed-context-failure.test.d.ts +1 -0
  116. package/dist/core/__tests__/cron-armed-context-failure.test.js +145 -0
  117. package/dist/core/__tests__/daemon-wedge.test.js +0 -4
  118. package/dist/core/__tests__/dead-node-policy-table.test.js +0 -4
  119. package/dist/core/__tests__/fixtures/fake-engine.js +4 -14
  120. package/dist/core/__tests__/fixtures/fake-pi-host.js +5 -5
  121. package/dist/core/__tests__/focuses.test.js +0 -6
  122. package/dist/core/__tests__/fs-utils.test.d.ts +1 -0
  123. package/dist/core/__tests__/fs-utils.test.js +50 -0
  124. package/dist/core/__tests__/helpers/harness.js +1 -1
  125. package/dist/core/__tests__/inline-memory-refs.test.js +0 -2
  126. package/dist/core/__tests__/integration/broker-fork-seam.test.js +0 -2
  127. package/dist/core/__tests__/integration/broker-sdk-wiring.test.js +3 -16
  128. package/dist/core/__tests__/integration/command-plugins.test.js +0 -24
  129. package/dist/core/__tests__/integration/deferred-no-wake.test.js +0 -6
  130. package/dist/core/__tests__/integration/flagship-lifecycle.test.js +0 -26
  131. package/dist/core/__tests__/integration/live-mutation-verbs.test.js +0 -4
  132. package/dist/core/__tests__/integration/live-mutation.test.js +1 -5
  133. package/dist/core/__tests__/integration/refresh-stall-recycle.test.js +1 -5
  134. package/dist/core/__tests__/integration/revive.test.js +0 -12
  135. package/dist/core/__tests__/integration/subscription-delivery.test.js +0 -6
  136. package/dist/core/__tests__/integration/tmux-surface.test.js +24 -8
  137. package/dist/core/__tests__/integration/worktree-reap.test.js +0 -6
  138. package/dist/core/__tests__/lifecycle.test.js +0 -18
  139. package/dist/core/__tests__/memory-resolver-precedence.test.js +1 -1
  140. package/dist/core/__tests__/migration.test.js +2 -2
  141. package/dist/core/__tests__/on-read-dedup-resume.test.js +3 -3
  142. package/dist/core/__tests__/pid-identity-match.test.js +1 -1
  143. package/dist/core/__tests__/plugin-kinds.test.js +1 -1
  144. package/dist/core/__tests__/plugin-page-components.test.js +1 -1
  145. package/dist/core/__tests__/preview-registry-sync.test.js +42 -1
  146. package/dist/core/__tests__/respawn-throttle.test.js +0 -4
  147. package/dist/core/__tests__/revive-all.test.js +5 -5
  148. package/dist/core/__tests__/revive-parked-fresh.test.js +62 -11
  149. package/dist/core/__tests__/seam/broker-attach-multiclient.test.js +0 -8
  150. package/dist/core/__tests__/seam/broker-attach-stream.test.js +0 -8
  151. package/dist/core/__tests__/seam/broker-crash-teardown.test.js +0 -2
  152. package/dist/core/__tests__/tmux-driver-boundary.test.d.ts +1 -0
  153. package/dist/core/__tests__/tmux-driver-boundary.test.js +50 -0
  154. package/dist/core/activity-signal.d.ts +7 -0
  155. package/dist/core/activity-signal.js +38 -0
  156. package/dist/core/auto-update.js +2 -1
  157. package/dist/core/bootstrap.js +2 -1
  158. package/dist/core/canvas/__tests__/remote-canvas-source.test.js +1 -1
  159. package/dist/core/canvas/__tests__/remote-transport.test.js +1 -1
  160. package/dist/core/canvas/__tests__/render-remote.test.js +1 -1
  161. package/dist/core/canvas/browse/app.js +3 -2
  162. package/dist/core/canvas/browse/model.d.ts +1 -1
  163. package/dist/core/canvas/browse/model.js +0 -4
  164. package/dist/core/canvas/canvas.d.ts +22 -0
  165. package/dist/core/canvas/canvas.js +50 -18
  166. package/dist/core/canvas/crons.js +0 -6
  167. package/dist/core/canvas/db.d.ts +14 -20
  168. package/dist/core/canvas/db.js +30 -1332
  169. package/dist/core/canvas/focuses.js +0 -4
  170. package/dist/core/canvas/history.js +2 -1
  171. package/dist/core/canvas/migrations.d.ts +20 -0
  172. package/dist/core/canvas/migrations.js +1339 -0
  173. package/dist/core/canvas/nav-model.js +1 -11
  174. package/dist/core/canvas/nav-render.js +0 -8
  175. package/dist/core/canvas/remote-canvas-source.js +0 -2
  176. package/dist/core/canvas/render-source.js +31 -13
  177. package/dist/core/command-manifests/manifest.js +0 -10
  178. package/dist/core/command-manifests/schema.js +0 -4
  179. package/dist/core/command-plugins/transport/http-invoke.js +0 -14
  180. package/dist/core/command.js +0 -4
  181. package/dist/core/config.js +2 -1
  182. package/dist/core/feed/inbox.d.ts +10 -0
  183. package/dist/core/feed/inbox.js +18 -12
  184. package/dist/core/fs-utils.d.ts +9 -2
  185. package/dist/core/fs-utils.js +7 -15
  186. package/dist/core/help.js +0 -8
  187. package/dist/core/host-exports/export.d.ts +2 -2
  188. package/dist/core/host-exports/export.js +9 -12
  189. package/dist/core/human/answer.d.ts +2 -2
  190. package/dist/core/human/convention.d.ts +2 -5
  191. package/dist/core/human/convention.js +3 -13
  192. package/dist/core/human/page-schema.d.ts +2 -2
  193. package/dist/core/human/page-schema.js +2 -2
  194. package/dist/core/human/root.js +6 -23
  195. package/dist/core/human/scan.js +24 -7
  196. package/dist/core/human/tickets.d.ts +4 -1
  197. package/dist/core/human/tickets.js +20 -6
  198. package/dist/core/io.js +0 -12
  199. package/dist/core/memory/identity.js +0 -4
  200. package/dist/core/memory/inline-ref-inventory.js +3 -2
  201. package/dist/core/memory/lint.js +3 -6
  202. package/dist/core/memory-resolver.d.ts +5 -0
  203. package/dist/core/memory-resolver.js +14 -12
  204. package/dist/core/pagination.js +0 -2
  205. package/dist/core/preview-registry.d.ts +14 -0
  206. package/dist/core/preview-registry.js +2 -4
  207. package/dist/core/preview-result-path.js +2 -1
  208. package/dist/core/profiles/manifest.d.ts +37 -5
  209. package/dist/core/profiles/manifest.js +44 -24
  210. package/dist/core/profiles/select.js +0 -4
  211. package/dist/core/review/signal.js +4 -23
  212. package/dist/core/review/ticket-filter.d.ts +3 -3
  213. package/dist/core/review/ticket-filter.js +4 -9
  214. package/dist/core/runtime/__tests__/engine-route.test.js +1 -1
  215. package/dist/core/runtime/__tests__/spawn-env.test.js +0 -8
  216. package/dist/core/runtime/bearings-render.d.ts +56 -0
  217. package/dist/core/runtime/bearings-render.js +241 -0
  218. package/dist/core/runtime/bearings.d.ts +2 -17
  219. package/dist/core/runtime/bearings.js +12 -198
  220. package/dist/core/runtime/broker/auth-reload.d.ts +39 -0
  221. package/dist/core/runtime/broker/auth-reload.js +149 -0
  222. package/dist/core/runtime/broker/engine-drive.d.ts +26 -0
  223. package/dist/core/runtime/broker/engine-drive.js +257 -0
  224. package/dist/core/runtime/broker/engine-routing.d.ts +143 -0
  225. package/dist/core/runtime/broker/engine-routing.js +211 -0
  226. package/dist/core/runtime/broker/event-projection.js +3 -2
  227. package/dist/core/runtime/broker/fault-retry.js +5 -4
  228. package/dist/core/runtime/broker/frame-client.d.ts +38 -0
  229. package/dist/core/runtime/broker/frame-client.js +129 -0
  230. package/dist/core/runtime/broker/frame-dispatch.d.ts +12 -189
  231. package/dist/core/runtime/broker/frame-dispatch.js +89 -1476
  232. package/dist/core/runtime/broker/frame-engine-control.d.ts +20 -0
  233. package/dist/core/runtime/broker/frame-engine-control.js +231 -0
  234. package/dist/core/runtime/broker/frame-memory-refs.d.ts +19 -0
  235. package/dist/core/runtime/broker/frame-memory-refs.js +41 -0
  236. package/dist/core/runtime/broker/frame-model-query.d.ts +13 -0
  237. package/dist/core/runtime/broker/frame-model-query.js +69 -0
  238. package/dist/core/runtime/broker/frame-read.d.ts +19 -0
  239. package/dist/core/runtime/broker/frame-read.js +116 -0
  240. package/dist/core/runtime/broker/frame-replies.d.ts +24 -0
  241. package/dist/core/runtime/broker/frame-replies.js +64 -0
  242. package/dist/core/runtime/broker/frame-session.d.ts +34 -0
  243. package/dist/core/runtime/broker/frame-session.js +203 -0
  244. package/dist/core/runtime/broker/turn-ignition.d.ts +42 -0
  245. package/dist/core/runtime/broker/turn-ignition.js +74 -0
  246. package/dist/core/runtime/broker-extension-render.js +9 -85
  247. package/dist/core/runtime/broker-protocol.js +0 -4
  248. package/dist/core/runtime/broker-sdk.js +2 -1
  249. package/dist/core/runtime/broker.d.ts +1 -1
  250. package/dist/core/runtime/broker.js +5 -23
  251. package/dist/core/runtime/fault.js +3 -2
  252. package/dist/core/runtime/headless-pi.js +3 -2
  253. package/dist/core/runtime/host.js +2 -3
  254. package/dist/core/runtime/kickoff.js +0 -4
  255. package/dist/core/runtime/launch-target.js +2 -1
  256. package/dist/core/runtime/launch.js +3 -6
  257. package/dist/core/runtime/lifecycle.js +17 -26
  258. package/dist/core/runtime/managed-provider-cooling.d.ts +1 -1
  259. package/dist/core/runtime/naming.js +0 -2
  260. package/dist/core/runtime/nodes.js +2 -1
  261. package/dist/core/runtime/persona.js +0 -6
  262. package/dist/core/runtime/placement-tmux.js +2 -5
  263. package/dist/core/runtime/placement.js +4 -11
  264. package/dist/core/runtime/recap.js +4 -3
  265. package/dist/core/runtime/reopen.js +4 -7
  266. package/dist/core/runtime/reset.js +0 -4
  267. package/dist/core/runtime/resume-root.js +0 -2
  268. package/dist/core/runtime/revive.js +17 -13
  269. package/dist/core/runtime/session-list-cache.js +2 -2
  270. package/dist/core/runtime/shell-expansion.js +0 -2
  271. package/dist/core/runtime/spawn.js +0 -2
  272. package/dist/core/runtime/stream-watchdog.d.ts +0 -1
  273. package/dist/core/runtime/stream-watchdog.js +13 -18
  274. package/dist/core/runtime/tmux-bindings.js +0 -2
  275. package/dist/core/runtime/tmux-driver.d.ts +12 -0
  276. package/dist/core/runtime/tmux-driver.js +18 -13
  277. package/dist/core/runtime/warm-pool.js +0 -4
  278. package/dist/core/scope.js +4 -3
  279. package/dist/core/spawn.js +2 -3
  280. package/dist/core/subscription-state.js +10 -17
  281. package/dist/core/substrate/__tests__/surface-match-pre-command.test.js +3 -3
  282. package/dist/core/substrate/render.js +0 -12
  283. package/dist/core/substrate/schema.js +0 -12
  284. package/dist/core/substrate/surface-match.js +0 -2
  285. package/dist/core/termrender/display.js +2 -1
  286. package/dist/core/tui/page-host.d.ts +3 -3
  287. package/dist/core/tui/page-host.js +4 -4
  288. package/dist/core/tui/panel.js +3 -3
  289. package/dist/core/wake.js +0 -12
  290. package/dist/core/worktree.js +0 -2
  291. package/dist/daemon/__tests__/reconciler-signature.test.d.ts +1 -0
  292. package/dist/daemon/__tests__/reconciler-signature.test.js +36 -0
  293. package/dist/daemon/api/__tests__/seam/api-server.test.js +2 -16
  294. package/dist/daemon/api/__tests__/seam/b10-attach-modelauth.test.js +3 -11
  295. package/dist/daemon/api/__tests__/seam/leaf-api-parity.test.js +0 -6
  296. package/dist/daemon/api/handlers/attach.js +2 -1
  297. package/dist/daemon/api/handlers/broker-ops.js +3 -6
  298. package/dist/daemon/api/handlers/canvas.js +12 -18
  299. package/dist/daemon/api/handlers/feedback-comments.js +0 -8
  300. package/dist/daemon/api/handlers/health.js +3 -2
  301. package/dist/daemon/api/handlers/human-requests.js +0 -10
  302. package/dist/daemon/api/handlers/human.js +1 -5
  303. package/dist/daemon/api/handlers/inbox.js +0 -18
  304. package/dist/daemon/api/handlers/memory.js +1 -1
  305. package/dist/daemon/api/handlers/messages.js +4 -8
  306. package/dist/daemon/api/handlers/nodes.js +10 -21
  307. package/dist/daemon/api/handlers/reports.js +1 -1
  308. package/dist/daemon/api/handlers/reviews.js +1 -6
  309. package/dist/daemon/api/map.js +0 -6
  310. package/dist/daemon/api/router.js +0 -4
  311. package/dist/daemon/api/server.js +2 -5
  312. package/dist/daemon/boot-migrations.d.ts +14 -0
  313. package/dist/daemon/boot-migrations.js +31 -0
  314. package/dist/daemon/cron/armed-context.d.ts +21 -0
  315. package/dist/daemon/cron/armed-context.js +100 -0
  316. package/dist/daemon/cron/capture.d.ts +20 -0
  317. package/dist/daemon/cron/capture.js +45 -0
  318. package/dist/daemon/cron/disposition.d.ts +19 -0
  319. package/dist/daemon/cron/disposition.js +52 -0
  320. package/dist/daemon/cron/escalation.d.ts +12 -0
  321. package/dist/daemon/cron/escalation.js +124 -0
  322. package/dist/daemon/cron/held.d.ts +4 -0
  323. package/dist/daemon/cron/held.js +20 -0
  324. package/dist/daemon/cron/lease-recovery.d.ts +3 -0
  325. package/dist/daemon/cron/lease-recovery.js +119 -0
  326. package/dist/daemon/cron/live-runs.d.ts +11 -0
  327. package/dist/daemon/cron/live-runs.js +23 -0
  328. package/dist/daemon/cron/passes.d.ts +11 -0
  329. package/dist/daemon/cron/passes.js +78 -0
  330. package/dist/daemon/cron/settle.d.ts +28 -0
  331. package/dist/daemon/cron/settle.js +37 -0
  332. package/dist/daemon/cron/sinks.d.ts +2 -0
  333. package/dist/daemon/cron/sinks.js +100 -0
  334. package/dist/daemon/cron-run.d.ts +5 -41
  335. package/dist/daemon/cron-run.js +193 -882
  336. package/dist/daemon/crtrd-cli.js +2 -1
  337. package/dist/daemon/crtrd.js +22 -22
  338. package/dist/daemon/fleet.js +0 -4
  339. package/dist/daemon/human/deliver-action.js +2 -1
  340. package/dist/daemon/human/sweep.js +15 -4
  341. package/dist/daemon/manage.d.ts +2 -2
  342. package/dist/daemon/manage.js +4 -13
  343. package/dist/daemon/pidfile.js +2 -2
  344. package/dist/daemon/reconcilers/broker-supervision.js +26 -29
  345. package/dist/daemon/reconcilers/dormant-inbox.d.ts +1 -1
  346. package/dist/daemon/reconcilers/dormant-inbox.js +2 -1
  347. package/dist/daemon/reconcilers/live-obligation.js +2 -2
  348. package/dist/daemon/reconcilers/pending-review-submit.d.ts +1 -1
  349. package/dist/daemon/reconcilers/pending-review-submit.js +2 -1
  350. package/dist/daemon/reconcilers/storage-maintenance.d.ts +2 -1
  351. package/dist/daemon/reconcilers/storage-maintenance.js +2 -1
  352. package/dist/daemon/review/companion.js +2 -2
  353. package/dist/daemon/review/sweep.js +8 -3
  354. package/dist/migrations/001-surfaces-frontmatter.js +0 -6
  355. package/dist/migrations/corpus.js +2 -5
  356. package/dist/migrations/registry.js +15 -2
  357. package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +0 -4
  358. package/dist/pi-extensions/broker-local.js +2 -1
  359. package/dist/pi-extensions/canvas-bash-valve.js +3 -17
  360. package/dist/pi-extensions/canvas-context-intro.js +2 -7
  361. package/dist/pi-extensions/canvas-doc-substrate.js +2 -3
  362. package/dist/pi-extensions/canvas-goal-capture.js +2 -1
  363. package/dist/pi-extensions/canvas-inbox-watcher.js +5 -12
  364. package/dist/pi-extensions/canvas-passive-context.js +2 -5
  365. package/dist/pi-extensions/canvas-preview-result.js +2 -1
  366. package/dist/pi-extensions/canvas-prompt-scrub.js +2 -1
  367. package/dist/pi-extensions/canvas-recap.js +3 -16
  368. package/dist/pi-extensions/canvas-review-boundary.js +2 -5
  369. package/dist/pi-extensions/canvas-stophook.js +2 -26
  370. package/dist/pi-extensions/canvas-structured-output.js +2 -1
  371. package/dist/pi-extensions/canvas-tool-guide.js +2 -1
  372. package/dist/shared/__tests__/env-boundary.test.d.ts +1 -0
  373. package/dist/shared/__tests__/env-boundary.test.js +93 -0
  374. package/dist/shared/env.d.ts +68 -0
  375. package/dist/shared/env.js +171 -0
  376. package/package.json +3 -1
  377. package/runtime.lock.json +2 -2
  378. package/dist/core/canvas/render.d.ts +0 -47
  379. package/dist/core/canvas/render.js +0 -245
@@ -126,7 +126,7 @@ export const findLeaf = defineLeaf({
126
126
  const units = selector.store !== null
127
127
  ? searchUnits(kindFilter, loadMemoryStoreView(requireMountedStore(selector.store), false).docs, false)
128
128
  : searchUnits(kindFilter, listAllMemoryDocs(selector.scope, false, true), true);
129
- // --- grep mode: regex over every unit's body, one row per matching line ---
129
+ // grep mode: regex over every unit's body, one row per matching line
130
130
  if (grep) {
131
131
  let regex;
132
132
  try {
@@ -167,7 +167,7 @@ export const findLeaf = defineLeaf({
167
167
  registerMemoryListingExposure(listingIdentity);
168
168
  return output;
169
169
  }
170
- // --- ranked mode: weighted relevance over name/routing-line/short-form(+body) ---
170
+ // ranked mode: weighted relevance over name/routing-line/short-form(+body)
171
171
  const terms = query
172
172
  .toLowerCase()
173
173
  .split(/\s+/)
@@ -3,6 +3,7 @@
3
3
  // verification, the plugin candidate gate, and the build's builtin gate consume
4
4
  // too; this leaf only picks WHAT to lint (a target view, or one exact store
5
5
  // with --dir) and shapes the findings for the CLI.
6
+ import { envProfileId } from '../../shared/env.js';
6
7
  import { defineLeaf } from '../../core/command.js';
7
8
  import { warn } from '../../core/output.js';
8
9
  import { ExitCode } from '../../types.js';
@@ -26,7 +27,7 @@ function findingRow(finding) {
26
27
  function resolvableNamesForStore(store) {
27
28
  const view = loadMemoryTargetView({
28
29
  cwd: store.ownerDir ?? store.storeRoot,
29
- profileId: process.env['CRTR_PROFILE_ID'] || null,
30
+ profileId: envProfileId() || null,
30
31
  nodeId: null,
31
32
  }, { quiet: true, includeDescendants: true });
32
33
  return resolvableCanonicalNames(view.docs);
@@ -1,11 +1,6 @@
1
- import { createMemoryDocSnapshot, resolveMemoryDocs } from '../../core/memory-resolver.js';
1
+ import { createMemoryDocSnapshot, readMemoryDocContent, resolveMemoryDocs } from '../../core/memory-resolver.js';
2
2
  import type { MemoryDoc } from '../../core/memory-resolver.js';
3
- export { createMemoryDocSnapshot, resolveMemoryDocs };
4
- /** Load the body at an already-resolved memory path with the same frontmatter
5
- * stripping and in-node path interpolation as `memory read`. Callers that
6
- * already have the resolved path (such as slash-command discovery) avoid
7
- * re-walking every memory store for each document. */
8
- export declare function readMemoryDocContent(path: string, includeFrontmatter?: boolean): string;
3
+ export { createMemoryDocSnapshot, readMemoryDocContent, resolveMemoryDocs };
9
4
  /** One physical document sharing a canonical identity, as the path-aware
10
5
  * surfaces report it. `--dir <its project>` is how a non-winner is reached. */
11
6
  export declare function candidateRow(doc: MemoryDoc, winner: MemoryDoc): Record<string, unknown>;
@@ -1,12 +1,11 @@
1
+ import { envNodeId } from '../../shared/env.js';
1
2
  import { CrtrClient } from '../../api/index.js';
2
- import { interpolateNodePaths } from '../../core/canvas/paths.js';
3
3
  import { defineLeaf } from '../../core/command.js';
4
4
  import { CrtrError } from '../../core/errors.js';
5
5
  import { memoryExtensionEffectiveCatalog, projectEffectiveMemoryExtensions } from '../../core/memory/extensions.js';
6
- import { readText, realpathOrSelf } from '../../core/fs-utils.js';
7
- import { parseFrontmatterGeneric } from '../../core/frontmatter.js';
6
+ import { realpathOrSelf } from '../../core/fs-utils.js';
8
7
  import { docLinkNames } from '../../core/memory/doc-link-grammar.js';
9
- import { createMemoryDocSnapshot, listAllMemoryDocs, loadMemoryStoreView, resolveMemoryCandidates, resolveMemoryDocs, } from '../../core/memory-resolver.js';
8
+ import { createMemoryDocSnapshot, listAllMemoryDocs, loadMemoryStoreView, readMemoryDocContent, resolveMemoryCandidates, resolveMemoryDocs, } from '../../core/memory-resolver.js';
10
9
  import { expandShellBlocks, hasShellBlocks, makeNodeShellRunner } from '../../core/runtime/shell-expansion.js';
11
10
  import { emptyContextExposureState, exposedAtOrAbove, exposureTarget, loadContextExposureState, registerDocumentExposure, registerExposure, saveContextExposureState, } from '../../core/substrate/injected-store.js';
12
11
  import { dirDedupKey, docListingDirs, docsByName, isDirName, renderDirListing } from '../../core/substrate/listings.js';
@@ -15,17 +14,7 @@ import { renderResult } from '../../core/render.js';
15
14
  import { effectiveDocKind } from '../../core/substrate/schema.js';
16
15
  import { canonicalSegments } from '../../core/memory/identity.js';
17
16
  import { MEMORY_KINDS, requireMountedStore, resolveReadSelector, selectorParam } from './shared.js';
18
- export { createMemoryDocSnapshot, resolveMemoryDocs };
19
- /** Load the body at an already-resolved memory path with the same frontmatter
20
- * stripping and in-node path interpolation as `memory read`. Callers that
21
- * already have the resolved path (such as slash-command discovery) avoid
22
- * re-walking every memory store for each document. */
23
- export function readMemoryDocContent(path, includeFrontmatter = false) {
24
- const raw = readText(path);
25
- const content = includeFrontmatter ? raw : parseFrontmatterGeneric(raw).body;
26
- const nodeId = process.env['CRTR_NODE_ID'];
27
- return nodeId ? interpolateNodePaths(content, nodeId) : content;
28
- }
17
+ export { createMemoryDocSnapshot, readMemoryDocContent, resolveMemoryDocs };
29
18
  /** The node-config subject for memory-read-event gate evaluation. A CLI
30
19
  * process cannot take it from canvas-db (V-1); one `/v1` request supplies it.
31
20
  * With no node identity or no reachable daemon the answer is null — gated
@@ -150,7 +139,7 @@ export const readLeaf = defineLeaf({
150
139
  }
151
140
  fail = () => resolveMemoryCandidates(nameRaw, resolveOpts);
152
141
  }
153
- const nodeId = process.env['CRTR_NODE_ID'] || undefined;
142
+ const nodeId = envNodeId() || undefined;
154
143
  if (set === null) {
155
144
  // No document answers this name — a directory with no document of its own
156
145
  // still does, through its listing. An explicit dir read is a deliberate
@@ -3,6 +3,7 @@
3
3
  // scope modules and build their documented output objects on top of the small
4
4
  // helpers here. Nothing in this file forks on kind or re-implements the
5
5
  // schema/gate/resolver — it only composes them.
6
+ import { envNodeId, envProfileId } from '../../shared/env.js';
6
7
  import { existsSync, readdirSync, realpathSync, rmdirSync, statSync } from 'node:fs';
7
8
  import { dirname, join, resolve as resolvePath } from 'node:path';
8
9
  import { stringify as yamlStringify, parse as yamlParse } from 'yaml';
@@ -40,7 +41,7 @@ export function scopeRank(scope) {
40
41
  return scope === 'node' ? -1 : scope === 'project' ? 0 : scope === 'profile' ? 1 : scope === 'user' ? 2 : 3;
41
42
  }
42
43
  export function registerMemoryListingExposure(identity) {
43
- const nodeId = process.env['CRTR_NODE_ID'];
44
+ const nodeId = envNodeId();
44
45
  if (nodeId === undefined || nodeId === '')
45
46
  return;
46
47
  const state = loadContextExposureState(nodeId);
@@ -151,7 +152,7 @@ export function resolveWriteSelector(input) {
151
152
  if (scope === 'node') {
152
153
  // The this-node store: the same dir render.ts's nodeLocalDocs loads at
153
154
  // boot, so a doc written here rides into this node's knowledge block.
154
- const nodeId = process.env['CRTR_NODE_ID'] || '';
155
+ const nodeId = envNodeId() || '';
155
156
  if (nodeId === '') {
156
157
  throw usage('node scope requires a running node (CRTR_NODE_ID unset); rerun inside a node');
157
158
  }
@@ -163,7 +164,7 @@ export function resolveWriteSelector(input) {
163
164
  // is a durable id this process was launched under, so it resolves strictly
164
165
  // — a stale one must fail rather than write into a neighbouring store.
165
166
  const typed = input.profile !== undefined && input.profile !== '';
166
- const profileIdOrName = typed ? input.profile : process.env['CRTR_PROFILE_ID'] || '';
167
+ const profileIdOrName = typed ? input.profile : envProfileId() || '';
167
168
  if (profileIdOrName === '') {
168
169
  throw usage('profile scope requires a selected profile; rerun inside a profiled node or pass --profile');
169
170
  }
@@ -209,14 +210,12 @@ export function requireMountedStore(store) {
209
210
  next: `Run \`crtr sys migrate --dir ${repositoryRoot}\`.`,
210
211
  });
211
212
  }
212
- // ---------------------------------------------------------------------------
213
213
  // Canonical name → physical placement
214
214
  //
215
215
  // Every surface takes the FULL canonical name. Placement strips the selected
216
216
  // store's namespace off it, checks that neither the identity nor either
217
217
  // physical representation of the local name is already taken, and picks the
218
218
  // form the new file takes.
219
- // ---------------------------------------------------------------------------
220
219
  /** Normalize and validate a full canonical name: ordering prefixes are
221
220
  * physical-path-only, a trailing `INDEX` is never an address, and the segment
222
221
  * charset is fixed. */
@@ -709,13 +708,11 @@ export function applyExtensionChanges(frontmatter, sets, unsets) {
709
708
  else
710
709
  frontmatter['extensions'] = extensions;
711
710
  }
712
- // ---------------------------------------------------------------------------
713
711
  // Help material shared by `write` and `edit`
714
712
  //
715
713
  // Single-sourced: both leaves compose these, so a wording fix lands on both.
716
714
  // Anything exclusive to one leaf (creation rules on `write`, the rationale
717
715
  // contract on `edit`) stays owned by that leaf.
718
- // ---------------------------------------------------------------------------
719
716
  /** The frontmatter-overlay flags, keyed by flag name, carrying the prose true
720
717
  * on BOTH leaves. `write` appends its creation clauses with `withConstraint`;
721
718
  * neither leaf restates the other's rules. */
@@ -1,3 +1,4 @@
1
+ import { envNodeId, envProfileId } from '../../shared/env.js';
1
2
  import { join } from 'node:path';
2
3
  import { defineLeaf } from '../../core/command.js';
3
4
  import { usage } from '../../core/errors.js';
@@ -188,14 +189,14 @@ export const writeLeaf = defineLeaf({
188
189
  ? {
189
190
  resolvable: resolvableCanonicalNames(loadMemoryTargetView({
190
191
  cwd: store.ownerDir ?? process.cwd(),
191
- profileId: process.env['CRTR_PROFILE_ID'] || null,
192
- nodeId: process.env['CRTR_NODE_ID'] || null,
192
+ profileId: envProfileId() || null,
193
+ nodeId: envNodeId() || null,
193
194
  }, { quiet: true, includeDescendants: true }).docs),
194
195
  }
195
196
  : {}),
196
197
  ...(carriesBootEntry
197
198
  ? {
198
- nestedStore: descendantStoreRoots(projectScopeRoots(process.cwd(), process.env['CRTR_PROFILE_ID'] || null))
199
+ nestedStore: descendantStoreRoots(projectScopeRoots(process.cwd(), envProfileId() || null))
199
200
  .map((root) => realpathOrSelf(join(root, 'memory')))
200
201
  .includes(realpathOrSelf(memoryDir)),
201
202
  }
@@ -1,4 +1,5 @@
1
1
  // `crtr node bash` — file-backed background job controls.
2
+ import { envNodeId } from '../../shared/env.js';
2
3
  import { defineBranch, defineLeaf } from '../../core/command.js';
3
4
  import { InputError } from '../../core/io.js';
4
5
  import { displayMessage } from '../../core/runtime/tmux-chrome.js';
@@ -12,7 +13,7 @@ function resolveBashTarget(input, missingMessage) {
12
13
  if (id === undefined || id === '')
13
14
  id = nodeInPane(pane);
14
15
  if (id === undefined || id === '')
15
- id = process.env['CRTR_NODE_ID'];
16
+ id = envNodeId();
16
17
  if (id === undefined || id === '') {
17
18
  throw new InputError({ error: 'no_node', message: missingMessage, next: 'Run from inside a node, pass --node <id>, or --pane <pane>.' });
18
19
  }
@@ -1,4 +1,5 @@
1
1
  // `crtr node new` — creation commands and persona-kind helpers.
2
+ import { envNodeId } from '../../shared/env.js';
2
3
  import { defineLeaf } from '../../core/command.js';
3
4
  import { InputError } from '../../core/io.js';
4
5
  import { mark } from '../../core/timing.js';
@@ -207,7 +208,7 @@ async function runNodeCreation(input) {
207
208
  if (noKickoff && mode === 'orchestrator') {
208
209
  throw new InputError({ error: 'no_kickoff_orchestrator_unsupported', message: '--no-kickoff cannot be combined with --mode orchestrator', field: 'no-kickoff', next: 'Drop --no-kickoff and provide a prompt, or spawn in base mode.' });
209
210
  }
210
- const parentId = parent ?? process.env['CRTR_NODE_ID'];
211
+ const parentId = parent ?? envNodeId();
211
212
  mark('node_new.create_start', { root });
212
213
  const detail = await cliClient().createNode({
213
214
  ...(kind !== undefined ? { kind } : {}),
@@ -89,9 +89,7 @@ const nodeList = defineLeaf({
89
89
  return { nodes };
90
90
  },
91
91
  });
92
- // ---------------------------------------------------------------------------
93
92
  // node inspect show — a node + its place in the spine
94
- // ---------------------------------------------------------------------------
95
93
  // The renderer receives only the command result, while JSON output bypasses it.
96
94
  // Keep remote provenance out of the returned DTO-shaped object without adding a
97
95
  // second wire field or mutating the remote response.
@@ -233,9 +231,7 @@ const nodeShow = defineLeaf({
233
231
  return parts.join('\n\n');
234
232
  },
235
233
  });
236
- // ---------------------------------------------------------------------------
237
234
  // node fork — branch a conversation + durable context into an idle root
238
- // ---------------------------------------------------------------------------
239
235
  export const nodeFork = defineLeaf({
240
236
  name: 'fork',
241
237
  description: 'fork a node into an independent idle root with the same conversation and durable context',
@@ -287,9 +283,7 @@ export const nodeFork = defineLeaf({
287
283
  return `Forked independent idle root ${result['node_id']}.${placement} It will make no model call until its first real user message.`;
288
284
  },
289
285
  });
290
- // ---------------------------------------------------------------------------
291
286
  // node inspect — read the graph (list, show, context)
292
- // ---------------------------------------------------------------------------
293
287
  export const nodeInspect = defineBranch({
294
288
  name: 'inspect',
295
289
  description: 'read one node — its graph position, context, and artifacts',
@@ -1,4 +1,5 @@
1
1
  // `crtr node` lifecycle and durable configuration commands.
2
+ import { envNodeId } from '../../shared/env.js';
2
3
  import { defineBranch, defineLeaf } from '../../core/command.js';
3
4
  import { InputError } from '../../core/io.js';
4
5
  import { cliClient, getNodeOrNull, rethrowAsCliError } from '../api-client.js';
@@ -65,9 +66,7 @@ const nodeRecycle = defineLeaf({
65
66
  return lines.join('\n');
66
67
  },
67
68
  });
68
- // ---------------------------------------------------------------------------
69
69
  // node lifecycle close — close a node + cascade-cancel its exclusive subtree (Alt+C → x)
70
- // ---------------------------------------------------------------------------
71
70
  const nodeClose = defineLeaf({
72
71
  name: 'close',
73
72
  description: 'close a node + cascade-cancel its exclusive subtree (revivable)',
@@ -110,9 +109,7 @@ const nodeClose = defineLeaf({
110
109
  return `Closed ${r['node_id']} and its exclusive subtree — ${r['count']} node(s) closed${spared > 0 ? `, ${spared} spared (still managed from outside the subtree)` : ''}.`;
111
110
  },
112
111
  });
113
- // ---------------------------------------------------------------------------
114
112
  // node config — reconfigure a node's settings headlessly (model/lifecycle/kind/mode/name)
115
- // ---------------------------------------------------------------------------
116
113
  const NODE_CONFIG_MODEL_EFFECT = 'Model change first: a live broker switches immediately over its socket; a dormant node persists the durable model override and launch recipe for its next revive.';
117
114
  export const nodeConfig = defineLeaf({
118
115
  name: 'config',
@@ -268,7 +265,7 @@ export const nodePromote = defineLeaf({
268
265
  effects: ['Flips mode→orchestrator + kind→chosen (lifecycle unchanged unless --resident, which also flips lifecycle→resident); --model durably pins the model tier; rewrites the launch spec to that kind\'s orchestrator persona; seeds context/roadmap.md scaffold if absent.', 'The next model request is gated on appending and persisting your new-role guidance. If a fresh context starts first, its rebuilt baseline carries the new role.'],
269
266
  },
270
267
  run: async (input) => {
271
- const id = input['node'] ?? process.env['CRTR_NODE_ID'];
268
+ const id = input['node'] ?? envNodeId();
272
269
  if (id === undefined || id === '')
273
270
  throw new InputError({ error: 'no_node', message: 'no node to promote (set CRTR_NODE_ID or pass --node)', next: 'Run from inside a node, or pass --node <id>.' });
274
271
  const kind = input['kind'];
@@ -300,7 +297,7 @@ async function resolveLifecycleNode(input, pane) {
300
297
  if (id === undefined || id === '')
301
298
  id = nodeInPane(pane);
302
299
  if (id === undefined || id === '')
303
- id = process.env['CRTR_NODE_ID'];
300
+ id = envNodeId();
304
301
  if (id === undefined || id === '')
305
302
  throw new InputError({ error: 'no_node', message: 'no node (set CRTR_NODE_ID, pass --node, or run from the agent\'s pane)', next: 'Run from inside a node, pass --node <id>, or --pane <pane>.' });
306
303
  const exists = await getNodeOrNull(id);
@@ -308,13 +305,11 @@ async function resolveLifecycleNode(input, pane) {
308
305
  throw new InputError({ error: 'not_found', message: `no node: ${id}`, next: 'List nodes with `crtr node inspect list`.' });
309
306
  return id;
310
307
  }
311
- // ---------------------------------------------------------------------------
312
308
  // node lifecycle demote — flip a node to TERMINAL in place (the friendly half
313
309
  // of the promote/demote pair; bound to Alt+C → d, and → D with --detach). It
314
310
  // stays focused and running but now owes a final up the spine. `node config
315
311
  // --lifecycle` remains the orthogonal low-level command (it also does resident).
316
312
  // See vision F5.
317
- // ---------------------------------------------------------------------------
318
313
  const nodeDemote = defineLeaf({
319
314
  name: 'demote',
320
315
  description: 'demote a node to terminal in place; it stays focused and running but now owes a final; --detach also closes its viewer (broker keeps running off-screen)',
@@ -352,9 +347,7 @@ const nodeDemote = defineLeaf({
352
347
  },
353
348
  render: (r) => `Demoted ${r['node_id']} — lifecycle now terminal (in place)${r['detached'] === true ? ', viewer closed (broker still running off-screen)' : ''}.`,
354
349
  });
355
- // ---------------------------------------------------------------------------
356
350
  // node lifecycle — recycle, close, or demote a node
357
- // ---------------------------------------------------------------------------
358
351
  export const nodeLifecycle = defineBranch({
359
352
  name: 'lifecycle',
360
353
  description: 'lifecycle actions — recycle, close, demote, or revive a node',
@@ -365,9 +358,7 @@ export const nodeLifecycle = defineBranch({
365
358
  },
366
359
  children: [nodeRecycle, nodeClose, nodeDemote, nodeReviveLeaf],
367
360
  });
368
- // ---------------------------------------------------------------------------
369
361
  // node yield — refresh: discard context, revive fresh in the same mode against roadmap or goal
370
- // ---------------------------------------------------------------------------
371
362
  export const nodeYield = defineLeaf({
372
363
  name: 'yield',
373
364
  description: 'refresh your context, same mode, against your roadmap or goal',
@@ -395,7 +386,7 @@ export const nodeYield = defineLeaf({
395
386
  effects: ['The stophook shuts the process down on next stop and the daemon revives it fresh.', '--kind and/or --model durably reshape the node (kind respecialization, model-tier pin) before the refresh-revive; --promote additionally flips mode→orchestrator and seeds a roadmap scaffold — mode is otherwise left exactly as it was.'],
396
387
  },
397
388
  run: async (input) => {
398
- const id = process.env['CRTR_NODE_ID'];
389
+ const id = envNodeId();
399
390
  if (id === undefined || id === '')
400
391
  throw new InputError({ error: 'no_node', message: 'no node to yield (CRTR_NODE_ID unset)', next: 'Run from inside a node.' });
401
392
  const kind = input['kind'];
@@ -1,4 +1,5 @@
1
1
  // `crtr node message` — immediate inbox delivery and typed-output requests.
2
+ import { envNodeId } from '../../shared/env.js';
2
3
  import { defineBranch, defineLeaf } from '../../core/command.js';
3
4
  import { InputError } from '../../core/io.js';
4
5
  import { OUTPUT_SCHEMA_CONSTRAINTS, OUTPUT_SCHEMA_TRANSPORT, parseOutputSchemaValue } from '../../core/runtime/structured-output.js';
@@ -22,7 +23,7 @@ async function resolveMsgTarget(input) {
22
23
  });
23
24
  }
24
25
  if (self) {
25
- const id = process.env['CRTR_NODE_ID'];
26
+ const id = envNodeId();
26
27
  if (id === undefined || id === '') {
27
28
  throw new InputError({ error: 'no_node', message: 'no node to target with --self (CRTR_NODE_ID unset)', next: 'Run from inside a node, or pass --to <node-id>.' });
28
29
  }
@@ -80,7 +81,7 @@ async function runMessageEngine(input) {
80
81
  if (outputSchema !== null && tierRaw === 'deferred') {
81
82
  throw new InputError({ error: 'output_schema_deferred_tier', message: '--output-schema cannot combine with --tier deferred because the target must answer', field: 'tier', next: 'Use normal, urgent, or critical tier, or drop --output-schema.' });
82
83
  }
83
- const senderFrom = process.env['CRTR_NODE_ID'];
84
+ const senderFrom = envNodeId();
84
85
  const client = cliClient();
85
86
  if (!hasBody && !hasSituational && outputSchema === null) {
86
87
  throw new InputError({ error: 'empty_body', message: 'a message body is required unless --output-schema or --situational-context is supplied', field: 'body', next: 'Pass the message on stdin, or supply one of the documented alternatives.' });
@@ -1,9 +1,10 @@
1
1
  // `crtr node subscription` — push-delivery edge controls.
2
+ import { envNodeId } from '../../shared/env.js';
2
3
  import { defineBranch, defineLeaf } from '../../core/command.js';
3
4
  import { InputError } from '../../core/io.js';
4
5
  import { cliClient, rethrowAsCliError } from '../api-client.js';
5
6
  function resolveSubscriber(input) {
6
- const sub = input['subscriber'] ?? process.env['CRTR_NODE_ID'];
7
+ const sub = input['subscriber'] ?? envNodeId();
7
8
  if (sub === undefined || sub === '') {
8
9
  throw new InputError({ error: 'no_subscriber', message: 'no subscriber (set CRTR_NODE_ID or pass --subscriber)', field: 'subscriber', next: 'Run from inside a node, or pass --subscriber <id>.' });
9
10
  }
@@ -1,11 +1,12 @@
1
1
  // `crtr node wait` — durable controller and deadline waits.
2
+ import { envNodeId, envProfileId } from '../../shared/env.js';
2
3
  import { defineBranch, defineLeaf } from '../../core/command.js';
3
4
  import { InputError } from '../../core/io.js';
4
5
  import { parseWhen } from '../../core/wake.js';
5
6
  import { cliClient, getNodeOrNull, rethrowAsCliError } from '../api-client.js';
6
7
  import { shellQuote } from '../surface/node/placement.js';
7
8
  function armerId() {
8
- const id = process.env['CRTR_NODE_ID'];
9
+ const id = envNodeId();
9
10
  if (id === undefined || id === '') {
10
11
  throw new InputError({ error: 'no_node', message: 'no node to anchor a deadline cron (CRTR_NODE_ID unset)', next: 'Run from inside a node.' });
11
12
  }
@@ -84,7 +85,7 @@ const nodeWaitDeadline = defineLeaf({
84
85
  const parsed = parseWhen(until, parseOpts(now, tz));
85
86
  if ('error' in parsed)
86
87
  throwTimeError(parsed.error);
87
- const profile = process.env['CRTR_PROFILE_ID'];
88
+ const profile = envProfileId();
88
89
  // One deadline per node: the arm seam REPLACES this node's pending
89
90
  // cancel-on-wake cron rather than stacking a second one (see armCron in
90
91
  // core/canvas/crons.ts, where the invariant is enforced for every caller).
@@ -129,7 +130,7 @@ const nodeWait = defineLeaf({
129
130
  effects: ['Durably records the controller on this node. The stop guard permits dormancy only while that controller remains active or idle; its next inbox delivery clears the record before the delivery wakes this node.'],
130
131
  },
131
132
  run: async (input) => {
132
- const nodeId = process.env['CRTR_NODE_ID'];
133
+ const nodeId = envNodeId();
133
134
  if (nodeId === undefined || nodeId === '')
134
135
  throw new InputError({ error: 'no_node', message: 'no node to wait (CRTR_NODE_ID unset)', next: 'Run from inside the terminal node that is blocked.' });
135
136
  const node = await getNodeOrNull(nodeId);
@@ -17,6 +17,7 @@
17
17
  // and the on-disk existence filter is a local fs read (neither touches canvas
18
18
  // state). The /context popup (src/pi-extensions/canvas-context.ts) shells this
19
19
  // leaf inside a tmux display-popup. Piped / non-TTY it prints the roster.
20
+ import { envNodeId } from '../shared/env.js';
20
21
  import { spawnSync } from 'node:child_process';
21
22
  import { existsSync, mkdirSync, mkdtempSync, rmSync, symlinkSync } from 'node:fs';
22
23
  import { tmpdir } from 'node:os';
@@ -106,7 +107,7 @@ export const nodeContextLeaf = defineLeaf({
106
107
  ],
107
108
  },
108
109
  run: async (input) => {
109
- const selfId = input['node']?.trim() || process.env['CRTR_NODE_ID'];
110
+ const selfId = input['node']?.trim() || envNodeId();
110
111
  if (selfId === undefined || selfId === '') {
111
112
  throw new InputError({
112
113
  error: 'no_node',
@@ -52,9 +52,7 @@ export function isNowEligibleFault(fault) {
52
52
  return true;
53
53
  return fault.retry.by === 'daemon';
54
54
  }
55
- // ---------------------------------------------------------------------------
56
55
  // Local, db-free helpers (inlined so the leaf never imports the canvas barrel)
57
- // ---------------------------------------------------------------------------
58
56
  /** Inlined from `runtime/busy.ts` (which reaches the canvas barrel via `jobDir`):
59
57
  * is the node mid-turn right now? `<jobDir>/busy` exists for the span pi is
60
58
  * inside a run. `jobDir` here is the pure path helper from paths.js. */
@@ -2,12 +2,13 @@
2
2
  // `node new --worktree <base>`; this branch holds the single close
3
3
  // verb, symmetric with birth, so a git worktree's whole lifecycle sits under
4
4
  // `node`.
5
+ import { envNodeId } from '../shared/env.js';
5
6
  import { defineBranch, defineLeaf } from '../core/command.js';
6
7
  import { InputError } from '../core/io.js';
7
8
  import { ApiError } from '../api/index.js';
8
9
  import { cliClient } from './api-client.js';
9
10
  function requireCallerNode() {
10
- const id = process.env['CRTR_NODE_ID'];
11
+ const id = envNodeId();
11
12
  if (id === undefined || id.trim() === '') {
12
13
  throw new InputError({
13
14
  error: 'no_node_context',
@@ -1,4 +1,4 @@
1
- import type { ActionPresentation, ActionRequest, ActionOutcome } from "./model.js";
1
+ import type { ActionPresentation, ActionRequest, ActionOutcome } from './model.js';
2
2
  /** How an action reads before it runs — and whether it needs confirming. */
3
3
  export declare function presentAction(request: ActionRequest): ActionPresentation;
4
4
  /** Actions available for a request's subject right now, in offer order. */
@@ -1,9 +1,9 @@
1
1
  // Mutations for `crtr pkg browse`. Each action delegates to the same leaf the
2
2
  // CLI runs, so the browser and `crtr pkg …` can never drift.
3
- import { marketAdd, marketRemove, marketUpdate } from "../market-manage.js";
4
- import { pluginDisable, pluginEnable, pluginInstall, pluginRemove, pluginUpdate, } from "../plugin-manage.js";
5
- import { listAllMarketplaces } from "../../../core/resolver.js";
6
- import { projectScopeRoot } from "../../../core/scope.js";
3
+ import { marketAdd, marketRemove, marketUpdate } from '../market-manage.js';
4
+ import { pluginDisable, pluginEnable, pluginInstall, pluginRemove, pluginUpdate, } from '../plugin-manage.js';
5
+ import { listAllMarketplaces } from '../../../core/resolver.js';
6
+ import { projectScopeRoot } from '../../../core/scope.js';
7
7
  function pluginName(request) {
8
8
  return request.entry?.name ?? "plugin";
9
9
  }
@@ -1,4 +1,4 @@
1
- import { type Catalog, type PluginCapability, type PluginEntry } from "./model.js";
1
+ import { type Catalog, type PluginCapability, type PluginEntry } from './model.js';
2
2
  /** Every rail source with its rows: the installed library first, then each
3
3
  * registered marketplace. Called on open and after any action that could
4
4
  * change install state. Must never throw for a broken marketplace — record it
@@ -1,20 +1,20 @@
1
1
  // Catalog reads for `crtr pkg browse`. Everything here is read-only; mutations
2
2
  // live in actions.ts.
3
- import { statSync } from "node:fs";
4
- import { isAbsolute, join } from "node:path";
5
- import { SUBTREE_NAMES, coreCommandPaths } from "../../../build-root.js";
6
- import { discoverPluginCommandCandidates, validateEffectiveCommandPlugins, validatePluginCommands } from "../../../core/command-plugins/discovery.js";
7
- import { qualifiedTopLevelCommandName } from "../../../core/command-manifests/registry.js";
8
- import { listDirs, pathExists, walkFiles } from "../../../core/fs-utils.js";
9
- import { currentSha, gitSync } from "../../../core/git.js";
10
- import { listPluginMemoryDocs } from "../../../core/memory-resolver.js";
11
- import { memoryExtensionEffectiveCatalog, projectEffectiveMemoryExtensions } from "../../../core/memory/extensions.js";
12
- import { readMarketplaceManifest, readPluginManifest } from "../../../core/manifest.js";
13
- import { readConfig } from "../../../core/config.js";
14
- import { parseSubstrateDoc } from "../../../core/substrate/schema.js";
15
- import { projectScopeRoot, userScopeRoot } from "../../../core/scope.js";
16
- import { isGitUrl } from "../shared.js";
17
- import { INSTALLED_SOURCE_ID, marketplaceSourceId, } from "./model.js";
3
+ import { statSync } from 'node:fs';
4
+ import { isAbsolute, join } from 'node:path';
5
+ import { SUBTREE_NAMES, coreCommandPaths } from '../../../build-root.js';
6
+ import { discoverPluginCommandCandidates, validateEffectiveCommandPlugins, validatePluginCommands } from '../../../core/command-plugins/discovery.js';
7
+ import { qualifiedTopLevelCommandName } from '../../../core/command-manifests/registry.js';
8
+ import { listDirs, pathExists, walkFiles } from '../../../core/fs-utils.js';
9
+ import { currentSha, gitSync } from '../../../core/git.js';
10
+ import { listPluginMemoryDocs } from '../../../core/memory-resolver.js';
11
+ import { memoryExtensionEffectiveCatalog, projectEffectiveMemoryExtensions } from '../../../core/memory/extensions.js';
12
+ import { readMarketplaceManifest, readPluginManifest } from '../../../core/manifest.js';
13
+ import { readConfig } from '../../../core/config.js';
14
+ import { parseSubstrateDoc } from '../../../core/substrate/schema.js';
15
+ import { projectScopeRoot, userScopeRoot } from '../../../core/scope.js';
16
+ import { isGitUrl } from '../shared.js';
17
+ import { INSTALLED_SOURCE_ID, marketplaceSourceId, } from './model.js';
18
18
  function parseSemver(version) {
19
19
  const match = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z.-]+)?$/.exec(version);
20
20
  return match === null
@@ -1,3 +1,3 @@
1
- import type { CommandNode, PluginCapability } from "./model.js";
1
+ import type { CommandNode, PluginCapability } from './model.js';
2
2
  /** Every line of one command's contract view, wrapped to `width`. */
3
3
  export declare function renderCommandView(node: CommandNode, capability: PluginCapability, width: number): string[];
@@ -3,9 +3,9 @@
3
3
  //
4
4
  // Pure rendering. It returns styled lines already wrapped to `width`; the
5
5
  // dossier owns scrolling and never truncates what comes back.
6
- import { visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
7
- import { theme, wrapText } from "../../../core/tui/panel.js";
8
- import { commandLabel } from "./model.js";
6
+ import { visibleWidth, wrapTextWithAnsi } from '@earendil-works/pi-tui';
7
+ import { theme, wrapText } from '../../../core/tui/panel.js';
8
+ import { commandLabel } from './model.js';
9
9
  /** Every line of one command's contract view, wrapped to `width`. */
10
10
  export function renderCommandView(node, capability, width) {
11
11
  const out = [];
@@ -1,4 +1,4 @@
1
- import type { ActionOutcome, CatalogSource, PluginCapability, PluginEntry } from "./model.js";
1
+ import type { ActionOutcome, CatalogSource, PluginCapability, PluginEntry } from './model.js';
2
2
  export declare class PkgDetailPanel {
3
3
  private entry;
4
4
  private source;
@@ -4,11 +4,11 @@
4
4
  // `enter` deeper — a whole memory doc or a whole command contract. The overview
5
5
  // is not a teaser: every command node and every memory doc it lists is a real
6
6
  // row a cursor can land on and open, and nothing is elided with `+N more`.
7
- import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
8
- import { padAnsi, theme, wrapText } from "../../../core/tui/panel.js";
9
- import { renderCommandView } from "./command-view.js";
10
- import { renderDocView } from "./doc-view.js";
11
- import { commandLabel, displayVersion, plural } from "./model.js";
7
+ import { truncateToWidth, visibleWidth } from '@earendil-works/pi-tui';
8
+ import { padAnsi, theme, wrapText } from '../../../core/tui/panel.js';
9
+ import { renderCommandView } from './command-view.js';
10
+ import { renderDocView } from './doc-view.js';
11
+ import { commandLabel, displayVersion, plural } from './model.js';
12
12
  /** One labelled row and its explanation. Inline while both fit; otherwise the
13
13
  * explanation wraps underneath, hanging under the label rather than resetting
14
14
  * to the left margin. Nothing is ever truncated to make it fit. */
@@ -90,7 +90,7 @@ export class PkgDetailPanel {
90
90
  get viewing() {
91
91
  return this.view.kind !== "overview";
92
92
  }
93
- // --- navigation ----------------------------------------------------------
93
+ // navigation
94
94
  /** `j`/`k`: move the cursor between selectable rows on the overview, scroll a
95
95
  * line inside an opened view — and scroll the overview too when the plugin
96
96
  * has nothing selectable to move between. */
@@ -170,7 +170,7 @@ export class PkgDetailPanel {
170
170
  this.collapsed.add(key);
171
171
  return true;
172
172
  }
173
- // --- the selectable model ------------------------------------------------
173
+ // the selectable model
174
174
  /** Command rows in display order, skipping the children of folded branches. */
175
175
  commandRows() {
176
176
  const rows = [];
@@ -195,7 +195,7 @@ export class PkgDetailPanel {
195
195
  ...capability.docs.map((doc) => ({ kind: "doc", doc })),
196
196
  ];
197
197
  }
198
- // --- rendering -----------------------------------------------------------
198
+ // rendering
199
199
  render(width, height, focused) {
200
200
  const lines = this.lines(width, focused);
201
201
  this.lastLines = lines.length;
@@ -1,3 +1,3 @@
1
- import type { DocDetail } from "./model.js";
1
+ import type { DocDetail } from './model.js';
2
2
  /** Every line of one memory doc's reader view, wrapped to `width`. */
3
3
  export declare function renderDocView(doc: DocDetail, width: number): string[];
@@ -4,9 +4,9 @@
4
4
  // Pure rendering. It receives a doc already read from disk and returns styled
5
5
  // lines already wrapped to `width`; the dossier owns scrolling and never
6
6
  // truncates what comes back.
7
- import { getMarkdownTheme } from "@earendil-works/pi-coding-agent";
8
- import { Markdown, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
9
- import { theme, wrapText } from "../../../core/tui/panel.js";
7
+ import { getMarkdownTheme } from '@earendil-works/pi-coding-agent';
8
+ import { Markdown, visibleWidth, wrapTextWithAnsi } from '@earendil-works/pi-tui';
9
+ import { theme, wrapText } from '../../../core/tui/panel.js';
10
10
  /** Every line of one memory doc's reader view, wrapped to `width`. */
11
11
  export function renderDocView(doc, width) {
12
12
  const out = [];
@@ -1,4 +1,4 @@
1
- import type { CatalogSource, PluginEntry } from "./model.js";
1
+ import type { CatalogSource, PluginEntry } from './model.js';
2
2
  /** The middle column of the plugin browser. It owns filtering and row position;
3
3
  * the shell owns horizontal navigation and actions. */
4
4
  export declare class PkgListPanel {