@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
@@ -11,7 +11,7 @@
11
11
  // `FoldState`) so this layer owns no singletons and each consumer keeps its own
12
12
  // state.
13
13
  import { getNode, subscribersOf, subscriptionsOf } from './index.js';
14
- import { activeFaultForDisplay } from './render.js';
14
+ import { activeFaultForDisplay } from './render-source.js';
15
15
  import { readFault } from '../runtime/fault.js';
16
16
  import { faultSummary } from './status-glyph.js';
17
17
  import { statusRank } from './node-order.js';
@@ -19,7 +19,6 @@ import { nodeGlyph, navLabel, shortId, tokensCell, activityCell, cycleBadge, ask
19
19
  // Re-export the pure render/telemetry half so existing nav-model importers
20
20
  // (chrome/canvas-panels, overlays/graph, resume) keep resolving these here.
21
21
  export { PI_MAX_WIDGET_LINES, VIEWPORT_FALLBACK_ROWS, ESC, RESET, BOLD, DIM, REVERSE, BG_ATTACHED, GREEN, RED, YELLOW, CYAN, GRAY, nodeGlyph, visibleWidth, truncate, fillWidth, fillBar, readTelemetry, fmtTokens, tokensCell, activityCell, cycleBadge, navLabel, shortId, askBadge, liveBelowBadge, isAttached, hasBrokerViewers, fetchAsksMap, graphWidgetBudget, GRAPH_HINT, } from './nav-render.js';
22
- // ---------------------------------------------------------------------------
23
22
  // Read primitives — UNCACHED, deliberately
24
23
  //
25
24
  // These once memoized into module-global Maps that a `beginFrame()` cleared at
@@ -36,7 +35,6 @@ export { PI_MAX_WIDGET_LINES, VIEWPORT_FALLBACK_ROWS, ESC, RESET, BOLD, DIM, REV
36
35
  // goes straight to the canvas db. The only frame cache still in play is
37
36
  // nav-render's telemetry memo, whose TUI callers clear it themselves via
38
37
  // `beginFrameTelem()`.
39
- // ---------------------------------------------------------------------------
40
38
  /** subscriptionsOf (a node's reports); an unreadable row reads as no edges. */
41
39
  function subsOf(id) {
42
40
  try {
@@ -55,7 +53,6 @@ function mgrsOf(id) {
55
53
  return [];
56
54
  }
57
55
  }
58
- // ---------------------------------------------------------------------------
59
56
  // Attachment — is a human currently WATCHING a node? A separate axis from
60
57
  // running (status 'active' = the engine is live on its detached broker, watched
61
58
  // or not). Two signals:
@@ -65,10 +62,7 @@ function mgrsOf(id) {
65
62
  // web tabs) to job/attach.json on every viewer change
66
63
  // (src/core/runtime/broker.ts). Trusted only while the node is
67
64
  // 'active': a broker crash can leave a stale file.
68
- // ---------------------------------------------------------------------------
69
- // ---------------------------------------------------------------------------
70
65
  // Graph queries (dependency-free, straight off the canvas db)
71
- // ---------------------------------------------------------------------------
72
66
  /** First manager (by created) — the UP step for the ancestry spine. */
73
67
  export function managerOf(id) {
74
68
  return mgrsOf(id)[0]?.node_id;
@@ -130,9 +124,7 @@ export function subtreeIds(root) {
130
124
  }
131
125
  return out;
132
126
  }
133
- // ---------------------------------------------------------------------------
134
127
  // Shared cell builders
135
- // ---------------------------------------------------------------------------
136
128
  /** ⤳M direct-children badge — the single child-count signal, shown on any row
137
129
  * with navigable children (folded or not). In GRAPH the ▸ caret separately marks
138
130
  * a collapsed row, so this stays a pure count and never doubles as a fold badge. */
@@ -148,11 +140,9 @@ export function childBadge(node) {
148
140
  export function sortedChildIds(id) {
149
141
  return convoChildIds(id).sort((a, b) => statusRank(getNode(a)?.status) - statusRank(getNode(b)?.status));
150
142
  }
151
- // ---------------------------------------------------------------------------
152
143
  // GRAPH model — flatten the local graph fold-aware. Rebuilt every render (cheap
153
144
  // sqlite reads) so a finished child / new spawn shows live; the manual fold
154
145
  // overrides and the cursor id are the only persisted state.
155
- // ---------------------------------------------------------------------------
156
146
  export function computeSubtreeActivity(root, self) {
157
147
  const expand = new Set();
158
148
  const activeBelow = new Map();
@@ -15,22 +15,18 @@ import { isBusy } from '../runtime/busy.js';
15
15
  import { readFault } from '../runtime/fault.js';
16
16
  import { activeFaultForDisplay } from './render-source.js';
17
17
  import { resolveNodeVisual } from './status-glyph.js';
18
- // ---------------------------------------------------------------------------
19
18
  // Tuning constants
20
- // ---------------------------------------------------------------------------
21
19
  /** pi's InteractiveMode.MAX_WIDGET_LINES — the hard cap on lines in a string
22
20
  * array widget; anything beyond it pi truncates with its own "... (widget
23
21
  * truncated)". Our GRAPH viewport stays at/under this and scrolls internally. */
24
22
  export const PI_MAX_WIDGET_LINES = 10;
25
23
  export const VIEWPORT_FALLBACK_ROWS = 30;
26
- // ---------------------------------------------------------------------------
27
24
  // ANSI styling. pi renders embedded escapes in widget lines and measures width
28
25
  // ANSI-aware, so raw escapes are safe and need no pi-tui dependency. The cursor
29
26
  // (selected row) uses a theme-agnostic ATTRIBUTE (reverse), so it reads under
30
27
  // NO_COLOR; the attached-row tint is a background COLOUR, while the overlay-
31
28
  // aware glyph (●/○/✓/✗/⟳/⚠) carries the live signal independently, even where
32
29
  // colour is stripped.
33
- // ---------------------------------------------------------------------------
34
30
  export const ESC = '\x1b[';
35
31
  export const RESET = `${ESC}0m`;
36
32
  export const BOLD = `${ESC}1m`;
@@ -129,14 +125,12 @@ export function fillBar(content, width, open) {
129
125
  const pad = Math.max(0, width - visibleWidth(clipped));
130
126
  return `${open}${reasserted}${' '.repeat(pad)}${RESET}`;
131
127
  }
132
- // ---------------------------------------------------------------------------
133
128
  // Per-frame telemetry cache. Its callers are TUI render passes ONLY (the GRAPH
134
129
  // overlay and the canvas panels), each of which calls beginFrameTelem() at the
135
130
  // top of the pass, so token/activity cells read fresh once per frame instead of
136
131
  // once per row and a stale entry can at worst be one frame old. Never memoize a
137
132
  // canvas read for a caller that is not a render pass: crtrd lives for hours, and
138
133
  // a memo it never clears is permanent, not per-frame.
139
- // ---------------------------------------------------------------------------
140
134
  const frameTelem = new Map();
141
135
  /** Clear the per-frame telemetry cache. Every render pass calls this first, so
142
136
  * one keystroke re-reads each node's telemetry exactly once. */
@@ -246,12 +240,10 @@ export function liveBelowBadge(node, activeBelow) {
246
240
  const n = activeBelow.get(node.node_id) ?? 0;
247
241
  return n > 0 ? ` ${GREEN}⇣${n}${RESET}` : '';
248
242
  }
249
- // ---------------------------------------------------------------------------
250
243
  // Per-node ask counts — ONE shell-out per poll. `crtr canvas attention map`
251
244
  // buckets a whole sub-DAG's pending asks by node in a single process, so the
252
245
  // timer stays cheap (< 2 s) regardless of how many nodes are visible. --json
253
246
  // gives a parseable {counts} blob (the default render is XML chrome).
254
- // ---------------------------------------------------------------------------
255
247
  export function fetchAsksMap(rootId) {
256
248
  try {
257
249
  const raw = execFileSync('crtr', ['canvas', 'attention', 'map', '--view', rootId, '--json'], {
@@ -31,6 +31,14 @@ export interface RemoteShowResponse {
31
31
  session_file?: string | null;
32
32
  };
33
33
  follow_up: string;
34
+ fault?: {
35
+ link: string;
36
+ kind: string;
37
+ retry: {
38
+ disposition: string;
39
+ };
40
+ since: string;
41
+ } | null;
34
42
  }
35
43
  export declare class RemoteCanvasSource implements CanvasSource {
36
44
  private readonly previewEndpoint;
@@ -12,7 +12,6 @@
12
12
  // "every process is a /v1 client" architecture (which governs LOCAL access).
13
13
  import { createRemoteExec } from './remote-transport.js';
14
14
  const CACHE_TTL_MS = 1000;
15
- // ---------------------------------------------------------------------------
16
15
  // Remote JSON shape validation.
17
16
  //
18
17
  // `execJson<T>` parses stdout and casts it to T with NO structural check — a
@@ -21,7 +20,6 @@ const CACHE_TTL_MS = 1000;
21
20
  // `ticketCountsForView` validate before caching/returning; on a shape mismatch
22
21
  // they degrade exactly like the existing network-failure path (null/[]/{}),
23
22
  // never a malformed object a caller might dereference.
24
- // ---------------------------------------------------------------------------
25
23
  function isPlainObject(x) {
26
24
  return typeof x === 'object' && x !== null && !Array.isArray(x);
27
25
  }
@@ -49,6 +47,14 @@ function isValidShowResponse(x) {
49
47
  return false;
50
48
  if (typeof x['follow_up'] !== 'string')
51
49
  return false;
50
+ const fault = x['fault'];
51
+ if (fault !== undefined && fault !== null) {
52
+ if (!isPlainObject(fault) || typeof fault['link'] !== 'string' || typeof fault['kind'] !== 'string' || typeof fault['since'] !== 'string')
53
+ return false;
54
+ const retry = fault['retry'];
55
+ if (!isPlainObject(retry) || typeof retry['disposition'] !== 'string')
56
+ return false;
57
+ }
52
58
  return true;
53
59
  }
54
60
  /** A `reports`/`managers` entry (`RemoteShowResponse`) — validated per-element
@@ -123,7 +129,7 @@ export class RemoteCanvasSource {
123
129
  // node/inspect.ts's remote `show` passthrough render — see only well-formed
124
130
  // neighbor entries, never a malformed one they'd dereference unguarded.
125
131
  const res = isValidShowResponse(raw)
126
- ? { ...raw, reports: raw.reports.filter(isValidNeighborEntry), managers: raw.managers.filter(isValidNeighborEntry) }
132
+ ? { ...raw, fault: raw.fault ?? null, reports: raw.reports.filter(isValidNeighborEntry), managers: raw.managers.filter(isValidNeighborEntry) }
127
133
  : null;
128
134
  this.nodeCache.set(id, { value: res, at: Date.now() });
129
135
  return res;
@@ -3,10 +3,10 @@
3
3
  // Every `*FromSource` variant + the pure/disk helpers they share live here so a
4
4
  // CLI process rendering the canvas through a `CanvasSource` (the API-backed
5
5
  // `ApiCanvasSource`, or a `RemoteCanvasSource`) NEVER drags in `core/canvas/db.ts`
6
- // (`openDb`) or the `core/canvas/canvas.ts` accessors. The sync, db-backed
7
- // renderers (`renderTree`/`dashboardRows`/…) stay in `render.ts`, which imports the
8
- // shared helpers FROM here and re-exports this whole module (`export *`) so its
9
- // existing importers are unaffected.
6
+ // (`openDb`) or the `core/canvas/canvas.ts` accessors. This is the whole module now
7
+ // — the sync, db-backed renderers (`renderTree`/`dashboardRows`/…) that used to live
8
+ // in a separate `render.ts` re-export shim have been retired; their few real callers
9
+ // migrated onto these `*FromSource` variants directly.
10
10
  //
11
11
  // ALLOWED imports (verified openDb-free): node:fs/path, ./pid.js, ./labels.js,
12
12
  // ./status-glyph.js, ../runtime/fault.js, ./paths.js, ./remote-canvas-source.js,
@@ -21,10 +21,8 @@ import { isPidAlive } from './pid.js';
21
21
  import { resolveNodeVisual, faultSummary } from './status-glyph.js';
22
22
  import { readFault } from '../runtime/fault.js';
23
23
  import { parseCard } from '../../shared/generated-context.js';
24
- // ---------------------------------------------------------------------------
25
24
  // Active-fault overlay: stale daemon-owned provider faults are hidden once the
26
25
  // broker pid is gone; manual/client faults stay visible until cleared.
27
- // ---------------------------------------------------------------------------
28
26
  export function activeFaultForDisplay(node, fault) {
29
27
  if (fault === null)
30
28
  return null;
@@ -52,7 +50,6 @@ export function fmtCtx(tokensIn) {
52
50
  return '0k';
53
51
  return `${Math.floor(tokensIn / 1000)}k`;
54
52
  }
55
- // ---------------------------------------------------------------------------
56
53
  // Source-driven tree builder (openDb-free)
57
54
  //
58
55
  // Ticket counts are threaded in as a `Record<node_id, count>` fetched ONCE per
@@ -61,7 +58,6 @@ export function fmtCtx(tokensIn) {
61
58
  // LOCAL disk reads (telemetry.json / fault.json), valid for a same-host source
62
59
  // but meaningless for a truly remote canvas, so both are suppressed when the
63
60
  // source is a `RemoteCanvasSource`.
64
- // ---------------------------------------------------------------------------
65
61
  async function nodeLineFromSource(source, nodeId, indent, connector, remote, tickets) {
66
62
  const node = await source.getNode(nodeId);
67
63
  if (node === null)
@@ -80,8 +76,15 @@ async function walkTreeFromSource(source, nodeId, indent, isLast, visited, out,
80
76
  visited.add(nodeId);
81
77
  const connector = isLast ? '└─ ' : '├─ ';
82
78
  out.push(await nodeLineFromSource(source, nodeId, indent, connector, remote, tickets));
83
- const children = await source.subscriptionsOf(nodeId).catch(() => []);
84
79
  const childIndent = indent + (isLast ? ' ' : '│ ');
80
+ let children;
81
+ try {
82
+ children = await source.subscriptionsOf(nodeId);
83
+ }
84
+ catch (err) {
85
+ out.push(`${childIndent}⚠ children unavailable: ${err instanceof Error ? err.message : String(err)}`);
86
+ return;
87
+ }
85
88
  for (let i = 0; i < children.length; i++) {
86
89
  const child = children[i];
87
90
  const childIsLast = i === children.length - 1;
@@ -98,7 +101,15 @@ export async function renderTreeFromSource(source, rootId) {
98
101
  const node = await source.getNode(rootId);
99
102
  if (node === null)
100
103
  return `? <missing node: ${rootId}>`;
101
- const tickets = await source.ticketCountsForView(rootId).catch(() => ({}));
104
+ let tickets;
105
+ let ticketsUnavailable = null;
106
+ try {
107
+ tickets = await source.ticketCountsForView(rootId);
108
+ }
109
+ catch (err) {
110
+ tickets = {};
111
+ ticketsUnavailable = err instanceof Error ? err.message : String(err);
112
+ }
102
113
  const ctx = remote ? '0k' : fmtCtx(readNodeTelemetry(rootId).tokens_in);
103
114
  const asks = tickets[rootId] ?? 0;
104
115
  const askSuffix = asks > 0 ? ` ⚑${asks}` : '';
@@ -108,12 +119,21 @@ export async function renderTreeFromSource(source, rootId) {
108
119
  const out = [];
109
120
  out.push(`${glyph} ${fullName(node)} [${node.kind}/${node.mode}] ctx ${ctx}${askSuffix}${faultSuffix}`);
110
121
  const visited = new Set([rootId]);
111
- const children = await source.subscriptionsOf(rootId).catch(() => []);
122
+ let children;
123
+ try {
124
+ children = await source.subscriptionsOf(rootId);
125
+ }
126
+ catch (err) {
127
+ out.push(`⚠ children unavailable: ${err instanceof Error ? err.message : String(err)}`);
128
+ children = [];
129
+ }
112
130
  for (let i = 0; i < children.length; i++) {
113
131
  const child = children[i];
114
132
  const isLast = i === children.length - 1;
115
133
  await walkTreeFromSource(source, child.node_id, '', isLast, visited, out, remote, tickets);
116
134
  }
135
+ if (ticketsUnavailable !== null)
136
+ out.push(`Tickets: unavailable (${ticketsUnavailable}).`);
117
137
  return out.join('\n');
118
138
  }
119
139
  export async function renderForestFromSource(source) {
@@ -424,13 +444,11 @@ export async function dashboardRowsAllFromSource(source, localFocusedNodeIds = n
424
444
  const focusedNodeIds = remote ? new Set() : localFocusedNodeIds;
425
445
  return (await source.listNodes()).map((row) => dashboardRowFromNodeRow(row, focusedNodeIds, remote));
426
446
  }
427
- // ---------------------------------------------------------------------------
428
447
  // Lazy enrichment (source-driven) — fold expensive per-node reads into rows on
429
448
  // demand. Ask counts are supplied by the caller (`asks` — a pre-fetched
430
449
  // `ticketCountsForView` map merged across the visible roots), NOT scanned here
431
450
  // (`ticketCountsForNodes` reaches openDb). Telemetry (ctx tokens) + goal/session
432
451
  // are LOCAL disk reads, suppressed for a `RemoteCanvasSource`.
433
- // ---------------------------------------------------------------------------
434
452
  export async function enrichRowsFromSource(source, rows, asks) {
435
453
  const todo = rows.filter((r) => r.enriched !== true);
436
454
  if (todo.length === 0)
@@ -9,9 +9,7 @@
9
9
  // the raw manifest bytes passed in.
10
10
  import { validateCommandNode } from './schema.js';
11
11
  import { isRecord } from '../../shared/predicates.js';
12
- // ---------------------------------------------------------------------------
13
12
  // Validation helpers
14
- // ---------------------------------------------------------------------------
15
13
  const KEBAB = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
16
14
  const REST_METHODS = new Set(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']);
17
15
  function typeName(v) {
@@ -21,9 +19,7 @@ function typeName(v) {
21
19
  return 'array';
22
20
  return typeof v;
23
21
  }
24
- // ---------------------------------------------------------------------------
25
22
  // Top-level manifest validation
26
- // ---------------------------------------------------------------------------
27
23
  /**
28
24
  * Validates a HTTP-transport plugin manifest exactly per §4, performing strict atomic
29
25
  * validation and order-independent forest materialization. Returns either a
@@ -130,9 +126,7 @@ export function validateCommandManifest(raw, options) {
130
126
  // Any issue found -> zero contributions
131
127
  return { issues };
132
128
  }
133
- // ---------------------------------------------------------------------------
134
129
  // Timeouts validation
135
- // ---------------------------------------------------------------------------
136
130
  function validateTimeouts(raw, issue) {
137
131
  if (!isRecord(raw)) {
138
132
  issue('command_manifest_invalid', 'timeouts must be an object', typeName(raw), '{ connectMs?, requestMs?, streamIdleMs? }', 'Fix timeouts.', 'timeouts');
@@ -156,9 +150,7 @@ function validateTimeouts(raw, issue) {
156
150
  }
157
151
  return out;
158
152
  }
159
- // ---------------------------------------------------------------------------
160
153
  // Help addenda validation
161
- // ---------------------------------------------------------------------------
162
154
  /** Validate the optional top-level `helpAddenda` map: core command path →
163
155
  * addendum text rendered as an attributed block beneath that core command's
164
156
  * help. When the caller supplies the core command path set, a key naming no
@@ -228,9 +220,7 @@ function validateMount(raw, index, transport, issue) {
228
220
  const typedNode = node;
229
221
  return { parent, node: typedNode };
230
222
  }
231
- // ---------------------------------------------------------------------------
232
223
  // Forest materialization (order-independent)
233
- // ---------------------------------------------------------------------------
234
224
  /**
235
225
  * Materializes a flat list of mounts into a forest of top-level branches,
236
226
  * resolving nested mounts whose parent exists in a top-level inline tree regardless of order. Nested mounts that provide another nested mount's parent must appear first. Validates self-containment and no duplicates.
@@ -24,6 +24,8 @@ export type LeafAdapter = (leaf: DeclLeafBase, commandPath: readonly string[]) =
24
24
  export interface CommandContribution {
25
25
  contributor: CommandContributorRef;
26
26
  node: DeclBranch<DeclLeafBase>;
27
+ /** Root name from the manifest, before collision qualification. */
28
+ manifestName: string;
27
29
  adaptLeaf: LeafAdapter;
28
30
  }
29
31
  /** Per-contributor collision issue. */
@@ -1,8 +1,6 @@
1
1
  // Unified command-manifest schema. Transport is the sole leaf-dialect discriminator.
2
2
  import { isRecord } from '../../shared/predicates.js';
3
- // ---------------------------------------------------------------------------
4
3
  // Validation helpers
5
- // ---------------------------------------------------------------------------
6
4
  const TIERS = new Set(['normal', 'common', 'important']);
7
5
  const FLAG_TYPES = new Set(['string', 'int', 'bool', 'path', 'enum']);
8
6
  const KEBAB = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
@@ -471,9 +469,7 @@ function validateLeaf(raw, path, transport, issue) {
471
469
  const rest = validateRestMapping(raw['rest'], path, params, issue);
472
470
  return rest === null ? null : { ...base, rest };
473
471
  }
474
- // ---------------------------------------------------------------------------
475
472
  // REST mapping validation (§4.4)
476
- // ---------------------------------------------------------------------------
477
473
  const REST_METHODS_SET = new Set(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']);
478
474
  // RFC 7230 `token` grammar — the legal charset for an HTTP header field name.
479
475
  const HTTP_TOKEN = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
@@ -6,15 +6,17 @@ export function adaptPluginContributions(contributions) {
6
6
  contributions: contributions.map((contribution) => ({
7
7
  contributor: { name: contribution.plugin.name, scope: contribution.plugin.scope, sourceMarketplace: contribution.plugin.sourceMarketplace },
8
8
  node: contribution.node,
9
+ manifestName: contribution.manifestName,
9
10
  adaptLeaf: (leaf, commandPath) => {
10
11
  const transport = contribution.transport;
12
+ const manifestCommandPath = [contribution.manifestName, ...commandPath.slice(1)];
11
13
  if (transport.kind === 'exec') {
12
14
  const executable = transport.executable;
13
15
  if (executable === undefined)
14
16
  throw new Error(`exec plugin "${contribution.plugin.name}" has an executable leaf without transport.executable`);
15
17
  return {
16
18
  outputKind: 'object',
17
- run: async (input) => executeExternalLeaf({ plugin: contribution.plugin, executable, commandPath: [...commandPath], output: leaf.output }, input),
19
+ run: async (input) => executeExternalLeaf({ plugin: contribution.plugin, executable, commandPath: manifestCommandPath, output: leaf.output }, input),
18
20
  };
19
21
  }
20
22
  const httpLeaf = leaf;
@@ -24,7 +26,7 @@ export function adaptPluginContributions(contributions) {
24
26
  registration: { name: contribution.plugin.name, endpoint: transport.endpoint, ...(transport.authEnv !== undefined ? { authEnv: transport.authEnv } : {}) },
25
27
  ...(contribution.manifest?.baseUrl !== undefined ? { baseUrl: contribution.manifest.baseUrl } : {}),
26
28
  ...(contribution.manifest?.timeouts !== undefined ? { timeouts: contribution.manifest.timeouts } : {}),
27
- params: httpLeaf.params, rest: httpLeaf.rest, output: httpLeaf.output, commandPath: [...commandPath],
29
+ params: httpLeaf.params, rest: httpLeaf.rest, output: httpLeaf.output, commandPath: manifestCommandPath,
28
30
  }, input, context),
29
31
  };
30
32
  },
@@ -14,6 +14,7 @@ export interface ValidatedContribution {
14
14
  transport: PluginTransport;
15
15
  node: DeclBranchGeneric<DeclLeafGeneric>;
16
16
  manifest: ValidatedCommandManifest;
17
+ manifestName: string;
17
18
  }
18
19
  export interface PluginCommandValidation {
19
20
  plugin: InstalledPlugin;
@@ -130,7 +130,7 @@ export function validatePluginCommands(plugin, reservedNames = new Set(), coreCo
130
130
  : transport;
131
131
  if (resolvedTransport === undefined)
132
132
  return { plugin, manifestPath, transport, contributions: [], issues };
133
- return { plugin, manifestPath, transport: resolvedTransport, manifest: validation.manifest, contributions: validation.manifest.roots.map((node) => ({ plugin, transport: resolvedTransport, node, manifest: validation.manifest })), issues };
133
+ return { plugin, manifestPath, transport: resolvedTransport, manifest: validation.manifest, contributions: validation.manifest.roots.map((node) => ({ plugin, transport: resolvedTransport, node, manifest: validation.manifest, manifestName: node.name })), issues };
134
134
  }
135
135
  export function discoverPluginCommandCandidates(startDir = process.cwd(), profileId) {
136
136
  return effectiveCommandPlugins(startDir, profileId).map((plugin) => validatePluginCommands(plugin));
@@ -20,9 +20,7 @@ import { CrtrError } from '../../errors.js';
20
20
  import { diag, recordPreviewError, recordPreviewJsonLine, writeStdout } from '../../io.js';
21
21
  import { ExitCode } from '../../../types.js';
22
22
  import { isRecord } from '../../../shared/predicates.js';
23
- // ---------------------------------------------------------------------------
24
23
  // Constants
25
- // ---------------------------------------------------------------------------
26
24
  const DEFAULT_CONNECT_MS = 10_000;
27
25
  const DEFAULT_REQUEST_MS = 30_000;
28
26
  const DEFAULT_STREAM_IDLE_MS = 60_000;
@@ -33,9 +31,7 @@ const RESERVED_CODES = new Set(['internal', 'unknown_path', 'command_collision',
33
31
  /** Marker for the idle-timeout abort so the relay loop can distinguish it. */
34
32
  class StreamIdleError extends Error {
35
33
  }
36
- // ---------------------------------------------------------------------------
37
34
  // Public entry
38
- // ---------------------------------------------------------------------------
39
35
  /**
40
36
  * Execute one HTTP-transport plugin leaf. Unary leaves return the validated response
41
37
  * object (crtr's ordinary dispatcher renders it, or mirrors it under --json).
@@ -59,9 +55,7 @@ export async function invokeHttpLeaf(spec, input, context) {
59
55
  return mapUnary2xx(spec, body);
60
56
  throwNon2xx(spec, status, body); // always throws
61
57
  }
62
- // ---------------------------------------------------------------------------
63
58
  // Request construction (§6.1) — pure, no network I/O
64
- // ---------------------------------------------------------------------------
65
59
  function buildRequestPlan(spec, input, context) {
66
60
  const paramsByName = new Map();
67
61
  for (const p of spec.params)
@@ -245,9 +239,7 @@ function encodePathSegmentValue(value) {
245
239
  function paramKey(name) {
246
240
  return name.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
247
241
  }
248
- // ---------------------------------------------------------------------------
249
242
  // Unary lifecycle (§6.4) — connect + total clocks, buffered body
250
- // ---------------------------------------------------------------------------
251
243
  function requestUnary(spec, plan) {
252
244
  const connectMs = spec.timeouts?.connectMs ?? DEFAULT_CONNECT_MS;
253
245
  const requestMs = spec.timeouts?.requestMs ?? DEFAULT_REQUEST_MS;
@@ -335,9 +327,7 @@ function mapUnary2xx(spec, body) {
335
327
  }
336
328
  return parsed;
337
329
  }
338
- // ---------------------------------------------------------------------------
339
330
  // Streaming lifecycle (§6.3) — connect + idle clocks, verbatim frame relay
340
- // ---------------------------------------------------------------------------
341
331
  async function requestStreaming(spec, plan) {
342
332
  const connectMs = spec.timeouts?.connectMs ?? DEFAULT_CONNECT_MS;
343
333
  const idleMs = spec.timeouts?.streamIdleMs ?? DEFAULT_STREAM_IDLE_MS;
@@ -461,9 +451,7 @@ async function requestStreaming(spec, plan) {
461
451
  throw unreachable(spec, plan, detail);
462
452
  }
463
453
  }
464
- // ---------------------------------------------------------------------------
465
454
  // Error mapping (§6.5)
466
- // ---------------------------------------------------------------------------
467
455
  /** Extract a non-2xx error. A conforming nested `{ error: { code, message,
468
456
  * field?, next? } }` with an acceptable snake_case, non-reserved code becomes a
469
457
  * CrtrError carrying that code; a reserved/ill-formed code demotes to
@@ -526,9 +514,7 @@ function unreachable(spec, plan, detail) {
526
514
  next: `Check connectivity to ${plan.url.host} and retry; run \`crtr pkg plugin update ${spec.registration.name}\` if the command surface changed.`,
527
515
  });
528
516
  }
529
- // ---------------------------------------------------------------------------
530
517
  // Small helpers
531
- // ---------------------------------------------------------------------------
532
518
  function label(spec) {
533
519
  return `HTTP-transport plugin "${spec.registration.name}" command \`${spec.commandPath.join(' ')}\``;
534
520
  }
@@ -15,9 +15,7 @@ import { spawnSync } from 'node:child_process';
15
15
  import { constants as osConstants } from 'node:os';
16
16
  import { isatty } from 'node:tty';
17
17
  import { resolve } from 'node:path';
18
- // ---------------------------------------------------------------------------
19
18
  // Factory functions
20
- // ---------------------------------------------------------------------------
21
19
  function freezeCommandMetadata(value) {
22
20
  if (value === null || typeof value !== 'object' || Object.isFrozen(value))
23
21
  return value;
@@ -140,9 +138,9 @@ export function defineRoot(opts) {
140
138
  /** Validate and return child names for an unknown-path error. */
141
139
  function childNames(node) {
142
140
  if (node.kind === 'root')
143
- return node.subtrees.map((s) => s.name);
141
+ return node.subtrees.filter((s) => s.tier !== 'hidden').map((s) => s.name);
144
142
  if (node.kind === 'branch')
145
- return node.children.map((c) => c.name);
143
+ return node.children.filter((c) => c.tier !== 'hidden').map((c) => c.name);
146
144
  return [];
147
145
  }
148
146
  /** Walk argv tokens to the deepest matched node.
@@ -301,9 +299,7 @@ export function unknownPathError(node, path, bad) {
301
299
  next: `Valid children: ${validStr}. Run \`${entryCmd}\` for the full list.`,
302
300
  });
303
301
  }
304
- // ---------------------------------------------------------------------------
305
302
  // Argv parser
306
- // ---------------------------------------------------------------------------
307
303
  function parseArgvError(code, message, received, field, next) {
308
304
  return new CrtrError(code, message, ExitCode.USAGE, {
309
305
  received,
@@ -1,3 +1,4 @@
1
+ import { envProfileId } from '../shared/env.js';
1
2
  import { dirname, join } from 'node:path';
2
3
  import { isDeepStrictEqual } from 'node:util';
3
4
  import { CONFIG_FILE, STATE_FILE, CONDENSED_HISTORY_MODES, MOUSE_MODE_DEFAULTS, WHIP_MESSAGE_MODES, DEFAULT_WHIP_MESSAGES, defaultScopeConfig, defaultScopeState, defaultModelLaddersConfig, defaultKindsConfig, defaultRemoteCanvasConfig } from '../types.js';
@@ -762,7 +763,7 @@ function readRawProjectScopeConfigs(targetCwd, targetProfileId) {
762
763
  * the target node's) MUST pass the target's own cwd/profile explicitly, or
763
764
  * `spawnEnv.allow` resolves from the wrong scope entirely (the C-1
764
765
  * follow-up fix this parameterization exists for). */
765
- export function readMergedLaunchConfig(targetCwd = process.cwd(), targetProfileId = process.env['CRTR_PROFILE_ID'] || null) {
766
+ export function readMergedLaunchConfig(targetCwd = process.cwd(), targetProfileId = envProfileId() || null) {
766
767
  const defaults = defaultScopeConfig();
767
768
  const userRaw = readRawScopeConfig('user');
768
769
  const profileRaw = readRawProfileConfig(targetProfileId);
@@ -42,6 +42,10 @@ function classifyProviderText(message) {
42
42
  return 'context-overflow';
43
43
  if (/websocket.*(?:closed.*\b(?:1002|1003|1007|1008|1009|1010)\b|protocol error|policy violation|unsupported data|invalid (?:frame|payload)|message too big|mandatory extension)/i.test(m))
44
44
  return 'protocol';
45
+ // proper-lockfile contention on a credential file: another process holds the
46
+ // mutex right now, so the same request wins once it releases.
47
+ if (/lock file is already being held/i.test(m))
48
+ return 'overloaded';
45
49
  if (/overloaded|\b5\d\d\b|internal server error|api.?error|capacity|server.{0,3}busy|temporarily unavailable/i.test(m))
46
50
  return 'overloaded';
47
51
  if (/connection|econnreset|etimedout|enotfound|econnrefused|network|fetch failed|socket hang|timed? out|timeout|\bterminated\b|other side closed|premature close|websocket\s+(?:was\s+)?closed|websocket stream closed before response\.completed|websocket\s+error|websocket.*interrupt/i.test(m))
@@ -55,6 +59,10 @@ function classifyErrno(raw) {
55
59
  if (/^(ECONNREFUSED|ECONNRESET|ETIMEDOUT|ENOENT|ENOTFOUND|EAI_AGAIN|EPIPE|EHOSTUNREACH|ENETUNREACH)$/i.test(code)) {
56
60
  return 'connection';
57
61
  }
62
+ // A held filesystem mutex, not a broken link: back off on time rather than
63
+ // behind the online probe a connection fault waits on.
64
+ if (/^ELOCKED$/i.test(code))
65
+ return 'overloaded';
58
66
  return null;
59
67
  }
60
68
  function classifyHttp(raw) {
@@ -76,6 +76,16 @@ export declare function cancelPendingEntries(nodeId: string, select: (entry: Inb
76
76
  export declare function readCursor(nodeId: string): InboxEntryId | undefined;
77
77
  /** Persist one validated exact cursor identity (atomic tmp+rename). */
78
78
  export declare function writeCursor(nodeId: string, entryId: string): void;
79
+ /** Is there mail this node has not seen that would drive a turn? The one
80
+ * authority for "wake-worthy inbox state", read by the park clock, the
81
+ * settlement policy, the review-companion guard, and reviveNode's parked
82
+ * re-entry rule. Deferred entries are excluded everywhere for the same
83
+ * reason: a deferred entry rides the next natural cycle and never starts one.
84
+ * A cursor that no longer resolves against the physical inbox is a fault, not
85
+ * an answer — every caller's conservative reading of this predicate is `true`
86
+ * (do not park, do not shut down, do not resume quietly), so report it and
87
+ * return that. */
88
+ export declare function hasUnseenWakeMail(nodeId: string): boolean;
79
89
  /** Clip a body to a bounded preview, reporting whether anything was dropped. */
80
90
  export declare function clipBody(body: string): {
81
91
  text: string;
@@ -44,15 +44,11 @@ export function finalizeInboxEntry(input) {
44
44
  ts: new Date().toISOString(),
45
45
  };
46
46
  }
47
- // ---------------------------------------------------------------------------
48
47
  // Cursor sidecar path
49
- // ---------------------------------------------------------------------------
50
48
  function cursorPath(nodeId) {
51
49
  return `${inboxPath(nodeId)}.cursor`;
52
50
  }
53
- // ---------------------------------------------------------------------------
54
51
  // Append
55
- // ---------------------------------------------------------------------------
56
52
  /** Spill an oversized inline body to a durable file — the SAME atomic
57
53
  * tmp+rename pattern a push report uses — and return its absolute path.
58
54
  * Called only when the body would clip in the digest; short bodies never
@@ -151,9 +147,7 @@ export function appendControllerDeathNotice(nodeId, controller) {
151
147
  throw error;
152
148
  }
153
149
  }
154
- // ---------------------------------------------------------------------------
155
150
  // Read
156
- // ---------------------------------------------------------------------------
157
151
  function readPhysicalInbox(nodeId) {
158
152
  const p = inboxPath(nodeId);
159
153
  if (!existsSync(p))
@@ -212,9 +206,7 @@ export function readInboxRange(nodeId, afterEntryId, throughEntryId) {
212
206
  throw new Error(`inbox range is reversed: ${after} through ${through}`);
213
207
  return entries.slice(afterIndex + 1, throughIndex + 1);
214
208
  }
215
- // ---------------------------------------------------------------------------
216
209
  // Cancel set (interrupt-canceled entries)
217
- // ---------------------------------------------------------------------------
218
210
  function canceledPath(nodeId) {
219
211
  return `${inboxPath(nodeId)}.canceled`;
220
212
  }
@@ -267,9 +259,7 @@ export function cancelPendingEntries(nodeId, select) {
267
259
  appendFileSync(canceledPath(nodeId), targets.join('\n') + '\n', { encoding: 'utf8', flag: 'a' });
268
260
  return targets;
269
261
  }
270
- // ---------------------------------------------------------------------------
271
262
  // Cursor persistence
272
- // ---------------------------------------------------------------------------
273
263
  /** Read and validate the persisted exact cursor identity. */
274
264
  export function readCursor(nodeId) {
275
265
  const p = cursorPath(nodeId);
@@ -291,9 +281,25 @@ export function writeCursor(nodeId, entryId) {
291
281
  writeFileSync(tmp, value, 'utf8');
292
282
  renameSync(tmp, p);
293
283
  }
294
- // ---------------------------------------------------------------------------
284
+ /** Is there mail this node has not seen that would drive a turn? The one
285
+ * authority for "wake-worthy inbox state", read by the park clock, the
286
+ * settlement policy, the review-companion guard, and reviveNode's parked
287
+ * re-entry rule. Deferred entries are excluded everywhere for the same
288
+ * reason: a deferred entry rides the next natural cycle and never starts one.
289
+ * A cursor that no longer resolves against the physical inbox is a fault, not
290
+ * an answer — every caller's conservative reading of this predicate is `true`
291
+ * (do not park, do not shut down, do not resume quietly), so report it and
292
+ * return that. */
293
+ export function hasUnseenWakeMail(nodeId) {
294
+ try {
295
+ return readInboxSince(nodeId, readCursor(nodeId)).some((entry) => entry.tier !== 'deferred');
296
+ }
297
+ catch (error) {
298
+ emitEvent({ level: 'warn', event: 'inbox.wake_scan.failed', node_id: nodeId, error });
299
+ return true;
300
+ }
301
+ }
295
302
  // Coalesce
296
- // ---------------------------------------------------------------------------
297
303
  /** Bounds for inlining a message body's preview in the digest. */
298
304
  const BODY_MAX_LINES = 12;
299
305
  const BODY_MAX_CHARS = 1000;