@north-light/crouter 0.3.232 → 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 (392) hide show
  1. package/dist/api/client.js +16 -15
  2. package/dist/builtin-memory/internal/memory-loading.md +4 -3
  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 +532 -534
  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 +0 -8
  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 +5 -18
  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/memory/__tests__/command-selector-and-mutation-guards.test.js +4 -4
  42. package/dist/commands/memory/find.js +2 -2
  43. package/dist/commands/memory/lint.js +2 -1
  44. package/dist/commands/memory/read.d.ts +2 -7
  45. package/dist/commands/memory/read.js +5 -16
  46. package/dist/commands/memory/shared.d.ts +1 -1
  47. package/dist/commands/memory/shared.js +7 -10
  48. package/dist/commands/memory/write.js +4 -3
  49. package/dist/commands/node/bash.js +2 -1
  50. package/dist/commands/node/create.js +2 -1
  51. package/dist/commands/node/inspect.js +0 -6
  52. package/dist/commands/node/lifecycle.js +4 -13
  53. package/dist/commands/node/message.js +3 -2
  54. package/dist/commands/node/subscription.js +2 -1
  55. package/dist/commands/node/wait.js +4 -3
  56. package/dist/commands/node-context.js +2 -1
  57. package/dist/commands/node-lifecycle-revive.js +0 -2
  58. package/dist/commands/node-worktree.js +2 -1
  59. package/dist/commands/pkg/browse/actions.d.ts +1 -1
  60. package/dist/commands/pkg/browse/actions.js +4 -4
  61. package/dist/commands/pkg/browse/catalog.d.ts +1 -1
  62. package/dist/commands/pkg/browse/catalog.js +15 -15
  63. package/dist/commands/pkg/browse/command-view.d.ts +1 -1
  64. package/dist/commands/pkg/browse/command-view.js +3 -3
  65. package/dist/commands/pkg/browse/detail-panel.d.ts +1 -1
  66. package/dist/commands/pkg/browse/detail-panel.js +8 -8
  67. package/dist/commands/pkg/browse/doc-view.d.ts +1 -1
  68. package/dist/commands/pkg/browse/doc-view.js +3 -3
  69. package/dist/commands/pkg/browse/list-panel.d.ts +1 -1
  70. package/dist/commands/pkg/browse/list-panel.js +3 -3
  71. package/dist/commands/pkg/browse/model.d.ts +3 -3
  72. package/dist/commands/pkg/browse/shell.d.ts +2 -2
  73. package/dist/commands/pkg/browse/shell.js +13 -13
  74. package/dist/commands/pkg/plugin-manage.js +2 -1
  75. package/dist/commands/pkg/shared.js +0 -2
  76. package/dist/commands/profile/delete.js +2 -1
  77. package/dist/commands/push.js +2 -3
  78. package/dist/commands/revive.js +0 -2
  79. package/dist/commands/surface/node/placement.js +2 -1
  80. package/dist/commands/surface-edit.js +2 -1
  81. package/dist/commands/sys/config.js +0 -4
  82. package/dist/commands/sys/daemon.js +0 -10
  83. package/dist/commands/sys/doctor.js +3 -2
  84. package/dist/commands/sys/panels/broker-limits-panel.d.ts +2 -2
  85. package/dist/commands/sys/panels/broker-limits-panel.js +4 -4
  86. package/dist/commands/sys/panels/dependencies-panel.d.ts +2 -2
  87. package/dist/commands/sys/panels/dependencies-panel.js +2 -2
  88. package/dist/commands/sys/panels/keybindings-panel.d.ts +1 -1
  89. package/dist/commands/sys/panels/keybindings-panel.js +5 -5
  90. package/dist/commands/sys/panels/models-panel.d.ts +4 -4
  91. package/dist/commands/sys/panels/models-panel.js +11 -10
  92. package/dist/commands/sys/panels/plugins-panel.d.ts +2 -2
  93. package/dist/commands/sys/panels/plugins-panel.js +2 -2
  94. package/dist/commands/sys/panels/prefs-panel.d.ts +2 -2
  95. package/dist/commands/sys/panels/prefs-panel.js +4 -4
  96. package/dist/commands/sys/panels/profiles-panel.d.ts +1 -1
  97. package/dist/commands/sys/panels/profiles-panel.js +14 -13
  98. package/dist/commands/sys/panels/provider-panel.d.ts +3 -3
  99. package/dist/commands/sys/panels/provider-panel.js +8 -8
  100. package/dist/commands/sys/settings-shell.d.ts +3 -3
  101. package/dist/commands/sys/settings-shell.js +15 -15
  102. package/dist/commands/sys/setup-settings-runner.d.ts +3 -3
  103. package/dist/commands/sys/setup-settings-runner.js +5 -5
  104. package/dist/commands/sys/setup-wizard.d.ts +4 -4
  105. package/dist/commands/sys/setup-wizard.js +8 -8
  106. package/dist/commands/sys/support/prepare.js +4 -4
  107. package/dist/commands/sys/support/submit.js +2 -2
  108. package/dist/commands/sys/sync-deps.js +0 -2
  109. package/dist/commands/sys/sync-project-guidance.js +2 -1
  110. package/dist/commands/sys/sysprompt.js +2 -1
  111. package/dist/core/__tests__/boot.test.js +0 -8
  112. package/dist/core/__tests__/broker-extension-canvas-db-boundary.test.js +23 -1
  113. package/dist/core/__tests__/canvas-db-boundary.test.d.ts +1 -0
  114. package/dist/core/__tests__/canvas-db-boundary.test.js +46 -0
  115. package/dist/core/__tests__/canvas.test.js +1 -1
  116. package/dist/core/__tests__/child-death-wake.test.js +0 -2
  117. package/dist/core/__tests__/cron-armed-context-failure.test.d.ts +1 -0
  118. package/dist/core/__tests__/cron-armed-context-failure.test.js +145 -0
  119. package/dist/core/__tests__/daemon-wedge.test.js +0 -4
  120. package/dist/core/__tests__/dead-node-policy-table.test.js +0 -4
  121. package/dist/core/__tests__/fixtures/fake-engine.js +4 -14
  122. package/dist/core/__tests__/fixtures/fake-pi-host.js +5 -5
  123. package/dist/core/__tests__/focuses.test.js +0 -6
  124. package/dist/core/__tests__/fs-utils.test.d.ts +1 -0
  125. package/dist/core/__tests__/fs-utils.test.js +50 -0
  126. package/dist/core/__tests__/helpers/harness.js +1 -1
  127. package/dist/core/__tests__/inline-memory-refs.test.js +0 -2
  128. package/dist/core/__tests__/integration/broker-fork-seam.test.js +0 -2
  129. package/dist/core/__tests__/integration/broker-sdk-wiring.test.js +3 -16
  130. package/dist/core/__tests__/integration/command-plugins.test.js +0 -24
  131. package/dist/core/__tests__/integration/deferred-no-wake.test.js +0 -6
  132. package/dist/core/__tests__/integration/flagship-lifecycle.test.js +0 -26
  133. package/dist/core/__tests__/integration/live-mutation-verbs.test.js +0 -4
  134. package/dist/core/__tests__/integration/live-mutation.test.js +1 -5
  135. package/dist/core/__tests__/integration/refresh-stall-recycle.test.js +1 -5
  136. package/dist/core/__tests__/integration/revive.test.js +0 -12
  137. package/dist/core/__tests__/integration/subscription-delivery.test.js +0 -6
  138. package/dist/core/__tests__/integration/tmux-surface.test.js +24 -8
  139. package/dist/core/__tests__/integration/worktree-reap.test.js +0 -6
  140. package/dist/core/__tests__/lifecycle.test.js +0 -18
  141. package/dist/core/__tests__/memory-resolver-precedence.test.js +1 -1
  142. package/dist/core/__tests__/migration.test.js +2 -2
  143. package/dist/core/__tests__/on-read-dedup-resume.test.js +3 -3
  144. package/dist/core/__tests__/pid-identity-match.test.js +1 -1
  145. package/dist/core/__tests__/plugin-kinds.test.js +1 -1
  146. package/dist/core/__tests__/plugin-page-components.test.js +1 -1
  147. package/dist/core/__tests__/preview-registry-sync.test.js +42 -1
  148. package/dist/core/__tests__/respawn-throttle.test.js +0 -4
  149. package/dist/core/__tests__/revive-all.test.js +5 -5
  150. package/dist/core/__tests__/revive-parked-fresh.test.js +62 -11
  151. package/dist/core/__tests__/seam/broker-attach-multiclient.test.js +0 -8
  152. package/dist/core/__tests__/seam/broker-attach-stream.test.js +0 -8
  153. package/dist/core/__tests__/seam/broker-crash-teardown.test.js +0 -2
  154. package/dist/core/__tests__/tmux-driver-boundary.test.d.ts +1 -0
  155. package/dist/core/__tests__/tmux-driver-boundary.test.js +50 -0
  156. package/dist/core/activity-signal.d.ts +7 -0
  157. package/dist/core/activity-signal.js +38 -0
  158. package/dist/core/auto-update.js +2 -1
  159. package/dist/core/bootstrap.js +2 -1
  160. package/dist/core/canvas/__tests__/remote-canvas-source.test.js +1 -1
  161. package/dist/core/canvas/__tests__/remote-transport.test.js +1 -1
  162. package/dist/core/canvas/__tests__/render-remote.test.js +1 -1
  163. package/dist/core/canvas/browse/app.js +3 -2
  164. package/dist/core/canvas/browse/model.d.ts +1 -1
  165. package/dist/core/canvas/browse/model.js +0 -4
  166. package/dist/core/canvas/canvas.d.ts +22 -0
  167. package/dist/core/canvas/canvas.js +50 -18
  168. package/dist/core/canvas/crons.js +0 -6
  169. package/dist/core/canvas/db.d.ts +14 -20
  170. package/dist/core/canvas/db.js +30 -1332
  171. package/dist/core/canvas/focuses.js +0 -4
  172. package/dist/core/canvas/history.js +2 -1
  173. package/dist/core/canvas/migrations.d.ts +20 -0
  174. package/dist/core/canvas/migrations.js +1339 -0
  175. package/dist/core/canvas/nav-model.js +1 -11
  176. package/dist/core/canvas/nav-render.js +0 -8
  177. package/dist/core/canvas/remote-canvas-source.js +0 -2
  178. package/dist/core/canvas/render-source.js +31 -13
  179. package/dist/core/command-manifests/manifest.js +0 -10
  180. package/dist/core/command-manifests/schema.js +0 -4
  181. package/dist/core/command-plugins/transport/http-invoke.js +0 -14
  182. package/dist/core/command.js +0 -4
  183. package/dist/core/config.js +2 -1
  184. package/dist/core/feed/inbox.d.ts +10 -0
  185. package/dist/core/feed/inbox.js +18 -12
  186. package/dist/core/fs-utils.d.ts +9 -2
  187. package/dist/core/fs-utils.js +7 -15
  188. package/dist/core/help.js +0 -8
  189. package/dist/core/host-exports/export.d.ts +2 -2
  190. package/dist/core/host-exports/export.js +9 -12
  191. package/dist/core/human/answer.d.ts +2 -2
  192. package/dist/core/human/convention.d.ts +2 -5
  193. package/dist/core/human/convention.js +3 -13
  194. package/dist/core/human/page-schema.d.ts +2 -2
  195. package/dist/core/human/page-schema.js +2 -2
  196. package/dist/core/human/root.js +6 -23
  197. package/dist/core/human/scan.js +24 -7
  198. package/dist/core/human/tickets.d.ts +4 -1
  199. package/dist/core/human/tickets.js +20 -6
  200. package/dist/core/io.js +0 -12
  201. package/dist/core/memory/identity.js +0 -4
  202. package/dist/core/memory/inline-ref-inventory.js +3 -2
  203. package/dist/core/memory/lint.js +3 -6
  204. package/dist/core/memory-resolver.d.ts +5 -0
  205. package/dist/core/memory-resolver.js +14 -12
  206. package/dist/core/pagination.js +0 -2
  207. package/dist/core/preview-registry.d.ts +14 -0
  208. package/dist/core/preview-registry.js +2 -4
  209. package/dist/core/preview-result-path.js +2 -1
  210. package/dist/core/profiles/manifest.d.ts +37 -5
  211. package/dist/core/profiles/manifest.js +44 -24
  212. package/dist/core/profiles/select.js +0 -4
  213. package/dist/core/review/signal.js +4 -23
  214. package/dist/core/review/ticket-filter.d.ts +3 -3
  215. package/dist/core/review/ticket-filter.js +4 -9
  216. package/dist/core/runtime/__tests__/engine-route.test.js +1 -1
  217. package/dist/core/runtime/__tests__/spawn-env.test.js +0 -8
  218. package/dist/core/runtime/bearings-render.d.ts +56 -0
  219. package/dist/core/runtime/bearings-render.js +241 -0
  220. package/dist/core/runtime/bearings.d.ts +2 -17
  221. package/dist/core/runtime/bearings.js +12 -198
  222. package/dist/core/runtime/broker/auth-reload.d.ts +39 -0
  223. package/dist/core/runtime/broker/auth-reload.js +149 -0
  224. package/dist/core/runtime/broker/engine-drive.d.ts +26 -0
  225. package/dist/core/runtime/broker/engine-drive.js +257 -0
  226. package/dist/core/runtime/broker/engine-routing.d.ts +143 -0
  227. package/dist/core/runtime/broker/engine-routing.js +211 -0
  228. package/dist/core/runtime/broker/event-projection.js +3 -2
  229. package/dist/core/runtime/broker/fault-retry.js +5 -4
  230. package/dist/core/runtime/broker/frame-client.d.ts +38 -0
  231. package/dist/core/runtime/broker/frame-client.js +129 -0
  232. package/dist/core/runtime/broker/frame-dispatch.d.ts +12 -189
  233. package/dist/core/runtime/broker/frame-dispatch.js +89 -1476
  234. package/dist/core/runtime/broker/frame-engine-control.d.ts +20 -0
  235. package/dist/core/runtime/broker/frame-engine-control.js +231 -0
  236. package/dist/core/runtime/broker/frame-memory-refs.d.ts +19 -0
  237. package/dist/core/runtime/broker/frame-memory-refs.js +41 -0
  238. package/dist/core/runtime/broker/frame-model-query.d.ts +13 -0
  239. package/dist/core/runtime/broker/frame-model-query.js +69 -0
  240. package/dist/core/runtime/broker/frame-read.d.ts +19 -0
  241. package/dist/core/runtime/broker/frame-read.js +116 -0
  242. package/dist/core/runtime/broker/frame-replies.d.ts +24 -0
  243. package/dist/core/runtime/broker/frame-replies.js +64 -0
  244. package/dist/core/runtime/broker/frame-session.d.ts +34 -0
  245. package/dist/core/runtime/broker/frame-session.js +203 -0
  246. package/dist/core/runtime/broker/turn-ignition.d.ts +42 -0
  247. package/dist/core/runtime/broker/turn-ignition.js +74 -0
  248. package/dist/core/runtime/broker-extension-render.js +9 -85
  249. package/dist/core/runtime/broker-protocol.js +0 -4
  250. package/dist/core/runtime/broker-sdk.js +2 -1
  251. package/dist/core/runtime/broker.d.ts +1 -1
  252. package/dist/core/runtime/broker.js +5 -23
  253. package/dist/core/runtime/fault.js +3 -2
  254. package/dist/core/runtime/headless-pi.js +3 -2
  255. package/dist/core/runtime/host.js +2 -3
  256. package/dist/core/runtime/kickoff.js +0 -4
  257. package/dist/core/runtime/launch-target.js +2 -1
  258. package/dist/core/runtime/launch.js +3 -6
  259. package/dist/core/runtime/lifecycle.js +17 -26
  260. package/dist/core/runtime/managed-provider-cooling.d.ts +1 -1
  261. package/dist/core/runtime/naming.js +0 -2
  262. package/dist/core/runtime/nodes.js +2 -1
  263. package/dist/core/runtime/persona.js +0 -6
  264. package/dist/core/runtime/placement-tmux.js +2 -5
  265. package/dist/core/runtime/placement.js +4 -11
  266. package/dist/core/runtime/recap.js +4 -3
  267. package/dist/core/runtime/reopen.js +4 -7
  268. package/dist/core/runtime/reset.js +0 -4
  269. package/dist/core/runtime/resume-root.js +0 -2
  270. package/dist/core/runtime/revive.js +17 -13
  271. package/dist/core/runtime/session-list-cache.js +2 -2
  272. package/dist/core/runtime/shell-expansion.js +0 -2
  273. package/dist/core/runtime/spawn.js +0 -2
  274. package/dist/core/runtime/stream-watchdog.d.ts +0 -1
  275. package/dist/core/runtime/stream-watchdog.js +13 -18
  276. package/dist/core/runtime/tmux-bindings.js +0 -2
  277. package/dist/core/runtime/tmux-driver.d.ts +12 -0
  278. package/dist/core/runtime/tmux-driver.js +18 -13
  279. package/dist/core/runtime/warm-pool.js +0 -4
  280. package/dist/core/scope.js +4 -3
  281. package/dist/core/spawn.js +2 -3
  282. package/dist/core/subscription-state.js +10 -17
  283. package/dist/core/substrate/__tests__/surface-match-pre-command.test.d.ts +1 -0
  284. package/dist/core/substrate/__tests__/surface-match-pre-command.test.js +92 -0
  285. package/dist/core/substrate/frontmatter-validation.js +1 -1
  286. package/dist/core/substrate/injected-store.d.ts +6 -0
  287. package/dist/core/substrate/injected-store.js +24 -0
  288. package/dist/core/substrate/on-read.d.ts +17 -1
  289. package/dist/core/substrate/on-read.js +36 -2
  290. package/dist/core/substrate/render.js +0 -12
  291. package/dist/core/substrate/schema.d.ts +3 -3
  292. package/dist/core/substrate/schema.js +4 -16
  293. package/dist/core/substrate/surface-match.d.ts +19 -0
  294. package/dist/core/substrate/surface-match.js +60 -2
  295. package/dist/core/termrender/display.js +2 -1
  296. package/dist/core/tui/page-host.d.ts +3 -3
  297. package/dist/core/tui/page-host.js +4 -4
  298. package/dist/core/tui/panel.js +3 -3
  299. package/dist/core/wake.js +0 -12
  300. package/dist/core/worktree.js +0 -2
  301. package/dist/daemon/__tests__/reconciler-signature.test.d.ts +1 -0
  302. package/dist/daemon/__tests__/reconciler-signature.test.js +36 -0
  303. package/dist/daemon/api/__tests__/seam/api-server.test.js +2 -16
  304. package/dist/daemon/api/__tests__/seam/b10-attach-modelauth.test.js +3 -11
  305. package/dist/daemon/api/__tests__/seam/leaf-api-parity.test.js +0 -6
  306. package/dist/daemon/api/handlers/attach.js +2 -1
  307. package/dist/daemon/api/handlers/broker-ops.js +3 -6
  308. package/dist/daemon/api/handlers/canvas.js +12 -18
  309. package/dist/daemon/api/handlers/feedback-comments.js +0 -8
  310. package/dist/daemon/api/handlers/health.js +3 -2
  311. package/dist/daemon/api/handlers/human-requests.js +0 -10
  312. package/dist/daemon/api/handlers/human.js +1 -5
  313. package/dist/daemon/api/handlers/inbox.js +0 -18
  314. package/dist/daemon/api/handlers/memory.js +1 -1
  315. package/dist/daemon/api/handlers/messages.js +4 -8
  316. package/dist/daemon/api/handlers/nodes.js +10 -21
  317. package/dist/daemon/api/handlers/reports.js +1 -1
  318. package/dist/daemon/api/handlers/reviews.js +1 -6
  319. package/dist/daemon/api/map.js +0 -6
  320. package/dist/daemon/api/router.js +0 -4
  321. package/dist/daemon/api/server.js +2 -5
  322. package/dist/daemon/boot-migrations.d.ts +14 -0
  323. package/dist/daemon/boot-migrations.js +31 -0
  324. package/dist/daemon/cron/armed-context.d.ts +21 -0
  325. package/dist/daemon/cron/armed-context.js +100 -0
  326. package/dist/daemon/cron/capture.d.ts +20 -0
  327. package/dist/daemon/cron/capture.js +45 -0
  328. package/dist/daemon/cron/disposition.d.ts +19 -0
  329. package/dist/daemon/cron/disposition.js +52 -0
  330. package/dist/daemon/cron/escalation.d.ts +12 -0
  331. package/dist/daemon/cron/escalation.js +124 -0
  332. package/dist/daemon/cron/held.d.ts +4 -0
  333. package/dist/daemon/cron/held.js +20 -0
  334. package/dist/daemon/cron/lease-recovery.d.ts +3 -0
  335. package/dist/daemon/cron/lease-recovery.js +119 -0
  336. package/dist/daemon/cron/live-runs.d.ts +11 -0
  337. package/dist/daemon/cron/live-runs.js +23 -0
  338. package/dist/daemon/cron/passes.d.ts +11 -0
  339. package/dist/daemon/cron/passes.js +78 -0
  340. package/dist/daemon/cron/settle.d.ts +28 -0
  341. package/dist/daemon/cron/settle.js +37 -0
  342. package/dist/daemon/cron/sinks.d.ts +2 -0
  343. package/dist/daemon/cron/sinks.js +100 -0
  344. package/dist/daemon/cron-run.d.ts +5 -41
  345. package/dist/daemon/cron-run.js +193 -882
  346. package/dist/daemon/crtrd-cli.js +2 -1
  347. package/dist/daemon/crtrd.js +22 -22
  348. package/dist/daemon/fleet.js +0 -4
  349. package/dist/daemon/human/deliver-action.js +2 -1
  350. package/dist/daemon/human/sweep.js +15 -4
  351. package/dist/daemon/manage.d.ts +2 -2
  352. package/dist/daemon/manage.js +4 -13
  353. package/dist/daemon/pidfile.js +2 -2
  354. package/dist/daemon/reconcilers/broker-supervision.js +26 -29
  355. package/dist/daemon/reconcilers/dormant-inbox.d.ts +1 -1
  356. package/dist/daemon/reconcilers/dormant-inbox.js +2 -1
  357. package/dist/daemon/reconcilers/live-obligation.js +2 -2
  358. package/dist/daemon/reconcilers/pending-review-submit.d.ts +1 -1
  359. package/dist/daemon/reconcilers/pending-review-submit.js +2 -1
  360. package/dist/daemon/reconcilers/storage-maintenance.d.ts +2 -1
  361. package/dist/daemon/reconcilers/storage-maintenance.js +2 -1
  362. package/dist/daemon/review/companion.js +2 -2
  363. package/dist/daemon/review/sweep.js +8 -3
  364. package/dist/migrations/001-surfaces-frontmatter.js +0 -6
  365. package/dist/migrations/corpus.js +2 -5
  366. package/dist/migrations/registry.js +15 -2
  367. package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +0 -4
  368. package/dist/pi-extensions/__tests__/pre-command-gate.test.d.ts +1 -0
  369. package/dist/pi-extensions/__tests__/pre-command-gate.test.js +220 -0
  370. package/dist/pi-extensions/broker-local.js +2 -1
  371. package/dist/pi-extensions/canvas-bash-valve.js +3 -17
  372. package/dist/pi-extensions/canvas-context-intro.js +2 -7
  373. package/dist/pi-extensions/canvas-doc-substrate.d.ts +14 -0
  374. package/dist/pi-extensions/canvas-doc-substrate.js +77 -5
  375. package/dist/pi-extensions/canvas-goal-capture.js +2 -1
  376. package/dist/pi-extensions/canvas-inbox-watcher.js +5 -12
  377. package/dist/pi-extensions/canvas-passive-context.js +2 -5
  378. package/dist/pi-extensions/canvas-preview-result.js +2 -1
  379. package/dist/pi-extensions/canvas-prompt-scrub.js +2 -1
  380. package/dist/pi-extensions/canvas-recap.js +3 -16
  381. package/dist/pi-extensions/canvas-review-boundary.js +2 -5
  382. package/dist/pi-extensions/canvas-stophook.js +2 -26
  383. package/dist/pi-extensions/canvas-structured-output.js +2 -1
  384. package/dist/pi-extensions/canvas-tool-guide.js +2 -1
  385. package/dist/shared/__tests__/env-boundary.test.d.ts +1 -0
  386. package/dist/shared/__tests__/env-boundary.test.js +93 -0
  387. package/dist/shared/env.d.ts +68 -0
  388. package/dist/shared/env.js +171 -0
  389. package/package.json +3 -1
  390. package/runtime.lock.json +2 -2
  391. package/dist/core/canvas/render.d.ts +0 -47
  392. package/dist/core/canvas/render.js +0 -245
@@ -0,0 +1,241 @@
1
+ // bearings-render.ts — the pure rendering core shared by bearings.ts (canvas-
2
+ // backed, warm-spare live-delivery + cron-wake lanes) and broker-extension-
3
+ // render.ts (DTO-backed, broker-side cold-boot lane). Both lanes render
4
+ // byte-identical bearings for a real node; this module holds every function
5
+ // that does not need canvas.db to do it.
6
+ //
7
+ // No import of canvas.js/db.js/canvas/index.js — only canvas/paths.js and
8
+ // canvas/labels.js (both already db.ts-free), substrate-free, plus node
9
+ // builtins. This keeps broker-extension-canvas-db-boundary.test.ts's static
10
+ // import walk green with no changes to that test.
11
+ import { contextDir } from '../canvas/paths.js';
12
+ import { fullName } from '../canvas/labels.js';
13
+ import { tmuxSessionsReachable } from './tmux-driver.js';
14
+ import { spawnSync } from 'node:child_process';
15
+ import { readdirSync } from 'node:fs';
16
+ import { hostname } from 'node:os';
17
+ import { loadProfileManifest } from '../profiles/manifest.js';
18
+ export function escapeAttribute(value) {
19
+ return value.replaceAll('&', '&amp;').replaceAll('"', '&quot;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');
20
+ }
21
+ /** A one-line snapshot of the host/runtime the node's engine is executing in —
22
+ * platform, hostname, node version, and whether a tmux server is reachable.
23
+ * The engine is headless, so this reflects the broker/daemon environment the
24
+ * node's own bash tools inherit — NOT a viewer pane.
25
+ *
26
+ * The tmux bit deliberately reports SERVER REACHABILITY, not "am I inside a
27
+ * tmux pane" — the latter is a constant `no` for every node and so tells the
28
+ * agent nothing: a broker is detached and paneless, and DELETES $TMUX /
29
+ * $TMUX_PANE from its env on boot (broker.ts) so its children never act on the
30
+ * launching parent's pane. The actionable question is whether this process's
31
+ * bash tools can drive tmux at all, and `tmuxSessionsReachable()` exits 0 iff
32
+ * a server is running with at least one session. The label says `tmux server`
33
+ * so it cannot be misread as pane residency. */
34
+ function runtimeLine() {
35
+ const tmuxServer = tmuxSessionsReachable();
36
+ const parts = [
37
+ `${process.platform} ${process.arch}`,
38
+ hostname(),
39
+ `node ${process.version}`,
40
+ `tmux server: ${tmuxServer ? 'reachable' : 'none'}`,
41
+ ];
42
+ return `Host: ${parts.join(' · ')}`;
43
+ }
44
+ function directoryListingLines(cwd) {
45
+ try {
46
+ const entries = readdirSync(cwd, { withFileTypes: true });
47
+ const sorted = entries.sort((a, b) => {
48
+ if (a.isDirectory() !== b.isDirectory())
49
+ return a.isDirectory() ? -1 : 1;
50
+ return a.name.localeCompare(b.name);
51
+ });
52
+ const shown = sorted.slice(0, 50).map((entry) => `${entry.name}${entry.isDirectory() ? '/' : ''}`);
53
+ const hidden = sorted.length - shown.length;
54
+ return hidden > 0 ? [...shown, `[+${hidden} more]`] : shown;
55
+ }
56
+ catch {
57
+ return ['Directory listing: unavailable (cwd missing or unreadable).'];
58
+ }
59
+ }
60
+ function runCommand(cwd, cmd, args) {
61
+ const result = spawnSync(cmd, args, { cwd, encoding: 'utf8' });
62
+ if (result.error !== undefined) {
63
+ return { ok: false, stdout: '', stderr: result.error.message };
64
+ }
65
+ return {
66
+ ok: result.status === 0,
67
+ stdout: (result.stdout ?? '').trim(),
68
+ stderr: (result.stderr ?? '').trim(),
69
+ };
70
+ }
71
+ function runGit(cwd, args) {
72
+ const result = runCommand(cwd, 'git', args);
73
+ return result.ok ? result.stdout : '';
74
+ }
75
+ function parseWorktrees(lines) {
76
+ const worktrees = [];
77
+ let cur = {};
78
+ for (const line of lines) {
79
+ if (line.startsWith('worktree ')) {
80
+ if (cur.path !== undefined) {
81
+ worktrees.push({ path: cur.path, branch: cur.branch ?? 'detached HEAD' });
82
+ }
83
+ cur = { path: line.slice('worktree '.length) };
84
+ }
85
+ else if (line.startsWith('branch ')) {
86
+ cur.branch = line.slice('branch '.length).replace(/^refs\/heads\//, '');
87
+ }
88
+ }
89
+ if (cur.path !== undefined) {
90
+ worktrees.push({ path: cur.path, branch: cur.branch ?? 'detached HEAD' });
91
+ }
92
+ return worktrees;
93
+ }
94
+ /** The git status/worktree snapshot lines. The initial probe distinguishes a
95
+ * genuine non-repo (`not a git repository` in stderr) from any other failure
96
+ * (spawn error, permission, a hung/killed process) — collapsing both into
97
+ * "not a git repository" would mislabel a real outage as a documented,
98
+ * benign state. Every other probe here is genuinely optional and already
99
+ * falls back to 'unknown'/'HEAD'/a zero count on failure, which stays as-is. */
100
+ function gitSnapshotLines(cwd) {
101
+ const probe = runCommand(cwd, 'git', ['rev-parse', '--is-inside-work-tree']);
102
+ if (!probe.ok) {
103
+ if (/not a git repository/i.test(probe.stderr)) {
104
+ return ['Git: not a git repository.'];
105
+ }
106
+ return [`Git: unavailable (${probe.stderr || 'git command failed'}).`];
107
+ }
108
+ const branch = runGit(cwd, ['branch', '--show-current']) || runGit(cwd, ['rev-parse', '--abbrev-ref', 'HEAD']) || 'HEAD';
109
+ const sha = runGit(cwd, ['rev-parse', '--short', 'HEAD']) || 'unknown';
110
+ const statusCount = runGit(cwd, ['status', '--porcelain'])
111
+ .split('\n')
112
+ .filter((line) => line !== '').length;
113
+ const worktreeLines = runGit(cwd, ['worktree', 'list', '--porcelain']).split('\n');
114
+ const worktrees = parseWorktrees(worktreeLines);
115
+ const dirty = statusCount === 0 ? 'clean' : `${statusCount} uncommitted change${statusCount === 1 ? '' : 's'}`;
116
+ const summary = `Git: ${branch} @ ${sha}; ${dirty}; ${worktrees.length} worktree${worktrees.length === 1 ? '' : 's'}.`;
117
+ const worktreeSummary = worktrees.length === 0
118
+ ? 'Worktrees: none.'
119
+ : `Worktrees: ${worktrees.map((w) => `${w.path} (${w.branch})`).join(', ')}`;
120
+ return [summary, worktreeSummary];
121
+ }
122
+ /** The `<project_context>` block — the `<environment>` snapshot only: a
123
+ * host/runtime line (platform, hostname, node version, tmux-or-not — the
124
+ * headless engine's own process env, which its bash tools inherit), the cwd
125
+ * dir listing, and git status/worktrees. Project guidance lives in the
126
+ * document substrate, so this wrapper stays environment-only. It always emits
127
+ * because the environment snapshot is always present. */
128
+ export function buildProjectContextBlock(cwd) {
129
+ const envLines = directoryListingLines(cwd);
130
+ const gitLines = gitSnapshotLines(cwd);
131
+ let out = '<project_context>\n\n';
132
+ out += `<environment cwd="${escapeAttribute(cwd)}">\n`;
133
+ out += `${runtimeLine()}\n`;
134
+ out += `Directory:\n${envLines.map((line) => ` ${line}`).join('\n')}\n`;
135
+ out += `${gitLines.map((line) => `${line}`).join('\n')}\n`;
136
+ out += '</environment>\n</project_context>';
137
+ return out;
138
+ }
139
+ /** The context-directory note — names the path INLINE (no XML attribute, so it
140
+ * never scopes a sibling block) and frames the dir as durable, shared scratch.
141
+ * Present for every node, rendered as the `## Your context directory`
142
+ * subsection of <crtr-bearings>. */
143
+ export function contextDirNote(nodeId) {
144
+ return (`Your context directory is \`${contextDir(nodeId)}\` (also in the env as \`$CRTR_CONTEXT_DIR\`) — ` +
145
+ 'durable scratch space on disk, and the one place the other nodes on the canvas can read from. ' +
146
+ 'Put documents here that you want to share by reference instead of re-explaining them in a ' +
147
+ 'prompt: specs, designs, findings, notes worth pointing a sibling, child, or parent at. It is a ' +
148
+ 'shared document store, not a task tracker. **Address it by absolute path** — write artifacts as ' +
149
+ '`$CRTR_CONTEXT_DIR/<name>.md` (or the full path above). Your working dir is the project, NOT ' +
150
+ 'this directory, so a bare relative `context/...` lands in the repo, not here — and other nodes ' +
151
+ 'can only read what is actually here. When you report a file, report its absolute path.');
152
+ }
153
+ /** The profile-purview note — rendered ONLY when the profile resolves to a
154
+ * manifest that names at least one project dir. Phrased as PURVIEW, not a cwd
155
+ * replacement: the node's cwd is still its one working dir (bash/edits run
156
+ * there), the profile just widens what it may ALSO read/act on. Omitted
157
+ * entirely for a profile with no project purview (including the stable root
158
+ * profile) or a deleted/unreadable manifest — never throws. This absolute-path
159
+ * disclosure is intentional (spec-approved): the agent must know its full
160
+ * purview, not a redacted count. */
161
+ export function profileNote(cwd, profileId) {
162
+ if (profileId === undefined || profileId === null || profileId === '')
163
+ return '';
164
+ let entry;
165
+ try {
166
+ entry = loadProfileManifest(profileId);
167
+ }
168
+ catch {
169
+ return '';
170
+ }
171
+ const projects = entry.manifest.projects.map((project) => project.path);
172
+ if (projects.length === 0)
173
+ return '';
174
+ return (`Your working dir is \`${cwd}\`. Your profile **${entry.manifest.name}** also has purview ` +
175
+ `over: ${projects.join(', ')}.`);
176
+ }
177
+ /** Orchestrator-only framing: a resident orchestrator survives refresh cycles,
178
+ * so its context dir is also where a future cycle of itself resumes the work.
179
+ * Used inside the bearings block AND in the promotion guidance dump, so a
180
+ * promoted node gets the same note a born-orchestrator gets. */
181
+ export function orchestratorContextNote(nodeId) {
182
+ return (`Because you persist across refresh cycles, your context directory (${contextDir(nodeId)}) is ` +
183
+ `also where a future cycle of you resumes the work — keep the working notes and decisions a ` +
184
+ `refreshed you would need there, alongside the docs you share with the nodes you spawn.`);
185
+ }
186
+ /** One relative's line in the map: `<id> — <kind> · <status>`, plus the node's
187
+ * task description when it carries one (orientation: what that relative is for). */
188
+ export function relativeLabel(node) {
189
+ const desc = fullName(node);
190
+ const descPart = desc !== '' && desc !== node.kind && desc !== node.name ? ` — ${desc}` : '';
191
+ return `${node.node_id} (${node.kind} · ${node.status})${descPart}`;
192
+ }
193
+ /** Most children listed in the mini-map before the rest collapse to `+N more`. */
194
+ const MAX_CHILDREN_SHOWN = 5;
195
+ /** The graph mini-map: the ancestry trunk (root first), then this node marked
196
+ * `● you`, then its immediate children. Returns '' for a lone root with no
197
+ * children (nothing to map). */
198
+ export function renderGraphMap(nodeId, kind, mode, trunk, children) {
199
+ if (trunk.length === 0 && children.length === 0)
200
+ return '';
201
+ const indent = (depth) => ' '.repeat(depth);
202
+ const lines = [
203
+ 'Your place in the canvas graph (ancestry above you, your direct children below):',
204
+ ];
205
+ trunk.forEach((m, i) => {
206
+ lines.push(`${indent(i)}${i === 0 ? '' : '└ '}${relativeLabel(m)}`);
207
+ });
208
+ const selfDepth = trunk.length;
209
+ lines.push(`${indent(selfDepth)}${selfDepth === 0 ? '' : '└ '}● you: ${nodeId} (${kind}, ${mode})`);
210
+ // Cap the child list so a wide fan-out can't flood the boot context. Children
211
+ // are sorted live-first, so the shown slice favors active nodes; the rest
212
+ // collapse to a `+N more` tail (active-first ordering means anything elided is
213
+ // the most-finished work).
214
+ const shown = children.slice(0, MAX_CHILDREN_SHOWN);
215
+ const hidden = children.length - shown.length;
216
+ shown.forEach((m, i) => {
217
+ const last = i === shown.length - 1 && hidden === 0;
218
+ lines.push(`${indent(selfDepth + 1)}${last ? '└ ' : '├ '}${relativeLabel(m)}`);
219
+ });
220
+ if (hidden > 0) {
221
+ lines.push(`${indent(selfDepth + 1)}└ +${hidden} more`);
222
+ }
223
+ return lines.join('\n');
224
+ }
225
+ /** The identity assertion injected at session start. A fork names its source
226
+ * while treating the copied first-person narrative as reference. The caller
227
+ * resolves `sourceLabel`: the bare `forkFrom` id when the source lookup
228
+ * fails/is unsafe, else `${forkFrom} ("${fullName(src)}")`. */
229
+ export function buildIdentityAssertion(nodeId, kind, mode, forkFrom, sourceLabel) {
230
+ const lines = [`You are node ${nodeId} — kind ${kind}, mode ${mode}.`];
231
+ if (forkFrom !== undefined && forkFrom !== null && forkFrom !== '') {
232
+ lines.push('', `You are a new FORK of ${sourceLabel}, not ${sourceLabel}. Pi copied the earlier conversation ` +
233
+ `into this session. Treat that first-person history as reference material, not your own past.`);
234
+ }
235
+ return lines.join('\n');
236
+ }
237
+ export function worktreeNote(worktree) {
238
+ if (worktree?.state !== 'open')
239
+ return '';
240
+ return `Branch: \`${worktree.branch}\`\nWorktree: \`${worktree.path}\`\nBase: \`${worktree.base_ref}\` @ \`${worktree.base_sha}\`\nCommit work here. Land it with \`crtr node worktree close\` before finishing. Auto-drop applies only when the worktree is clean (no uncommitted changes) and nothing remains to land (zero commits ahead of the base, or its commits are already contained in the base); unlanded commits (commits not already contained in the base) or uncommitted changes require an explicit close/landing decision.`;
241
+ }
@@ -1,19 +1,9 @@
1
1
  import { type Cron } from '../canvas/index.js';
2
2
  import type { ExposureTarget } from '../substrate/injected-store.js';
3
+ import { buildProjectContextBlock, contextDirNote, orchestratorContextNote } from './bearings-render.js';
3
4
  /** The bearings custom type is shared with every attached renderer. */
4
5
  export { CONTEXT_INTRO_CUSTOM_TYPE } from '../../shared/generated-context.js';
5
- /** The `<project_context>` block — the `<environment>` snapshot only: a
6
- * host/runtime line (platform, hostname, node version, tmux-or-not — the
7
- * headless engine's own process env, which its bash tools inherit), the cwd
8
- * dir listing, and git status/worktrees. Project guidance lives in the
9
- * document substrate, so this wrapper stays environment-only. It always emits
10
- * because the environment snapshot is always present. Exported for testing. */
11
- export declare function buildProjectContextBlock(cwd: string): string;
12
- /** The context-directory note — names the path INLINE (no XML attribute, so it
13
- * never scopes a sibling block) and frames the dir as durable, shared scratch.
14
- * Present for every node, rendered as the `## Your context directory`
15
- * subsection of <crtr-bearings>. */
16
- export declare function contextDirNote(nodeId: string): string;
6
+ export { buildProjectContextBlock, contextDirNote, orchestratorContextNote };
17
7
  /** The profile-purview note — rendered ONLY when `profile_id` resolves to a
18
8
  * manifest that names at least one project dir. Phrased as PURVIEW, not a cwd
19
9
  * replacement: the node's cwd is still its one working dir (bash/edits run
@@ -24,11 +14,6 @@ export declare function contextDirNote(nodeId: string): string;
24
14
  * is intentional (spec-approved): the agent must know its full purview, not a
25
15
  * redacted count. */
26
16
  export declare function profileNote(nodeId: string): string;
27
- /** Orchestrator-only framing: a resident orchestrator survives refresh cycles,
28
- * so its context dir is also where a future cycle of itself resumes the work.
29
- * Used inside the bearings block AND in the promotion guidance dump, so a
30
- * promoted node gets the same note a born-orchestrator gets. */
31
- export declare function orchestratorContextNote(nodeId: string): string;
32
17
  /** The graph mini-map: the ancestry trunk (root first), then this node marked
33
18
  * `● you`, then its immediate children. Returns '' for a lone root with no
34
19
  * children (nothing to map). Exported for testing. */
@@ -10,139 +10,15 @@
10
10
  //
11
11
  // promote.ts separately delivers orchestratorContextNote() when a base node
12
12
  // becomes an orchestrator after birth.
13
- import { contextDir, getNode, fullName, isSafeNodeId, subscriptionsOf, } from '../canvas/index.js';
14
- import { execSync } from 'node:child_process';
15
- import { readdirSync } from 'node:fs';
16
- import { spawnSync } from 'node:child_process';
17
- import { hostname } from 'node:os';
13
+ import { getNode, fullName, isSafeNodeId, subscriptionsOf, } from '../canvas/index.js';
18
14
  import { cadenceDisplay } from '../wake.js';
19
15
  import { renderKnowledgeBlock, renderWorkspaceOpenDocs } from '../substrate/index.js';
20
- import { loadProfileManifest } from '../profiles/manifest.js';
21
16
  import { situationalContextEnvelope } from './situational-context.js';
22
17
  import { statusRank } from '../canvas/node-order.js';
18
+ import { buildProjectContextBlock, contextDirNote, orchestratorContextNote, profileNote as profileNoteShared, renderGraphMap, buildIdentityAssertion as buildIdentityAssertionShared, worktreeNote, } from './bearings-render.js';
23
19
  /** The bearings custom type is shared with every attached renderer. */
24
20
  export { CONTEXT_INTRO_CUSTOM_TYPE } from '../../shared/generated-context.js';
25
- function escapeAttribute(value) {
26
- return value.replaceAll('&', '&amp;').replaceAll('"', '&quot;').replaceAll('<', '&lt;').replaceAll('>', '&gt;');
27
- }
28
- /** A one-line snapshot of the host/runtime the node's engine is executing in —
29
- * platform, hostname, node version, and whether a tmux server is reachable.
30
- * The engine is headless, so this reflects the broker/daemon environment the
31
- * node's own bash tools inherit — NOT a viewer pane.
32
- *
33
- * The tmux bit deliberately reports SERVER REACHABILITY, not "am I inside a
34
- * tmux pane" — the latter is a constant `no` for every node and so tells the
35
- * agent nothing: a broker is detached and paneless, and DELETES $TMUX /
36
- * $TMUX_PANE from its env on boot (broker.ts) so its children never act on the
37
- * launching parent's pane. The actionable question is whether this process's
38
- * bash tools can drive tmux at all, and `tmux list-sessions` exits 0 iff a
39
- * server is running. The label says `tmux server` so it cannot be misread as
40
- * pane residency. */
41
- function runtimeLine() {
42
- const tmuxServer = spawnSync('tmux', ['list-sessions'], { encoding: 'utf8', stdio: 'ignore' }).status === 0;
43
- const parts = [
44
- `${process.platform} ${process.arch}`,
45
- hostname(),
46
- `node ${process.version}`,
47
- `tmux server: ${tmuxServer ? 'reachable' : 'none'}`,
48
- ];
49
- return `Host: ${parts.join(' · ')}`;
50
- }
51
- function directoryListingLines(cwd) {
52
- try {
53
- const entries = readdirSync(cwd, { withFileTypes: true });
54
- const sorted = entries.sort((a, b) => {
55
- if (a.isDirectory() !== b.isDirectory())
56
- return a.isDirectory() ? -1 : 1;
57
- return a.name.localeCompare(b.name);
58
- });
59
- const shown = sorted.slice(0, 50).map((entry) => `${entry.name}${entry.isDirectory() ? '/' : ''}`);
60
- const hidden = sorted.length - shown.length;
61
- return hidden > 0 ? [...shown, `[+${hidden} more]`] : shown;
62
- }
63
- catch {
64
- return ['Directory listing: unavailable (cwd missing or unreadable).'];
65
- }
66
- }
67
- function gitSnapshot(cwd) {
68
- try {
69
- execSync('git rev-parse --is-inside-work-tree', { cwd, stdio: ['ignore', 'pipe', 'pipe'] });
70
- }
71
- catch {
72
- return { lines: ['Git: not a git repository.'] };
73
- }
74
- const branch = runGit(cwd, 'git branch --show-current') || runGit(cwd, 'git rev-parse --abbrev-ref HEAD') || 'HEAD';
75
- const sha = runGit(cwd, 'git rev-parse --short HEAD') || 'unknown';
76
- const statusCount = runGit(cwd, 'git status --porcelain')
77
- .split('\n')
78
- .filter((line) => line !== '').length;
79
- const worktreeLines = runGit(cwd, 'git worktree list --porcelain').split('\n');
80
- const worktrees = parseWorktrees(worktreeLines);
81
- const dirty = statusCount === 0 ? 'clean' : `${statusCount} uncommitted change${statusCount === 1 ? '' : 's'}`;
82
- const summary = `Git: ${branch} @ ${sha}; ${dirty}; ${worktrees.length} worktree${worktrees.length === 1 ? '' : 's'}.`;
83
- const worktreeSummary = worktrees.length === 0
84
- ? 'Worktrees: none.'
85
- : `Worktrees: ${worktrees.map((w) => `${w.path} (${w.branch})`).join(', ')}`;
86
- return { lines: [summary, worktreeSummary] };
87
- }
88
- function runGit(cwd, command) {
89
- try {
90
- return execSync(command, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim();
91
- }
92
- catch {
93
- return '';
94
- }
95
- }
96
- function parseWorktrees(lines) {
97
- const worktrees = [];
98
- let cur = {};
99
- for (const line of lines) {
100
- if (line.startsWith('worktree ')) {
101
- if (cur.path !== undefined) {
102
- worktrees.push({ path: cur.path, branch: cur.branch ?? 'detached HEAD' });
103
- }
104
- cur = { path: line.slice('worktree '.length) };
105
- }
106
- else if (line.startsWith('branch ')) {
107
- cur.branch = line.slice('branch '.length).replace(/^refs\/heads\//, '');
108
- }
109
- }
110
- if (cur.path !== undefined) {
111
- worktrees.push({ path: cur.path, branch: cur.branch ?? 'detached HEAD' });
112
- }
113
- return worktrees;
114
- }
115
- /** The `<project_context>` block — the `<environment>` snapshot only: a
116
- * host/runtime line (platform, hostname, node version, tmux-or-not — the
117
- * headless engine's own process env, which its bash tools inherit), the cwd
118
- * dir listing, and git status/worktrees. Project guidance lives in the
119
- * document substrate, so this wrapper stays environment-only. It always emits
120
- * because the environment snapshot is always present. Exported for testing. */
121
- export function buildProjectContextBlock(cwd) {
122
- const envLines = directoryListingLines(cwd);
123
- const git = gitSnapshot(cwd);
124
- let out = '<project_context>\n\n';
125
- out += `<environment cwd="${escapeAttribute(cwd)}">\n`;
126
- out += `${runtimeLine()}\n`;
127
- out += `Directory:\n${envLines.map((line) => ` ${line}`).join('\n')}\n`;
128
- out += `${git.lines.map((line) => `${line}`).join('\n')}\n`;
129
- out += '</environment>\n</project_context>';
130
- return out;
131
- }
132
- /** The context-directory note — names the path INLINE (no XML attribute, so it
133
- * never scopes a sibling block) and frames the dir as durable, shared scratch.
134
- * Present for every node, rendered as the `## Your context directory`
135
- * subsection of <crtr-bearings>. */
136
- export function contextDirNote(nodeId) {
137
- return (`Your context directory is \`${contextDir(nodeId)}\` (also in the env as \`$CRTR_CONTEXT_DIR\`) — ` +
138
- 'durable scratch space on disk, and the one place the other nodes on the canvas can read from. ' +
139
- 'Put documents here that you want to share by reference instead of re-explaining them in a ' +
140
- 'prompt: specs, designs, findings, notes worth pointing a sibling, child, or parent at. It is a ' +
141
- 'shared document store, not a task tracker. **Address it by absolute path** — write artifacts as ' +
142
- '`$CRTR_CONTEXT_DIR/<name>.md` (or the full path above). Your working dir is the project, NOT ' +
143
- 'this directory, so a bare relative `context/...` lands in the repo, not here — and other nodes ' +
144
- 'can only read what is actually here. When you report a file, report its absolute path.');
145
- }
21
+ export { buildProjectContextBlock, contextDirNote, orchestratorContextNote };
146
22
  /** The profile-purview note — rendered ONLY when `profile_id` resolves to a
147
23
  * manifest that names at least one project dir. Phrased as PURVIEW, not a cwd
148
24
  * replacement: the node's cwd is still its one working dir (bash/edits run
@@ -154,44 +30,14 @@ export function contextDirNote(nodeId) {
154
30
  * redacted count. */
155
31
  export function profileNote(nodeId) {
156
32
  const meta = getNode(nodeId);
157
- const profileId = meta?.profile_id;
158
- if (profileId === undefined || profileId === null || profileId === '')
159
- return '';
160
- let entry;
161
- try {
162
- entry = loadProfileManifest(profileId);
163
- }
164
- catch {
165
- return '';
166
- }
167
- const projects = entry.manifest.projects.map((project) => project.path);
168
- if (projects.length === 0)
33
+ if (meta === null)
169
34
  return '';
170
- return (`Your working dir is \`${meta.cwd}\`. Your profile **${entry.manifest.name}** also has purview ` +
171
- `over: ${projects.join(', ')}.`);
172
- }
173
- /** Orchestrator-only framing: a resident orchestrator survives refresh cycles,
174
- * so its context dir is also where a future cycle of itself resumes the work.
175
- * Used inside the bearings block AND in the promotion guidance dump, so a
176
- * promoted node gets the same note a born-orchestrator gets. */
177
- export function orchestratorContextNote(nodeId) {
178
- return (`Because you persist across refresh cycles, your context directory (${contextDir(nodeId)}) is ` +
179
- `also where a future cycle of you resumes the work — keep the working notes and decisions a ` +
180
- `refreshed you would need there, alongside the docs you share with the nodes you spawn.`);
35
+ return profileNoteShared(meta.cwd, meta.profile_id);
181
36
  }
182
- // ---------------------------------------------------------------------------
183
37
  // Graph mini-map — the node's place in the canvas, rendered into the identity
184
38
  // block so a node boots oriented: who is above it (its ancestry trunk, root
185
39
  // first) and who is directly below it (its immediate children + their statuses).
186
40
  // Not the full subtree — just the load-bearing spine up and one level down.
187
- // ---------------------------------------------------------------------------
188
- /** One relative's line in the map: `<id> — <kind> · <status>`, plus the node's
189
- * task description when it carries one (orientation: what that relative is for). */
190
- function relativeLabel(meta) {
191
- const desc = fullName(meta);
192
- const descPart = desc !== '' && desc !== meta.kind && desc !== meta.name ? ` — ${desc}` : '';
193
- return `${meta.node_id} (${meta.kind} · ${meta.status})${descPart}`;
194
- }
195
41
  /** Ancestry trunk from the root DOWN to (but excluding) `nodeId`, climbing the
196
42
  * spine `parent` edge. Cycle-guarded. Root first. */
197
43
  function ancestorTrunk(nodeId) {
@@ -227,47 +73,18 @@ export function buildGraphMap(nodeId) {
227
73
  return '';
228
74
  const trunk = ancestorTrunk(nodeId);
229
75
  const children = childNodes(nodeId);
230
- if (trunk.length === 0 && children.length === 0)
231
- return '';
232
- const indent = (depth) => ' '.repeat(depth);
233
- const lines = [
234
- 'Your place in the canvas graph (ancestry above you, your direct children below):',
235
- ];
236
- trunk.forEach((m, i) => {
237
- lines.push(`${indent(i)}${i === 0 ? '' : '└ '}${relativeLabel(m)}`);
238
- });
239
- const selfDepth = trunk.length;
240
- lines.push(`${indent(selfDepth)}${selfDepth === 0 ? '' : '└ '}● you: ${nodeId} (${meta.kind}, ${meta.mode})`);
241
- // Cap the child list so a wide fan-out can't flood the boot context. Children
242
- // are sorted live-first, so the shown slice favors active nodes; the rest
243
- // collapse to a `+N more` tail (active-first ordering means anything elided is
244
- // the most-finished work).
245
- const shown = children.slice(0, MAX_CHILDREN_SHOWN);
246
- const hidden = children.length - shown.length;
247
- shown.forEach((m, i) => {
248
- const last = i === shown.length - 1 && hidden === 0;
249
- lines.push(`${indent(selfDepth + 1)}${last ? '└ ' : '├ '}${relativeLabel(m)}`);
250
- });
251
- if (hidden > 0) {
252
- lines.push(`${indent(selfDepth + 1)}└ +${hidden} more`);
253
- }
254
- return lines.join('\n');
76
+ return renderGraphMap(nodeId, meta.kind, meta.mode, trunk, children);
255
77
  }
256
78
  /** The identity assertion injected at session start. A fork names its source
257
79
  * while treating the copied first-person narrative as reference. */
258
80
  function buildIdentityAssertion(nodeId) {
259
81
  const meta = getNode(nodeId);
260
- const lines = [`You are node ${nodeId} — kind ${meta?.kind ?? 'general'}, mode ${meta?.mode ?? 'base'}.`];
261
82
  const forkFrom = meta?.fork_from;
262
- if (forkFrom !== undefined && forkFrom !== null && forkFrom !== '') {
263
- // A known node id carries its human label. Paths and session ids remain
264
- // opaque provenance rather than being passed to the node-id lookup.
265
- const src = isSafeNodeId(forkFrom) ? getNode(forkFrom) : null;
266
- const sourceLabel = src !== null ? `${forkFrom} ("${fullName(src)}")` : forkFrom;
267
- lines.push('', `You are a new FORK of ${sourceLabel}, not ${sourceLabel}. Pi copied the earlier conversation ` +
268
- `into this session. Treat that first-person history as reference material, not your own past.`);
269
- }
270
- return lines.join('\n');
83
+ // A known node id carries its human label. Paths and session ids remain
84
+ // opaque provenance rather than being passed to the node-id lookup.
85
+ const src = forkFrom !== undefined && forkFrom !== null && forkFrom !== '' && isSafeNodeId(forkFrom) ? getNode(forkFrom) : null;
86
+ const sourceLabel = src !== null ? `${forkFrom} ("${fullName(src)}")` : (forkFrom ?? '');
87
+ return buildIdentityAssertionShared(nodeId, meta?.kind ?? 'general', meta?.mode ?? 'base', forkFrom, sourceLabel);
271
88
  }
272
89
  /** The armer rendered for prose, ROLE-explicit so a newborn never mistakes the
273
90
  * id for its own: `node <id> ("<name>")` when the name resolved, `node <id>
@@ -325,10 +142,7 @@ export function buildWakeBearings(origin) {
325
142
  return `<crtr-wake>\n${body}\n</crtr-wake>`;
326
143
  }
327
144
  function managedWorktreeNote(node) {
328
- const wt = node?.managed_worktree;
329
- if (wt?.state !== 'open')
330
- return '';
331
- return `Branch: \`${wt.branch}\`\nWorktree: \`${wt.path}\`\nBase: \`${wt.base_ref}\` @ \`${wt.base_sha}\`\nCommit work here. Land it with \`crtr node worktree close\` before finishing. Auto-drop applies only when the worktree is clean (no uncommitted changes) and nothing remains to land (zero commits ahead of the base, or its commits are already contained in the base); unlanded commits (commits not already contained in the base) or uncommitted changes require an explicit close/landing decision.`;
145
+ return worktreeNote(node?.managed_worktree);
332
146
  }
333
147
  /** The full boot intro, four top-level sections:
334
148
  *
@@ -0,0 +1,39 @@
1
+ import type { AgentSessionServices, ModelRegistry } from '@earendil-works/pi-coding-agent';
2
+ import type { BrokerSdkConfig } from '../launch.js';
3
+ import type { BrokerClient } from './client-registry.js';
4
+ import type { FrameReplies } from './frame-replies.js';
5
+ import type { RebindDriver } from './rebind.js';
6
+ import type { BrokerSession } from './read-ops.js';
7
+ type ResolvedLaunchModel = {
8
+ model: NonNullable<ReturnType<ModelRegistry['find']>>;
9
+ thinkingLevel: string | undefined;
10
+ } | undefined;
11
+ /**
12
+ * The `reload_auth` frame and the two pieces of work it starts: a model
13
+ * reconcile against freshly re-read credentials, and — when this node is
14
+ * sitting on a recorded provider auth fault — a rewind-and-redrive of the turn
15
+ * that fault killed. Owns the in-flight state both need so a burst of
16
+ * canvas-wide /login fans collapses into one reconcile per generation.
17
+ */
18
+ export type AuthReloadGate = {
19
+ /** The in-flight reconcile an idle prompt must not overtake, if any. */
20
+ inFlight(): Promise<void> | undefined;
21
+ /** Handle a `reload_auth` frame. Open to any client, observers included. */
22
+ handle(client: BrokerClient): void;
23
+ /** A session replacement invalidates the reconcile the old one was waiting on. */
24
+ resetForSession(): void;
25
+ };
26
+ export declare function createAuthReloadGate(deps: {
27
+ nodeId: string;
28
+ cfg: BrokerSdkConfig;
29
+ replies: FrameReplies;
30
+ rebind: RebindDriver;
31
+ session: () => BrokerSession;
32
+ services: () => AgentSessionServices;
33
+ broadcastModelChanged: (persist?: boolean, pinnedOverride?: boolean, userSelected?: boolean) => Promise<void>;
34
+ formatModelSpec: (model: BrokerSession['model'], thinkingLevel?: string) => string | null;
35
+ resolveLaunchModel: (services: AgentSessionServices, cfg: BrokerSdkConfig) => Promise<ResolvedLaunchModel>;
36
+ /** A reconcile that finally resolves a model releases the held launch prompt. */
37
+ driveKickoff: () => void;
38
+ }): AuthReloadGate;
39
+ export {};