@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
@@ -17,6 +17,7 @@
17
17
  // so node target metadata comes from each node's durable meta.json — the source
18
18
  // of truth the database index is rebuilt from — through its existing read-only
19
19
  // helper. Missing or corrupt metadata remains a global blocker.
20
+ import { envProfileId } from '../shared/env.js';
20
21
  import { readdirSync } from 'node:fs';
21
22
  import { dirname, join } from 'node:path';
22
23
  import { readMetaObject } from '../core/canvas/meta-file.js';
@@ -79,7 +80,7 @@ function nodeStoresWithDocs() {
79
80
  * so discovery must run after that lane, not against `listProfiles()`. */
80
81
  export async function discoverCorpus(opts) {
81
82
  const cwd = realpathOrSelf(opts.cwd ?? process.cwd());
82
- const selectedProfileId = process.env['CRTR_PROFILE_ID'] || null;
83
+ const selectedProfileId = envProfileId() || null;
83
84
  const explicitDirs = opts.explicitDirs ?? [];
84
85
  const roots = [];
85
86
  const rootSeen = new Set();
@@ -336,9 +337,7 @@ export async function discoverCorpus(opts) {
336
337
  nodeMetadata,
337
338
  };
338
339
  }
339
- // ---------------------------------------------------------------------------
340
340
  // Snapshots
341
- // ---------------------------------------------------------------------------
342
341
  /** Read every discovered store's documents. `preloaded` supplies the per-store
343
342
  * convergent lane's FOLDED snapshots so the corpus plans against the bytes a
344
343
  * write run leaves behind — identical under `--dry-run`, where the same fold
@@ -387,9 +386,7 @@ export function buildCorpusSnapshot(discovery, snapshots) {
387
386
  }));
388
387
  return makeCorpusSnapshot(stores, discovery.targets, discovery.roots);
389
388
  }
390
- // ---------------------------------------------------------------------------
391
389
  // The runner
392
- // ---------------------------------------------------------------------------
393
390
  function cloneCorpus(corpus) {
394
391
  const stores = corpus.stores.map((store) => ({
395
392
  ...store,
@@ -1,5 +1,18 @@
1
- // The ordered state-migration registry — ONE list for both lanes, so a cut
2
- // that spans canvas-db and document stores ships as adjacent entries.
1
+ // The ordered state-migration registry — the SECOND of this repo's three
2
+ // migration homes, and the one for on-disk memory documents, profile
3
+ // manifests, and the corpus lane, driven by `crtr sys migrate`. ONE list for
4
+ // both non-journaled lanes here, so a cut that spans canvas-db and document
5
+ // stores ships as adjacent entries (the journaled lane's entries splice into
6
+ // `../core/canvas/migrations.ts`'s `MIGRATIONS` chain at its tail).
7
+ //
8
+ // The other two homes:
9
+ // - `../core/canvas/migrations.ts` — canvas.db's own versioned schema chain,
10
+ // gated by `PRAGMA user_version`. Steps there may freely read node meta,
11
+ // query other tables mid-step, and build shell commands from canvas data
12
+ // — exactly what this registry's import contract (below) forbids, which
13
+ // is why that chain is not expressed as registrants here.
14
+ // - `../daemon/boot-migrations.ts` — filesystem repairs that run
15
+ // unconditionally at every daemon boot, outside any version gate.
3
16
  //
4
17
  // Authoring contract (each migration is one file, `NNN-<desc>.ts`, or one
5
18
  // directory, `NNN-<desc>/index.ts`, appended here in order):
@@ -221,7 +221,6 @@ test('queued continuation finishes before natural-stop routing commits', { timeo
221
221
  assert.equal(shutdown, true, 'settlement routes from the completed continuation');
222
222
  assert.equal(getNode('leaf')?.intent ?? null, null, 'the intermediate natural stop never idle-released the node');
223
223
  });
224
- // ---------------------------------------------------------------------------
225
224
  // §5.1 — the §1.7 settlement branch map on the focuses table. Every assertion is
226
225
  // on the canvas focuses/runtime rows after firing agent_settled (TMUX_PANE is
227
226
  // cleared in beforeEach). NOTE (broker-host cut): the placement read `focusOf`
@@ -232,7 +231,6 @@ test('queued continuation finishes before natural-stop routing commits', { timeo
232
231
  // but-not-yet-realized viewer, which liveOrPrune passes through). status='done'
233
232
  // is reached by setting the runtime row directly (the branch reads
234
233
  // getNode(nodeId).status).
235
- // ---------------------------------------------------------------------------
236
234
  test('§5.1.2 truly-done + focused → focus row CLOSED, own presence nulled (Q1)', async () => {
237
235
  // R carries a LOCATION so the close-path presence-null is observable.
238
236
  createNode(node('R', { parent: null, lifecycle: 'terminal', pane: '%r', tmux_session: 'Suser', window: '@wr' }));
@@ -323,10 +321,8 @@ test('refresh-yield (intent=refresh) writes NO push — silent to subscribers',
323
321
  assert.equal(readInboxSince('root').length, 0, 'a yield is silent: NO inbox pointer');
324
322
  assert.equal(pi.injected.length, 0, 'no reprompt on a refresh-yield');
325
323
  });
326
- // ---------------------------------------------------------------------------
327
324
  // turn_end — stale-⚠ fix: reaching turn_end proves the current provider
328
325
  // round-trip and any wedge episode are both over, so both fault links clear.
329
- // ---------------------------------------------------------------------------
330
326
  /** A turn_end event carrying one assistant text block. */
331
327
  function turnEndEvent(text) {
332
328
  return { message: { role: 'assistant', content: [{ type: 'text', text }] } };
@@ -3,6 +3,7 @@
3
3
  // These helpers are intentionally independent of the canvas state store. A
4
4
  // broker may read its own context files directly, but all node metadata reads
5
5
  // and generated-name writes go through crtrd's broker operation projection.
6
+ import { envHomeOverride } from '../shared/env.js';
6
7
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
7
8
  import { homedir } from 'node:os';
8
9
  import { join } from 'node:path';
@@ -14,7 +15,7 @@ function contextDir(nodeId) {
14
15
  if (nodeId === '' || nodeId === '.' || nodeId === '..' || /[\\/\0]/u.test(nodeId)) {
15
16
  throw new TypeError('node_id must be a non-empty path segment');
16
17
  }
17
- const canvasHome = process.env['CRTR_HOME']?.trim() || join(homedir(), CRTR_DIR_NAME, 'canvas');
18
+ const canvasHome = envHomeOverride()?.trim() || join(homedir(), CRTR_DIR_NAME, 'canvas');
18
19
  return join(canvasHome, 'nodes', nodeId, 'context');
19
20
  }
20
21
  /** Persist a bare root's first real interactive message without clobbering an
@@ -24,30 +24,24 @@
24
24
  // Deviation from upstream: this backend pins `bash` as the shell (upstream
25
25
  // consults user shell config via non-exported helpers). Acceptable — crtr's
26
26
  // agents are bash-contract anyway.
27
+ import { envBashValveMs, envNodeId } from '../shared/env.js';
27
28
  import { spawn } from 'node:child_process';
28
29
  import { closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, rmSync, statSync, writeFileSync, } from 'node:fs';
29
30
  import { homedir } from 'node:os';
30
31
  import { dirname } from 'node:path';
32
+ import { performance } from 'node:perf_hooks';
31
33
  import { backgroundBashJob, bashJobPaths, formatBashElapsed, newBashJobId, normalizeBashJobPurpose } from '../core/bash-jobs.js';
32
34
  import { readConfig } from '../core/config.js';
33
35
  import { PREVIEW_RESULT_PATH_ENV, previewResultPath } from '../core/preview-result-path.js';
34
36
  import { resolveAdmittedHostCommand } from '../core/runtime/spawn-env.js';
35
37
  import { Type } from 'typebox';
36
38
  import { createBashToolDefinition } from '@earendil-works/pi-coding-agent';
37
- // ---------------------------------------------------------------------------
38
39
  // Deadline — 5 minutes by default, overridable via CRTR_BASH_VALVE_MS
39
40
  // (undocumented; test-only seam so the E2E valve check doesn't sleep 5m).
40
- // ---------------------------------------------------------------------------
41
- const DEFAULT_VALVE_MS = 5 * 60 * 1000;
42
41
  const POLL_MS = 200;
43
42
  function resolveValveMs() {
44
- const raw = process.env['CRTR_BASH_VALVE_MS'];
45
- if (raw === undefined)
46
- return DEFAULT_VALVE_MS;
47
- const n = Number(raw);
48
- return Number.isFinite(n) && n > 0 ? n : DEFAULT_VALVE_MS;
43
+ return envBashValveMs();
49
44
  }
50
- // ---------------------------------------------------------------------------
51
45
  // The detached supervisor. Spawned with `detached: true` so it becomes its
52
46
  // own process-group leader (pgid === its pid) — killing `-pgid` reaches the
53
47
  // real command and anything it forked, even after this pi process exits.
@@ -59,7 +53,6 @@ function resolveValveMs() {
59
53
  //
60
54
  // `--` before the paths is a dummy $0 (a standard `bash -c script -- args`
61
55
  // idiom) so the real payload lands at $1 onward.
62
- // ---------------------------------------------------------------------------
63
56
  // The elapsed rendering mirrors formatBashElapsed() in core/bash-jobs.ts
64
57
  // (`12s` / `3m 40s` / `1h 04m`) so the completion message reads the same as the
65
58
  // handoff notice; it measures TOTAL runtime, foreground portion included.
@@ -166,12 +159,10 @@ function readExitCode(path) {
166
159
  return null;
167
160
  }
168
161
  }
169
- // ---------------------------------------------------------------------------
170
162
  // Long-sleep guard. A command that opens with `sleep <long>` is a node burning
171
163
  // a live window to wait on a clock — the runtime already has waiting primitives
172
164
  // that cost nothing (`crtr cron` for a scheduled wake, `crtr node wait deadline`
173
165
  // for an inbox-vs-deadline race). Anything over 10s is refused before it runs.
174
- // ---------------------------------------------------------------------------
175
166
  const MAX_INLINE_SLEEP_SECONDS = 10;
176
167
  const SLEEP_UNIT_SECONDS = { s: 1, m: 60, h: 3600, d: 86400 };
177
168
  /** Seconds a leading `sleep ...` will block for, or null when the command does
@@ -247,7 +238,7 @@ export function createValveOperations(nodeId, contextDir, takePurpose = () => nu
247
238
  let offset = 0;
248
239
  let settled = false;
249
240
  let pgid;
250
- const startedAt = Date.now();
241
+ const startedAt = performance.now();
251
242
  const valveMs = resolveValveMs();
252
243
  let interval;
253
244
  const teardown = () => {
@@ -265,7 +256,7 @@ export function createValveOperations(nodeId, contextDir, takePurpose = () => nu
265
256
  act();
266
257
  };
267
258
  function finishBackground(auto = false) {
268
- const elapsed = formatBashElapsed(Date.now() - startedAt);
259
+ const elapsed = formatBashElapsed(performance.now() - startedAt);
269
260
  const notice = `\n[backgrounded as job ${paths.jobId} after ${elapsed} in the foreground${auto ? ' (auto)' : ''}. ` +
270
261
  `It will interrupt you with a message when it exits. ` +
271
262
  `Watch: tail -f ${paths.jobLog} ; stop: kill -- -${pgid}]`;
@@ -354,7 +345,7 @@ export function createValveOperations(nodeId, contextDir, takePurpose = () => nu
354
345
  // time out or delete the directory during that in-progress exit.
355
346
  if (existsSync(paths.jobExit))
356
347
  return;
357
- const elapsedMs = Date.now() - startedAt;
348
+ const elapsedMs = performance.now() - startedAt;
358
349
  if (timeout !== undefined && timeout > 0) {
359
350
  if (elapsedMs >= timeout * 1000) {
360
351
  if (pgid)
@@ -371,10 +362,7 @@ export function createValveOperations(nodeId, contextDir, takePurpose = () => nu
371
362
  },
372
363
  };
373
364
  }
374
- // ---------------------------------------------------------------------------
375
365
  // Extension
376
- // ---------------------------------------------------------------------------
377
- // ---------------------------------------------------------------------------
378
366
  // `purpose` — the one line a person reads while a command runs
379
367
  //
380
368
  // An optional deviation from taking pi's definition verbatim, and the only one
@@ -396,7 +384,6 @@ export function createValveOperations(nodeId, contextDir, takePurpose = () => nu
396
384
  // Upstream ask: pi's own bash tool should carry this (Claude Code's bash tool
397
385
  // has shipped an equivalent `description` field for years). Until it does, the
398
386
  // field is re-declared here on top of pi's schema rather than forked into it.
399
- // ---------------------------------------------------------------------------
400
387
  const PURPOSE_DESCRIPTION = [
401
388
  'One short clause naming what this step is FOR, in the words of the person you',
402
389
  'are working for — "Reading the Acme thread", not "Fetching emails" and never',
@@ -437,7 +424,7 @@ export function createValveToolDefinition(nodeId, contextDir, cwd, exposePurpose
437
424
  return { ...schemaDefinition, execute };
438
425
  }
439
426
  export default function (pi) {
440
- const nodeId = process.env['CRTR_NODE_ID'];
427
+ const nodeId = envNodeId();
441
428
  if (nodeId === undefined || nodeId.trim() === '')
442
429
  return; // inert — builtin bash stays
443
430
  const contextDir = process.env['CRTR_CONTEXT_DIR'];
@@ -22,6 +22,7 @@
22
22
  //
23
23
  // Plain TS-with-types — no imports from @earendil-works/* so this compiles inside
24
24
  // crouter's own tsc build without a dep on the pi packages.
25
+ import { envNodeId } from '../shared/env.js';
25
26
  import { CONTEXT_INTRO_CUSTOM_TYPE, formatCard, parseCard } from '../shared/generated-context.js';
26
27
  import { brokerExtensionState } from '../core/runtime/broker/daemon-ops.js';
27
28
  import { buildContextBearingsFromState, buildForkBearingsFromState, buildProjectContextBlockForBroker, renderPreferencesFromState, } from '../core/runtime/broker-extension-render.js';
@@ -35,9 +36,7 @@ import { truncateToWidth } from './truncate.js';
35
36
  * the block builder in `core/runtime/bearings.ts` because the warm-pool claim's
36
37
  * live delivery needs it too; re-exported here as this module's own surface. */
37
38
  export { CONTEXT_INTRO_CUSTOM_TYPE };
38
- // ---------------------------------------------------------------------------
39
39
  // Block builder
40
- // ---------------------------------------------------------------------------
41
40
  /** Build the full bearings baseline for a conversation without inherited
42
41
  * crouter context. Exported for testing. */
43
42
  export async function buildContextIntro(nodeId, target) {
@@ -50,9 +49,7 @@ export async function buildForkContextIntro(nodeId) {
50
49
  const state = await brokerExtensionState(nodeId);
51
50
  return buildForkBearingsFromState(state, situationalContextEnvelope(nodeId) ?? '');
52
51
  }
53
- // ---------------------------------------------------------------------------
54
52
  // Collapsed-by-default rendering
55
- // ---------------------------------------------------------------------------
56
53
  /** Pull the plain text out of a custom message's content (string or blocks). */
57
54
  function messageText(message) {
58
55
  if (typeof message.content === 'string')
@@ -127,16 +124,14 @@ export function renderContextMessage(message, options, theme) {
127
124
  },
128
125
  };
129
126
  }
130
- // ---------------------------------------------------------------------------
131
127
  // Extension
132
- // ---------------------------------------------------------------------------
133
128
  /** Register new-context baseline assembly; a strict resume changes nothing. */
134
129
  export function registerCanvasContextIntro(pi) {
135
130
  // Collapse the block to a one-liner until the user expands tool output (Ctrl+O).
136
131
  // Harmless to register outside TUI mode (it's only consulted while rendering).
137
132
  pi.registerMessageRenderer(CONTEXT_INTRO_CUSTOM_TYPE, renderContextMessage);
138
133
  pi.on('session_start', async (event, ctx) => {
139
- const nodeId = process.env['CRTR_NODE_ID'];
134
+ const nodeId = envNodeId();
140
135
  if (nodeId === undefined || nodeId.trim() === '')
141
136
  return;
142
137
  const reason = event?.reason;
@@ -14,6 +14,7 @@
14
14
  // Plain TS-with-types — NO imports from @earendil-works/* (a local structural
15
15
  // PiLike interface stands in), so it compiles inside crouter's own tsc build
16
16
  // with no dep on the pi packages. Mirrors canvas-context-intro.ts.
17
+ import { envNodeId } from '../shared/env.js';
17
18
  import { homedir } from 'node:os';
18
19
  import { join, resolve } from 'node:path';
19
20
  import { brokerExtensionState } from '../core/runtime/broker/daemon-ops.js';
@@ -22,9 +23,7 @@ import { corpusHasPreCommandSurfaces, demotePreCommandExposures, renderOnCommand
22
23
  import { clearSessionCache } from '../core/substrate/session-cache.js';
23
24
  import { cloneContextExposureState, exposureTarget, freezePreferenceSnapshot, loadContextExposureState, mergeContextExposureState, replaceContextExposureState, saveContextExposureState, sharedContextExposureState, } from '../core/substrate/injected-store.js';
24
25
  import { autoLoadedContextInner, mergeAutoLoadedContext } from './envelope-merge.js';
25
- // ---------------------------------------------------------------------------
26
26
  // Extension
27
- // ---------------------------------------------------------------------------
28
27
  /** The stable seam that closes pi's native "Available tools" list. We splice the
29
28
  * substrate's boot sections right before it — the agent decides which capability
30
29
  * to reach for while reading the tools, so memory docs/preferences must sit
@@ -63,7 +62,7 @@ const HELD_SIBLING_REASON = [
63
62
  'Read it, then re-issue your WHOLE command unchanged. The re-issue executes.',
64
63
  ].join('\n');
65
64
  export function registerCanvasDocSubstrate(pi) {
66
- const nodeId = process.env['CRTR_NODE_ID'];
65
+ const nodeId = envNodeId();
67
66
  if (nodeId === undefined || nodeId.trim() === '')
68
67
  return; // not a canvas node — inert
69
68
  // One in-place state object is shared with context-intro and rehydrated on a
@@ -29,6 +29,7 @@
29
29
  //
30
30
  // Plain TS-with-types — no imports from @earendil-works/* so this compiles inside
31
31
  // crouter's own tsc build without a dep on the pi packages.
32
+ import { envNodeId } from '../shared/env.js';
32
33
  import { captureGoalIfAbsent, generateAndCommitInitialName, } from './broker-local.js';
33
34
  import { parseCard } from '../shared/generated-context.js';
34
35
  /**
@@ -41,7 +42,7 @@ import { parseCard } from '../shared/generated-context.js';
41
42
  export function registerCanvasGoalCapture(pi) {
42
43
  pi.on('input', (event) => {
43
44
  try {
44
- const nodeId = process.env['CRTR_NODE_ID'];
45
+ const nodeId = envNodeId();
45
46
  if (nodeId === undefined || nodeId.trim() === '')
46
47
  return; // not a canvas node
47
48
  const text = (event.text ?? '').trim();
@@ -11,6 +11,7 @@
11
11
  // trigger, so the agent cannot run on context alone before hearing the person.
12
12
  // A context-only batch keeps its wake by triggering on its LAST card while idle,
13
13
  // and rides the running turn while streaming.
14
+ import { envNodeId, envProfileId, envWatcherDebounceMs, envWatcherTickMs } from '../shared/env.js';
14
15
  import { coalesceBrokerInbox, entryCards, readBrokerCanceledEntryIds, readBrokerCursor, readBrokerInboxSince, reportRefNodeId, } from '../core/runtime/broker/inbox.js';
15
16
  import { advanceBrokerInboxCursor, brokerExtensionState } from '../core/runtime/broker/daemon-ops.js';
16
17
  import { emitEvent } from '../core/events/emit.js';
@@ -18,8 +19,6 @@ import { errorClassFromError } from '../core/events/errors.js';
18
19
  import { operationIdContext } from '../core/events/operation-id.js';
19
20
  import { situationalContextEnvelope, SITUATIONAL_CONTEXT_CUSTOM_TYPE } from '../core/runtime/situational-context.js';
20
21
  import { isProfilePaused } from '../core/profiles/manifest.js';
21
- const DEFAULT_TICK_MS = 800;
22
- const DEFAULT_DEBOUNCE_MS = 1000;
23
22
  const MAX_PENDING_HANDOFFS = 64;
24
23
  const MAX_SOURCE_OPERATION_IDS = 16;
25
24
  let currentWatcher;
@@ -31,26 +30,20 @@ class WatcherFatalError extends Error {
31
30
  }
32
31
  }
33
32
  function resolveCadence() {
34
- const parse = (raw, fallback) => {
35
- if (raw === undefined)
36
- return fallback;
37
- const value = Number(raw);
38
- return Number.isFinite(value) && value > 0 ? value : fallback;
39
- };
40
33
  return {
41
- tickMs: parse(process.env['CRTR_WATCHER_TICK_MS'], DEFAULT_TICK_MS),
42
- debounceMs: parse(process.env['CRTR_WATCHER_DEBOUNCE_MS'], DEFAULT_DEBOUNCE_MS),
34
+ tickMs: envWatcherTickMs(),
35
+ debounceMs: envWatcherDebounceMs(),
43
36
  };
44
37
  }
45
38
  function currentNodeId() {
46
- const nodeId = process.env['CRTR_NODE_ID'];
39
+ const nodeId = envNodeId();
47
40
  return nodeId === undefined || nodeId.trim() === '' ? undefined : nodeId;
48
41
  }
49
42
  /** The node's profile, read fresh each poll: `crtr profile pause` mutates the
50
43
  * manifest file directly, so a live broker sees the flip on its next tick with
51
44
  * no daemon round-trip. '' is the historical no-profile row. */
52
45
  function profilePaused() {
53
- const profileId = process.env['CRTR_PROFILE_ID'];
46
+ const profileId = envProfileId();
54
47
  return profileId !== undefined && profileId !== '' && isProfilePaused(profileId);
55
48
  }
56
49
  function isFilesystemError(error) {
@@ -18,6 +18,7 @@
18
18
  //
19
19
  // Plain TS-with-types — no imports from @earendil-works/* so this compiles inside
20
20
  // crouter's own tsc build without a dep on the pi packages.
21
+ import { envNodeId } from '../shared/env.js';
21
22
  import { existsSync, readFileSync } from 'node:fs';
22
23
  import { brokerExtensionState } from '../core/runtime/broker/daemon-ops.js';
23
24
  import { reportPathFromRef } from '../core/runtime/broker-extension-render.js';
@@ -26,9 +27,7 @@ import { parseCard } from '../shared/generated-context.js';
26
27
  // Per-entry body cap so a single fat report can't blow the context budget. The
27
28
  // full report stays on disk at `ref` if the agent needs more.
28
29
  const BODY_CAP = 4_000;
29
- // ---------------------------------------------------------------------------
30
30
  // Report dereference — turn a passive pointer into the message text it carries.
31
- // ---------------------------------------------------------------------------
32
31
  /** Strip the leading YAML frontmatter block a report is written with, returning
33
32
  * just the body. Tolerant: no frontmatter → returns the input unchanged. */
34
33
  function stripFrontmatter(raw) {
@@ -126,9 +125,7 @@ export function placeBacklog(text, preText) {
126
125
  const bodyStart = text.length - card.body.length - CARD_CLOSE.length;
127
126
  return `${text.slice(0, bodyStart)}${preText}\n${text.slice(bodyStart)}`;
128
127
  }
129
- // ---------------------------------------------------------------------------
130
128
  // Extension
131
- // ---------------------------------------------------------------------------
132
129
  /**
133
130
  * Register the passive-context drain on `pi`.
134
131
  *
@@ -138,7 +135,7 @@ export function placeBacklog(text, preText) {
138
135
  export function registerCanvasPassiveContext(pi) {
139
136
  pi.on('input', async (event) => {
140
137
  try {
141
- const nodeId = process.env['CRTR_NODE_ID'];
138
+ const nodeId = envNodeId();
142
139
  if (nodeId === undefined || nodeId.trim() === '')
143
140
  return; // not a canvas node
144
141
  const drained = drainBrokerPassive(nodeId);
@@ -6,6 +6,7 @@
6
6
  // mirrors its record there, and this hook attaches the record to Pi's
7
7
  // ToolResult.details. The attach viewer can render a structured preview
8
8
  // without parsing the agent-facing text.
9
+ import { envNodeId } from '../shared/env.js';
9
10
  import { existsSync, readFileSync, rmSync } from 'node:fs';
10
11
  import { previewResultPath } from '../core/preview-result-path.js';
11
12
  function isPreviewRecord(value) {
@@ -19,7 +20,7 @@ function isPreviewRecord(value) {
19
20
  /** Loaded into every canvas broker. Plain Pi sessions have no node id and
20
21
  * deliberately receive no filesystem transport. */
21
22
  export function registerCanvasPreviewResult(pi) {
22
- if (process.env['CRTR_NODE_ID'] === undefined || process.env['CRTR_NODE_ID'] === '')
23
+ if (envNodeId() === undefined || envNodeId() === '')
23
24
  return;
24
25
  pi.on('tool_result', (event) => {
25
26
  if (event.toolName !== 'bash')
@@ -23,6 +23,7 @@
23
23
  //
24
24
  // Every rule below is anchored to pi's exact wording and degrades to a no-op if
25
25
  // pi rewords, so a pi upgrade can never break a turn here.
26
+ import { envNodeId } from '../shared/env.js';
26
27
  const PI_IDENTITY = 'You are an expert coding assistant operating inside pi, a coding agent harness.';
27
28
  const NEUTRAL_IDENTITY = 'You are an expert coding assistant.';
28
29
  const PI_DOCS_MARKER = 'Pi documentation (read only';
@@ -40,7 +41,7 @@ export function scrubPromptSelfReference(systemPrompt) {
40
41
  }
41
42
  /** Register the system-prompt scrub for a canvas node. */
42
43
  export function registerCanvasPromptScrub(pi) {
43
- const nodeId = process.env['CRTR_NODE_ID'];
44
+ const nodeId = envNodeId();
44
45
  if (nodeId === undefined || nodeId.trim() === '')
45
46
  return;
46
47
  pi.on('before_agent_start', (event) => {
@@ -38,20 +38,17 @@
38
38
  //
39
39
  // Plain TS-with-types — no imports from @earendil-works/* so this compiles
40
40
  // inside crouter's own tsc build without a dep on the pi packages.
41
+ import { envNodeId, envRecapIdleMs } from '../shared/env.js';
41
42
  import { brokerExtensionState } from '../core/runtime/broker/daemon-ops.js';
42
43
  import { generateRecap } from '../core/runtime/recap.js';
43
44
  import { isReviewBoundary } from '../core/runtime/session-visibility.js';
44
45
  import { editorLabelForBrokerNode } from '../core/runtime/broker-extension-render.js';
45
46
  import { generateAndCommitRecapName, readRoadmap } from './broker-local.js';
46
- // ---------------------------------------------------------------------------
47
47
  // Tuning constants
48
- // ---------------------------------------------------------------------------
49
48
  /** No-new-message window before a recap is shown. 60s by default; override with
50
49
  * CRTR_RECAP_IDLE_MS (for fast runtime verification). */
51
50
  function idleMs() {
52
- const raw = process.env['CRTR_RECAP_IDLE_MS'];
53
- const n = raw !== undefined ? parseInt(raw, 10) : NaN;
54
- return Number.isFinite(n) && n > 0 ? n : 60_000;
51
+ return envRecapIdleMs();
55
52
  }
56
53
  /** How often the single low-rate timer checks elapsed-since-last-message. */
57
54
  const POLL_MS = 7_000;
@@ -62,14 +59,10 @@ const CONVO_EDGE_MESSAGES = 5;
62
59
  * (10 messages × this ≈ the total budget the model needs for the gist). */
63
60
  const MESSAGE_CHAR_BUDGET = 1_200;
64
61
  const WIDGET_KEY = 'crtr-recap';
65
- // ---------------------------------------------------------------------------
66
62
  // Module-level state — persists across /reload so the timer doesn't stack.
67
- // ---------------------------------------------------------------------------
68
63
  /** The one live poll timer. Cleared and replaced on every re-registration. */
69
64
  let liveTimer;
70
- // ---------------------------------------------------------------------------
71
65
  // Conversation extraction — literal user+assistant text ONLY.
72
- // ---------------------------------------------------------------------------
73
66
  /** Pull the literal text out of a message's content (string or text blocks),
74
67
  * dropping non-text blocks (thinking, toolCall, image). */
75
68
  function messageText(msg) {
@@ -162,9 +155,7 @@ function buildNamingConversation(sm, assistantPartial, reviewId) {
162
155
  });
163
156
  return sampled.join('\n\n');
164
157
  }
165
- // ---------------------------------------------------------------------------
166
158
  // Extension
167
- // ---------------------------------------------------------------------------
168
159
  /**
169
160
  * Register the inactivity-recap chrome on `pi`.
170
161
  *
@@ -172,7 +163,7 @@ function buildNamingConversation(sm, assistantPartial, reviewId) {
172
163
  * or legacy job agent loads it as a no-op.
173
164
  */
174
165
  export function registerCanvasRecap(pi, recapGenerator = generateRecap, nameRegenerator) {
175
- const nodeId = process.env['CRTR_NODE_ID'];
166
+ const nodeId = envNodeId();
176
167
  if (nodeId === undefined || nodeId.trim() === '')
177
168
  return; // not a canvas node
178
169
  // Captured on session_start; used by the timer and the message handlers.
@@ -344,9 +335,7 @@ export function registerCanvasRecap(pi, recapGenerator = generateRecap, nameRege
344
335
  requestRecap(true);
345
336
  },
346
337
  });
347
- // -------------------------------------------------------------------------
348
338
  // Events
349
- // -------------------------------------------------------------------------
350
339
  pi.on('session_start', (_event, ctx) => {
351
340
  ui = ctx.ui;
352
341
  sessionManager = ctx.sessionManager;
@@ -382,9 +371,7 @@ export function registerCanvasRecap(pi, recapGenerator = generateRecap, nameRege
382
371
  captureLiveModel(ctx);
383
372
  onMessage();
384
373
  });
385
- // -------------------------------------------------------------------------
386
374
  // The single low-rate idle poll.
387
- // -------------------------------------------------------------------------
388
375
  if (liveTimer !== undefined)
389
376
  clearInterval(liveTimer);
390
377
  const timer = setInterval(() => {
@@ -4,13 +4,12 @@
4
4
  // inherited fork context and before canvas-context-intro appends fork bearings.
5
5
  // The display projection starts at this marker while the model keeps the full
6
6
  // inherited context.
7
+ import { envNodeId } from '../shared/env.js';
7
8
  import { brokerExtensionState } from '../core/runtime/broker/daemon-ops.js';
8
9
  import { isReviewBoundary, REVIEW_BOUNDARY_CUSTOM_TYPE } from '../core/runtime/session-visibility.js';
9
10
  import { formatCard, parseCard } from '../shared/generated-context.js';
10
11
  import { truncateToWidth } from './truncate.js';
11
- // ---------------------------------------------------------------------------
12
12
  // Collapsed-by-default rendering
13
- // ---------------------------------------------------------------------------
14
13
  /** Render the marker as a single compact divider; its full content remains in
15
14
  * the message list for the model and generated-context presenters. */
16
15
  export function renderReviewBoundary(message, options, theme) {
@@ -32,9 +31,7 @@ export function renderReviewBoundary(message, options, theme) {
32
31
  },
33
32
  };
34
33
  }
35
- // ---------------------------------------------------------------------------
36
34
  // Extension
37
- // ---------------------------------------------------------------------------
38
35
  /**
39
36
  * Register the review boundary marker. Companion nodes receive one boundary
40
37
  * marker on session_start that separates inherited context from the visible
@@ -43,7 +40,7 @@ export function renderReviewBoundary(message, options, theme) {
43
40
  export function registerCanvasReviewBoundary(pi) {
44
41
  pi.registerMessageRenderer(REVIEW_BOUNDARY_CUSTOM_TYPE, renderReviewBoundary);
45
42
  pi.on('session_start', async (_event, ctx) => {
46
- const nodeId = process.env['CRTR_NODE_ID'];
43
+ const nodeId = envNodeId();
47
44
  if (!nodeId || nodeId.trim() === '')
48
45
  return;
49
46
  let node;