@north-light/crouter 0.3.353 → 0.3.355

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 (626) hide show
  1. package/dist/api/client.d.ts +35 -25
  2. package/dist/api/client.js +2 -2
  3. package/dist/api/dto/broker-ops.d.ts +2 -36
  4. package/dist/api/dto/canvas.d.ts +8 -13
  5. package/dist/api/dto/config.d.ts +4 -0
  6. package/dist/api/dto/delivery.d.ts +61 -0
  7. package/dist/api/dto/docs.d.ts +135 -0
  8. package/dist/api/dto/docs.js +1 -0
  9. package/dist/api/dto/health.d.ts +26 -79
  10. package/dist/api/dto/node-outcomes.d.ts +1 -3
  11. package/dist/api/dto/nodes.d.ts +5 -26
  12. package/dist/api/dto/objects.d.ts +168 -0
  13. package/dist/api/dto/objects.js +1 -0
  14. package/dist/api/dto/profiles.d.ts +14 -8
  15. package/dist/api/dto/reports.d.ts +9 -7
  16. package/dist/api/dto/reviews.d.ts +11 -3
  17. package/dist/api/dto/worktree.d.ts +1 -1
  18. package/dist/api/index.d.ts +0 -1
  19. package/dist/api/index.js +1 -1
  20. package/dist/api/routes.d.ts +14 -12
  21. package/dist/api/routes.js +1 -1
  22. package/dist/build-root.js +1 -1
  23. package/dist/builtin-memory/00-runtime-base/00-authoring.md +10 -5
  24. package/dist/builtin-memory/02-turn-lifecycle/02-resident.md +1 -0
  25. package/dist/builtin-memory/04-orchestration-kernel.md +10 -31
  26. package/dist/builtin-memory/05-kinds/advisor/01-orchestrator.md +1 -1
  27. package/dist/builtin-memory/05-kinds/explore/00-base.md +2 -2
  28. package/dist/builtin-memory/05-kinds/explore/01-orchestrator.md +2 -2
  29. package/dist/builtin-memory/crouter-concepts/INDEX.md +2 -2
  30. package/dist/builtin-memory/crouter-concepts/lifecycle-and-wakes.md +1 -1
  31. package/dist/builtin-memory/crouter-concepts/memory.md +15 -15
  32. package/dist/builtin-memory/crouter-concepts/nodes-and-the-canvas.md +1 -1
  33. package/dist/builtin-memory/crouter-concepts/profiles-kinds-and-modes.md +5 -5
  34. package/dist/builtin-memory/crouter-concepts/scopes-and-trust.md +1 -1
  35. package/dist/builtin-memory/crouter-concepts/why-a-daemon.md +1 -1
  36. package/dist/builtin-memory/crouter-sdk/INDEX.md +2 -2
  37. package/dist/builtin-memory/crouter-sdk/README.md +5 -6
  38. package/dist/builtin-memory/crouter-sdk/errors.md +0 -13
  39. package/dist/builtin-memory/crouter-sdk/guides/README.md +0 -1
  40. package/dist/builtin-memory/crouter-sdk/guides/event-driven-assistant.md +2 -2
  41. package/dist/builtin-memory/crouter-sdk/nodes.md +0 -2
  42. package/dist/builtin-memory/crouter-sdk/resources.md +0 -1
  43. package/dist/builtin-memory/explore/exploration-doc.md +4 -4
  44. package/dist/builtin-memory/insights/capture.md +6 -6
  45. package/dist/builtin-memory/insights/init.md +14 -14
  46. package/dist/builtin-memory/internal/INDEX.md +4 -4
  47. package/dist/builtin-memory/internal/agent-shaping.md +20 -20
  48. package/dist/builtin-memory/internal/examples/imessage-assistant.md +2 -2
  49. package/dist/builtin-memory/internal/marketplaces.md +2 -2
  50. package/dist/builtin-memory/internal/memory-loading.md +31 -30
  51. package/dist/builtin-memory/internal/nodes-and-canvas.md +1 -1
  52. package/dist/builtin-memory/internal/plugins.md +16 -14
  53. package/dist/builtin-memory/internal/storage-tiers.md +4 -4
  54. package/dist/builtin-memory/memory-read-orientation.md +1 -1
  55. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +32 -40
  56. package/dist/clients/attach/input/controller.js +1 -1
  57. package/dist/clients/attach/input/ref-autocomplete.js +1 -1
  58. package/dist/clients/attach/input/titled-editor.js +1 -1
  59. package/dist/clients/attach/overlays/file-review.d.ts +1 -0
  60. package/dist/clients/attach/overlays/file-review.js +2 -2
  61. package/dist/clients/attach/render/chat-view.d.ts +6 -0
  62. package/dist/clients/attach/render/chat-view.js +2 -2
  63. package/dist/clients/attach/render/crtr-output.d.ts +1 -2
  64. package/dist/clients/attach/render/crtr-output.js +17 -17
  65. package/dist/clients/attach/render/doc-links.d.ts +34 -0
  66. package/dist/clients/attach/render/doc-links.js +1 -0
  67. package/dist/clients/attach/render/group-activity.d.ts +8 -8
  68. package/dist/clients/attach/render/group-activity.js +2 -2
  69. package/dist/clients/attach/render/group-recap.js +1 -1
  70. package/dist/clients/attach/render/markdown-source.js +5 -5
  71. package/dist/clients/attach/render/tool-calls.d.ts +1 -1
  72. package/dist/clients/attach/session/doc-materialize.d.ts +53 -0
  73. package/dist/clients/attach/session/doc-materialize.js +3 -0
  74. package/dist/clients/attach/session/document-links.d.ts +21 -0
  75. package/dist/clients/attach/session/document-links.js +1 -0
  76. package/dist/clients/attach/session/file-links.d.ts +2 -0
  77. package/dist/clients/attach/session/file-links.js +1 -1
  78. package/dist/clients/attach/session/profile-files.js +1 -1
  79. package/dist/clients/attach/slash/dispatch.js +1 -1
  80. package/dist/clients/attach/viewer.js +816 -1561
  81. package/dist/clients/inbox/review/review-client.d.ts +5 -1
  82. package/dist/clients/inbox/review/review-client.js +1 -1
  83. package/dist/commands/api-client.d.ts +2 -0
  84. package/dist/commands/api-client.js +3 -3
  85. package/dist/commands/canvas/edges.d.ts +2 -0
  86. package/dist/commands/canvas/edges.js +2 -0
  87. package/dist/commands/canvas/list.d.ts +4 -0
  88. package/dist/commands/canvas/list.js +3 -0
  89. package/dist/commands/canvas/read.d.ts +8 -0
  90. package/dist/commands/canvas/read.js +5 -0
  91. package/dist/commands/canvas/search.d.ts +2 -0
  92. package/dist/commands/canvas/search.js +3 -0
  93. package/dist/commands/canvas/unwatch.d.ts +2 -0
  94. package/dist/commands/canvas/unwatch.js +1 -0
  95. package/dist/commands/canvas/watch.d.ts +3 -0
  96. package/dist/commands/canvas/watch.js +1 -0
  97. package/dist/commands/canvas-analytics.js +1 -1
  98. package/dist/commands/canvas-history/grep.js +4 -4
  99. package/dist/commands/canvas-history/read.js +2 -2
  100. package/dist/commands/canvas-history/search.js +7 -7
  101. package/dist/commands/canvas-history/shared.d.ts +0 -1
  102. package/dist/commands/canvas-history/shared.js +1 -1
  103. package/dist/commands/canvas-history/stats.js +2 -2
  104. package/dist/commands/canvas-history.js +1 -1
  105. package/dist/commands/canvas.js +1 -1
  106. package/dist/commands/doc/delete.d.ts +2 -0
  107. package/dist/commands/doc/delete.js +1 -0
  108. package/dist/commands/doc/edit.d.ts +2 -0
  109. package/dist/commands/doc/edit.js +1 -0
  110. package/dist/commands/doc/history.d.ts +2 -0
  111. package/dist/commands/doc/history.js +2 -0
  112. package/dist/commands/doc/lint.d.ts +2 -0
  113. package/dist/commands/doc/lint.js +2 -0
  114. package/dist/commands/doc/list.d.ts +2 -0
  115. package/dist/commands/doc/list.js +1 -0
  116. package/dist/commands/doc/move.d.ts +2 -0
  117. package/dist/commands/doc/move.js +1 -0
  118. package/dist/commands/doc/read.d.ts +2 -0
  119. package/dist/commands/doc/read.js +1 -0
  120. package/dist/commands/doc/shared.d.ts +25 -0
  121. package/dist/commands/doc/shared.js +2 -0
  122. package/dist/commands/doc/write.d.ts +2 -0
  123. package/dist/commands/doc/write.js +1 -0
  124. package/dist/commands/{memory.d.ts → doc.d.ts} +1 -1
  125. package/dist/commands/doc.js +1 -0
  126. package/dist/commands/human/review.js +1 -1
  127. package/dist/commands/node/bash.js +2 -2
  128. package/dist/commands/node/create.js +1 -1
  129. package/dist/commands/node/inspect.js +10 -10
  130. package/dist/commands/node/lifecycle.js +2 -2
  131. package/dist/commands/node/outcome.js +1 -1
  132. package/dist/commands/node/subscription.js +1 -1
  133. package/dist/commands/node-context.js +2 -2
  134. package/dist/commands/pkg/browse/catalog.js +1 -1
  135. package/dist/commands/pkg/browse/doc-view.js +1 -1
  136. package/dist/commands/pkg/browse/model.d.ts +9 -17
  137. package/dist/commands/pkg/plugin-inspect.js +1 -1
  138. package/dist/commands/pkg/plugin-manage.d.ts +4 -8
  139. package/dist/commands/pkg/plugin-manage.js +15 -17
  140. package/dist/commands/profile/list.js +1 -1
  141. package/dist/commands/profile/new.js +1 -1
  142. package/dist/commands/profile/project.js +1 -1
  143. package/dist/commands/profile/show.js +1 -1
  144. package/dist/commands/push.js +3 -3
  145. package/dist/commands/sys/config.js +1 -1
  146. package/dist/commands/sys/context/admin/actions.d.ts +18 -43
  147. package/dist/commands/sys/context/admin/actions.js +1 -2
  148. package/dist/commands/sys/context/admin/detail-panel.d.ts +17 -25
  149. package/dist/commands/sys/context/admin/detail-panel.js +2 -1
  150. package/dist/commands/sys/context/admin/docs-panel.d.ts +7 -6
  151. package/dist/commands/sys/context/admin/docs-panel.js +1 -1
  152. package/dist/commands/sys/context/admin/list-view.d.ts +12 -12
  153. package/dist/commands/sys/context/admin/list-view.js +1 -1
  154. package/dist/commands/sys/context/admin/model.d.ts +96 -69
  155. package/dist/commands/sys/context/admin/model.js +1 -1
  156. package/dist/commands/sys/context/admin/rail-panel.d.ts +7 -5
  157. package/dist/commands/sys/context/admin/rail-panel.js +1 -1
  158. package/dist/commands/sys/context/admin/read-view.d.ts +10 -14
  159. package/dist/commands/sys/context/admin/read-view.js +1 -2
  160. package/dist/commands/sys/context/admin/shell.d.ts +26 -31
  161. package/dist/commands/sys/context/admin/shell.js +1 -1
  162. package/dist/commands/sys/context/admin.js +2 -2
  163. package/dist/commands/sys/context/doc.js +2 -3
  164. package/dist/commands/sys/context/expose.js +2 -2
  165. package/dist/commands/sys/context/prompt-review.js +4 -4
  166. package/dist/commands/sys/context/resolve.d.ts +92 -65
  167. package/dist/commands/sys/context/resolve.js +7 -9
  168. package/dist/commands/sys/context.js +1 -1
  169. package/dist/commands/sys/doctor.js +1 -1
  170. package/dist/commands/sys/migrate.js +1 -1
  171. package/dist/commands/sys/panels/profiles-panel.d.ts +3 -1
  172. package/dist/commands/sys/panels/profiles-panel.js +1 -1
  173. package/dist/commands/sys/sync-deps.d.ts +6 -11
  174. package/dist/commands/sys/sync-deps.js +7 -8
  175. package/dist/commands/sys/sync-project-guidance.js +5 -4
  176. package/dist/commands/sys/sync-shared.d.ts +41 -4
  177. package/dist/commands/sys/sync-shared.js +1 -1
  178. package/dist/commands/sys/sync-skills.js +2 -2
  179. package/dist/commands/sys/sync.js +1 -1
  180. package/dist/core/bash-job-supervisor.d.ts +2 -3
  181. package/dist/core/bash-job-supervisor.js +3 -3
  182. package/dist/core/bash-jobs.d.ts +0 -5
  183. package/dist/core/bash-jobs.js +7 -7
  184. package/dist/core/bootstrap.js +2 -2
  185. package/dist/core/canvas/canvas.d.ts +6 -50
  186. package/dist/core/canvas/canvas.js +16 -21
  187. package/dist/core/canvas/db.d.ts +4 -1
  188. package/dist/core/canvas/db.js +2 -2
  189. package/dist/core/canvas/history.d.ts +11 -39
  190. package/dist/core/canvas/history.js +9 -20
  191. package/dist/core/canvas/install-id.d.ts +2 -2
  192. package/dist/core/canvas/install-id.js +1 -1
  193. package/dist/core/canvas/memory-reads.d.ts +5 -0
  194. package/dist/core/canvas/memory-reads.js +1 -1
  195. package/dist/core/canvas/migrations.js +215 -41
  196. package/dist/core/canvas/node-agent-paths.js +1 -1
  197. package/dist/core/canvas/node-tokens.js +1 -1
  198. package/dist/core/canvas/paths.d.ts +0 -4
  199. package/dist/core/canvas/paths.js +1 -1
  200. package/dist/core/canvas/remote-canvas-source.d.ts +0 -1
  201. package/dist/core/canvas/remote-canvas-source.js +1 -1
  202. package/dist/core/canvas/render-source.d.ts +9 -6
  203. package/dist/core/canvas/render-source.js +6 -6
  204. package/dist/core/canvas/sessions.js +2 -2
  205. package/dist/core/canvas/source.d.ts +6 -0
  206. package/dist/core/canvas/tree.d.ts +29 -0
  207. package/dist/core/canvas/tree.js +12 -0
  208. package/dist/core/canvas/types.d.ts +0 -20
  209. package/dist/core/command-plugins/bundle.js +1 -1
  210. package/dist/core/config.d.ts +1 -1
  211. package/dist/core/conversation-store/listener.js +4 -2
  212. package/dist/core/feed/feed.d.ts +9 -7
  213. package/dist/core/feed/feed.js +3 -7
  214. package/dist/core/feed/inbox.d.ts +3 -5
  215. package/dist/core/feed/inbox.js +1 -10
  216. package/dist/core/feed/messages.d.ts +33 -6
  217. package/dist/core/feed/messages.js +10 -7
  218. package/dist/core/feed/reports.d.ts +10 -0
  219. package/dist/core/feed/reports.js +2 -0
  220. package/dist/core/grants/remove.js +1 -1
  221. package/dist/core/graph/access.d.ts +24 -0
  222. package/dist/core/graph/access.js +8 -0
  223. package/dist/core/graph/bodies.d.ts +6 -0
  224. package/dist/core/graph/bodies.js +1 -0
  225. package/dist/core/graph/deletion.d.ts +10 -0
  226. package/dist/core/graph/deletion.js +5 -0
  227. package/dist/core/graph/diff.d.ts +4 -0
  228. package/dist/core/graph/diff.js +4 -0
  229. package/dist/core/graph/documents.d.ts +167 -0
  230. package/dist/core/graph/documents.js +10 -0
  231. package/dist/core/graph/edges.d.ts +25 -0
  232. package/dist/core/graph/edges.js +1 -0
  233. package/dist/core/graph/events.d.ts +26 -0
  234. package/dist/core/graph/events.js +7 -0
  235. package/dist/core/graph/exposures.d.ts +53 -0
  236. package/dist/core/graph/exposures.js +4 -0
  237. package/dist/core/graph/jobs.d.ts +31 -0
  238. package/dist/core/graph/jobs.js +2 -0
  239. package/dist/core/graph/names.d.ts +30 -0
  240. package/dist/core/graph/names.js +3 -0
  241. package/dist/core/graph/objects.d.ts +55 -0
  242. package/dist/core/graph/objects.js +9 -0
  243. package/dist/core/graph/package-docs.d.ts +31 -0
  244. package/dist/core/graph/package-docs.js +5 -0
  245. package/dist/core/graph/package-files.d.ts +20 -0
  246. package/dist/core/graph/package-files.js +2 -0
  247. package/dist/core/graph/reader.d.ts +21 -0
  248. package/dist/core/graph/reader.js +3 -0
  249. package/dist/core/graph/repo-sync/exchange.d.ts +34 -0
  250. package/dist/core/graph/repo-sync/exchange.js +2 -0
  251. package/dist/core/graph/repo-sync/identity.d.ts +30 -0
  252. package/dist/core/graph/repo-sync/identity.js +1 -0
  253. package/dist/core/graph/repo-sync/index.d.ts +4 -0
  254. package/dist/core/graph/repo-sync/index.js +1 -0
  255. package/dist/core/graph/repo-sync/mirror.d.ts +30 -0
  256. package/dist/core/graph/repo-sync/mirror.js +6 -0
  257. package/dist/core/graph/repo-sync/ref-format.d.ts +76 -0
  258. package/dist/core/graph/repo-sync/ref-format.js +2 -0
  259. package/dist/core/graph/repo-sync/repos.d.ts +17 -0
  260. package/dist/core/graph/repo-sync/repos.js +3 -0
  261. package/dist/core/graph/repo-sync/sync.d.ts +42 -0
  262. package/dist/core/graph/repo-sync/sync.js +2 -0
  263. package/dist/core/graph/rules.d.ts +26 -0
  264. package/dist/core/graph/rules.js +3 -0
  265. package/dist/core/graph/spaces/app.d.ts +3 -0
  266. package/dist/core/graph/spaces/app.js +1 -0
  267. package/dist/core/graph/spaces/index.d.ts +19 -0
  268. package/dist/core/graph/spaces/index.js +1 -0
  269. package/dist/core/graph/spaces/person.d.ts +5 -0
  270. package/dist/core/graph/spaces/person.js +2 -0
  271. package/dist/core/graph/spaces/repo.d.ts +4 -0
  272. package/dist/core/graph/spaces/repo.js +1 -0
  273. package/dist/core/graph/types.d.ts +75 -0
  274. package/dist/core/graph/types.js +1 -0
  275. package/dist/core/graph/watches.d.ts +62 -0
  276. package/dist/core/graph/watches.js +18 -0
  277. package/dist/core/human/feedback-companion.js +1 -1
  278. package/dist/core/human/requests.d.ts +3 -1
  279. package/dist/core/human/requests.js +6 -6
  280. package/dist/core/inspector/model.js +2 -2
  281. package/dist/core/io.js +5 -5
  282. package/dist/core/layout-migrate/index.d.ts +24 -1
  283. package/dist/core/layout-migrate/index.js +4 -4
  284. package/dist/core/layout-migrate/paths.d.ts +4 -0
  285. package/dist/core/layout-migrate/paths.js +1 -1
  286. package/dist/core/layout-migrate/stored-paths.d.ts +7 -0
  287. package/dist/core/layout-migrate/stored-paths.js +11 -11
  288. package/dist/core/layout-migrate/stores.d.ts +19 -6
  289. package/dist/core/layout-migrate/stores.js +3 -4
  290. package/dist/core/manifest.d.ts +0 -2
  291. package/dist/core/manifest.js +1 -1
  292. package/dist/core/{memory/extensions.d.ts → plugin-extensions.d.ts} +9 -10
  293. package/dist/core/plugin-extensions.js +1 -0
  294. package/dist/core/preview-registry.d.ts +1 -1
  295. package/dist/core/preview-registry.js +3 -2
  296. package/dist/core/profiles/manifest.d.ts +7 -7
  297. package/dist/core/profiles/manifest.js +1 -1
  298. package/dist/core/profiles/select.js +3 -3
  299. package/dist/core/review/birth.js +1 -1
  300. package/dist/core/review/companion.js +1 -1
  301. package/dist/core/review/stage.d.ts +12 -1
  302. package/dist/core/review/stage.js +1 -1
  303. package/dist/core/review/store.d.ts +2 -0
  304. package/dist/core/review/store.js +1 -1
  305. package/dist/core/review/types.d.ts +7 -1
  306. package/dist/core/runs/events.js +1 -1
  307. package/dist/core/runs/operations.js +5 -5
  308. package/dist/core/runs/questions.js +2 -2
  309. package/dist/core/runtime/bearings-render.d.ts +13 -7
  310. package/dist/core/runtime/bearings-render.js +12 -12
  311. package/dist/core/runtime/bearings.d.ts +8 -18
  312. package/dist/core/runtime/bearings.js +7 -7
  313. package/dist/core/runtime/broker/daemon-ops.d.ts +7 -12
  314. package/dist/core/runtime/broker/daemon-ops.js +1 -1
  315. package/dist/core/runtime/broker/fault-retry.d.ts +2 -0
  316. package/dist/core/runtime/broker/fault-retry.js +1 -1
  317. package/dist/core/runtime/broker/frame-dispatch.d.ts +1 -1
  318. package/dist/core/runtime/broker/frame-memory-refs.d.ts +7 -2
  319. package/dist/core/runtime/broker/frame-memory-refs.js +1 -1
  320. package/dist/core/runtime/broker/inbox.d.ts +1 -4
  321. package/dist/core/runtime/broker/inbox.js +1 -10
  322. package/dist/core/runtime/broker/rebind.js +1 -1
  323. package/dist/core/runtime/broker/retry-card-elision.d.ts +7 -0
  324. package/dist/core/runtime/broker/retry-card-elision.js +1 -0
  325. package/dist/core/runtime/broker-extension-render.d.ts +0 -13
  326. package/dist/core/runtime/broker-extension-render.js +2 -4
  327. package/dist/core/runtime/broker-persona-guidance.js +3 -3
  328. package/dist/core/runtime/broker-protocol.d.ts +1 -1
  329. package/dist/core/runtime/broker.js +1 -1
  330. package/dist/core/runtime/close.d.ts +9 -4
  331. package/dist/core/runtime/close.js +1 -1
  332. package/dist/core/runtime/deliver-live.js +1 -1
  333. package/dist/core/runtime/kickoff.js +17 -19
  334. package/dist/core/runtime/launch-target.d.ts +7 -0
  335. package/dist/core/runtime/launch-target.js +1 -1
  336. package/dist/core/runtime/ledger.d.ts +10 -0
  337. package/dist/core/runtime/ledger.js +3 -0
  338. package/dist/core/runtime/lifecycle.js +2 -2
  339. package/dist/core/runtime/nodes.d.ts +10 -2
  340. package/dist/core/runtime/nodes.js +1 -1
  341. package/dist/core/runtime/outcome-document.d.ts +2 -2
  342. package/dist/core/runtime/outcome-document.js +1 -1
  343. package/dist/core/runtime/persona.js +2 -2
  344. package/dist/core/runtime/placement.js +1 -1
  345. package/dist/core/runtime/promote.d.ts +2 -2
  346. package/dist/core/runtime/promote.js +1 -1
  347. package/dist/core/runtime/prospective-inventory-cli.js +2 -2
  348. package/dist/core/runtime/recycle.js +1 -1
  349. package/dist/core/runtime/reopen.js +1 -1
  350. package/dist/core/runtime/revive.js +2 -2
  351. package/dist/core/runtime/roadmap.d.ts +20 -9
  352. package/dist/core/runtime/roadmap.js +10 -6
  353. package/dist/core/runtime/spawn-env.js +1 -1
  354. package/dist/core/runtime/spawn.d.ts +5 -3
  355. package/dist/core/runtime/spawn.js +2 -2
  356. package/dist/core/runtime/structured-output.d.ts +3 -1
  357. package/dist/core/runtime/structured-output.js +4 -4
  358. package/dist/core/scope.d.ts +1 -32
  359. package/dist/core/scope.js +1 -1
  360. package/dist/core/scoped-state/db.js +2 -1
  361. package/dist/core/scoped-state/migrate.js +2 -2
  362. package/dist/core/scoped-state/profiles.d.ts +11 -0
  363. package/dist/core/scoped-state/profiles.js +1 -1
  364. package/dist/core/scoped-state/schema.d.ts +1 -1
  365. package/dist/core/scoped-state/schema.js +7 -5
  366. package/dist/core/spaces/permissions.d.ts +0 -14
  367. package/dist/core/spaces/permissions.js +1 -1
  368. package/dist/core/spaces/stop.d.ts +13 -0
  369. package/dist/core/spaces/stop.js +2 -2
  370. package/dist/core/storage/tables.d.ts +1 -1
  371. package/dist/core/substrate/delivery/corpus.d.ts +21 -0
  372. package/dist/core/substrate/delivery/corpus.js +3 -0
  373. package/dist/core/substrate/delivery/deliver.d.ts +76 -0
  374. package/dist/core/substrate/delivery/deliver.js +6 -0
  375. package/dist/core/substrate/delivery/listings.d.ts +30 -0
  376. package/dist/core/substrate/delivery/listings.js +7 -0
  377. package/dist/core/substrate/delivery/match.d.ts +28 -0
  378. package/dist/core/substrate/delivery/match.js +1 -0
  379. package/dist/core/substrate/delivery/plan.d.ts +67 -0
  380. package/dist/core/substrate/delivery/plan.js +3 -0
  381. package/dist/core/substrate/delivery/render-boot.d.ts +20 -0
  382. package/dist/core/substrate/delivery/render-boot.js +54 -0
  383. package/dist/core/substrate/delivery/render-event.d.ts +54 -0
  384. package/dist/core/substrate/delivery/render-event.js +8 -0
  385. package/dist/core/substrate/delivery/sub-persona-menu.d.ts +2 -0
  386. package/dist/core/substrate/delivery/sub-persona-menu.js +6 -0
  387. package/dist/core/substrate/delivery/types.d.ts +30 -0
  388. package/dist/core/substrate/delivery/types.js +0 -0
  389. package/dist/core/substrate/gate-explain.d.ts +0 -13
  390. package/dist/core/substrate/gate-explain.js +1 -1
  391. package/dist/core/substrate/schema.d.ts +11 -139
  392. package/dist/core/substrate/schema.js +1 -1
  393. package/dist/core/substrate/subject-fields.d.ts +3 -0
  394. package/dist/core/substrate/subject.d.ts +2 -1
  395. package/dist/core/substrate/subject.js +1 -1
  396. package/dist/daemon/api/handlers/app-profiles.js +1 -1
  397. package/dist/daemon/api/handlers/bash-jobs.js +1 -2
  398. package/dist/daemon/api/handlers/broker-ops.js +1 -1
  399. package/dist/daemon/api/handlers/canvas.js +5 -5
  400. package/dist/daemon/api/handlers/daemon.js +1 -1
  401. package/dist/daemon/api/handlers/delivery.d.ts +2 -0
  402. package/dist/daemon/api/handlers/delivery.js +1 -0
  403. package/dist/daemon/api/handlers/{memory.d.ts → docs.d.ts} +1 -1
  404. package/dist/daemon/api/handlers/docs.js +1 -0
  405. package/dist/daemon/api/handlers/hook-exec.js +1 -1
  406. package/dist/daemon/api/handlers/messages.js +2 -2
  407. package/dist/daemon/api/handlers/node-records.js +2 -2
  408. package/dist/daemon/api/handlers/nodes.js +1 -1
  409. package/dist/daemon/api/handlers/objects.d.ts +2 -0
  410. package/dist/daemon/api/handlers/objects.js +5 -0
  411. package/dist/daemon/api/handlers/package-docs.d.ts +2 -0
  412. package/dist/daemon/api/handlers/package-docs.js +1 -0
  413. package/dist/daemon/api/handlers/profiles.js +1 -1
  414. package/dist/daemon/api/handlers/reports.d.ts +2 -2
  415. package/dist/daemon/api/handlers/reports.js +4 -4
  416. package/dist/daemon/api/handlers/reviews.d.ts +4 -1
  417. package/dist/daemon/api/handlers/reviews.js +1 -1
  418. package/dist/daemon/api/map.d.ts +2 -2
  419. package/dist/daemon/api/map.js +2 -2
  420. package/dist/daemon/api/operations.d.ts +1 -1
  421. package/dist/daemon/api/operations.js +1 -1
  422. package/dist/daemon/api/reader.d.ts +17 -0
  423. package/dist/daemon/api/reader.js +1 -0
  424. package/dist/daemon/api/server.js +1 -1
  425. package/dist/daemon/control.d.ts +2 -3
  426. package/dist/daemon/crtrd.js +8 -6
  427. package/dist/daemon/fleet.js +4 -4
  428. package/dist/daemon/reconcilers/bash-deadline.d.ts +11 -1
  429. package/dist/daemon/reconcilers/bash-deadline.js +2 -1
  430. package/dist/daemon/reconcilers/broker-supervision.js +4 -4
  431. package/dist/daemon/reconcilers/live-obligation.js +1 -1
  432. package/dist/daemon/reconcilers/node-deadline.js +1 -1
  433. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +1 -1
  434. package/dist/daemon/reconcilers/storage-maintenance.d.ts +1 -0
  435. package/dist/daemon/reconcilers/storage-maintenance.js +1 -1
  436. package/dist/daemon/review/deliver.js +2 -2
  437. package/dist/daemon/review/finish.js +1 -1
  438. package/dist/hook-authoring.d.ts +0 -1
  439. package/dist/hook-authoring.js +3 -3
  440. package/dist/migrations/004-canvas-documents/apply.d.ts +10 -0
  441. package/dist/migrations/004-canvas-documents/apply.js +1 -0
  442. package/dist/migrations/004-canvas-documents/fields.d.ts +24 -0
  443. package/dist/migrations/004-canvas-documents/fields.js +2 -0
  444. package/dist/migrations/004-canvas-documents/index.d.ts +16 -0
  445. package/dist/migrations/004-canvas-documents/index.js +2 -0
  446. package/dist/migrations/004-canvas-documents/legacy/discover.d.ts +16 -0
  447. package/dist/migrations/004-canvas-documents/legacy/discover.js +1 -0
  448. package/dist/migrations/004-canvas-documents/legacy/history.d.ts +5 -0
  449. package/dist/migrations/004-canvas-documents/legacy/history.js +2 -0
  450. package/dist/migrations/004-canvas-documents/legacy/nested.d.ts +7 -0
  451. package/dist/migrations/004-canvas-documents/legacy/nested.js +1 -0
  452. package/dist/migrations/004-canvas-documents/legacy/parse.d.ts +14 -0
  453. package/dist/migrations/004-canvas-documents/legacy/parse.js +1 -0
  454. package/dist/migrations/004-canvas-documents/legacy/types.d.ts +91 -0
  455. package/dist/migrations/004-canvas-documents/legacy/types.js +0 -0
  456. package/dist/migrations/004-canvas-documents/legacy-edges.d.ts +6 -0
  457. package/dist/migrations/004-canvas-documents/legacy-edges.js +18 -0
  458. package/dist/migrations/004-canvas-documents/plan.d.ts +102 -0
  459. package/dist/migrations/004-canvas-documents/plan.js +3 -0
  460. package/dist/migrations/004-canvas-documents/pointers.d.ts +23 -0
  461. package/dist/migrations/004-canvas-documents/pointers.js +4 -0
  462. package/dist/migrations/activation.d.ts +9 -18
  463. package/dist/migrations/activation.js +2 -2
  464. package/dist/pi-extensions/broker-local.d.ts +2 -2
  465. package/dist/pi-extensions/broker-local.js +2 -2
  466. package/dist/pi-extensions/canvas-bash-valve.js +5 -4
  467. package/dist/pi-extensions/canvas-context-intro.js +2 -2
  468. package/dist/pi-extensions/canvas-doc-substrate.d.ts +0 -1
  469. package/dist/pi-extensions/canvas-doc-substrate.js +6 -6
  470. package/dist/pi-extensions/canvas-inbox-watcher.js +2 -2
  471. package/dist/pi-extensions/canvas-passive-context.js +1 -1
  472. package/dist/pi-extensions/canvas-recap.js +2 -2
  473. package/dist/pi-extensions/canvas-stophook.d.ts +1 -1
  474. package/dist/pi-extensions/canvas-stophook.js +1 -1
  475. package/dist/prompts/review.js +3 -3
  476. package/dist/shared/env.d.ts +5 -0
  477. package/dist/shared/env.js +1 -1
  478. package/dist/shared/generated-context.js +2 -10
  479. package/dist/shared/inbox-entry-body.d.ts +22 -0
  480. package/dist/shared/inbox-entry-body.js +10 -0
  481. package/dist/{core/memory → shared}/inline-ref-guidance.d.ts +2 -2
  482. package/dist/shared/inline-ref-guidance.js +2 -0
  483. package/dist/types.d.ts +3 -1
  484. package/docs/cli/memory-and-preferences.md +12 -14
  485. package/docs/concepts/README.md +1 -1
  486. package/docs/concepts/lifecycle-and-wakes.md +1 -1
  487. package/docs/concepts/memory.md +15 -15
  488. package/docs/concepts/nodes-and-the-canvas.md +1 -1
  489. package/docs/concepts/profiles-kinds-and-modes.md +5 -5
  490. package/docs/concepts/scopes-and-trust.md +1 -1
  491. package/docs/concepts/why-a-daemon.md +1 -1
  492. package/docs/sdk/README.md +5 -6
  493. package/docs/sdk/errors.md +0 -13
  494. package/docs/sdk/guides/README.md +0 -1
  495. package/docs/sdk/nodes.md +0 -2
  496. package/docs/sdk/resources.md +0 -1
  497. package/package.json +2 -2
  498. package/packages/crouter-identity/package.json +1 -1
  499. package/runtime.lock.json +11 -11
  500. package/scripts/lib/isolation-env.mjs +8 -2
  501. package/dist/api/dto/memory.d.ts +0 -171
  502. package/dist/api/dto/memory.js +0 -1
  503. package/dist/builtin-memory/crouter-sdk/guides/app-memory.md +0 -73
  504. package/dist/builtin-memory/crouter-sdk/memory.md +0 -87
  505. package/dist/commands/broker-permissions.d.ts +0 -2
  506. package/dist/commands/broker-permissions.js +0 -1
  507. package/dist/commands/memory/client.d.ts +0 -11
  508. package/dist/commands/memory/client.js +0 -1
  509. package/dist/commands/memory/delete.d.ts +0 -1
  510. package/dist/commands/memory/delete.js +0 -1
  511. package/dist/commands/memory/edit.d.ts +0 -1
  512. package/dist/commands/memory/edit.js +0 -21
  513. package/dist/commands/memory/find.d.ts +0 -1
  514. package/dist/commands/memory/find.js +0 -1
  515. package/dist/commands/memory/history.d.ts +0 -1
  516. package/dist/commands/memory/history.js +0 -5
  517. package/dist/commands/memory/lint.d.ts +0 -1
  518. package/dist/commands/memory/lint.js +0 -1
  519. package/dist/commands/memory/list.d.ts +0 -1
  520. package/dist/commands/memory/list.js +0 -1
  521. package/dist/commands/memory/move.d.ts +0 -1
  522. package/dist/commands/memory/move.js +0 -1
  523. package/dist/commands/memory/origin.d.ts +0 -1
  524. package/dist/commands/memory/origin.js +0 -1
  525. package/dist/commands/memory/read.d.ts +0 -1
  526. package/dist/commands/memory/read.js +0 -6
  527. package/dist/commands/memory/shared.d.ts +0 -22
  528. package/dist/commands/memory/shared.js +0 -1
  529. package/dist/commands/memory/write.d.ts +0 -1
  530. package/dist/commands/memory/write.js +0 -13
  531. package/dist/commands/memory.js +0 -1
  532. package/dist/commands/node-inspect-artifacts.d.ts +0 -1
  533. package/dist/commands/node-inspect-artifacts.js +0 -8
  534. package/dist/core/help/memory-extensions.d.ts +0 -3
  535. package/dist/core/help/memory-extensions.js +0 -2
  536. package/dist/core/memory/cli-selector.d.ts +0 -13
  537. package/dist/core/memory/cli-selector.js +0 -1
  538. package/dist/core/memory/doc-link-grammar.d.ts +0 -20
  539. package/dist/core/memory/doc-link-grammar.js +0 -2
  540. package/dist/core/memory/extensions.js +0 -1
  541. package/dist/core/memory/history.d.ts +0 -87
  542. package/dist/core/memory/history.js +0 -6
  543. package/dist/core/memory/identity.d.ts +0 -65
  544. package/dist/core/memory/identity.js +0 -1
  545. package/dist/core/memory/inline-ref-guidance.js +0 -2
  546. package/dist/core/memory/inline-ref-inventory.d.ts +0 -9
  547. package/dist/core/memory/inline-ref-inventory.js +0 -1
  548. package/dist/core/memory/lint.d.ts +0 -163
  549. package/dist/core/memory/lint.js +0 -2
  550. package/dist/core/memory/mutation-domain.d.ts +0 -113
  551. package/dist/core/memory/mutation-domain.js +0 -12
  552. package/dist/core/memory/mutations.d.ts +0 -74
  553. package/dist/core/memory/mutations.js +0 -1
  554. package/dist/core/memory/project-namespace.d.ts +0 -31
  555. package/dist/core/memory/project-namespace.js +0 -2
  556. package/dist/core/memory/repository-association.d.ts +0 -31
  557. package/dist/core/memory/repository-association.js +0 -1
  558. package/dist/core/memory/service.d.ts +0 -145
  559. package/dist/core/memory/service.js +0 -2
  560. package/dist/core/memory/tree.d.ts +0 -39
  561. package/dist/core/memory/tree.js +0 -1
  562. package/dist/core/memory-resolver.d.ts +0 -280
  563. package/dist/core/memory-resolver.js +0 -2
  564. package/dist/core/nested-stores.d.ts +0 -18
  565. package/dist/core/nested-stores.js +0 -1
  566. package/dist/core/runtime/memory.d.ts +0 -3
  567. package/dist/core/runtime/memory.js +0 -1
  568. package/dist/core/substrate/frontmatter-validation.d.ts +0 -11
  569. package/dist/core/substrate/frontmatter-validation.js +0 -1
  570. package/dist/core/substrate/gate.d.ts +0 -16
  571. package/dist/core/substrate/gate.js +0 -1
  572. package/dist/core/substrate/index.d.ts +0 -18
  573. package/dist/core/substrate/index.js +0 -1
  574. package/dist/core/substrate/injected-store.d.ts +0 -75
  575. package/dist/core/substrate/injected-store.js +0 -2
  576. package/dist/core/substrate/listings.d.ts +0 -28
  577. package/dist/core/substrate/listings.js +0 -1
  578. package/dist/core/substrate/memory-events.d.ts +0 -6
  579. package/dist/core/substrate/memory-events.js +0 -1
  580. package/dist/core/substrate/on-read-node.d.ts +0 -6
  581. package/dist/core/substrate/on-read-node.js +0 -1
  582. package/dist/core/substrate/on-read.d.ts +0 -101
  583. package/dist/core/substrate/on-read.js +0 -9
  584. package/dist/core/substrate/plan.d.ts +0 -94
  585. package/dist/core/substrate/plan.js +0 -1
  586. package/dist/core/substrate/render-node.d.ts +0 -12
  587. package/dist/core/substrate/render-node.js +0 -1
  588. package/dist/core/substrate/render.d.ts +0 -41
  589. package/dist/core/substrate/render.js +0 -65
  590. package/dist/core/substrate/session-cache.d.ts +0 -27
  591. package/dist/core/substrate/session-cache.js +0 -1
  592. package/dist/core/substrate/surface-match.d.ts +0 -73
  593. package/dist/core/substrate/surface-match.js +0 -1
  594. package/dist/daemon/api/handlers/memory.js +0 -5
  595. package/dist/migrations/001-surfaces-frontmatter.d.ts +0 -2
  596. package/dist/migrations/001-surfaces-frontmatter.js +0 -1
  597. package/dist/migrations/002-profile-project-memory.d.ts +0 -2
  598. package/dist/migrations/002-profile-project-memory.js +0 -1
  599. package/dist/migrations/003-repository-root-memory-identity/front-door.d.ts +0 -26
  600. package/dist/migrations/003-repository-root-memory-identity/front-door.js +0 -11
  601. package/dist/migrations/003-repository-root-memory-identity/index.d.ts +0 -2
  602. package/dist/migrations/003-repository-root-memory-identity/index.js +0 -5
  603. package/dist/migrations/003-repository-root-memory-identity/references.d.ts +0 -95
  604. package/dist/migrations/003-repository-root-memory-identity/references.js +0 -5
  605. package/dist/migrations/003-repository-root-memory-identity/repository-facts.d.ts +0 -39
  606. package/dist/migrations/003-repository-root-memory-identity/repository-facts.js +0 -3
  607. package/dist/migrations/convergent.d.ts +0 -43
  608. package/dist/migrations/convergent.js +0 -2
  609. package/dist/migrations/corpus.d.ts +0 -75
  610. package/dist/migrations/corpus.js +0 -3
  611. package/dist/migrations/frontmatter-splice.d.ts +0 -15
  612. package/dist/migrations/frontmatter-splice.js +0 -8
  613. package/dist/migrations/profile-manifests.d.ts +0 -30
  614. package/dist/migrations/profile-manifests.js +0 -2
  615. package/dist/migrations/registry.d.ts +0 -7
  616. package/dist/migrations/registry.js +0 -1
  617. package/dist/migrations/runner.d.ts +0 -43
  618. package/dist/migrations/runner.js +0 -1
  619. package/dist/migrations/types.d.ts +0 -211
  620. package/dist/shared/birth-announcement.d.ts +0 -12
  621. package/dist/shared/birth-announcement.js +0 -1
  622. package/docs/sdk/guides/app-memory.md +0 -22
  623. package/docs/sdk/memory.md +0 -85
  624. /package/dist/{migrations/types.js → api/dto/delivery.js} +0 -0
  625. /package/dist/{core/memory → shared}/inline-ref-grammar.d.ts +0 -0
  626. /package/dist/{core/memory → shared}/inline-ref-grammar.js +0 -0
@@ -1 +1 @@
1
- var y=Object.defineProperty;var o=(e,t)=>y(e,"name",{value:t,configurable:!0});import{envNodeId as _}from"../../shared/env.js";import{randomUUID as q}from"node:crypto";import{resolve as x}from"node:path";import{defineBranch as w,defineLeaf as s}from"../../core/command.js";import{InputError as l}from"../../core/io.js";import{cliClient as r}from"../api-client.js";function d(){const e=_();return e===void 0||e===""?null:e}o(d,"nodeActor");function b(){const e=d();if(e!==null)return e;throw new l({error:"review_origin_unavailable",message:"a review must be filed from a node with a live or resumable session",field:"CRTR_NODE_ID",next:"Run this from the node whose document context should be forked for the review."})}o(b,"requireOriginNode");function k(e){if(typeof e=="string"&&e.trim()!=="")return e.trim();throw new l({error:"missing_parameter",message:"a non-empty subtitle is required",field:"subtitle",next:"Run `crtr human review new -h` and provide one plain-English sentence explaining the review needed and why it matters."})}o(k,"requireSubtitle");function v(e,t){if(typeof e=="string"&&e.trim()!=="")return e;throw new l({error:"missing_parameter",message:"non-empty comment text is required on stdin",field:"text",next:`Run \`crtr human review comment ${t} -h\` and pipe the comment text on stdin.`})}o(v,"requireCommentText");function h(e,t){if(!(t?.providedParams.has("deliver")??e.deliver!==void 0))return;const i=e.deliver;return i==="wake"||i==="quiet"?i:void 0}o(h,"explicitDeliver");function f(e,t=!0){const{comment:n}=e;return{comment_id:n.comment_id,status:n.status,revision:n.revision,...t?{anchor:n.anchor}:{},author:n.author,text:n.text,changed:e.changed,delivery:e.delivery??null}}o(f,"mutationOutput");function C(e){return{comment_id:e.comment_id,status:e.status,revision:e.revision,anchor:{line:e.anchor.line,end_line:e.anchor.end_line},anchor_state:e.anchor_state,author:e.author,updated_by:e.updated_by,created_at:e.created_at,updated_at:e.updated_at,text_preview:e.text.slice(0,200)}}o(C,"commentPreview");const R=s({name:"new",description:"queue a document review with a collaborating companion agent",whenToUse:"filing a live document for a person to review alongside a companion agent.",help:{name:"human review new",summary:"queue a live document review and return its ticket handle immediately",guide:"The person reviews the live file in the attach viewer alongside a collaborating companion agent. Keep editing the file in place; approval is pushed back as an approval fact, and a changed document instructs the origin to re-read it.",params:[{kind:"positional",name:"file",type:"path",required:!0,constraint:"Document or source file to review. Resolved to an absolute path; the daemon validates that it is readable reviewable text."},{kind:"flag",name:"subtitle",type:"string",required:!0,constraint:"One plain-English sentence stating the review needed and why it matters."}],output:[{name:"review_id",type:"string",required:!0,constraint:"Daemon-owned review id for the document, comments, and companion."},{name:"review_dir",type:"string",required:!0,constraint:"Directory containing the staged review and immutable result.json."},{name:"result_path",type:"string",required:!0,constraint:"The immutable terminal result path: <review_dir>/result.json."},{name:"follow_up",type:"string",required:!0,constraint:"The review remains live for the person and companion; approval is delivered to the origin inbox."}],outputKind:"object",effects:["The daemon creates the durable review and companion session fork.","The reviewed file remains live in the attach viewer; this command does not submit an approval."]},run:o(async e=>{const t=x(e.file),n=k(e.subtitle),i=await r().createReview({origin_kind:"ticket",origin_node_id:b(),file:t,idempotency_key:q(),subtitle:n});return{review_id:i.review_id,review_dir:i.review_dir,result_path:`${i.review_dir}/result.json`,follow_up:"The review is live for the person in the attach viewer with its companion agent. Approval is pushed to the origin inbox when submitted."}},"run")}),j=s({name:"create",description:"add an anchored review comment",whenToUse:"recording a new finding or instruction against one or more document lines.",help:{name:"human review comment create",summary:"add a daemon-owned comment anchored to document lines",guide:"Without --review, the daemon targets the calling companion node's open review. Comment text is supplied on stdin; agent comments use whole-line anchors.",params:[{kind:"flag",name:"review",type:"string",required:!1,constraint:"Review id. Omit only to target the calling companion node's open review."},{kind:"flag",name:"line",type:"int",required:!0,constraint:"First 1-based document line in the anchor."},{kind:"flag",name:"end-line",type:"int",required:!1,constraint:"Last 1-based document line in the anchor. Default: --line."},{kind:"flag",name:"deliver",type:"enum",choices:["wake","quiet"],required:!1,constraint:"wake interrupts the companion now; quiet leaves a durable message for its next turn. Default: wake unless the caller is the companion."},{kind:"stdin",name:"text",required:!0,constraint:"Non-empty comment text."}],output:[{name:"comment_id",type:"string",required:!0,constraint:"Daemon-owned comment id."},{name:"status",type:"string",required:!0,constraint:"Current comment status: open, resolved, or deleted."},{name:"revision",type:"integer",required:!0,constraint:"Current per-comment revision."},{name:"author",type:"object",required:!0,constraint:"Daemon-derived human or node attribution."},{name:"text",type:"string",required:!0,constraint:"Stored full comment text."},{name:"changed",type:"boolean",required:!0,constraint:"Whether this invocation changed the comment."},{name:"delivery",type:"object | null",required:!0,constraint:"Best-effort companion notification outcome, or null when no notification was needed."}],outputKind:"object",effects:["Creates a daemon-owned comment and audit event, then best-effort notifies the companion."]},run:o(async(e,t)=>{const n=e.review,i=e.line,a=e.endLine,u=await r().createReviewComment(n,{text:v(e.text,"create"),anchor:{line:i,end_line:a??i},actor_node_id:d(),...h(e,t)===void 0?{}:{deliver:h(e,t)}});return f(u,!1)},"run")}),D=s({name:"list",description:"list review comments in render order",whenToUse:"choosing a comment to inspect or checking the current anchored comment set.",help:{name:"human review comment list",summary:"list render-ordered review comments with short text previews",guide:"Without --review, the daemon targets the calling companion node's open review. Use show for full comment text and audit history.",params:[{kind:"flag",name:"review",type:"string",required:!1,constraint:"Review id. Omit only to target the calling companion node's open review."},{kind:"flag",name:"status",type:"enum",choices:["open","resolved","deleted","all"],required:!1,default:"open",constraint:"Comment statuses to include. Default: open."}],output:[{name:"review_id",type:"string",required:!0,constraint:"Resolved daemon-owned review id."},{name:"comments",type:"object[]",required:!0,constraint:"Comments in render order with status, anchor, attribution, and a 200-character text_preview."},{name:"follow_up",type:"string",required:!0,constraint:"Points to show for full text and per-comment history."}],outputKind:"object",effects:["None. Read-only."]},run:o(async e=>{const t=e.review,n=e.status,i=await r().listReviewComments(t,t===void 0?{status:n,actor_node_id:d()}:{status:n});return{review_id:i.review_id,comments:i.comments.map(C),follow_up:"Use `crtr human review comment show <comment_id>` for full comment text and event history."}},"run")}),T=s({name:"show",description:"read one comment and its event history",whenToUse:"you need a comment's full text or complete per-comment audit history.",help:{name:"human review comment show",summary:"read one full review comment and its event history",params:[{kind:"positional",name:"comment_id",type:"string",required:!0,constraint:"Daemon-owned comment id from comment list."}],output:[{name:"comment",type:"object",required:!0,constraint:"Full daemon-owned comment projection, including text and attribution."},{name:"events",type:"object[]",required:!0,constraint:"Complete append-only event history for this comment, in event sequence order."}],outputKind:"object",effects:["None. Read-only."]},run:o(async e=>{const t=await r().getReviewComment(e.comment_id);return{comment:t.comment,events:t.events}},"run")});function c(e){const t=e==="edit";return s({name:e,description:e==="edit"?"replace a comment's text":`${e} one review comment`,whenToUse:e==="edit"?"changing the stored text of an existing open or resolved comment.":`${e==="resolve"?"marking a comment addressed":e==="reopen"?"returning a resolved comment to open":"retaining a terminal comment tombstone while removing it from active work"}.`,help:{name:`human review comment ${e}`,summary:e==="edit"?"replace a review comment's full text":`${e} a review comment`,guide:t?"Replacement comment text is supplied on stdin.":void 0,params:[{kind:"positional",name:"comment_id",type:"string",required:!0,constraint:"Daemon-owned comment id from comment list."},{kind:"flag",name:"deliver",type:"enum",choices:["wake","quiet"],required:!1,constraint:`wake interrupts the companion now; quiet leaves a durable message for its next turn. Default: ${e==="resolve"||e==="delete"?"quiet":"wake"} unless the caller is the companion.`},...t?[{kind:"stdin",name:"text",required:!0,constraint:"Non-empty replacement comment text."}]:[]],output:[{name:"comment_id",type:"string",required:!0,constraint:"Daemon-owned comment id."},{name:"status",type:"string",required:!0,constraint:"Current comment status: open, resolved, or deleted."},{name:"revision",type:"integer",required:!0,constraint:"Current per-comment revision."},{name:"anchor",type:"object",required:!0,constraint:"Current whole-line anchor."},{name:"author",type:"object",required:!0,constraint:"Daemon-derived human or node attribution."},{name:"text",type:"string",required:!0,constraint:"Stored full comment text."},{name:"changed",type:"boolean",required:!0,constraint:"Whether this invocation changed the comment."},{name:"delivery",type:"object | null",required:!0,constraint:"Best-effort companion notification outcome, or null when no notification was needed."}],outputKind:"object",effects:["Mutates the daemon-owned comment and appends an audit event when its state changes; notification is best-effort."]},run:o(async(n,i)=>{const a=n.comment_id,u=d(),p=h(n,i),m={actor_node_id:u,...p===void 0?{}:{deliver:p}},g=e==="edit"?await r().editReviewComment(a,{...m,text:v(n.text,"edit")}):e==="resolve"?await r().resolveReviewComment(a,m):e==="reopen"?await r().reopenReviewComment(a,m):await r().deleteReviewComment(a,m);return f(g)},"run")})}o(c,"commentMutationLeaf");const U=w({name:"comment",description:"create, inspect, and update daemon-owned review comments",whenToUse:"working with the anchored comments on a document review.",help:{name:"human review comment",summary:"operate on daemon-owned anchored review comments",model:"create and list address a review; all other verbs address one comment id. Create, edit, and reopen default to wake, while resolve and delete default to quiet."},children:[j,D,T,c("edit"),c("resolve"),c("reopen"),c("delete")]}),E=w({name:"review",description:"queue a document review or operate on its anchored comments",whenToUse:"a person and companion agent should collaborate on a live document review, or you need to work with that review's anchored comments.",help:{name:"human review",summary:"queue live document reviews and manage their daemon-owned comments",model:"new creates the durable review, ticket, and companion. comment contains the operations on the review's shared anchored comments."},children:[R,U]});export{E as humanReviewBranch};
1
+ var y=Object.defineProperty;var o=(e,t)=>y(e,"name",{value:t,configurable:!0});import{envNodeId as _}from"../../shared/env.js";import{randomUUID as q}from"node:crypto";import{defineBranch as w,defineLeaf as s}from"../../core/command.js";import{InputError as l}from"../../core/io.js";import{cliClient as r}from"../api-client.js";function d(){const e=_();return e===void 0||e===""?null:e}o(d,"nodeActor");function x(){const e=d();if(e!==null)return e;throw new l({error:"review_origin_unavailable",message:"a review must be filed from a node with a live or resumable session",field:"CRTR_NODE_ID",next:"Run this from the node whose document context should be forked for the review."})}o(x,"requireOriginNode");function b(e){if(typeof e=="string"&&e.trim()!=="")return e.trim();throw new l({error:"missing_parameter",message:"a non-empty subtitle is required",field:"subtitle",next:"Run `crtr human review new -h` and provide one plain-English sentence explaining the review needed and why it matters."})}o(b,"requireSubtitle");function v(e,t){if(typeof e=="string"&&e.trim()!=="")return e;throw new l({error:"missing_parameter",message:"non-empty comment text is required on stdin",field:"text",next:`Run \`crtr human review comment ${t} -h\` and pipe the comment text on stdin.`})}o(v,"requireCommentText");function h(e,t){if(!(t?.providedParams.has("deliver")??e.deliver!==void 0))return;const i=e.deliver;return i==="wake"||i==="quiet"?i:void 0}o(h,"explicitDeliver");function f(e,t=!0){const{comment:n}=e;return{comment_id:n.comment_id,status:n.status,revision:n.revision,...t?{anchor:n.anchor}:{},author:n.author,text:n.text,changed:e.changed,delivery:e.delivery??null}}o(f,"mutationOutput");function k(e){return{comment_id:e.comment_id,status:e.status,revision:e.revision,anchor:{line:e.anchor.line,end_line:e.anchor.end_line},anchor_state:e.anchor_state,author:e.author,updated_by:e.updated_by,created_at:e.created_at,updated_at:e.updated_at,text_preview:e.text.slice(0,200)}}o(k,"commentPreview");const C=s({name:"new",description:"queue a document review with a collaborating companion agent",whenToUse:"filing a live document for a person to review alongside a companion agent.",help:{name:"human review new",summary:"queue a live document review and return its ticket handle immediately",guide:"The person reviews the document in the attach viewer alongside a collaborating companion agent. The daemon reads the document as you and snapshots its current revision into the review. When the person submits, their edits are saved as a new revision of the document and the approval is pushed to your inbox. If the document gets a new revision while the review is open, the save is refused as `stale_revision` and the review stays open; do not edit a document while it is under review.",params:[{kind:"positional",name:"document",type:"string",required:!0,constraint:"The document to review: a name, `<owner>/<name>` or id, `[[ ]]` optional. It must have one head; merge several heads first."},{kind:"flag",name:"subtitle",type:"string",required:!0,constraint:"One plain-English sentence stating the review needed and why it matters."}],output:[{name:"review_id",type:"string",required:!0,constraint:"Daemon-owned review id for the document, comments, and companion."},{name:"document",type:"string",required:!0,constraint:"The reviewed document's `<owner>/<name>`."},{name:"review_dir",type:"string",required:!0,constraint:"Directory containing the review's document.md snapshot and immutable result.json."},{name:"result_path",type:"string",required:!0,constraint:"The immutable terminal result path: <review_dir>/result.json."},{name:"follow_up",type:"string",required:!0,constraint:"The review remains live for the person and companion; approval is delivered to the origin inbox."}],outputKind:"object",effects:["Records your read of the document and creates the durable review, its `document.md` snapshot, and the companion session fork.","On submit, a changed snapshot becomes a new revision authored by the person; this command does not submit an approval."]},run:o(async e=>{const t=b(e.subtitle),n=await r().createReview({origin_kind:"ticket",origin_node_id:x(),document:e.document,idempotency_key:q(),subtitle:t});return{review_id:n.review_id,document:n.document??null,review_dir:n.review_dir,result_path:`${n.review_dir}/result.json`,follow_up:"The review is live for the person in the attach viewer with its companion agent. Approval is pushed to the origin inbox when submitted."}},"run")}),R=s({name:"create",description:"add an anchored review comment",whenToUse:"recording a new finding or instruction against one or more document lines.",help:{name:"human review comment create",summary:"add a daemon-owned comment anchored to document lines",guide:"Without --review, the daemon targets the calling companion node's open review. Comment text is supplied on stdin; agent comments use whole-line anchors.",params:[{kind:"flag",name:"review",type:"string",required:!1,constraint:"Review id. Omit only to target the calling companion node's open review."},{kind:"flag",name:"line",type:"int",required:!0,constraint:"First 1-based document line in the anchor."},{kind:"flag",name:"end-line",type:"int",required:!1,constraint:"Last 1-based document line in the anchor. Default: --line."},{kind:"flag",name:"deliver",type:"enum",choices:["wake","quiet"],required:!1,constraint:"wake interrupts the companion now; quiet leaves a durable message for its next turn. Default: wake unless the caller is the companion."},{kind:"stdin",name:"text",required:!0,constraint:"Non-empty comment text."}],output:[{name:"comment_id",type:"string",required:!0,constraint:"Daemon-owned comment id."},{name:"status",type:"string",required:!0,constraint:"Current comment status: open, resolved, or deleted."},{name:"revision",type:"integer",required:!0,constraint:"Current per-comment revision."},{name:"author",type:"object",required:!0,constraint:"Daemon-derived human or node attribution."},{name:"text",type:"string",required:!0,constraint:"Stored full comment text."},{name:"changed",type:"boolean",required:!0,constraint:"Whether this invocation changed the comment."},{name:"delivery",type:"object | null",required:!0,constraint:"Best-effort companion notification outcome, or null when no notification was needed."}],outputKind:"object",effects:["Creates a daemon-owned comment and audit event, then best-effort notifies the companion."]},run:o(async(e,t)=>{const n=e.review,i=e.line,a=e.endLine,u=await r().createReviewComment(n,{text:v(e.text,"create"),anchor:{line:i,end_line:a??i},actor_node_id:d(),...h(e,t)===void 0?{}:{deliver:h(e,t)}});return f(u,!1)},"run")}),j=s({name:"list",description:"list review comments in render order",whenToUse:"choosing a comment to inspect or checking the current anchored comment set.",help:{name:"human review comment list",summary:"list render-ordered review comments with short text previews",guide:"Without --review, the daemon targets the calling companion node's open review. Use show for full comment text and audit history.",params:[{kind:"flag",name:"review",type:"string",required:!1,constraint:"Review id. Omit only to target the calling companion node's open review."},{kind:"flag",name:"status",type:"enum",choices:["open","resolved","deleted","all"],required:!1,default:"open",constraint:"Comment statuses to include. Default: open."}],output:[{name:"review_id",type:"string",required:!0,constraint:"Resolved daemon-owned review id."},{name:"comments",type:"object[]",required:!0,constraint:"Comments in render order with status, anchor, attribution, and a 200-character text_preview."},{name:"follow_up",type:"string",required:!0,constraint:"Points to show for full text and per-comment history."}],outputKind:"object",effects:["None. Read-only."]},run:o(async e=>{const t=e.review,n=e.status,i=await r().listReviewComments(t,t===void 0?{status:n,actor_node_id:d()}:{status:n});return{review_id:i.review_id,comments:i.comments.map(k),follow_up:"Use `crtr human review comment show <comment_id>` for full comment text and event history."}},"run")}),T=s({name:"show",description:"read one comment and its event history",whenToUse:"you need a comment's full text or complete per-comment audit history.",help:{name:"human review comment show",summary:"read one full review comment and its event history",params:[{kind:"positional",name:"comment_id",type:"string",required:!0,constraint:"Daemon-owned comment id from comment list."}],output:[{name:"comment",type:"object",required:!0,constraint:"Full daemon-owned comment projection, including text and attribution."},{name:"events",type:"object[]",required:!0,constraint:"Complete append-only event history for this comment, in event sequence order."}],outputKind:"object",effects:["None. Read-only."]},run:o(async e=>{const t=await r().getReviewComment(e.comment_id);return{comment:t.comment,events:t.events}},"run")});function c(e){const t=e==="edit";return s({name:e,description:e==="edit"?"replace a comment's text":`${e} one review comment`,whenToUse:e==="edit"?"changing the stored text of an existing open or resolved comment.":`${e==="resolve"?"marking a comment addressed":e==="reopen"?"returning a resolved comment to open":"retaining a terminal comment tombstone while removing it from active work"}.`,help:{name:`human review comment ${e}`,summary:e==="edit"?"replace a review comment's full text":`${e} a review comment`,guide:t?"Replacement comment text is supplied on stdin.":void 0,params:[{kind:"positional",name:"comment_id",type:"string",required:!0,constraint:"Daemon-owned comment id from comment list."},{kind:"flag",name:"deliver",type:"enum",choices:["wake","quiet"],required:!1,constraint:`wake interrupts the companion now; quiet leaves a durable message for its next turn. Default: ${e==="resolve"||e==="delete"?"quiet":"wake"} unless the caller is the companion.`},...t?[{kind:"stdin",name:"text",required:!0,constraint:"Non-empty replacement comment text."}]:[]],output:[{name:"comment_id",type:"string",required:!0,constraint:"Daemon-owned comment id."},{name:"status",type:"string",required:!0,constraint:"Current comment status: open, resolved, or deleted."},{name:"revision",type:"integer",required:!0,constraint:"Current per-comment revision."},{name:"anchor",type:"object",required:!0,constraint:"Current whole-line anchor."},{name:"author",type:"object",required:!0,constraint:"Daemon-derived human or node attribution."},{name:"text",type:"string",required:!0,constraint:"Stored full comment text."},{name:"changed",type:"boolean",required:!0,constraint:"Whether this invocation changed the comment."},{name:"delivery",type:"object | null",required:!0,constraint:"Best-effort companion notification outcome, or null when no notification was needed."}],outputKind:"object",effects:["Mutates the daemon-owned comment and appends an audit event when its state changes; notification is best-effort."]},run:o(async(n,i)=>{const a=n.comment_id,u=d(),p=h(n,i),m={actor_node_id:u,...p===void 0?{}:{deliver:p}},g=e==="edit"?await r().editReviewComment(a,{...m,text:v(n.text,"edit")}):e==="resolve"?await r().resolveReviewComment(a,m):e==="reopen"?await r().reopenReviewComment(a,m):await r().deleteReviewComment(a,m);return f(g)},"run")})}o(c,"commentMutationLeaf");const D=w({name:"comment",description:"create, inspect, and update daemon-owned review comments",whenToUse:"working with the anchored comments on a document review.",help:{name:"human review comment",summary:"operate on daemon-owned anchored review comments",model:"create and list address a review; all other verbs address one comment id. Create, edit, and reopen default to wake, while resolve and delete default to quiet."},children:[R,j,T,c("edit"),c("resolve"),c("reopen"),c("delete")]}),K=w({name:"review",description:"queue a document review or operate on its anchored comments",whenToUse:"a person and companion agent should collaborate on a live document review, or you need to work with that review's anchored comments.",help:{name:"human review",summary:"queue live document reviews and manage their daemon-owned comments",model:"new creates the durable review, ticket, and companion. comment contains the operations on the review's shared anchored comments."},children:[C,D]});export{K as humanReviewBranch};
@@ -1,5 +1,5 @@
1
- var w=Object.defineProperty;var r=(e,t)=>w(e,"name",{value:t,configurable:!0});import{envNodeId as _}from"../../shared/env.js";import{defineBranch as k,defineLeaf as c}from"../../core/command.js";import{InputError as s}from"../../core/io.js";import{existsSync as m}from"node:fs";import{activeBackgroundBashJobs as j,backgroundRunningBashJobs as y,bashJobCommandLine as x,bashJobPaths as v,clearJobDeadline as $,formatBashElapsed as g,isSafeBashJobId as f,jobStartedAtMs as I,readJobPgid as D,tailJobLog as T,writeJobDeadlineMs as M}from"../../core/bash-jobs.js";import{parseDurationMs as q}from"../../core/wake.js";import{cliClient as N,getNodeOrNull as h}from"../api-client.js";import{nodeInPane as E}from"../surface/node/placement.js";function u(e,t){const n=e.pane??process.env.TMUX_PANE;let o=e.node;if((o===void 0||o==="")&&(o=E(n)),(o===void 0||o==="")&&(o=_()),o===void 0||o==="")throw new s({error:"no_node",message:t,next:"Run from inside a node, pass --node <id>, or --pane <pane>."});return{id:o,pane:n}}r(u,"resolveBashTarget");const B=c({name:"background",description:"hand the focused node\u2019s running bash command to its background job system",whenToUse:"the tmux prefix menu invokes this when a foreground bash command should stop blocking the agent immediately but keep running as a file-backed background job",tier:"hidden",help:{name:"node bash background",summary:"hand every running foreground bash command for one node to its file-backed background job system",params:[{kind:"flag",name:"node",type:"string",required:!1,constraint:"Node whose running bash command to background. Defaults to the node in --pane, else the caller (CRTR_NODE_ID)."},{kind:"flag",name:"pane",type:"string",required:!1,constraint:"tmux pane showing the target node, when --node is omitted. Defaults to $TMUX_PANE. The prefix menu passes this for you."}],output:[{name:"node_id",type:"string",required:!0,constraint:"The target node."},{name:"jobs",type:"object[]",required:!0,constraint:"Per handed-off job {job_id, elapsed_ms} \u2014 the foreground elapsed time in milliseconds at the moment of handoff."}],outputKind:"object",effects:["Signals every foreground bash command currently running for the node to return from its tool call immediately while its detached supervisor keeps the command running.","Each handed-off command retains its log under the node context jobs directory and sends the node an urgent inbox message when it exits, which steers the node mid-turn."]},run:r(async e=>{const{id:t}=u(e,"no node found for the bash handoff"),n=await h(t);if(n===null)throw new s({error:"not_found",message:`no node: ${t}`,next:"List nodes with `crtr node inspect list`."});const o=y(n.paths.context_dir),d=Date.now(),i=o.map(l=>({job_id:l.jobId,elapsed_ms:d-(I(l.jobId)??d)}));return{node_id:t,jobs:i}},"run"),render:r(e=>{const t=e.jobs;if(t.length===0)return`No foreground bash command is running for ${e.node_id}.`;const n=Math.max(...t.map(o=>o.elapsed_ms));return`Handed ${t.length} bash command(s) to the background job system for ${e.node_id} \xB7 ran ${g(n)} in the foreground.`},"render")}),R=c({name:"list",description:"list a node\u2019s live background bash jobs",whenToUse:"the Inspector\u2019s jobs section reads this to show what a node handed off to the background",tier:"hidden",help:{name:"node bash list",summary:"read every still-running background bash job owned by one node",params:[{kind:"flag",name:"node",type:"string",required:!1,constraint:"Node whose background jobs to list. Defaults to the node in --pane, else the caller (CRTR_NODE_ID)."},{kind:"flag",name:"pane",type:"string",required:!1,constraint:"tmux pane showing the target node, when --node is omitted. Defaults to $TMUX_PANE."},{kind:"flag",name:"tail",type:"int",required:!1,default:0,constraint:"Include this many trailing log lines per job (0 = none)."}],output:[{name:"node_id",type:"string",required:!0,constraint:"The target node."},{name:"jobs",type:"object[]",required:!0,constraint:"Oldest-first {job_id, command, purpose, started_at, elapsed_ms, deadline_at, log_path, pgid, log_tail} for every live background job; purpose is a human-readable label or null; deadline_at is the auto-cancel time or null for a job that runs to completion."}],outputKind:"object",effects:["Reads the node context jobs directory. Changes nothing."]},run:r(async e=>{const{id:t}=u(e,"no node found for the bash job list"),n=await h(t);if(n===null)throw new s({error:"not_found",message:`no node: ${t}`,next:"List nodes with `crtr node inspect list`."});const o=Date.now(),d=Math.max(0,Math.trunc(Number(e.tail??0))),i=n.paths.context_dir,l=j(i).map(a=>({job_id:a.jobId,command:a.command,purpose:a.purpose,started_at:new Date(a.startedAtMs).toISOString(),elapsed_ms:o-a.startedAtMs,deadline_at:a.deadlineAtMs===null?null:new Date(a.deadlineAtMs).toISOString(),log_path:a.logPath,pgid:D(i,a.jobId)??null,log_tail:T(a.logPath,d)}));return{node_id:n.node_id,jobs:l}},"run"),render:r(e=>{const t=e.jobs;return t.length===0?`No background bash jobs for ${e.node_id}.`:t.map(n=>`${n.job_id} \xB7 ${g(n.elapsed_ms)} \xB7 ${x(n.command)}${n.purpose===null?"":`
1
+ var w=Object.defineProperty;var r=(e,t)=>w(e,"name",{value:t,configurable:!0});import{envNodeId as _}from"../../shared/env.js";import{defineBranch as j,defineLeaf as c}from"../../core/command.js";import{InputError as s}from"../../core/io.js";import{existsSync as m}from"node:fs";import{activeBackgroundBashJobs as k,backgroundRunningBashJobs as y,bashJobCommandLine as x,bashJobPaths as v,clearJobDeadline as $,formatBashElapsed as g,isSafeBashJobId as f,jobStartedAtMs as I,readJobPgid as D,tailJobLog as T,writeJobDeadlineMs as M}from"../../core/bash-jobs.js";import{parseDurationMs as q}from"../../core/wake.js";import{cliClient as N,getNodeOrNull as h}from"../api-client.js";import{nodeInPane as E}from"../surface/node/placement.js";function u(e,t){const n=e.pane??process.env.TMUX_PANE;let o=e.node;if((o===void 0||o==="")&&(o=E(n)),(o===void 0||o==="")&&(o=_()),o===void 0||o==="")throw new s({error:"no_node",message:t,next:"Run from inside a node, pass --node <id>, or --pane <pane>."});return{id:o,pane:n}}r(u,"resolveBashTarget");const B=c({name:"background",description:"hand the focused node\u2019s running bash command to its background job system",whenToUse:"the tmux prefix menu invokes this when a foreground bash command should stop blocking the agent immediately but keep running as a file-backed background job",tier:"hidden",help:{name:"node bash background",summary:"hand every running foreground bash command for one node to its file-backed background job system",params:[{kind:"flag",name:"node",type:"string",required:!1,constraint:"Node whose running bash command to background. Defaults to the node in --pane, else the caller (CRTR_NODE_ID)."},{kind:"flag",name:"pane",type:"string",required:!1,constraint:"tmux pane showing the target node, when --node is omitted. Defaults to $TMUX_PANE. The prefix menu passes this for you."}],output:[{name:"node_id",type:"string",required:!0,constraint:"The target node."},{name:"jobs",type:"object[]",required:!0,constraint:"Per handed-off job {job_id, elapsed_ms} \u2014 the foreground elapsed time in milliseconds at the moment of handoff."}],outputKind:"object",effects:["Signals every foreground bash command currently running for the node to return from its tool call immediately while its detached supervisor keeps the command running.","Each handed-off command retains its log under the node context jobs directory and becomes a job object; when it exits, the job\u2019s watchers (its node by default) get an urgent `exited` push carrying the exit code and log path."]},run:r(async e=>{const{id:t}=u(e,"no node found for the bash handoff"),n=await h(t);if(n===null)throw new s({error:"not_found",message:`no node: ${t}`,next:"List nodes with `crtr node inspect list`."});const o=y(n.paths.context_dir),d=Date.now(),i=o.map(l=>({job_id:l.jobId,elapsed_ms:d-(I(l.jobId)??d)}));return{node_id:t,jobs:i}},"run"),render:r(e=>{const t=e.jobs;if(t.length===0)return`No foreground bash command is running for ${e.node_id}.`;const n=Math.max(...t.map(o=>o.elapsed_ms));return`Handed ${t.length} bash command(s) to the background job system for ${e.node_id} \xB7 ran ${g(n)} in the foreground.`},"render")}),R=c({name:"list",description:"list a node\u2019s live background bash jobs",whenToUse:"the Inspector\u2019s jobs section reads this to show what a node handed off to the background",tier:"hidden",help:{name:"node bash list",summary:"read every still-running background bash job owned by one node",params:[{kind:"flag",name:"node",type:"string",required:!1,constraint:"Node whose background jobs to list. Defaults to the node in --pane, else the caller (CRTR_NODE_ID)."},{kind:"flag",name:"pane",type:"string",required:!1,constraint:"tmux pane showing the target node, when --node is omitted. Defaults to $TMUX_PANE."},{kind:"flag",name:"tail",type:"int",required:!1,default:0,constraint:"Include this many trailing log lines per job (0 = none)."}],output:[{name:"node_id",type:"string",required:!0,constraint:"The target node."},{name:"jobs",type:"object[]",required:!0,constraint:"Oldest-first {job_id, command, purpose, started_at, elapsed_ms, deadline_at, log_path, pgid, log_tail} for every live background job; purpose is a human-readable label or null; deadline_at is the auto-cancel time or null for a job that runs to completion."}],outputKind:"object",effects:["Reads the node context jobs directory. Changes nothing."]},run:r(async e=>{const{id:t}=u(e,"no node found for the bash job list"),n=await h(t);if(n===null)throw new s({error:"not_found",message:`no node: ${t}`,next:"List nodes with `crtr node inspect list`."});const o=Date.now(),d=Math.max(0,Math.trunc(Number(e.tail??0))),i=n.paths.context_dir,l=k(i).map(a=>({job_id:a.jobId,command:a.command,purpose:a.purpose,started_at:new Date(a.startedAtMs).toISOString(),elapsed_ms:o-a.startedAtMs,deadline_at:a.deadlineAtMs===null?null:new Date(a.deadlineAtMs).toISOString(),log_path:a.logPath,pgid:D(i,a.jobId)??null,log_tail:T(a.logPath,d)}));return{node_id:n.node_id,jobs:l}},"run"),render:r(e=>{const t=e.jobs;return t.length===0?`No background bash jobs for ${e.node_id}.`:t.map(n=>`${n.job_id} \xB7 ${g(n.elapsed_ms)} \xB7 ${x(n.command)}${n.purpose===null?"":`
2
2
  purpose: ${n.purpose}`}${n.deadline_at===null?"":`
3
3
  cancels at: ${n.deadline_at}`}
4
4
  log: ${n.log_path}`).join(`
5
- `)},"render")}),S=c({name:"kill",description:"stop one of a node\u2019s background bash jobs",whenToUse:"the user watching the Inspector or the attach cockpit\u2019s bash row wants a long-running background job to stop now, or an agent is abandoning work it backgrounded",tier:"hidden",help:{name:"node bash kill",summary:"best-effort stop a background bash job and tell the owning node it was canceled",params:[{kind:"positional",name:"job",required:!0,constraint:"Job id, as reported by node bash list."},{kind:"flag",name:"node",type:"string",required:!1,constraint:"Node owning the job. Defaults to the node in --pane, else the caller (CRTR_NODE_ID)."},{kind:"flag",name:"pane",type:"string",required:!1,constraint:"tmux pane showing the target node, when --node is omitted. Defaults to $TMUX_PANE."}],output:[{name:"node_id",type:"string",required:!0,constraint:"The owning node."},{name:"signaled",type:"boolean",required:!0,constraint:"True when at least one observed supervisor-tree process received SIGTERM."}],outputKind:"object",effects:["Snapshots the supervisor\u2019s observable process tree, sends SIGTERM then SIGKILL to those processes, and cannot stop a descendant that detached and reparented before the snapshot.","Records the job\u2019s exit so it stops showing as live, and sends the owning node an urgent inbox message saying the job was stopped \u2014 which steers that node mid-turn."]},run:r(async e=>{const{id:t}=u(e,"no node found for the bash job"),n=await h(t);if(n===null)throw new s({error:"not_found",message:`no node: ${t}`,next:"List nodes with `crtr node inspect list`."});const o=e.job;if(!f(o))throw new s({error:"invalid_job_id",message:`invalid background job id: ${o}`,next:"Use a job id reported by `crtr node bash list`."});const d=await N().stopBashJob(n.node_id,o);return{node_id:n.node_id,signaled:d.signaled}},"run"),render:r(e=>`Canceled a background bash job for ${e.node_id}${e.signaled===!0?"":" (no observed job process was still running)"}.`,"render")}),L=c({name:"extend",description:"claim an auto-backgrounded bash job so the daemon does not cancel it at its deadline",whenToUse:"a command the valve auto-backgrounded is worth keeping past its cancel deadline \u2014 give it more time with --for, or let it run to completion with --keep",tier:"hidden",help:{name:"node bash extend",summary:"move or clear the auto-cancel deadline on one of a node\u2019s live background bash jobs",params:[{kind:"positional",name:"job",required:!0,constraint:"Job id, as reported by node bash list or the auto-background notice."},{kind:"flag",name:"for",type:"string",required:!1,constraint:"New window from now before the job is canceled, as a duration (e.g. 90s, 10m, 1h30m). Mutually exclusive with --keep; give exactly one."},{kind:"flag",name:"keep",type:"bool",required:!1,constraint:"Remove the deadline entirely so the job runs to completion. Mutually exclusive with --for; give exactly one."},{kind:"flag",name:"node",type:"string",required:!1,constraint:"Node owning the job. Defaults to the node in --pane, else the caller (CRTR_NODE_ID)."},{kind:"flag",name:"pane",type:"string",required:!1,constraint:"tmux pane showing the target node, when --node is omitted. Defaults to $TMUX_PANE."}],output:[{name:"node_id",type:"string",required:!0,constraint:"The owning node."},{name:"deadline_at",type:"string",required:!1,constraint:"The job\u2019s new auto-cancel time (ISO), or absent when --keep cleared the deadline so the job runs to completion."}],outputKind:"object",effects:["Rewrites or removes the job\u2019s deadline file. The daemon\u2019s deadline lane reads it on its next scan. Signals nothing."]},run:r(async e=>{const{id:t}=u(e,"no node found for the bash job"),n=await h(t);if(n===null)throw new s({error:"not_found",message:`no node: ${t}`,next:"List nodes with `crtr node inspect list`."});const o=e.job;if(!f(o))throw new s({error:"invalid_job_id",message:`invalid background job id: ${o}`,next:"Use a job id reported by `crtr node bash list`."});const d=e.for,i=e.keep===!0;if(i===(d!==void 0&&d!==""))throw new s({error:"invalid_args",message:"give exactly one of --for <duration> or --keep",next:"Extend by a window with --for 10m, or drop the deadline with --keep."});const l=n.paths.context_dir,a=v(l,o);if(!m(a.dir))throw new s({error:"not_found",message:`no background job ${o} for ${n.node_id}`,next:"List live jobs with `crtr node bash list`."});if(!m(a.jobBg)||m(a.jobExit))throw new s({error:"not_running",message:`background job ${o} for ${n.node_id} is no longer running`,next:`It finished or was stopped, so there is no deadline to change. Log: ${a.jobLog}`});if(i)return $(a),{node_id:n.node_id};const b=q(d);if(b===null||b<=0)throw new s({error:"invalid_duration",message:`not a duration: ${d}`,next:"Use a value like 90s, 10m, or 1h30m."});const p=Date.now()+b;return M(a,p),{node_id:n.node_id,deadline_at:new Date(p).toISOString()}},"run"),render:r(e=>e.deadline_at===void 0?`Cleared the deadline \u2014 the background job will run to completion for ${e.node_id}.`:`Background job for ${e.node_id} will be canceled at ${e.deadline_at} unless extended again.`,"render")}),H=k({name:"bash",description:"control file-backed bash jobs owned by a node",whenToUse:"a running bash command must be handed off, listed, stopped, or its auto-cancel deadline changed \u2014 the tmux prefix menu backgrounds, the Inspector\u2019s jobs section lists and cancels",tier:"hidden",help:{name:"node bash",summary:"control file-backed bash jobs owned by a node"},children:[B,R,S,L]});export{H as nodeBash};
5
+ `)},"render")}),S=c({name:"kill",description:"stop one of a node\u2019s background bash jobs",whenToUse:"the user watching the Inspector or the attach cockpit\u2019s bash row wants a long-running background job to stop now, or an agent is abandoning work it backgrounded",tier:"hidden",help:{name:"node bash kill",summary:"best-effort stop a background bash job and tell the owning node it was canceled",params:[{kind:"positional",name:"job",required:!0,constraint:"Job id, as reported by node bash list."},{kind:"flag",name:"node",type:"string",required:!1,constraint:"Node owning the job. Defaults to the node in --pane, else the caller (CRTR_NODE_ID)."},{kind:"flag",name:"pane",type:"string",required:!1,constraint:"tmux pane showing the target node, when --node is omitted. Defaults to $TMUX_PANE."}],output:[{name:"node_id",type:"string",required:!0,constraint:"The owning node."},{name:"signaled",type:"boolean",required:!0,constraint:"True when at least one observed supervisor-tree process received SIGTERM."}],outputKind:"object",effects:["Snapshots the supervisor\u2019s observable process tree, sends SIGTERM then SIGKILL to those processes, and cannot stop a descendant that detached and reparented before the snapshot.","Records the job\u2019s exit so it stops showing as live, and ends its job object as killed; the job\u2019s watchers (its node by default) get an urgent `killed` push."]},run:r(async e=>{const{id:t}=u(e,"no node found for the bash job"),n=await h(t);if(n===null)throw new s({error:"not_found",message:`no node: ${t}`,next:"List nodes with `crtr node inspect list`."});const o=e.job;if(!f(o))throw new s({error:"invalid_job_id",message:`invalid background job id: ${o}`,next:"Use a job id reported by `crtr node bash list`."});const d=await N().stopBashJob(n.node_id,o);return{node_id:n.node_id,signaled:d.signaled}},"run"),render:r(e=>`Canceled a background bash job for ${e.node_id}${e.signaled===!0?"":" (no observed job process was still running)"}.`,"render")}),L=c({name:"extend",description:"claim an auto-backgrounded bash job so the daemon does not cancel it at its deadline",whenToUse:"a command the valve auto-backgrounded is worth keeping past its cancel deadline \u2014 give it more time with --for, or let it run to completion with --keep",tier:"hidden",help:{name:"node bash extend",summary:"move or clear the auto-cancel deadline on one of a node\u2019s live background bash jobs",params:[{kind:"positional",name:"job",required:!0,constraint:"Job id, as reported by node bash list or the auto-background notice."},{kind:"flag",name:"for",type:"string",required:!1,constraint:"New window from now before the job is canceled, as a duration (e.g. 90s, 10m, 1h30m). Mutually exclusive with --keep; give exactly one."},{kind:"flag",name:"keep",type:"bool",required:!1,constraint:"Remove the deadline entirely so the job runs to completion. Mutually exclusive with --for; give exactly one."},{kind:"flag",name:"node",type:"string",required:!1,constraint:"Node owning the job. Defaults to the node in --pane, else the caller (CRTR_NODE_ID)."},{kind:"flag",name:"pane",type:"string",required:!1,constraint:"tmux pane showing the target node, when --node is omitted. Defaults to $TMUX_PANE."}],output:[{name:"node_id",type:"string",required:!0,constraint:"The owning node."},{name:"deadline_at",type:"string",required:!1,constraint:"The job\u2019s new auto-cancel time (ISO), or absent when --keep cleared the deadline so the job runs to completion."}],outputKind:"object",effects:["Rewrites or removes the job\u2019s deadline file. The daemon\u2019s deadline lane reads it on its next scan. Signals nothing."]},run:r(async e=>{const{id:t}=u(e,"no node found for the bash job"),n=await h(t);if(n===null)throw new s({error:"not_found",message:`no node: ${t}`,next:"List nodes with `crtr node inspect list`."});const o=e.job;if(!f(o))throw new s({error:"invalid_job_id",message:`invalid background job id: ${o}`,next:"Use a job id reported by `crtr node bash list`."});const d=e.for,i=e.keep===!0;if(i===(d!==void 0&&d!==""))throw new s({error:"invalid_args",message:"give exactly one of --for <duration> or --keep",next:"Extend by a window with --for 10m, or drop the deadline with --keep."});const l=n.paths.context_dir,a=v(l,o);if(!m(a.dir))throw new s({error:"not_found",message:`no background job ${o} for ${n.node_id}`,next:"List live jobs with `crtr node bash list`."});if(!m(a.jobBg)||m(a.jobExit))throw new s({error:"not_running",message:`background job ${o} for ${n.node_id} is no longer running`,next:`It finished or was stopped, so there is no deadline to change. Log: ${a.jobLog}`});if(i)return $(a),{node_id:n.node_id};const b=q(d);if(b===null||b<=0)throw new s({error:"invalid_duration",message:`not a duration: ${d}`,next:"Use a value like 90s, 10m, or 1h30m."});const p=Date.now()+b;return M(a,p),{node_id:n.node_id,deadline_at:new Date(p).toISOString()}},"run"),render:r(e=>e.deadline_at===void 0?`Cleared the deadline \u2014 the background job will run to completion for ${e.node_id}.`:`Background job for ${e.node_id} will be canceled at ${e.deadline_at} unless extended again.`,"render")}),H=j({name:"bash",description:"control file-backed bash jobs owned by a node",whenToUse:"a running bash command must be handed off, listed, stopped, or its auto-cancel deadline changed \u2014 the tmux prefix menu backgrounds, the Inspector\u2019s jobs section lists and cancels",tier:"hidden",help:{name:"node bash",summary:"control file-backed bash jobs owned by a node"},children:[B,R,S,L]});export{H as nodeBash};
@@ -1,2 +1,2 @@
1
- var $=Object.defineProperty;var a=(e,n)=>$(e,"name",{value:n,configurable:!0});import{envNodeId as z}from"../../shared/env.js";import{defineLeaf as B}from"../../core/command.js";import{InputError as o}from"../../core/io.js";import{requireScope as W}from"../../core/errors.js";import{mark as x}from"../../core/timing.js";import{OUTPUT_SCHEMA_CONSTRAINTS as J,OUTPUT_SCHEMA_TRANSPORT as Y,parseOutputSchemaValue as G}from"../../core/runtime/structured-output.js";import{MODEL_SPEC_FORMS as K,MODEL_SPEC_NEXT as V}from"../../core/runtime/model-selection.js";import{parseDurationMs as X}from"../../core/wake.js";import{kindsStateBlock as Q,profilesStateBlock as Z,resolveProfileViaDaemon as ee}from"../profile/daemon-profiles.js";import{cliClient as te,getNodeOrNull as oe,rethrowAsCliError as ne}from"../api-client.js";import{inTmux as R}from"../../core/runtime/placement-tmux.js";import{openSpawnViewer as re}from"../surface/node/placement.js";import{resolve as ie}from"node:path";const ae=1e5,M='--output-schema writes the accepted result to the node\u2019s context/result.json and pushes it as the node\u2019s final report; a node that cannot honestly satisfy the schema declines with `crtr push result --decline "<reason>" --code <token>`, which finishes it with a failure outcome (reason `declined`) whose `declined` field carries { reason, code, retryable } and no result. Name the code tokens you accept in the prompt; crouter carries the code verbatim and you validate it.',j=["A resident is told never to push a final, so the spawner gets no report wake when the work is done.","If it stops mid-task, nothing prompts it to continue.","After 15 unattended minutes it is concluded with only a deferred update, which does not wake the spawner."],L="To re-task a terminal child after its final, use `crtr node message send --reopen`; to make an existing node resident later, use `crtr node config --lifecycle resident`.",q="Do not wait, poll, or duplicate this child's assignment in your own context \u2014 there is no result to await, stopping will not strand you, and duplicated work wastes the delegated context. You're auto-subscribed, so its finish wakes you on its own. Two moves only: do other independent work right now, or stop and end your turn \u2014 the wake brings you back.";async function se(e){if(e===void 0||e==="")return q;const n=await oe(e);if(n===null||n.mode!=="orchestrator")return q;const t=n.context_tokens??null;return t===null||t<ae?q:`Child spawned \u2014 you're auto-subscribed, so its finish wakes you on its own; never wait, poll, or duplicate the child's assignment in your own context. But you're an orchestrator already carrying ~${Math.round(t/1e3)}k of context: rather than hold this window open for the result, checkpoint context/roadmap.md and \`crtr node yield\` now. Yielding ends this turn, and the child's completion then revives you fresh against your roadmap \u2014 let that clean revive absorb the result instead of this bloated context.`}a(se,"childFollowUp");function de(){return[{kind:"stdin",name:"prompt",required:!1,constraint:"First user message."},{kind:"flag",name:"kind",type:"string",required:!1,constraint:"Persona kind. Defaults to the profile's default kind; `general` when the profile has none. The <kinds> list below names every top-level installable kind."},{kind:"flag",name:"mode",type:"enum",choices:["base","orchestrator"],required:!1,default:"base",constraint:"Persona mode. base for one hands-on unit; orchestrator when the unit itself needs decomposition across children. When you hold several parallel units of one kind, spawn ONE orchestrator child of that kind to own the fan-out rather than spawning the units yourself \u2014 unless you are already an orchestrator of that kind, whose job is exactly that fan-out."},{kind:"flag",name:"cwd",type:"path",required:!1,constraint:"Pin the node to this directory. Defaults to the spawner\u2019s directory for a managed child, and to the directory this command runs in for a root."},{kind:"flag",name:"name",type:"string",required:!1,constraint:"Display name."},{kind:"flag",name:"parent",type:"string",required:!1,constraint:"Parent node id. Defaults to the calling node."},{kind:"flag",name:"node-id",type:"string",required:!1,constraint:"Exact id for the new node; lowercase letters, digits, and hyphens only, at most 128 characters. Rejected if already present."},{kind:"flag",name:"root",type:"bool",required:!1,constraint:"Spawn an independent root with no parent or subscription. Mutually exclusive with --worktree."},{kind:"flag",name:"resident",type:"bool",required:!1,constraint:"Birth the node as resident, a conversation partner for a person rather than a worker. Omit unless the user explicitly asks for a node they can talk with back and forth.",focusedHelp:{whenToUse:"Pass it ONLY when the user explicitly asks for a node they can talk with back and forth. Never infer it from task size, from expecting the user to look at the node, or from wanting to follow up later. "+L,value:"Boolean. Roots default to resident, so with --root it changes nothing. Managed children default to terminal; for a managed child the command first refuses, explains the cost, and says how to confirm.",effects:[...j,"It ends by going dormant or by being closed."]}},{kind:"flag",name:"terminal",type:"bool",required:!1,constraint:"Birth the node as terminal: it finalizes when its work is done and leaves nothing wakeable behind. Only meaningful with --root, whose default is resident; managed children are already terminal. Pass it for bounded programmatic work no one will open a viewer on. Mutually exclusive with --resident."},{kind:"flag",name:"force",type:"bool",required:!1,default:!1,hidden:!0,constraint:"Confirm a resident managed child (--resident without --root) after the user explicitly asked for one."},{kind:"flag",name:"worktree",type:"string",required:!1,constraint:"Local base branch for a crouter-managed worktree for this child. Mutually exclusive with --root."},{kind:"flag",name:"fork-from",type:"string",required:!1,constraint:"Fork the new node from an existing node id, absolute session path, or partial pi session uuid."},{kind:"flag",name:"model",type:"string",required:!1,constraint:"Durable model override. Omit unless the task or governing guidance requires a specific model.",focusedHelp:{whenToUse:"Use only when the user, task, or governing agent guidance already requires a specific provider, model, capability tier, or family. Omit it for routine delegation: the selected persona kind and profile own normal model selection, and review personas may enforce a quality floor.",value:`Accepted forms: ${K}. Concrete provider/id requests must be registered. Portable provider/tier, bare-tier, and family-alias requests resolve through the configured model ladders.`,effects:["The resolved concrete model is stored on the node and in its launch recipe.","A concrete provider/id runs exactly that model on every launch and revive. A portable provider/tier, bare tier, or family alias keeps its tier: when that model's provider is rate-limited or rejects its credential, the node moves to the next model in its route: another provider's model at the same tier first, then the configured fallback tier (strong by default).","A persona quality floor may raise a weaker request before its resolved model is stored."]}},{kind:"flag",name:"profile",type:"string",required:!1,constraint:"Profile override. Use only when the task needs different project purview, memory, or environment; otherwise omit for normal inheritance or selection.",focusedHelp:{whenToUse:"Use only when the work must run under a different profile because it needs that profile\u2019s project purview, memory store, stored environment, or defaults. Do not use it merely to change the working directory, persona kind, model, or graph ownership; those have separate parameters.",value:"Pass a profile id or name from the <profiles> list below; a close-enough name resolves to the nearest profile and the choice is reported on stderr. Omit it inside a node to inherit the caller\u2019s profile; outside a node, omission runs normal cwd-based profile selection.",effects:["The selected profile id is stored on the node and supplies its profile-scoped config, memory, environment, and injected bearings.","It does not change the parent or subscription; --cwd separately pins the working directory."]}},{kind:"flag",name:"situational-context",type:"string",required:!1,constraint:"Hidden ambient context appended to injected bearings, never visible chat."},{kind:"flag",name:"no-kickoff",type:"bool",required:!1,hidden:!0,constraint:"With --root, start the broker and session idle without a first message or model turn. Mutually exclusive with a prompt and --mode orchestrator."},{kind:"flag",name:"deadline",type:"string",required:!1,constraint:"Positive, representable wall-clock duration from spawn. Grammar: one or more positive integer-unit parts; units: s, m, h, d; adjacent parts are summed. Omit for no deadline."},{kind:"flag",name:"outcome-action",type:"string",required:!1,constraint:"Declared human-action name that receives the node outcome. Grammar: 1\u2013128 ASCII letters, digits, dots, underscores, or hyphens; starts with a letter or digit. Omit when no external delivery target is needed."},{kind:"flag",name:"outcome-payload",type:"string",required:!1,constraint:"JSON value, serialized at most 64 KiB, frozen and echoed to --outcome-action. Requires --outcome-action."},{kind:"flag",name:"output-schema",type:"string",required:!1,constraint:"Replace prose completion with a schema-constrained final result. Use only when its consumer requires fixed fields.",focusedHelp:{whenToUse:"Use only when the node\u2019s consumer needs a machine-checkable final object with fixed fields. Omit it for ordinary delegation or when a prose report is enough; structured output replaces the completion protocol, and an over-constrained schema can force retries.",value:`Pass the JSON Schema the result must satisfy. ${Y} ${J}`,effects:["The node must answer with `crtr push result` and cannot finish until the result is accepted; a plain `crtr push final` is blocked while the request is pending.",M,"The managed parent wakes through its existing subscription when the final report arrives; an independent root has no subscriber to wake.","An accepted result, or a decline, removes the request and terminates the node."]}}]}a(de,"nodeNewParams");function le(){return[{name:"node_id",type:"string",required:!0,constraint:"New node id."},{name:"name",type:"string",required:!0,constraint:"Display name."},{name:"frozen_at",type:"string",required:!1,constraint:"Present when a managed child exists but capacity deferred its broker start."},...R()?[{name:"window",type:"string",required:!1,constraint:"tmux window id when a viewer opens."},{name:"session",type:"string",required:!1,constraint:"tmux session when a viewer opens."}]:[],{name:"follow_up",type:"string",required:!0,constraint:"Managed child: continue unrelated work or stop; its final report wakes you. Root: hand it off; no report wakes you."}]}a(le,"nodeNewOutput");function ce(){return["Creates nodes/<id>/ in the canvas home, indexes the node in canvas.db, and starts its detached broker engine.","A managed child creates an active parent subscription; its pushes wake the parent. --root creates an independent node with spawned_by provenance and no parent subscription.","--worktree creates a managed worktree at ~/.crouter/canvas/worktrees/<node-id>/ on a new crtr/<node-id> branch.",M,"--deadline persists a wall-clock deadline from spawn; expiry cancels the node and records a failure outcome with reason deadline_exceeded.","--outcome-action registers one resolved local human-action target. When the node settles, crouter retries delivery of its outcome document until the target accepts it or permanently rejects it; --outcome-payload is frozen and echoed in that document.",...R()?["--root opens a viewer for the new node in the current tmux session."]:[]]}a(ce,"nodeNewEffects");async function ue(e){W("act","crtr node new");const n=e.prompt??"",t=e.noKickoff===!0;if(!t&&n.trim()==="")throw new o({error:"empty_prompt",message:"a prompt is required (stdin or positional)",next:"Pipe a task on stdin or pass it as an argument, or pass --no-kickoff to spawn an idle node with none."});if(t&&n.trim()!=="")throw new o({error:"no_kickoff_with_prompt",message:"--no-kickoff cannot be combined with a prompt",field:"no-kickoff",next:"Drop --no-kickoff, or drop the prompt."});const l=e.kind,N=e.mode??"base",c=e.cwd?.trim(),u=c!==void 0&&c!==""?ie(c):void 0,S=e.name,F=e.parent,r=e.root===!0,h=e.resident===!0,O=e.terminal===!0;if(h&&O)throw new o({error:"lifecycle_conflict",message:"--resident and --terminal cannot both be passed",field:"terminal",next:"Pass the one birth lifecycle you want, or neither to take the default (resident for --root, terminal for a managed child)."});if(h&&!r&&e.force!==!0)throw new o({error:"resident_child_requires_force",message:`--resident on a managed child makes it a conversation partner for a person, not a worker that reports back. ${j.join(" ")}`,field:"resident",next:`If the user did not explicitly ask for a node they can talk with back and forth, drop --resident; a terminal child's final report wakes you. ${L} If the user did explicitly ask for one, re-run with --force.`});const f=e.worktree;if(f!==void 0&&f.trim()==="")throw new o({error:"worktree_base_required",message:"--worktree requires a base branch",field:"worktree",next:"Pass an existing local branch that landing can fast-forward."});const m=f?.trim(),T=e.forkFrom,p=e.model?.trim();if(p==="")throw new o({error:"empty_spec",message:"a model spec is required",field:"model",next:`Run \`crtr node new --model -h\` and read the focused contract. ${V}`});const P=e.profile,D=P===void 0?void 0:(await ee(P)).id,w=e.situationalContext?.trim(),E=w!==void 0&&w!==""?w:void 0,C=G(e.outputSchema),A=C!==null?JSON.stringify(C):null,d=e.deadline;if(d!==void 0){const s=X(d),H=s===null?Number.NaN:new Date(Date.now()+s).getTime();if(s===null||!Number.isFinite(s)||s<=0||!Number.isFinite(H))throw new o({error:"invalid_deadline",message:"--deadline must be a positive representable duration",field:"deadline",next:"Pass positive integer-unit parts using s, m, h, or d."})}const k=e.outcomeAction,y=e.outcomePayload;if(y!==void 0&&k===void 0)throw new o({error:"outcome_payload_requires_action",message:"--outcome-payload requires --outcome-action",field:"outcome-payload",next:"Pass the declared delivery action with --outcome-action."});let g;if(y!==void 0)try{g=JSON.parse(y)}catch{throw new o({error:"invalid_outcome_payload",message:"--outcome-payload must be valid JSON",field:"outcome-payload",next:"Pass one valid JSON value."})}const b=e.nodeId?.trim();if(r&&m!==void 0)throw new o({error:"worktree_root_conflict",message:"--worktree is mutually exclusive with --root",field:"worktree",next:"Drop either --root or --worktree."});if(t&&!r)throw new o({error:"no_kickoff_requires_root",message:"--no-kickoff is only valid with --root",field:"no-kickoff",next:"Add --root, or drop --no-kickoff and provide a prompt."});if(t&&N==="orchestrator")throw new o({error:"no_kickoff_orchestrator_unsupported",message:"--no-kickoff cannot be combined with --mode orchestrator",field:"no-kickoff",next:"Drop --no-kickoff and provide a prompt, or spawn in base mode."});const v=z(),I=F??v;x("node_new.create_start",{root:r});const i=await te().createNode({...l!==void 0?{kind:l}:{},mode:N,cwd:process.cwd(),...u!==void 0&&u!==""?{pin_cwd:u}:{},...S!==void 0?{name:S}:{},prompt:n,...r?{root:!0}:{parent:I??null},...v!==void 0?{creator:v}:{},...h?{root_lifecycle:"resident"}:{},...O?{root_lifecycle:"terminal"}:{},...m!==void 0?{worktree:m}:{},...T!==void 0?{fork_from:T}:{},...p!==void 0?{model:p}:{},...D!==void 0?{profile:D}:{},...E!==void 0?{situational_context:E}:{},...t?{no_kickoff:!0}:{},...A!==null?{output_schema:A}:{},...d!==void 0?{deadline:d}:{},...k!==void 0?{outcome_delivery:{action:k,...g===void 0?{}:{payload:g}}}:{},...b!==void 0&&b!==""?{node_id:b}:{},...r?{prefer_warm:!0}:{}}).catch(ne);x("node_new.node_created",{node:i.node_id});let _={};r&&(_=re(i.node_id,i.cwd,i.name)),r&&x("node_new.viewer_window_opened");const U=!r&&i.frozen_at!==null;return{node_id:i.node_id,name:i.name,...U?{frozen_at:i.frozen_at}:{},window:_.window,session:_.session,follow_up:r?t?"Independent root spawned idle. No kickoff turn ran; hand it off or attach and prompt it. No report wakes you.":"Independent root spawned. You are not subscribed, so its finish does not wake you; hand it off and move on.":U?"Child row exists, but no broker started because capacity is full. It is frozen and will launch when a broker slot frees; its final report wakes you then.":await se(I)}}a(ue,"runNodeCreation");const Oe=B({name:"new",description:"create and start a node immediately",whenToUse:"a unit of work is genuinely separable from what you are doing: a bounded subtask you would otherwise grind out inline, several independent units worth running in parallel (usually via one orchestrator child of the matching kind rather than a flat fan-out from you), a scouting pass over unfamiliar code, a change that wants its own git worktree, or an answer you need in a fixed shape rather than as prose",tier:"important",help:{name:"node new",summary:"create a node immediately and start its detached broker",guide:"The prompt is the child\u2019s entire brief: it wakes with this prompt, its persona, and ambient bearings \u2014 none of your conversation carries over. State the outcome and constraints. Pass an implementation plan only when it is already settled or necessary to preserve an approved decision; otherwise, let the child investigate and choose how. Give the reason along with the request: the larger goal this serves and what the child's output enables, so the child resolves ambiguity toward your intent instead of guessing. Name by absolute path every artifact the task depends on (a spec, an explore report, an earlier child's findings); the child inherits only the files the prompt names. When the report must carry something specific\u2014findings inline, or paths to artifacts it wrote\u2014say so in the prompt.",params:de(),output:le(),outputKind:"object",effects:ce()},helpState:{leaf:Q,flags:{profile:Z}},run:a(e=>ue(e),"run"),render:a(e=>{const n=e.window?` in window ${e.window}`:"",t=[`Spawned "${e.name}" (${e.node_id})${n}.`];return e.session&&t.push(`- session: ${e.session}`),t.push("",String(e.follow_up)),t.join(`
1
+ var $=Object.defineProperty;var a=(e,n)=>$(e,"name",{value:n,configurable:!0});import{envNodeId as z}from"../../shared/env.js";import{defineLeaf as B}from"../../core/command.js";import{InputError as o}from"../../core/io.js";import{requireScope as W}from"../../core/errors.js";import{mark as x}from"../../core/timing.js";import{OUTPUT_SCHEMA_CONSTRAINTS as J,OUTPUT_SCHEMA_TRANSPORT as Y,parseOutputSchemaValue as G}from"../../core/runtime/structured-output.js";import{MODEL_SPEC_FORMS as K,MODEL_SPEC_NEXT as V}from"../../core/runtime/model-selection.js";import{parseDurationMs as X}from"../../core/wake.js";import{kindsStateBlock as Q,profilesStateBlock as Z,resolveProfileViaDaemon as ee}from"../profile/daemon-profiles.js";import{cliClient as te,getNodeOrNull as oe,rethrowAsCliError as ne}from"../api-client.js";import{inTmux as R}from"../../core/runtime/placement-tmux.js";import{openSpawnViewer as re}from"../surface/node/placement.js";import{resolve as ie}from"node:path";const ae=1e5,M='--output-schema writes the accepted result to the node\u2019s context/result.json and pushes it as the node\u2019s final report; a node that cannot honestly satisfy the schema declines with `crtr push result --decline "<reason>" --code <token>`, which finishes it with a failure outcome (reason `declined`) whose `declined` field carries { reason, code, retryable } and no result. Name the code tokens you accept in the prompt; crouter carries the code verbatim and you validate it.',j=["A resident is told never to push a final, so the spawner gets no report wake when the work is done.","If it stops mid-task, nothing prompts it to continue.","After 15 unattended minutes it is concluded with only a deferred update, which does not wake the spawner."],L="To re-task a terminal child after its final, use `crtr node message send --reopen`; to make an existing node resident later, use `crtr node config --lifecycle resident`.",q="Do not wait, poll, or duplicate this child's assignment in your own context \u2014 there is no result to await, stopping will not strand you, and duplicated work wastes the delegated context. You're auto-subscribed, so its finish wakes you on its own. Two moves only: do other independent work right now, or stop and end your turn \u2014 the wake brings you back.";async function se(e){if(e===void 0||e==="")return q;const n=await oe(e);if(n===null||n.mode!=="orchestrator")return q;const t=n.context_tokens??null;return t===null||t<ae?q:`Child spawned \u2014 you're auto-subscribed, so its finish wakes you on its own; never wait, poll, or duplicate the child's assignment in your own context. But you're an orchestrator already carrying ~${Math.round(t/1e3)}k of context: rather than hold this window open for the result, bring your document \`roadmap\` current (\`crtr doc edit roadmap\`) and \`crtr node yield\` now. Yielding ends this turn, and the child's completion then revives you fresh against your roadmap \u2014 let that clean revive absorb the result instead of this bloated context.`}a(se,"childFollowUp");function de(){return[{kind:"stdin",name:"prompt",required:!1,constraint:"First user message."},{kind:"flag",name:"kind",type:"string",required:!1,constraint:"Persona kind. Defaults to the profile's default kind; `general` when the profile has none. The <kinds> list below names every top-level installable kind."},{kind:"flag",name:"mode",type:"enum",choices:["base","orchestrator"],required:!1,default:"base",constraint:"Persona mode. base for one hands-on unit; orchestrator when the unit itself needs decomposition across children. When you hold several parallel units of one kind, spawn ONE orchestrator child of that kind to own the fan-out rather than spawning the units yourself \u2014 unless you are already an orchestrator of that kind, whose job is exactly that fan-out."},{kind:"flag",name:"cwd",type:"path",required:!1,constraint:"Pin the node to this directory. Defaults to the spawner\u2019s directory for a managed child, and to the directory this command runs in for a root."},{kind:"flag",name:"name",type:"string",required:!1,constraint:"Display name."},{kind:"flag",name:"parent",type:"string",required:!1,constraint:"Parent node id. Defaults to the calling node."},{kind:"flag",name:"node-id",type:"string",required:!1,constraint:"Exact id for the new node; lowercase letters, digits, and hyphens only, at most 128 characters. Rejected if already present."},{kind:"flag",name:"root",type:"bool",required:!1,constraint:"Spawn an independent root with no parent or subscription. Mutually exclusive with --worktree."},{kind:"flag",name:"resident",type:"bool",required:!1,constraint:"Birth the node as resident, a conversation partner for a person rather than a worker. Omit unless the user explicitly asks for a node they can talk with back and forth.",focusedHelp:{whenToUse:"Pass it ONLY when the user explicitly asks for a node they can talk with back and forth. Never infer it from task size, from expecting the user to look at the node, or from wanting to follow up later. "+L,value:"Boolean. Roots default to resident, so with --root it changes nothing. Managed children default to terminal; for a managed child the command first refuses, explains the cost, and says how to confirm.",effects:[...j,"It ends by going dormant or by being closed."]}},{kind:"flag",name:"terminal",type:"bool",required:!1,constraint:"Birth the node as terminal: it finalizes when its work is done and leaves nothing wakeable behind. Only meaningful with --root, whose default is resident; managed children are already terminal. Pass it for bounded programmatic work no one will open a viewer on. Mutually exclusive with --resident."},{kind:"flag",name:"force",type:"bool",required:!1,default:!1,hidden:!0,constraint:"Confirm a resident managed child (--resident without --root) after the user explicitly asked for one."},{kind:"flag",name:"worktree",type:"string",required:!1,constraint:"Local base branch for a crouter-managed worktree for this child. Mutually exclusive with --root."},{kind:"flag",name:"fork-from",type:"string",required:!1,constraint:"Fork the new node from an existing node id, absolute session path, or partial pi session uuid."},{kind:"flag",name:"model",type:"string",required:!1,constraint:"Durable model override. Omit unless the task or governing guidance requires a specific model.",focusedHelp:{whenToUse:"Use only when the user, task, or governing agent guidance already requires a specific provider, model, capability tier, or family. Omit it for routine delegation: the selected persona kind and profile own normal model selection, and review personas may enforce a quality floor.",value:`Accepted forms: ${K}. Concrete provider/id requests must be registered. Portable provider/tier, bare-tier, and family-alias requests resolve through the configured model ladders.`,effects:["The resolved concrete model is stored on the node and in its launch recipe.","A concrete provider/id runs exactly that model on every launch and revive. A portable provider/tier, bare tier, or family alias keeps its tier: when that model's provider is rate-limited or rejects its credential, the node moves to the next model in its route: another provider's model at the same tier first, then the configured fallback tier (strong by default).","A persona quality floor may raise a weaker request before its resolved model is stored."]}},{kind:"flag",name:"profile",type:"string",required:!1,constraint:"Profile override. Use only when the task needs different project purview, memory, or environment; otherwise omit for normal inheritance or selection.",focusedHelp:{whenToUse:"Use only when the work must run under a different profile because it needs that profile\u2019s project purview, memory store, stored environment, or defaults. Do not use it merely to change the working directory, persona kind, model, or graph ownership; those have separate parameters.",value:"Pass a profile id or name from the <profiles> list below; a close-enough name resolves to the nearest profile and the choice is reported on stderr. Omit it inside a node to inherit the caller\u2019s profile; outside a node, omission runs normal cwd-based profile selection.",effects:["The selected profile id is stored on the node and supplies its profile-scoped config, memory, environment, and injected bearings.","It does not change the parent or subscription; --cwd separately pins the working directory."]}},{kind:"flag",name:"situational-context",type:"string",required:!1,constraint:"Hidden ambient context appended to injected bearings, never visible chat."},{kind:"flag",name:"no-kickoff",type:"bool",required:!1,hidden:!0,constraint:"With --root, start the broker and session idle without a first message or model turn. Mutually exclusive with a prompt and --mode orchestrator."},{kind:"flag",name:"deadline",type:"string",required:!1,constraint:"Positive, representable wall-clock duration from spawn. Grammar: one or more positive integer-unit parts; units: s, m, h, d; adjacent parts are summed. Omit for no deadline."},{kind:"flag",name:"outcome-action",type:"string",required:!1,constraint:"Declared human-action name that receives the node outcome. Grammar: 1\u2013128 ASCII letters, digits, dots, underscores, or hyphens; starts with a letter or digit. Omit when no external delivery target is needed."},{kind:"flag",name:"outcome-payload",type:"string",required:!1,constraint:"JSON value, serialized at most 64 KiB, frozen and echoed to --outcome-action. Requires --outcome-action."},{kind:"flag",name:"output-schema",type:"string",required:!1,constraint:"Replace prose completion with a schema-constrained final result. Use only when its consumer requires fixed fields.",focusedHelp:{whenToUse:"Use only when the node\u2019s consumer needs a machine-checkable final object with fixed fields. Omit it for ordinary delegation or when a prose report is enough; structured output replaces the completion protocol, and an over-constrained schema can force retries.",value:`Pass the JSON Schema the result must satisfy. ${Y} ${J}`,effects:["The node must answer with `crtr push result` and cannot finish until the result is accepted; a plain `crtr push final` is blocked while the request is pending.",M,"The managed parent wakes through its existing subscription when the final report arrives; an independent root has no subscriber to wake.","An accepted result, or a decline, removes the request and terminates the node."]}}]}a(de,"nodeNewParams");function le(){return[{name:"node_id",type:"string",required:!0,constraint:"New node id."},{name:"name",type:"string",required:!0,constraint:"Display name."},{name:"frozen_at",type:"string",required:!1,constraint:"Present when a managed child exists but capacity deferred its broker start."},...R()?[{name:"window",type:"string",required:!1,constraint:"tmux window id when a viewer opens."},{name:"session",type:"string",required:!1,constraint:"tmux session when a viewer opens."}]:[],{name:"follow_up",type:"string",required:!0,constraint:"Managed child: continue unrelated work or stop; its final report wakes you. Root: hand it off; no report wakes you."}]}a(le,"nodeNewOutput");function ce(){return["Creates nodes/<id>/ in the canvas home, indexes the node in canvas.db, and starts its detached broker engine.","A managed child creates an active parent subscription; its pushes wake the parent. --root creates an independent node with spawned_by provenance and no parent subscription.","--worktree creates a managed worktree at ~/.crouter/canvas/worktrees/<node-id>/ on a new crtr/<node-id> branch.",M,"--deadline persists a wall-clock deadline from spawn; expiry cancels the node and records a failure outcome with reason deadline_exceeded.","--outcome-action registers one resolved local human-action target. When the node settles, crouter retries delivery of its outcome document until the target accepts it or permanently rejects it; --outcome-payload is frozen and echoed in that document.",...R()?["--root opens a viewer for the new node in the current tmux session."]:[]]}a(ce,"nodeNewEffects");async function ue(e){W("act","crtr node new");const n=e.prompt??"",t=e.noKickoff===!0;if(!t&&n.trim()==="")throw new o({error:"empty_prompt",message:"a prompt is required (stdin or positional)",next:"Pipe a task on stdin or pass it as an argument, or pass --no-kickoff to spawn an idle node with none."});if(t&&n.trim()!=="")throw new o({error:"no_kickoff_with_prompt",message:"--no-kickoff cannot be combined with a prompt",field:"no-kickoff",next:"Drop --no-kickoff, or drop the prompt."});const l=e.kind,N=e.mode??"base",c=e.cwd?.trim(),u=c!==void 0&&c!==""?ie(c):void 0,S=e.name,F=e.parent,r=e.root===!0,h=e.resident===!0,O=e.terminal===!0;if(h&&O)throw new o({error:"lifecycle_conflict",message:"--resident and --terminal cannot both be passed",field:"terminal",next:"Pass the one birth lifecycle you want, or neither to take the default (resident for --root, terminal for a managed child)."});if(h&&!r&&e.force!==!0)throw new o({error:"resident_child_requires_force",message:`--resident on a managed child makes it a conversation partner for a person, not a worker that reports back. ${j.join(" ")}`,field:"resident",next:`If the user did not explicitly ask for a node they can talk with back and forth, drop --resident; a terminal child's final report wakes you. ${L} If the user did explicitly ask for one, re-run with --force.`});const f=e.worktree;if(f!==void 0&&f.trim()==="")throw new o({error:"worktree_base_required",message:"--worktree requires a base branch",field:"worktree",next:"Pass an existing local branch that landing can fast-forward."});const m=f?.trim(),T=e.forkFrom,p=e.model?.trim();if(p==="")throw new o({error:"empty_spec",message:"a model spec is required",field:"model",next:`Run \`crtr node new --model -h\` and read the focused contract. ${V}`});const P=e.profile,D=P===void 0?void 0:(await ee(P)).id,w=e.situationalContext?.trim(),E=w!==void 0&&w!==""?w:void 0,C=G(e.outputSchema),A=C!==null?JSON.stringify(C):null,d=e.deadline;if(d!==void 0){const s=X(d),H=s===null?Number.NaN:new Date(Date.now()+s).getTime();if(s===null||!Number.isFinite(s)||s<=0||!Number.isFinite(H))throw new o({error:"invalid_deadline",message:"--deadline must be a positive representable duration",field:"deadline",next:"Pass positive integer-unit parts using s, m, h, or d."})}const k=e.outcomeAction,y=e.outcomePayload;if(y!==void 0&&k===void 0)throw new o({error:"outcome_payload_requires_action",message:"--outcome-payload requires --outcome-action",field:"outcome-payload",next:"Pass the declared delivery action with --outcome-action."});let g;if(y!==void 0)try{g=JSON.parse(y)}catch{throw new o({error:"invalid_outcome_payload",message:"--outcome-payload must be valid JSON",field:"outcome-payload",next:"Pass one valid JSON value."})}const b=e.nodeId?.trim();if(r&&m!==void 0)throw new o({error:"worktree_root_conflict",message:"--worktree is mutually exclusive with --root",field:"worktree",next:"Drop either --root or --worktree."});if(t&&!r)throw new o({error:"no_kickoff_requires_root",message:"--no-kickoff is only valid with --root",field:"no-kickoff",next:"Add --root, or drop --no-kickoff and provide a prompt."});if(t&&N==="orchestrator")throw new o({error:"no_kickoff_orchestrator_unsupported",message:"--no-kickoff cannot be combined with --mode orchestrator",field:"no-kickoff",next:"Drop --no-kickoff and provide a prompt, or spawn in base mode."});const v=z(),I=F??v;x("node_new.create_start",{root:r});const i=await te().createNode({...l!==void 0?{kind:l}:{},mode:N,cwd:process.cwd(),...u!==void 0&&u!==""?{pin_cwd:u}:{},...S!==void 0?{name:S}:{},prompt:n,...r?{root:!0}:{parent:I??null},...v!==void 0?{creator:v}:{},...h?{root_lifecycle:"resident"}:{},...O?{root_lifecycle:"terminal"}:{},...m!==void 0?{worktree:m}:{},...T!==void 0?{fork_from:T}:{},...p!==void 0?{model:p}:{},...D!==void 0?{profile:D}:{},...E!==void 0?{situational_context:E}:{},...t?{no_kickoff:!0}:{},...A!==null?{output_schema:A}:{},...d!==void 0?{deadline:d}:{},...k!==void 0?{outcome_delivery:{action:k,...g===void 0?{}:{payload:g}}}:{},...b!==void 0&&b!==""?{node_id:b}:{},...r?{prefer_warm:!0}:{}}).catch(ne);x("node_new.node_created",{node:i.node_id});let _={};r&&(_=re(i.node_id,i.cwd,i.name)),r&&x("node_new.viewer_window_opened");const U=!r&&i.frozen_at!==null;return{node_id:i.node_id,name:i.name,...U?{frozen_at:i.frozen_at}:{},window:_.window,session:_.session,follow_up:r?t?"Independent root spawned idle. No kickoff turn ran; hand it off or attach and prompt it. No report wakes you.":"Independent root spawned. You are not subscribed, so its finish does not wake you; hand it off and move on.":U?"Child row exists, but no broker started because capacity is full. It is frozen and will launch when a broker slot frees; its final report wakes you then.":await se(I)}}a(ue,"runNodeCreation");const Oe=B({name:"new",description:"create and start a node immediately",whenToUse:"a unit of work is genuinely separable from what you are doing: a bounded subtask you would otherwise grind out inline, several independent units worth running in parallel (usually via one orchestrator child of the matching kind rather than a flat fan-out from you), a scouting pass over unfamiliar code, a change that wants its own git worktree, or an answer you need in a fixed shape rather than as prose",tier:"important",help:{name:"node new",summary:"create a node immediately and start its detached broker",guide:"The prompt is the child\u2019s entire brief: it wakes with this prompt, its persona, and ambient bearings \u2014 none of your conversation carries over. State the outcome and constraints. Pass an implementation plan only when it is already settled or necessary to preserve an approved decision; otherwise, let the child investigate and choose how. Give the reason along with the request: the larger goal this serves and what the child's output enables, so the child resolves ambiguity toward your intent instead of guessing. Point at every document the task depends on (a spec, an explore map, an earlier child's findings) as `[[<node-id>/<name>]]`, or `[[name]]` for a repo, profile or user document; the child reads each with `crtr canvas read` and receives nothing the prompt does not point at. When the report must carry something specific\u2014findings inline, or pointers to documents it wrote\u2014say so in the prompt.",params:de(),output:le(),outputKind:"object",effects:ce()},helpState:{leaf:Q,flags:{profile:Z}},run:a(e=>ue(e),"run"),render:a(e=>{const n=e.window?` in window ${e.window}`:"",t=[`Spawned "${e.name}" (${e.node_id})${n}.`];return e.session&&t.push(`- session: ${e.session}`),t.push("",String(e.follow_up)),t.join(`
2
2
  `)},"render")});export{se as childFollowUp,Oe as nodeNew};
@@ -1,12 +1,12 @@
1
- var R=Object.defineProperty;var h=(t,e)=>R(t,"name",{value:e,configurable:!0});import{defineBranch as P,defineLeaf as k}from"../../core/command.js";import{InputError as v}from"../../core/io.js";import{requireScope as A}from"../../core/errors.js";import{isPidAlive as I}from"../../core/canvas/pid.js";import{fullName as $}from"../../core/canvas/labels.js";import{fuzzyMatch as O}from"../../core/tui/fuzzy.js";import{nodeSnapshotLeaf as C}from"../node-snapshot.js";import{nodeTranscriptLeaf as F}from"../node-transcript.js";import{nodeContextLeaf as L}from"../node-context.js";import{nodeArtifactsLeaf as U}from"../node-inspect-artifacts.js";import{cliClient as _,cliCanvasSource as x,rethrowAsCliError as y}from"../api-client.js";import{RemoteCanvasSource as q}from"../../core/canvas/remote-canvas-source.js";import{kindsStateBlock as D,profilesStateBlock as E,resolveProfileViaDaemon as z}from"../profile/daemon-profiles.js";import{focusViewer as B,nodeInPane as W}from"../surface/node/placement.js";const K=k({name:"list",description:"list nodes on the canvas",whenToUse:"you want a flat roster of the nodes on the canvas, sliced by displayed name, status, kind, mode, lifecycle, profile, cwd, and graph scope (--under a node, for that node plus its subscription descendants). The filters AND together. Use `node inspect show` instead to drill into one node and its spine neighbors, `canvas dashboard` for the tree shape, and `canvas attention` to find nodes blocked on the user",help:{name:"node inspect list",summary:"list nodes on the canvas, filtered by any combination of attribute slices and a graph-scope (--under) that AND together",params:[{kind:"flag",name:"status",type:"string",required:!1,constraint:"Filter status (comma-separated for several; accepted values: active, idle, done, dead, canceled). NOTE: `active` means the engine process is live (never closed), NOT that the node is generating right now \u2014 an active node is usually dormant between turns. To tell working-vs-idle, check the pi session-file mtime or CPU, not status."},{kind:"flag",name:"kind",type:"string",required:!1,constraint:"Filter by persona kind (comma-separated for several). The <kinds> list below names every top-level installable kind; a registered sub-kind filters too by exact path even though it is not listed."},{kind:"flag",name:"mode",type:"string",required:!1,constraint:"Filter by mode (comma-separated for several; accepted values: base, orchestrator)."},{kind:"flag",name:"lifecycle",type:"string",required:!1,constraint:"Filter by lifecycle (comma-separated for several; accepted values: terminal, resident)."},{kind:"flag",name:"profile",type:"string",required:!1,constraint:"Filter by profile. On the local canvas, accepts a profile id or manifest name from the <profiles> list below, or a close-enough name that resolves to the nearest profile. Against a remote canvas, pass the exact profile id carried by its node rows."},{kind:"flag",name:"name",type:"string",required:!1,constraint:"Filter by the displayed node label (handle plus generated description) with the canvas browser\u2019s case-insensitive fuzzy subsequence match."},{kind:"flag",name:"cwd",type:"string",required:!1,constraint:"Filter to nodes pinned to this exact cwd (absolute path). Roster is canvas-wide (all cwds) by default."},{kind:"flag",name:"under",type:"string",required:!1,constraint:'Graph scope: restrict to this node and its subscription descendants (the sub-DAG reachable downward \u2014 its reports, theirs, \u2026). This is "one initiative / my subtree". ANDs with every attribute filter above, narrowing any of them to this subtree.'},{kind:"flag",name:"canvas",type:"string",required:!1,constraint:"Read from the named remote canvas target (see `crtr canvas config list`) instead of the local canvas. Overrides `crtr canvas use`."}],output:[{name:"nodes",type:"object[]",required:!0,constraint:"Rows include {node_id, name, kind, mode, lifecycle, status, cwd, profile_id, parent, created}. `name` is the displayed node label searched by --name and shown in canvas views. `profile_id` is the stable profile-directory id (null only for historical no-profile nodes)."}],outputKind:"object",effects:["Read-only: lists nodes through the daemon API."]},helpState:{leaf:h(async()=>(await Promise.all([D(),E()])).join(`
2
- `),"leaf")},run:h(async t=>{const e=h(n=>{const w=t[n];return w!==void 0&&w!==""?w.split(",").map(b=>b.trim()).filter(b=>b!==""):void 0},"csv"),o=e("status"),s=e("kind"),p=e("mode"),r=e("lifecycle"),m=t.profile,a=t.name,d=t.cwd,i=t.under,l=x({canvasName:t.canvas}),f=l instanceof q?m:m===void 0?void 0:(await z(m)).id;let u;if(i!==void 0&&i!==""){if(await l.getNode(i).catch(y)===null)throw new v({error:"not_found",message:`no node: ${i}`,next:"List nodes with `crtr node inspect list`, then pass a real id to --under."});u=new Set([i,...await l.view(i)])}return{nodes:(await l.listNodes(o!==void 0?{status:o}:void 0).catch(y)).filter(n=>!(u!==void 0&&!u.has(n.node_id)||s!==void 0&&!s.includes(n.kind)||p!==void 0&&!p.includes(n.mode)||r!==void 0&&!r.includes(n.lifecycle)||f!==void 0&&n.profile_id!==f||d!==void 0&&d!==""&&n.cwd!==d)).map(n=>({...n,name:$(n)})).filter(n=>a===void 0||a===""||O(a,n.name))}},"run")}),S=new WeakSet;async function j(t,e){const o=await t.getNode(e.node_id).catch(y);return{node_id:e.node_id,name:o?$(o):"(missing)",kind:o?.kind??"?",status:o?o.intent?`${o.status}/${o.intent}`:o.status:"?",active:e.active}}h(j,"enrichNeighbor");const M=k({name:"show",description:"show a node + its spine neighbors, artifacts, and drill-in pointers",whenToUse:"you want one node in depth \u2014 the debugging entrypoint: a glanceable meta slice, named spine neighbors, artifact and pending-cron counts, and exact drill-in paths. Use `node inspect list` for the flat roster or `canvas dashboard` for the whole-tree shape",help:{name:"node inspect show",summary:"show a node in depth \u2014 meta slice, named neighbors, artifact/pending-cron counts, and drill-in pointers",params:[{kind:"positional",name:"node",required:!0,constraint:"Node id."},{kind:"flag",name:"canvas",type:"string",required:!1,constraint:"Read from the named remote canvas target (see `crtr canvas config list`) instead of the local canvas. Overrides `crtr canvas use`."}],output:[{name:"node",type:"object",required:!0,constraint:"The full node meta (system prompt + every telemetry field). The render surfaces a glanceable slice; `--json` carries the complete dump."},{name:"fault",type:"object",required:!0,constraint:"The active runtime fault projection, or null: {link, kind, retry: {disposition}, since}."},{name:"reports",type:"object[]",required:!0,constraint:"Who this node subscribes to (its reports/down), each enriched with {node_id, name, kind, status, active} \u2014 these are typically its spawned children."},{name:"managers",type:"object[]",required:!0,constraint:"Who subscribes to this node (its managers/up), same enriched shape."},{name:"artifacts",type:"object",required:!0,constraint:"Counts of what the node left on disk by corpus: {report, doc, roadmap}. Enumerate them with `node inspect artifacts <id>`."},{name:"pending_crons",type:"number",required:!0,constraint:"How many pending crons were created by this node. Inspect with `crtr cron list`."},{name:"paths",type:"object",required:!0,constraint:"Resolved on-disk locations: {context_dir, reports_dir, session_file} \u2014 read these directly to debug."},{name:"follow_up",type:"string",required:!0,constraint:"The concrete next commands to drill deeper."}],outputKind:"object",effects:["Read-only: reads the node, its edges and artifacts, and pending crons through crtrd."],preview:{suppressOutput:!0}},run:h(async t=>{const e=t.node,o=t.canvas,s=x({canvasName:o});if(s instanceof q){const n=await s.rawShow(e);if(n===null)throw new v({error:"not_found",message:`no node: ${e}`,next:"List nodes with `crtr node inspect list --canvas <name>`."});const w={...n};return S.add(w),w}const p=await s.getNode(e).catch(n=>y(n,"List nodes with `crtr node inspect list`."));if(p===null)throw new v({error:"not_found",message:`no node: ${e}`,next:"List nodes with `crtr node inspect list`."});const r=p,m=_(),a=await Promise.all((await s.subscriptionsOf(e)).map(n=>j(s,n))),d=await Promise.all((await s.subscribersOf(e)).map(n=>j(s,n))),i=await m.getArtifacts(e),l={report:i.artifacts.filter(n=>n.source.startsWith("report")).length,doc:i.artifacts.filter(n=>n.source==="doc").length,roadmap:i.artifacts.filter(n=>n.source==="roadmap").length},f=(await m.listCrons().catch(y)).filter(n=>n.created_by===e).length,u={context_dir:r.paths.context_dir,reports_dir:r.paths.reports_dir,...r.pi_session_file!==void 0?{session_file:r.pi_session_file}:{}},c=[],g=l.report+l.doc+l.roadmap;return g>0&&c.push(`Read its ${g} artifact(s): \`crtr node inspect artifacts ${e}\` \u2192 each ref feeds \`crtr canvas history read <ref>\`.`),c.push(`Browse its context + reports dirs (and its subscriptions') in nvim: \`crtr node inspect context ${e}\`.`),u.session_file&&c.push(`Read the raw conversation transcript: ${u.session_file}`),c.push(`On-disk context dir (ls/read directly): ${u.context_dir}`),f>0&&c.push(`It created ${f} pending cron(s): \`crtr cron list\`.`),a.length>0&&c.push(`Drill into a child/report: \`crtr node inspect show <node_id>\` (${a.map(n=>n.node_id).join(", ")}).`),c.push(`Full machine dump (system prompt + all telemetry): \`crtr --json node inspect show ${e}\`.`),{node:r,fault:r.fault??null,reports:a,managers:d,artifacts:l,pending_crons:f,paths:u,follow_up:c.join(`
3
- `)}},"run"),render:h(t=>{const e=t.node,o=String(e.node_id),s=e.pi_pid,r=S.has(t)?"remote (liveness not checked locally)":I(s)?"alive":"dead",m=e.intent?`/${String(e.intent)}`:"",a=t.fault,d=[];d.push(`# ${$(e)} (${o})`);const i=[`- kind/mode/lifecycle: ${e.kind} / ${e.mode} / ${e.lifecycle}`,`- status: ${e.status}${m}`,...a==null?[]:[`- fault: ${String(a.kind)} / ${String(a.retry?.disposition)} since ${String(a.since)}`],`- broker pid: ${s??"(none)"} \u2014 ${r}`,`- cwd: ${e.cwd}`,`- host: ${e.host_kind}`+(e.profile_id?` \xB7 profile ${e.profile_id}`:""),`- cycles: ${e.cycles} \xB7 created ${e.created}`];e.parent&&i.push(`- parent: ${e.parent}`),i.push(`- creator: ${e.creator??"an external process"}`),e.waiting_for&&i.push(`- waiting for message: ${e.waiting_for}`),d.push(i.join(`
4
- `));const l=h((u,c)=>{if(c.length===0)return`${u}: none`;const g=["node_id","name","kind","status","active"],n=`| ${g.join(" | ")} |`,w=`| ${g.map(()=>"---").join(" | ")} |`,b=c.map(T=>`| ${g.map(N=>String(T[N]??"").replace(/\|/g,"\\|").replace(/\n/g," ")).join(" | ")} |`).join(`
5
- `);return`${c.length} ${u}:
1
+ var N=Object.defineProperty;var h=(n,e)=>N(n,"name",{value:e,configurable:!0});import{defineBranch as R,defineLeaf as b}from"../../core/command.js";import{InputError as y}from"../../core/io.js";import{requireScope as P}from"../../core/errors.js";import{isPidAlive as I}from"../../core/canvas/pid.js";import{fullName as k}from"../../core/canvas/labels.js";import{fuzzyMatch as O}from"../../core/tui/fuzzy.js";import{nodeSnapshotLeaf as F}from"../node-snapshot.js";import{nodeTranscriptLeaf as L}from"../node-transcript.js";import{nodeContextLeaf as A}from"../node-context.js";import{cliClient as $,cliCanvasSource as x,rethrowAsCliError as w}from"../api-client.js";import{RemoteCanvasSource as q}from"../../core/canvas/remote-canvas-source.js";import{kindsStateBlock as C,profilesStateBlock as U,resolveProfileViaDaemon as D}from"../profile/daemon-profiles.js";import{focusViewer as z,nodeInPane as E}from"../surface/node/placement.js";const B=b({name:"list",description:"list nodes on the canvas",whenToUse:"you want a flat roster of the nodes on the canvas, sliced by displayed name, status, kind, mode, lifecycle, profile, cwd, and graph scope (--under a node, for that node plus its subscription descendants). The filters AND together. Use `node inspect show` instead to drill into one node and its spine neighbors, `canvas dashboard` for the tree shape, and `canvas attention` to find nodes blocked on the user",help:{name:"node inspect list",summary:"list nodes on the canvas, filtered by any combination of attribute slices and a graph-scope (--under) that AND together",params:[{kind:"flag",name:"status",type:"string",required:!1,constraint:"Filter status (comma-separated for several; accepted values: active, idle, done, dead, canceled). NOTE: `active` means the engine process is live (never closed), NOT that the node is generating right now \u2014 an active node is usually dormant between turns. To tell working-vs-idle, check the pi session-file mtime or CPU, not status."},{kind:"flag",name:"kind",type:"string",required:!1,constraint:"Filter by persona kind (comma-separated for several). The <kinds> list below names every top-level installable kind; a registered sub-kind filters too by exact path even though it is not listed."},{kind:"flag",name:"mode",type:"string",required:!1,constraint:"Filter by mode (comma-separated for several; accepted values: base, orchestrator)."},{kind:"flag",name:"lifecycle",type:"string",required:!1,constraint:"Filter by lifecycle (comma-separated for several; accepted values: terminal, resident)."},{kind:"flag",name:"profile",type:"string",required:!1,constraint:"Filter by profile. On the local canvas, accepts a profile id or manifest name from the <profiles> list below, or a close-enough name that resolves to the nearest profile. Against a remote canvas, pass the exact profile id carried by its node rows."},{kind:"flag",name:"name",type:"string",required:!1,constraint:"Filter by the displayed node label (handle plus generated description) with the canvas browser\u2019s case-insensitive fuzzy subsequence match."},{kind:"flag",name:"cwd",type:"string",required:!1,constraint:"Filter to nodes pinned to this exact cwd (absolute path). Roster is canvas-wide (all cwds) by default."},{kind:"flag",name:"under",type:"string",required:!1,constraint:'Graph scope: restrict to this node and its subscription descendants (the sub-DAG reachable downward \u2014 its reports, theirs, \u2026). This is "one initiative / my subtree". ANDs with every attribute filter above, narrowing any of them to this subtree.'},{kind:"flag",name:"canvas",type:"string",required:!1,constraint:"Read from the named remote canvas target (see `crtr canvas config list`) instead of the local canvas. Overrides `crtr canvas use`."}],output:[{name:"nodes",type:"object[]",required:!0,constraint:"Rows include {node_id, name, kind, mode, lifecycle, status, cwd, profile_id, parent, created}. `name` is the displayed node label searched by --name and shown in canvas views. `profile_id` is the stable profile-directory id (null only for historical no-profile nodes)."}],outputKind:"object",effects:["Read-only: lists nodes through the daemon API."]},helpState:{leaf:h(async()=>(await Promise.all([C(),U()])).join(`
2
+ `),"leaf")},run:h(async n=>{const e=h(r=>{const g=n[r];return g!==void 0&&g!==""?g.split(",").map(v=>v.trim()).filter(v=>v!==""):void 0},"csv"),o=e("status"),s=e("kind"),u=e("mode"),d=e("lifecycle"),f=n.profile,c=n.name,l=n.cwd,i=n.under,p=x({canvasName:n.canvas}),a=p instanceof q?f:f===void 0?void 0:(await D(f)).id;let t;if(i!==void 0&&i!==""){if(await p.getNode(i).catch(w)===null)throw new y({error:"not_found",message:`no node: ${i}`,next:"List nodes with `crtr node inspect list`, then pass a real id to --under."});t=new Set([i,...await p.view(i)])}return{nodes:(await p.listNodes(o!==void 0?{status:o}:void 0).catch(w)).filter(r=>!(t!==void 0&&!t.has(r.node_id)||s!==void 0&&!s.includes(r.kind)||u!==void 0&&!u.includes(r.mode)||d!==void 0&&!d.includes(r.lifecycle)||a!==void 0&&r.profile_id!==a||l!==void 0&&l!==""&&r.cwd!==l)).map(r=>({...r,name:k(r)})).filter(r=>c===void 0||c===""||O(c,r.name))}},"run")}),S=new WeakSet;async function j(n,e){const o=await n.getNode(e.node_id).catch(w);return{node_id:e.node_id,name:o?k(o):"(missing)",kind:o?.kind??"?",status:o?o.intent?`${o.status}/${o.intent}`:o.status:"?",active:e.active}}h(j,"enrichNeighbor");const K=b({name:"show",description:"show a node + its spine neighbors and drill-in pointers",whenToUse:"you want one node in depth \u2014 the debugging entrypoint: a glanceable meta slice, named spine neighbors, a pending-cron count, and exact drill-in paths. Use `node inspect list` for the flat roster or `canvas dashboard` for the whole-tree shape",help:{name:"node inspect show",summary:"show a node in depth \u2014 meta slice, named neighbors, pending-cron count, and drill-in pointers",params:[{kind:"positional",name:"node",required:!0,constraint:"Node id."},{kind:"flag",name:"canvas",type:"string",required:!1,constraint:"Read from the named remote canvas target (see `crtr canvas config list`) instead of the local canvas. Overrides `crtr canvas use`."}],output:[{name:"node",type:"object",required:!0,constraint:"The full node meta (system prompt + every telemetry field). The render surfaces a glanceable slice; `--json` carries the complete dump."},{name:"fault",type:"object",required:!0,constraint:"The active runtime fault projection, or null: {link, kind, retry: {disposition}, since}."},{name:"reports",type:"object[]",required:!0,constraint:"Who this node subscribes to (its reports/down), each enriched with {node_id, name, kind, status, active} \u2014 these are typically its spawned children."},{name:"managers",type:"object[]",required:!0,constraint:"Who subscribes to this node (its managers/up), same enriched shape."},{name:"pending_crons",type:"number",required:!0,constraint:"How many pending crons were created by this node. Inspect with `crtr cron list`."},{name:"paths",type:"object",required:!0,constraint:"Resolved on-disk locations: {context_dir, session_file} \u2014 read these directly to debug. Reports are documents: `crtr canvas read <node>/reports/<name>`."},{name:"follow_up",type:"string",required:!0,constraint:"The concrete next commands to drill deeper."}],outputKind:"object",effects:["Read-only: reads the node, its edges, and pending crons through crtrd."],preview:{suppressOutput:!0}},run:h(async n=>{const e=n.node,o=n.canvas,s=x({canvasName:o});if(s instanceof q){const t=await s.rawShow(e);if(t===null)throw new y({error:"not_found",message:`no node: ${e}`,next:"List nodes with `crtr node inspect list --canvas <name>`."});const m={...t};return S.add(m),m}const u=await s.getNode(e).catch(t=>w(t,"List nodes with `crtr node inspect list`."));if(u===null)throw new y({error:"not_found",message:`no node: ${e}`,next:"List nodes with `crtr node inspect list`."});const d=u,f=$(),c=await Promise.all((await s.subscriptionsOf(e)).map(t=>j(s,t))),l=await Promise.all((await s.subscribersOf(e)).map(t=>j(s,t))),i=(await f.listCrons().catch(w)).filter(t=>t.created_by===e).length,p={context_dir:d.paths.context_dir,...d.pi_session_file!==void 0?{session_file:d.pi_session_file}:{}},a=[];return a.push(`List the documents it owns (reports, plans, notes): \`crtr canvas list --owner ${e}\`.`),a.push(`Browse its context dir (and its subscriptions') in nvim: \`crtr node inspect context ${e}\`.`),p.session_file&&a.push(`Read the raw conversation transcript: ${p.session_file}`),a.push(`On-disk context dir (ls/read directly): ${p.context_dir}`),i>0&&a.push(`It created ${i} pending cron(s): \`crtr cron list\`.`),c.length>0&&a.push(`Drill into a child/report: \`crtr node inspect show <node_id>\` (${c.map(t=>t.node_id).join(", ")}).`),a.push(`Full machine dump (system prompt + all telemetry): \`crtr --json node inspect show ${e}\`.`),{node:d,fault:d.fault??null,reports:c,managers:l,pending_crons:i,paths:p,follow_up:a.join(`
3
+ `)}},"run"),render:h(n=>{const e=n.node,o=String(e.node_id),s=e.pi_pid,d=S.has(n)?"remote (liveness not checked locally)":I(s)?"alive":"dead",f=e.intent?`/${String(e.intent)}`:"",c=n.fault,l=[];l.push(`# ${k(e)} (${o})`);const i=[`- kind/mode/lifecycle: ${e.kind} / ${e.mode} / ${e.lifecycle}`,`- status: ${e.status}${f}`,...c==null?[]:[`- fault: ${String(c.kind)} / ${String(c.retry?.disposition)} since ${String(c.since)}`],`- broker pid: ${s??"(none)"} \u2014 ${d}`,`- cwd: ${e.cwd}`,`- host: ${e.host_kind}`+(e.profile_id?` \xB7 profile ${e.profile_id}`:""),`- cycles: ${e.cycles} \xB7 created ${e.created}`];e.parent&&i.push(`- parent: ${e.parent}`),i.push(`- creator: ${e.creator??"an external process"}`),e.waiting_for&&i.push(`- waiting for message: ${e.waiting_for}`),l.push(i.join(`
4
+ `));const p=h((a,t)=>{if(t.length===0)return`${a}: none`;const m=["node_id","name","kind","status","active"],_=`| ${m.join(" | ")} |`,r=`| ${m.map(()=>"---").join(" | ")} |`,g=t.map(v=>`| ${m.map(T=>String(v[T]??"").replace(/\|/g,"\\|").replace(/\n/g," ")).join(" | ")} |`).join(`
5
+ `);return`${t.length} ${a}:
6
6
 
7
- ${n}
8
- ${w}
9
- ${b}`},"table");d.push(l("reports (down \u2014 subscriptions, usually children)",t.reports)),d.push(l("managers (up \u2014 subscribers)",t.managers));const f=t.artifacts;return d.push(`artifacts: ${f.report} report(s), ${f.doc} doc(s), ${f.roadmap} roadmap \xB7 ${t.pending_crons} pending cron(s)`),d.push(`Drill deeper:
10
- ${String(t.follow_up)}`),d.join(`
7
+ ${_}
8
+ ${r}
9
+ ${g}`},"table");return l.push(p("reports (down \u2014 subscriptions, usually children)",n.reports)),l.push(p("managers (up \u2014 subscribers)",n.managers)),l.push(`${n.pending_crons} pending cron(s)`),l.push(`Drill deeper:
10
+ ${String(n.follow_up)}`),l.join(`
11
11
 
12
- `)},"render")}),ae=k({name:"fork",description:"fork a node into an independent idle root with the same conversation and durable context",whenToUse:"a conversation has already accumulated the context you want, and you want a new user-driven branch beside it without sending a prompt or inheriting any graph-management responsibility. This copies the conversation and durable context, then waits for the fork\u2019s first real user message to supply its mandate. Use `node new --fork-from` instead when spawning a managed worker with an immediate task",help:{name:"node fork",summary:"create an independent resident root from a node\u2019s conversation and durable context, with no kickoff prompt or model turn",params:[{kind:"flag",name:"node",type:"string",required:!1,constraint:"Source node id. Mutually exclusive with --pane; one of them is required."},{kind:"flag",name:"pane",type:"string",required:!1,constraint:"tmux pane showing the source node. Mutually exclusive with --node; one of them is required. The prefix menu passes this for you and the new viewer splits beside this pane."}],output:[{name:"node_id",type:"string",required:!0,constraint:"The new independent root id."},{name:"focused",type:"boolean",required:!0,constraint:"True when a new viewer pane opened beside the caller; false outside tmux."},{name:"session",type:"string",required:!1,constraint:"The tmux session containing the new viewer, when one opened."}],outputKind:"object",effects:["Creates a resident base-mode root with the source node\u2019s copied pi conversation, cwd, profile, kind, model override, and durable context files. Top-level initial-prompt.md, yield-message.md, result.json, and the jobs/ folder of background bash jobs are omitted.","Copies no parent, subscriptions, subscribers, children, crons, worktree, situational context, or lifecycle responsibility. Sends no prompt and consumes no model turn; the first real user message becomes the fork\u2019s mandate.","Inside tmux, opens and focuses a surface-attach viewer as an even-horizontal split in the source pane\u2019s window. Outside tmux, the root is still created but no interactive fallback opens."]},run:h(async t=>{A("act","crtr node fork");const e=t.pane,o=t.node;if(o!==void 0&&e!==void 0)throw new v({error:"conflicting_target",message:"--node and --pane are mutually exclusive",next:"Pass the source node id or its viewer pane, not both."});let s=o;if(s===void 0&&e!==void 0&&(s=W(e)),s===void 0||s==="")throw new v({error:"no_node",message:"no source node was provided or found in the pane",next:"Pass --node <id>, or --pane <pane-id> for a pane showing a node."});const p=await _().forkNode(s).catch(y),r=await B(p.node_id,{cwd:p.cwd,name:p.name,...e!==void 0?{pane:e}:{},newPane:!0});return{node_id:p.node_id,focused:r.focused,session:r.session}},"run"),render:h(t=>{const e=t.focused===!0?` Viewer opened beside the source${t.session?` in session ${t.session}`:""}.`:" No viewer opened because no tmux pane was available.";return`Forked independent idle root ${t.node_id}.${e} It will make no model call until its first real user message.`},"render")}),de=P({name:"inspect",description:"read one node \u2014 its graph position, context, and artifacts",whenToUse:"reading everything about ONE node you can name: enumerate the roster (`list`), inspect a node and its spine neighbors (`show`), browse its on-disk context dir (`context`), or list its reports/docs as refs (`artifacts`). Use `canvas dashboard` instead to render the whole tree SHAPE, or `canvas attention` to find which nodes are blocked on the user",help:{name:"node inspect",summary:"read one node (or the roster) \u2014 topology, context dir, and artifacts",model:"Inspection is read-only and never revives a node. The roster is the selection surface; one-node metadata points to progressively heavier views: a parsed broker snapshot, rendered conversation, context browser, or artifact references."},children:[K,M,C,F,L,U]});export{ae as nodeFork,de as nodeInspect};
12
+ `)},"render")}),oe=b({name:"fork",description:"fork a node into an independent idle root with the same conversation and durable context",whenToUse:"a conversation has already accumulated the context you want, and you want a new user-driven branch beside it without sending a prompt or inheriting any graph-management responsibility. This copies the conversation and durable context, then waits for the fork\u2019s first real user message to supply its mandate. Use `node new --fork-from` instead when spawning a managed worker with an immediate task",help:{name:"node fork",summary:"create an independent resident root from a node\u2019s conversation and durable context, with no kickoff prompt or model turn",params:[{kind:"flag",name:"node",type:"string",required:!1,constraint:"Source node id. Mutually exclusive with --pane; one of them is required."},{kind:"flag",name:"pane",type:"string",required:!1,constraint:"tmux pane showing the source node. Mutually exclusive with --node; one of them is required. The prefix menu passes this for you and the new viewer splits beside this pane."}],output:[{name:"node_id",type:"string",required:!0,constraint:"The new independent root id."},{name:"focused",type:"boolean",required:!0,constraint:"True when a new viewer pane opened beside the caller; false outside tmux."},{name:"session",type:"string",required:!1,constraint:"The tmux session containing the new viewer, when one opened."}],outputKind:"object",effects:["Creates a resident base-mode root with the source node\u2019s copied pi conversation, cwd, profile, kind, model override, and durable context files. Top-level initial-prompt.md, yield-message.md, result.json, and the jobs/ folder of background bash jobs are omitted.","Copies no parent, subscriptions, subscribers, children, crons, worktree, situational context, or lifecycle responsibility. Sends no prompt and consumes no model turn; the first real user message becomes the fork\u2019s mandate.","Inside tmux, opens and focuses a surface-attach viewer as an even-horizontal split in the source pane\u2019s window. Outside tmux, the root is still created but no interactive fallback opens."]},run:h(async n=>{P("act","crtr node fork");const e=n.pane,o=n.node;if(o!==void 0&&e!==void 0)throw new y({error:"conflicting_target",message:"--node and --pane are mutually exclusive",next:"Pass the source node id or its viewer pane, not both."});let s=o;if(s===void 0&&e!==void 0&&(s=E(e)),s===void 0||s==="")throw new y({error:"no_node",message:"no source node was provided or found in the pane",next:"Pass --node <id>, or --pane <pane-id> for a pane showing a node."});const u=await $().forkNode(s).catch(w),d=await z(u.node_id,{cwd:u.cwd,name:u.name,...e!==void 0?{pane:e}:{},newPane:!0});return{node_id:u.node_id,focused:d.focused,session:d.session}},"run"),render:h(n=>{const e=n.focused===!0?` Viewer opened beside the source${n.session?` in session ${n.session}`:""}.`:" No viewer opened because no tmux pane was available.";return`Forked independent idle root ${n.node_id}.${e} It will make no model call until its first real user message.`},"render")}),ie=R({name:"inspect",description:"read one node \u2014 its graph position, conversation, and context dir",whenToUse:"reading everything about ONE node you can name: enumerate the roster (`list`), inspect a node and its spine neighbors (`show`), or browse its on-disk context dir (`context`). The documents a node owns are `crtr canvas list --owner <id>`. Use `canvas dashboard` instead to render the whole tree SHAPE, or `canvas attention` to find which nodes are blocked on the user",help:{name:"node inspect",summary:"read one node (or the roster) \u2014 topology, conversation, and context dir",model:"Inspection is read-only and never revives a node. The roster is the selection surface; one-node metadata points to progressively heavier views: a parsed broker snapshot, rendered conversation, or context browser."},children:[B,K,F,L,A]});export{oe as nodeFork,ie as nodeInspect};
@@ -1,2 +1,2 @@
1
- var E=Object.defineProperty;var l=(e,t)=>E(e,"name",{value:t,configurable:!0});import{envNodeId as b}from"../../shared/env.js";import{defineBranch as C,defineLeaf as y}from"../../core/command.js";import{InputError as d}from"../../core/io.js";import{cliClient as u,getNodeOrNull as D,rethrowAsCliError as m}from"../api-client.js";import{DEFAULT_BASH_NICENESS as T,MAX_BASH_NICENESS as v,writeBashNiceness as $}from"../../core/bash-jobs.js";import{nodeReviveLeaf as P}from"../node-lifecycle-revive.js";import{kindsStateBlock as k}from"../profile/daemon-profiles.js";import{MODEL_SPEC_FORMS as S}from"../../core/runtime/model-selection.js";import{closePane as I,paneExists as R,respawnPaneSync as O,viewerEnv as U,viewerPaneOf as L,nodeInPane as _}from"../surface/node/placement.js";const j=y({name:"recycle",description:"finish the agent in your pane + recycle it into a fresh root",whenToUse:"you are at an agent's pane and done with it: finish it cleanly and recycle the pane in one move \u2014 push its last message as a final report to everyone waiting on it, mark it done, then boot a fresh crtr root in the same pane to keep working. The hands-on way to end an agent and immediately start over in place. Use `node lifecycle demote` instead to put it on a finishing track IN PLACE (flip terminal, keep it running) without ending it now, `node lifecycle close` to tear a node and its subtree down WITHOUT finishing (no report, revivable), and `push final` when the agent should finish ITSELF from inside its own turn",help:{name:"node lifecycle recycle",summary:"finish the agent in your current pane and recycle the pane \u2014 push its last message as a final report to everyone waiting on it, mark it done, then boot a fresh crtr root in the same pane",params:[{kind:"flag",name:"node",type:"string",required:!1,constraint:"Node to finish. Defaults to the node occupying --pane (or your current pane)."},{kind:"flag",name:"pane",type:"string",required:!1,constraint:"tmux pane id to recycle. Defaults to $TMUX_PANE / your current pane. The Alt+C menu passes this for you."}],output:[{name:"recycled",type:"boolean",required:!0,constraint:"True when the pane was recycled into a fresh root."},{name:"node_id",type:"string",required:!1,constraint:"The finished node."},{name:"new_root",type:"string",required:!1,constraint:"The fresh root node booted into the pane."}],outputKind:"object",effects:["Pushes a final report from the node (fans out to all subscribers) and marks it done.","Kills the agent's pi and respawns a fresh resident root in the same tmux pane."]},run:l(async e=>{const t=e.pane??process.env.TMUX_PANE;let n=e.node;if((n===void 0||n==="")&&(n=_(t)),n===void 0||n==="")throw new d({error:"no_node",message:"no node found in this pane to finish",next:"Pass --node <id>, or run from inside the agent's pane."});const i=await u().getNode(n).catch(c=>m(c,"List nodes with `crtr node inspect list`.")),o=(await u().recycleNode(n).catch(m)).node_id;let r=!1;return t!==void 0&&t!==""&&(r=O({pane:t,cwd:i.cwd,env:U(),command:"crtr"})),{recycled:r,node_id:n,new_root:o}},"run"),render:l(e=>{if(e.recycled!==!0)return e.new_root?`Finished ${e.node_id} and spawned a fresh broker root (${e.new_root}), but its viewer did not attach \u2014 the new broker did not come up. Re-focus the root to view it.`:`Recycle failed for ${e.node_id??"?"} \u2014 not in tmux, or no agent in this pane.`;const t=[`Recycled the pane \u2014 finished ${e.node_id}.`];return e.new_root&&t.push(`- new root: ${e.new_root}`),t.join(`
2
- `)},"render")}),M=y({name:"close",description:"close a node + cascade-cancel its exclusive subtree",whenToUse:"you want to tear a node down WITHOUT finishing it, cascade-cancelling every descendant it exclusively owns: abandoning a line of work, killing a stuck or wrong-turn subtree, clearing a branch you no longer need. A node that never produced substantive assistant output is deleted and cannot be revived; streaming nodes and nodes with an open managed worktree are retained. Other closed nodes keep their pi session and can be revived later (`node lifecycle revive`). Use `node lifecycle recycle` instead to FINISH the agent in your pane with a final report, and `push final` when a worker should end its own work normally (Alt+C \u2192 x)",help:{name:"node lifecycle close",summary:"close a node and cascade-cancel its subtree \u2014 kill its tmux window plus those of every descendant it EXCLUSIVELY owns (down the subscribes_to spine), mark retained nodes canceled, and leave them a notice they read on resume. A descendant still subscribed to by a manager outside the subtree is left running. A node that never produced substantive assistant output is deleted and cannot be revived; streaming nodes and nodes with an open managed worktree are retained. Other closed nodes keep their pi session and can be revived later (`crtr node lifecycle revive`)",params:[{kind:"flag",name:"node",type:"string",required:!1,constraint:"Node to close. Defaults to the node occupying --pane (or your current pane)."},{kind:"flag",name:"pane",type:"string",required:!1,constraint:"tmux pane id whose node to close. Defaults to $TMUX_PANE / your current pane. The Alt+C menu passes this for you."}],output:[{name:"node_id",type:"string",required:!1,constraint:"The node that was closed \u2014 the cascade root."},{name:"count",type:"number",required:!1,constraint:"How many nodes were closed (root + cascaded descendants)."},{name:"closed_ids",type:"string[]",required:!1,constraint:"All closed node ids, kill order (leaves first, root last)."},{name:"spared",type:"string[]",required:!1,constraint:"Descendants left alive because a manager outside the subtree still subscribes to them."}],outputKind:"object",effects:["Marks retained nodes and their exclusive descendants `canceled` and clears intent (the daemon never revives a canceled node).","Deletes a non-streaming node with no substantive assistant output unless it has an open managed worktree; a deleted node cannot be revived.","Kills each closed node's tmux window; retained nodes keep their pi sessions and canvas edges for a later revive.","Appends a cancellation notice to each retained node's inbox, surfaced on its next resume."]},run:l(async e=>{const t=e.pane??process.env.TMUX_PANE;let n=e.node;if((n===void 0||n==="")&&(n=_(t)),n===void 0||n==="")throw new d({error:"no_node",message:"no node found in this pane to close",next:"Pass --node <id>, or run from inside the agent's pane."});const i=await u().closeNode(n,process.ppid>0?{caller_pid:process.ppid}:{}).catch(s=>m(s,"List nodes with `crtr node inspect list`."));return{node_id:i.root,count:i.closed.length,closed_ids:i.closed,spared:i.spared}},"run"),render:l(e=>{const t=e.spared?.length??0;return`Closed ${e.node_id} and its exclusive subtree \u2014 ${e.count} node(s) closed${t>0?`, ${t} spared (still managed from outside the subtree)`:""}.`},"render")}),q="Model change first: a running broker, including one still starting right after `node new`, switches over its socket; a dormant node persists the durable model override and launch recipe for its next revive.",Q=y({name:"config",description:"reconfigure a node's settings headlessly \u2014 model, lifecycle (terminal/resident), kind, mode, name; repair a missing cwd",whenToUse:"you want to reconfigure a node's settings headlessly after spawn \u2014 swap its model/provider, flip it terminal\u2194resident (interactable), respecialize its kind, switch base\u2194orchestrator mode, or rename it \u2014 without opening its viewer. It also repairs a dormant node whose recorded cwd is gone, starting its next revive in a fresh conversation. The durable attribute setter; reuses the same recipes a revive reads. Use `node promote` for the opinionated self-promotion path, and `node lifecycle` for teardown actions (recycle/close/demote).",help:{name:"node config",summary:"reconfigure a node's settings headlessly \u2014 model, lifecycle, kind, mode, name; repair a missing cwd",params:[{kind:"flag",name:"node",type:"string",required:!1,constraint:"Target node id. Defaults to the node in --pane, else CRTR_NODE_ID."},{kind:"flag",name:"pane",type:"string",required:!1,constraint:"tmux pane id to resolve the node from. Defaults to $TMUX_PANE."},{kind:"flag",name:"model",type:"string",required:!1,constraint:"Durable model change. Omit unless the task or governing guidance requires a different model.",focusedHelp:{whenToUse:"Use only when intentionally changing this node\u2019s durable model because the user, task, or governing agent guidance requires a specific provider, model, capability tier, or family. Omit it to keep the current model; do not use it merely to inspect available models or experiment with a live node.",value:`Accepted durable forms: ${S}. A live broker also accepts a free-text substring matched against its loaded model registry; a dormant node cannot resolve a substring and requires a durable form.`,effects:[q,"A concrete provider/id runs exactly that model on every launch and revive. A portable provider/tier, bare tier, or family alias keeps its tier: when that model's provider is rate-limited or rejects its credential, the node moves to the next model in its route: another provider's model at the same tier first, then the configured fallback tier (strong by default)."]}},{kind:"flag",name:"lifecycle",type:"enum",choices:["terminal","resident"],required:!1,constraint:"Set lifecycle headlessly without closing the viewer. terminal owes a final and reaps when done; resident stays interactable while attached or awaiting something live."},{kind:"flag",name:"kind",type:"string",required:!1,constraint:"Persona kind. The <kinds> list below names every top-level installable kind and when to use each; a registered sub-kind is valid too by exact path but not listed here."},{kind:"flag",name:"mode",type:"enum",choices:["base","orchestrator"],required:!1,constraint:"Set persona mode headlessly. base is hands-on; orchestrator holds a roadmap and delegates. orchestrator seeds a roadmap scaffold if absent."},{kind:"flag",name:"name",type:"string",required:!1,constraint:"Rename the node; if the node has a live window, also rename that viewer window."},{kind:"flag",name:"nice",type:"int",required:!1,constraint:`Scheduler niceness every command this node's bash tool starts at, 0\u2013${v} (default ${T}). ${T} keeps the node's builds and test suites below the daemon, the brokers, the viewers, and the person's own shell; 0 lets them compete on equal terms with everything else on the host; ${v} makes them yield to it. Applies to the node's very next command, foreground and background alike.`},{kind:"flag",name:"cwd",type:"string",required:!1,constraint:"Repair-only replacement launch directory. Must be a non-empty absolute existing directory and used alone; the daemon accepts it only for a dormant, non-forked node with no open managed worktree whose recorded cwd is gone. Clears the saved Pi session, so the next revive starts fresh while durable node work remains."}],output:[{name:"node_id",type:"string",required:!1,constraint:"The reconfigured node, present for ordinary config changes."},{name:"model",type:"string",required:!1,constraint:"The resolved model, present only when --model changed it."},{name:"lifecycle",type:"string",required:!1,constraint:"The new lifecycle, present only when --lifecycle changed it."},{name:"kind",type:"string",required:!1,constraint:"The new kind, present only when --kind changed it."},{name:"mode",type:"string",required:!1,constraint:"The new mode, present only when --mode changed it."},{name:"name",type:"string",required:!1,constraint:"The new name, present only when --name changed it."},{name:"nice",type:"number",required:!1,constraint:"The new bash niceness, present only when --nice changed it."},{name:"cwd",type:"string",required:!1,constraint:"The lexically resolved replacement directory, present only after a cwd repair."}],outputKind:"object",effects:[q,"Kind, mode, and lifecycle changes rebuild the launch spec from the fresh node meta; setting mode to orchestrator seeds a roadmap scaffold if absent.","Name changes update the row and, when the node already has a live window, rename that viewer window to the new full name.","A cwd repair writes only the lexically resolved cwd and clears saved Pi session pointers; the old transcript and durable node work remain untouched.","A niceness change is stored with the node's own bash-job state, so it takes effect on the next command without a revive and survives one."]},helpState:{leaf:k},run:l(async e=>{const t=e.pane??process.env.TMUX_PANE,n=await A(e,t),i=e.cwd;if(i!==void 0&&["model","kind","lifecycle","mode","name","nice"].some(w=>e[w]!==void 0))throw new d({error:"cwd_not_exclusive",message:"cwd repair cannot be combined with another config flag",field:"cwd",next:"Pass --cwd alone."});const s=e.model?.trim();if(s==="")throw new d({error:"empty_spec",message:"a model spec is required",field:"model",next:"Run `crtr node config --model -h` and read the focused contract before retrying."});const o=e.kind?.trim(),r=e.lifecycle?.trim(),c=e.mode?.trim(),x=e.name,h=e.nice;if(h!==void 0&&(h<0||h>v))throw new d({error:"bad_nice",message:`niceness must be between 0 and ${v}: ${h}`,field:"nice",next:`A negative niceness needs root, so pass 0\u2013${v}.`});if(r!==void 0&&r.toLowerCase()!=="terminal"&&r.toLowerCase()!=="resident")throw new d({error:"bad_lifecycle",message:`invalid lifecycle: ${r}`,field:"lifecycle",next:"Pass `terminal` or `resident`."});if(c!==void 0&&c.toLowerCase()!=="base"&&c.toLowerCase()!=="orchestrator")throw new d({error:"bad_mode",message:`invalid mode: ${c}`,field:"mode",next:"Pass `base` or `orchestrator`."});const a=[],p={};if(s!==void 0&&(p.model=s,a.push("model")),o!==void 0&&(p.kind=o,a.push("kind")),r!==void 0&&(p.lifecycle=r.toLowerCase(),a.push("lifecycle")),c!==void 0&&(p.mode=c.toLowerCase(),a.push("mode")),x!==void 0&&(p.name=x,a.push("name")),i!==void 0&&(p.cwd=i,a.push("cwd")),h!==void 0){const w=await u().getNode(n).catch(N=>m(N));if($(w.paths.context_dir,h),a.push("nice"),a.length===1)return{node_id:n,nice:h}}if(a.length===0)throw new d({error:"no_change",message:"no config changes requested",next:"Pass at least one of --model, --lifecycle, --kind, --mode, --name, --nice, --cwd."});const g=await u().patchConfig(n,p).catch(w=>m(w,"List nodes with `crtr node inspect list`."));if(i!==void 0)return{cwd:g.cwd};const f={node_id:n};return a.includes("model")&&(f.model=g.model_override??s),a.includes("lifecycle")&&(f.lifecycle=g.lifecycle),a.includes("kind")&&(f.kind=g.kind),a.includes("mode")&&(f.mode=g.mode),a.includes("name")&&(f.name=g.name),a.includes("nice")&&(f.nice=h),f},"run"),render:l(e=>{if(e.cwd!==void 0)return`Repaired cwd: ${e.cwd}.`;const t=[];return e.model!==void 0&&t.push(`model=${e.model}`),e.lifecycle!==void 0&&t.push(`lifecycle=${e.lifecycle}`),e.kind!==void 0&&t.push(`kind=${e.kind}`),e.mode!==void 0&&t.push(`mode=${e.mode}`),e.name!==void 0&&t.push(`name=${e.name}`),e.nice!==void 0&&t.push(`nice=${e.nice}`),`Configured ${e.node_id} \u2014 ${t.join(", ")}`},"render")}),Z=y({name:"promote",description:"become an orchestrator of a chosen kind",whenToUse:"the remaining work contains independent units that can run in parallel, and the task is large enough that coordinating and integrating them will materially improve intelligence, productivity, or elapsed throughput. Become a long-lived, roadmap-holding orchestrator whose primary job is steering those children across refreshes (`node yield`). Keep sequential or tightly coupled work in base mode even when it spans phases or context windows; spawning one helper does not itself require promotion",tier:"important",help:{name:"node promote",summary:"promote yourself to an orchestrator when worthwhile parallel delegation should make coordination your primary job; task duration, sequential phases, or one helper do not qualify. Mode only \u2014 lifecycle stays as-is unless you pass --resident",params:[{kind:"flag",name:"kind",type:"string",required:!1,constraint:"Specialize as this kind of orchestrator. The <kinds> list below names every top-level installable kind and when to use each; a registered sub-kind is valid too by exact path but not listed here. For an exact composed persona list, use `crtr sys context prompt-review --list --kind <kind>` with the target kind. Defaults to your current kind. Promoting from a generic kind? CHOOSE a concrete one \u2014 it sets the orchestrator persona you revive into."},{kind:"flag",name:"resident",type:"bool",required:!1,constraint:"ALSO flip lifecycle\u2192resident: make the node interactable \u2014 it is never forced to submit a final, and unattended grace parks it after a summary turn instead, leaving it revivable fresh against its goal and roadmap. Omit to stay terminal/orchestrator (delegates + holds a roadmap, but still owes a final up the spine and reaps when done)."},{kind:"flag",name:"model",type:"enum",choices:["ultra","strong"],required:!1,constraint:"Change the model you run on, by capability tier: `ultra` (frontier \u2014 reserve for high-taste judgment or enormous work: speccing, planning something large, e2e-testing something hard to test) or `strong` (opus \u2014 regular dev work). Omit to keep your current model. An orchestrator steering work is never weaker than opus, so these are the only two choices; the change is durable across future revives."},{kind:"flag",name:"node",type:"string",required:!1,constraint:"Node to promote. Defaults to the caller (CRTR_NODE_ID)."}],output:[{name:"node_id",type:"string",required:!0,constraint:"The promoted node."},{name:"kind",type:"string",required:!0,constraint:"The kind it now orchestrates as."},{name:"model",type:"string",required:!1,constraint:"The model tier it now runs on, present only when you changed it with --model."},{name:"lifecycle",type:"string",required:!0,constraint:'"resident" if you passed --resident, else unchanged (typically "terminal").'},{name:"roadmap_written",type:"boolean",required:!0,constraint:"True if a roadmap scaffold was seeded by this call."},{name:"roadmap_path",type:"string",required:!0,constraint:"Absolute path to your roadmap doc (context/roadmap.md) \u2014 edit it to author your plan."},{name:"goal_path",type:"string",required:!0,constraint:"Absolute path to your goal doc (context/initial-prompt.md) \u2014 the mandate you were spawned with."}],outputKind:"object",effects:["Flips mode\u2192orchestrator + kind\u2192chosen (lifecycle unchanged unless --resident, which also flips lifecycle\u2192resident); --model durably sets the model; rewrites the launch spec to that kind's orchestrator persona; seeds context/roadmap.md scaffold if absent.","The next model request is gated on appending and persisting your new-role guidance. If a fresh context starts first, its rebuilt baseline carries the new role."]},helpState:{leaf:k},run:l(async e=>{const t=e.node??b();if(t===void 0||t==="")throw new d({error:"no_node",message:"no node to promote (set CRTR_NODE_ID or pass --node)",next:"Run from inside a node, or pass --node <id>."});const n=e.kind,i=e.model,s=e.resident===!0,o=await u().promoteNode(t,{...n!==void 0?{kind:n}:{},...s?{resident:!0}:{},...i!==void 0?{model:i}:{}}).catch(r=>m(r));return{node_id:o.node_id,kind:o.kind,lifecycle:o.lifecycle,...i!==void 0&&o.model_override!=null?{model:o.model_override}:{},roadmap_written:o.roadmap_written??!1,roadmap_path:o.roadmap_path??"",goal_path:o.goal_path??""}},"run")});async function A(e,t){let n=e.node;if((n===void 0||n==="")&&(n=_(t)),(n===void 0||n==="")&&(n=b()),n===void 0||n==="")throw new d({error:"no_node",message:"no node (set CRTR_NODE_ID, pass --node, or run from the agent's pane)",next:"Run from inside a node, pass --node <id>, or --pane <pane>."});if(await D(n)===null)throw new d({error:"not_found",message:`no node: ${n}`,next:"List nodes with `crtr node inspect list`."});return n}l(A,"resolveLifecycleNode");const F=y({name:"demote",description:"demote a node to terminal in place; it stays focused and running but now owes a final; --detach also closes its viewer (broker keeps running off-screen)",whenToUse:"you are watching a resident/interactive node and want to put it on a finishing track WITHOUT disturbing it: flip it terminal IN PLACE \u2014 it keeps its viewer and your focus, keeps running, but now owes a final report up the spine and reaps when done. The friendly counterpart to `node promote`. You can also let go entirely and close its viewer so the agent finishes off-screen on its broker. Use `node lifecycle recycle` instead to FINISH it now and reboot a fresh root in its pane, and `node config --lifecycle` for the orthogonal low-level flip (incl. terminal\u2192resident)",help:{name:"node lifecycle demote",summary:"demote a node to terminal IN PLACE \u2014 it stays focused and running but now owes a final up the spine and reaps when done. Pairs with `node promote` (mode\u2191); `--detach` also closes its viewer (the broker engine keeps running off-screen)",params:[{kind:"flag",name:"node",type:"string",required:!1,constraint:"Node to demote. Defaults to the node in --pane, else the caller (CRTR_NODE_ID)."},{kind:"flag",name:"pane",type:"string",required:!1,constraint:"tmux pane id whose node to demote, when --node is omitted. Defaults to $TMUX_PANE. The Alt+C menu passes this for you."},{kind:"flag",name:"detach",type:"bool",required:!1,constraint:"After flipping terminal, stop foregrounding the agent by closing its viewer pane. The broker engine keeps generating off-screen and \u2014 now terminal \u2014 pushes a final up the spine when done; a later `focus` reattaches a viewer. The Alt+C \u2192 D move."}],output:[{name:"node_id",type:"string",required:!0,constraint:"The demoted node."},{name:"detached",type:"boolean",required:!1,constraint:"True when --detach closed the agent's viewer pane (broker keeps running)."}],outputKind:"object",effects:["Flips the node's lifecycle\u2192terminal and rebuilds its launch spec so a future revive boots terminal \u2014 it stays focused and running, now owing a final up the spine.","The next model request is gated on appending and persisting the role transition. If a fresh context starts first, its rebuilt baseline carries the new lifecycle.","With --detach: closes the agent's viewer pane WITHOUT touching the broker engine \u2014 it keeps generating off-screen, reconnectable by a later `focus`."]},run:l(async e=>{const t=e.pane??process.env.TMUX_PANE,n=await A(e,t),i=await u().patchConfig(n,{lifecycle:"terminal"}).catch(o=>m(o));let s=!1;if(e.detach===!0){const o=L(n)??t;o!==void 0&&o!==""&&R(o)&&(s=I(o))}return{node_id:i.node_id,detached:s}},"run"),render:l(e=>`Demoted ${e.node_id} \u2014 lifecycle now terminal (in place)${e.detached===!0?", viewer closed (broker still running off-screen)":""}.`,"render")}),ee=C({name:"lifecycle",description:"lifecycle actions \u2014 recycle, close, demote, or revive a node",whenToUse:"you are ending, transitioning, or bringing back one node you can name: finish the agent in your pane and reboot a fresh root (`recycle`), tear a node + its exclusive subtree down without finishing (`close`), flip a node onto a finishing track in place (`demote`), or bring a dormant node back (`revive`). To merely SET the terminal/resident attribute headlessly use `node config --lifecycle`; to FINISH from inside a node's own turn use `crtr push final`; to mass-reconnect EVERY disconnected node at once use `canvas revive --all`.",help:{name:"node lifecycle",summary:"end, transition, or revive a node \u2014 recycle the pane, close + cascade-cancel a subtree, demote to terminal in place, or revive a dormant node",model:"Lifecycle actions preserve or replace an existing node identity. Finishing is permanent, closing is revivable, demotion changes the completion obligation in place, and revival relaunches a dormant broker. Teardown cascades only through descendants exclusively managed by the affected subtree."},children:[j,M,F,P]}),ne=y({name:"yield",description:"refresh your context, same mode, against your roadmap or goal",whenToUse:"your context is filling but the mandate remains; refresh in the same mode, or combine the refresh with promotion when meaningful independent work can run in parallel and coordinating it should become your primary job",help:{name:"node yield",summary:"request a fresh context window against your roadmap or original goal; mode stays unchanged unless --promote is passed",guide:"The yield aborts your turn the instant this command returns: no text or tool call you planned after it will run. So anything the user is owed \u2014 an answer, a status \u2014 must already be a visible message in the conversation before you call it; never plan to reply after yielding. Before invoking: (1) Bring `$CRTR_CONTEXT_DIR/roadmap.md` current \u2014 it, not the yield note, carries the goal into the fresh window. Keep its seeded sections (Goal, Exit criteria, Phases) and keep it short: strategy and present state only \u2014 what is settled, what remains, the current phase\u2019s one-line status, plus dead ends worth not repeating. Delete completed items instead of marking them done; the roadmap should shrink as work completes. (2) Move anything worth remembering but not needed next window into memory (`crtr memory write -h`): node-local for lessons specific to this node\u2019s task, the workspace store for lessons that would benefit other agents working here. (3) Send any pending human question; in-flight asks survive the refresh.",params:[{kind:"flag",name:"promote",type:"bool",required:!1,constraint:"Also become an orchestrator as you refresh. Pass only when (1) the remaining work contains independent units that can run in parallel and (2) the task is large enough that parallel execution will materially improve intelligence, productivity, or elapsed throughput after coordination and synthesis. Task length, context exhaustion, sequential phases, or one helper are not enough; keep hands-on work in base mode and yield again as needed. Promote when coordinating and integrating children becomes your primary job. Already an orchestrator? This is not an error: your mode stays orchestrator and a missing roadmap is seeded."},{kind:"flag",name:"kind",type:"string",required:!1,constraint:"Respecialize as you refresh. The <kinds> list below names every top-level available kind and when to choose it; a registered sub-kind is valid too by exact path but not listed here. Defaults to your current kind. With --promote it sets the orchestrator kind you become; without --promote it changes the kind while preserving your current mode."},{kind:"flag",name:"model",type:"enum",choices:["ultra","strong"],required:!1,constraint:"Change the model your fresh revive runs on, by capability tier: `ultra` (frontier \u2014 reserve for high-taste judgment or enormous work: speccing, planning something large, e2e-testing something hard to test) or `strong` (opus \u2014 regular dev work). Omit to keep your current model. A node steering work is never weaker than opus, so these are the only two choices; the change is durable across future revives."},{kind:"stdin",name:"message",required:!0,constraint:"A note to your future self \u2014 what you just tried, where things stand, and the first move on waking: enough to continue efficiently, no more. If the user asked something you have not answered in a visible message, say so and quote the question. Never describe a reply as sent unless it is visible in the conversation above this call. Durable strategy belongs in the roadmap, not here. Surfaced as <yield-message> in the next revive. Pass as a positional or pipe via heredoc."}],output:[{name:"node_id",type:"string",required:!0,constraint:"The yielding node."},{name:"model",type:"string",required:!1,constraint:"The model tier your fresh revive will run on, present only when you changed it with --model."}],outputKind:"object",effects:["Aborts your current turn as soon as this command returns \u2014 nothing after it runs \u2014 then the daemon revives the node fresh.","--kind and/or --model durably reshape the node (kind respecialization, model change) before the refresh-revive; --promote additionally flips mode\u2192orchestrator and seeds a roadmap scaffold \u2014 mode is otherwise left exactly as it was."]},helpState:{leaf:k},run:l(async e=>{const t=b();if(t===void 0||t==="")throw new d({error:"no_node",message:"no node to yield (CRTR_NODE_ID unset)",next:"Run from inside a node."});const n=e.kind,i=e.model,s=e.promote===!0,o=typeof e.message=="string"?e.message.trim():"";if(o==="")throw new d({error:"empty_message",message:"a yield message is required (stdin or positional)",next:"Pass a note to your future self as an argument or pipe it on stdin."});const r=await u().yieldNode(t,{note:o,...n!==void 0?{kind:n}:{},...i!==void 0?{model:i}:{},...s?{promote:!0}:{}}).catch(c=>m(c));return{node_id:r.node_id,...i!==void 0&&r.model_override!=null?{model:r.model_override}:{}}},"run")});export{Q as nodeConfig,ee as nodeLifecycle,Z as nodePromote,ne as nodeYield};
1
+ var C=Object.defineProperty;var l=(e,t)=>C(e,"name",{value:t,configurable:!0});import{envNodeId as k}from"../../shared/env.js";import{defineBranch as $,defineLeaf as y}from"../../core/command.js";import{InputError as d}from"../../core/io.js";import{cliClient as f,getNodeOrNull as P,rethrowAsCliError as p}from"../api-client.js";import{DEFAULT_BASH_NICENESS as N,MAX_BASH_NICENESS as v,writeBashNiceness as D}from"../../core/bash-jobs.js";import{nodeReviveLeaf as I}from"../node-lifecycle-revive.js";import{kindsStateBlock as _}from"../profile/daemon-profiles.js";import{MODEL_SPEC_FORMS as O}from"../../core/runtime/model-selection.js";import{closePane as R,paneExists as U,respawnPaneSync as L,viewerEnv as j,viewerPaneOf as M,nodeInPane as x}from"../surface/node/placement.js";const F=y({name:"recycle",description:"finish the agent in your pane + recycle it into a fresh root",whenToUse:"you are at an agent's pane and done with it: finish it cleanly and recycle the pane in one move \u2014 push its last message as a final report to everyone waiting on it, mark it done, then boot a fresh crtr root in the same pane to keep working. The hands-on way to end an agent and immediately start over in place. Use `node lifecycle demote` instead to put it on a finishing track IN PLACE (flip terminal, keep it running) without ending it now, `node lifecycle close` to tear a node and its subtree down WITHOUT finishing (no report, revivable), and `push final` when the agent should finish ITSELF from inside its own turn",help:{name:"node lifecycle recycle",summary:"finish the agent in your current pane and recycle the pane \u2014 push its last message as a final report to everyone waiting on it, mark it done, then boot a fresh crtr root in the same pane",params:[{kind:"flag",name:"node",type:"string",required:!1,constraint:"Node to finish. Defaults to the node occupying --pane (or your current pane)."},{kind:"flag",name:"pane",type:"string",required:!1,constraint:"tmux pane id to recycle. Defaults to $TMUX_PANE / your current pane. The Alt+C menu passes this for you."}],output:[{name:"recycled",type:"boolean",required:!0,constraint:"True when the pane was recycled into a fresh root."},{name:"node_id",type:"string",required:!1,constraint:"The finished node."},{name:"new_root",type:"string",required:!1,constraint:"The fresh root node booted into the pane."}],outputKind:"object",effects:["Pushes a final report from the node (fans out to all subscribers) and marks it done.","Kills the agent's pi and respawns a fresh resident root in the same tmux pane."]},run:l(async e=>{const t=e.pane??process.env.TMUX_PANE;let n=e.node;if((n===void 0||n==="")&&(n=x(t)),n===void 0||n==="")throw new d({error:"no_node",message:"no node found in this pane to finish",next:"Pass --node <id>, or run from inside the agent's pane."});const i=await f().getNode(n).catch(c=>p(c,"List nodes with `crtr node inspect list`.")),o=(await f().recycleNode(n).catch(p)).node_id;let a=!1;return t!==void 0&&t!==""&&(a=L({pane:t,cwd:i.cwd,env:j(),command:"crtr"})),{recycled:a,node_id:n,new_root:o}},"run"),render:l(e=>{if(e.recycled!==!0)return e.new_root?`Finished ${e.node_id} and spawned a fresh broker root (${e.new_root}), but its viewer did not attach \u2014 the new broker did not come up. Re-focus the root to view it.`:`Recycle failed for ${e.node_id??"?"} \u2014 not in tmux, or no agent in this pane.`;const t=[`Recycled the pane \u2014 finished ${e.node_id}.`];return e.new_root&&t.push(`- new root: ${e.new_root}`),t.join(`
2
+ `)},"render")}),H=y({name:"close",description:"close a node + cascade-cancel its exclusive subtree",whenToUse:"you want to tear a node down WITHOUT finishing it, cascade-cancelling every descendant it exclusively owns: abandoning a line of work, killing a stuck or wrong-turn subtree, clearing a branch you no longer need. A node that never produced substantive assistant output is deleted and cannot be revived; streaming nodes and nodes with an open managed worktree are retained. Other closed nodes keep their pi session and can be revived later (`node lifecycle revive`). Use `node lifecycle recycle` instead to FINISH the agent in your pane with a final report, and `push final` when a worker should end its own work normally (Alt+C \u2192 x)",help:{name:"node lifecycle close",summary:"close a node and cascade-cancel its subtree \u2014 kill its tmux window plus those of every descendant it EXCLUSIVELY owns (down the subscribes_to spine), mark retained nodes canceled, and leave them a notice they read on resume. A descendant still subscribed to by a manager outside the subtree is left running. A node that never produced substantive assistant output is deleted and cannot be revived; streaming nodes and nodes with an open managed worktree are retained. Other closed nodes keep their pi session and can be revived later (`crtr node lifecycle revive`)",params:[{kind:"flag",name:"node",type:"string",required:!1,constraint:"Node to close. Defaults to the node occupying --pane (or your current pane)."},{kind:"flag",name:"pane",type:"string",required:!1,constraint:"tmux pane id whose node to close. Defaults to $TMUX_PANE / your current pane. The Alt+C menu passes this for you."}],output:[{name:"node_id",type:"string",required:!1,constraint:"The node that was closed \u2014 the cascade root."},{name:"count",type:"number",required:!1,constraint:"How many nodes were closed (root + cascaded descendants)."},{name:"closed_ids",type:"string[]",required:!1,constraint:"All closed node ids, kill order (leaves first, root last)."},{name:"spared",type:"string[]",required:!1,constraint:"Descendants left alive because a manager outside the subtree still subscribes to them."}],outputKind:"object",effects:["Marks retained nodes and their exclusive descendants `canceled` and clears intent (the daemon never revives a canceled node).","Deletes a non-streaming node with no substantive assistant output unless it has an open managed worktree; a deleted node cannot be revived.","Kills each closed node's tmux window; retained nodes keep their pi sessions and canvas edges for a later revive.","Appends a cancellation notice to each retained node's inbox, surfaced on its next resume."]},run:l(async e=>{const t=e.pane??process.env.TMUX_PANE;let n=e.node;if((n===void 0||n==="")&&(n=x(t)),n===void 0||n==="")throw new d({error:"no_node",message:"no node found in this pane to close",next:"Pass --node <id>, or run from inside the agent's pane."});const i=await f().closeNode(n,process.ppid>0?{caller_pid:process.ppid}:{}).catch(s=>p(s,"List nodes with `crtr node inspect list`."));return{node_id:i.root,count:i.closed.length,closed_ids:i.closed,spared:i.spared}},"run"),render:l(e=>{const t=e.spared?.length??0;return`Closed ${e.node_id} and its exclusive subtree \u2014 ${e.count} node(s) closed${t>0?`, ${t} spared (still managed from outside the subtree)`:""}.`},"render")}),A="Model change first: a running broker, including one still starting right after `node new`, switches over its socket; a dormant node persists the durable model override and launch recipe for its next revive.",ee=y({name:"config",description:"reconfigure a node's settings headlessly \u2014 model, lifecycle (terminal/resident), kind, mode, name; repair a missing cwd",whenToUse:"you want to reconfigure a node's settings headlessly after spawn \u2014 swap its model/provider, flip it terminal\u2194resident (interactable), respecialize its kind, switch base\u2194orchestrator mode, or rename it \u2014 without opening its viewer. It also repairs a dormant node whose recorded cwd is gone, starting its next revive in a fresh conversation. The durable attribute setter; reuses the same recipes a revive reads. Use `node promote` for the opinionated self-promotion path, and `node lifecycle` for teardown actions (recycle/close/demote).",help:{name:"node config",summary:"reconfigure a node's settings headlessly \u2014 model, lifecycle, kind, mode, name; repair a missing cwd",params:[{kind:"flag",name:"node",type:"string",required:!1,constraint:"Target node id. Defaults to the node in --pane, else CRTR_NODE_ID."},{kind:"flag",name:"pane",type:"string",required:!1,constraint:"tmux pane id to resolve the node from. Defaults to $TMUX_PANE."},{kind:"flag",name:"model",type:"string",required:!1,constraint:"Durable model change. Omit unless the task or governing guidance requires a different model.",focusedHelp:{whenToUse:"Use only when intentionally changing this node\u2019s durable model because the user, task, or governing agent guidance requires a specific provider, model, capability tier, or family. Omit it to keep the current model; do not use it merely to inspect available models or experiment with a live node.",value:`Accepted durable forms: ${O}. A live broker also accepts a free-text substring matched against its loaded model registry; a dormant node cannot resolve a substring and requires a durable form.`,effects:[A,"A concrete provider/id runs exactly that model on every launch and revive. A portable provider/tier, bare tier, or family alias keeps its tier: when that model's provider is rate-limited or rejects its credential, the node moves to the next model in its route: another provider's model at the same tier first, then the configured fallback tier (strong by default)."]}},{kind:"flag",name:"lifecycle",type:"enum",choices:["terminal","resident"],required:!1,constraint:"Set lifecycle headlessly without closing the viewer. terminal owes a final and reaps when done; resident stays interactable while attached or awaiting something live."},{kind:"flag",name:"kind",type:"string",required:!1,constraint:"Persona kind. The <kinds> list below names every top-level installable kind and when to use each; a registered sub-kind is valid too by exact path but not listed here."},{kind:"flag",name:"mode",type:"enum",choices:["base","orchestrator"],required:!1,constraint:"Set persona mode headlessly. base is hands-on; orchestrator holds a roadmap and delegates. orchestrator creates your document `roadmap` if absent."},{kind:"flag",name:"name",type:"string",required:!1,constraint:"Rename the node; if the node has a live window, also rename that viewer window."},{kind:"flag",name:"nice",type:"int",required:!1,constraint:`Scheduler niceness every command this node's bash tool starts at, 0\u2013${v} (default ${N}). ${N} keeps the node's builds and test suites below the daemon, the brokers, the viewers, and the person's own shell; 0 lets them compete on equal terms with everything else on the host; ${v} makes them yield to it. Applies to the node's very next command, foreground and background alike.`},{kind:"flag",name:"listen",type:"string",required:!1,constraint:`The node's default listening configuration, as JSON mapping an event level ("1".."10") to how events at or above it reach the node: interrupt, steer, after-turn, or next-turn (e.g. '{"3":"next-turn"}' \u2014 events of level 3+ wait for its next turn and never wake it). Applies to every watch with no configuration of its own. Pass \`null\` to reset to the global default.`},{kind:"flag",name:"cwd",type:"string",required:!1,constraint:"Repair-only replacement launch directory. Must be a non-empty absolute existing directory and used alone; the daemon accepts it only for a dormant, non-forked node with no open managed worktree whose recorded cwd is gone. Clears the saved Pi session, so the next revive starts fresh while durable node work remains."}],output:[{name:"node_id",type:"string",required:!1,constraint:"The reconfigured node, present for ordinary config changes."},{name:"model",type:"string",required:!1,constraint:"The resolved model, present only when --model changed it."},{name:"lifecycle",type:"string",required:!1,constraint:"The new lifecycle, present only when --lifecycle changed it."},{name:"kind",type:"string",required:!1,constraint:"The new kind, present only when --kind changed it."},{name:"mode",type:"string",required:!1,constraint:"The new mode, present only when --mode changed it."},{name:"name",type:"string",required:!1,constraint:"The new name, present only when --name changed it."},{name:"nice",type:"number",required:!1,constraint:"The new bash niceness, present only when --nice changed it."},{name:"listen",type:"object",required:!1,constraint:"The new default listening configuration (null = global default), present only when --listen changed it."},{name:"cwd",type:"string",required:!1,constraint:"The lexically resolved replacement directory, present only after a cwd repair."}],outputKind:"object",effects:[A,"Kind, mode, and lifecycle changes rebuild the launch spec from the fresh node meta; setting mode to orchestrator creates your document `roadmap` if absent.","Name changes update the row and, when the node already has a live window, rename that viewer window to the new full name.","A cwd repair writes only the lexically resolved cwd and clears saved Pi session pointers; the old transcript and durable node work remain untouched.","A niceness change is stored with the node's own bash-job state, so it takes effect on the next command without a revive and survives one.","A listen change sets the node's default listening configuration; it never changes which watches it holds.","Every successful change publishes a `configured` event on the node (level 1) naming the changed settings, so its watchers can see it."]},helpState:{leaf:_},run:l(async e=>{const t=e.pane??process.env.TMUX_PANE,n=await E(e,t),i=e.cwd;if(i!==void 0&&["model","kind","lifecycle","mode","name","nice","listen"].some(w=>e[w]!==void 0))throw new d({error:"cwd_not_exclusive",message:"cwd repair cannot be combined with another config flag",field:"cwd",next:"Pass --cwd alone."});const s=e.model?.trim();if(s==="")throw new d({error:"empty_spec",message:"a model spec is required",field:"model",next:"Run `crtr node config --model -h` and read the focused contract before retrying."});const o=e.kind?.trim(),a=e.lifecycle?.trim(),c=e.mode?.trim(),T=e.name,u=e.nice,b=e.listen?.trim();let q;if(b!==void 0)try{q=JSON.parse(b)}catch{throw new d({error:"invalid_listen",message:`--listen is not JSON: ${b}`,field:"listen",next:`Pass a JSON object such as '{"3":"next-turn"}', or null.`})}if(u!==void 0&&(u<0||u>v))throw new d({error:"bad_nice",message:`niceness must be between 0 and ${v}: ${u}`,field:"nice",next:`A negative niceness needs root, so pass 0\u2013${v}.`});if(a!==void 0&&a.toLowerCase()!=="terminal"&&a.toLowerCase()!=="resident")throw new d({error:"bad_lifecycle",message:`invalid lifecycle: ${a}`,field:"lifecycle",next:"Pass `terminal` or `resident`."});if(c!==void 0&&c.toLowerCase()!=="base"&&c.toLowerCase()!=="orchestrator")throw new d({error:"bad_mode",message:`invalid mode: ${c}`,field:"mode",next:"Pass `base` or `orchestrator`."});const r=[],h={};if(s!==void 0&&(h.model=s,r.push("model")),o!==void 0&&(h.kind=o,r.push("kind")),a!==void 0&&(h.lifecycle=a.toLowerCase(),r.push("lifecycle")),c!==void 0&&(h.mode=c.toLowerCase(),r.push("mode")),T!==void 0&&(h.name=T,r.push("name")),i!==void 0&&(h.cwd=i,r.push("cwd")),b!==void 0&&(h.listen=q??null,r.push("listen")),u!==void 0){const w=await f().getNode(n).catch(S=>p(S));if(D(w.paths.context_dir,u),r.push("nice"),r.length===1)return{node_id:n,nice:u}}if(r.length===0)throw new d({error:"no_change",message:"no config changes requested",next:"Pass at least one of --model, --lifecycle, --kind, --mode, --name, --nice, --listen, --cwd."});const g=await f().patchConfig(n,h).catch(w=>p(w,"List nodes with `crtr node inspect list`."));if(i!==void 0)return{cwd:g.cwd};const m={node_id:n};return r.includes("model")&&(m.model=g.model_override??s),r.includes("lifecycle")&&(m.lifecycle=g.lifecycle),r.includes("kind")&&(m.kind=g.kind),r.includes("mode")&&(m.mode=g.mode),r.includes("name")&&(m.name=g.name),r.includes("nice")&&(m.nice=u),r.includes("listen")&&(m.listen=h.listen),m},"run"),render:l(e=>{if(e.cwd!==void 0)return`Repaired cwd: ${e.cwd}.`;const t=[];return e.model!==void 0&&t.push(`model=${e.model}`),e.lifecycle!==void 0&&t.push(`lifecycle=${e.lifecycle}`),e.kind!==void 0&&t.push(`kind=${e.kind}`),e.mode!==void 0&&t.push(`mode=${e.mode}`),e.name!==void 0&&t.push(`name=${e.name}`),e.nice!==void 0&&t.push(`nice=${e.nice}`),e.listen!==void 0&&t.push(`listen=${JSON.stringify(e.listen)}`),`Configured ${e.node_id} \u2014 ${t.join(", ")}`},"render")}),ne=y({name:"promote",description:"become an orchestrator of a chosen kind",whenToUse:"the remaining work contains independent units that can run in parallel, and the task is large enough that coordinating and integrating them will materially improve intelligence, productivity, or elapsed throughput. Become a long-lived, roadmap-holding orchestrator whose primary job is steering those children across refreshes (`node yield`). Keep sequential or tightly coupled work in base mode even when it spans phases or context windows; spawning one helper does not itself require promotion",tier:"important",help:{name:"node promote",summary:"promote yourself to an orchestrator when worthwhile parallel delegation should make coordination your primary job; task duration, sequential phases, or one helper do not qualify. Mode only \u2014 lifecycle stays as-is unless you pass --resident",params:[{kind:"flag",name:"kind",type:"string",required:!1,constraint:"Specialize as this kind of orchestrator. The <kinds> list below names every top-level installable kind and when to use each; a registered sub-kind is valid too by exact path but not listed here. For an exact composed persona list, use `crtr sys context prompt-review --list --kind <kind>` with the target kind. Defaults to your current kind. Promoting from a generic kind? CHOOSE a concrete one \u2014 it sets the orchestrator persona you revive into."},{kind:"flag",name:"resident",type:"bool",required:!1,constraint:"ALSO flip lifecycle\u2192resident: make the node interactable \u2014 it is never forced to submit a final, and unattended grace parks it after a summary turn instead, leaving it revivable fresh against its goal and roadmap. Omit to stay terminal/orchestrator (delegates + holds a roadmap, but still owes a final up the spine and reaps when done)."},{kind:"flag",name:"model",type:"enum",choices:["ultra","strong"],required:!1,constraint:"Change the model you run on, by capability tier: `ultra` (frontier \u2014 reserve for high-taste judgment or enormous work: speccing, planning something large, e2e-testing something hard to test) or `strong` (opus \u2014 regular dev work). Omit to keep your current model. An orchestrator steering work is never weaker than opus, so these are the only two choices; the change is durable across future revives."},{kind:"flag",name:"node",type:"string",required:!1,constraint:"Node to promote. Defaults to the caller (CRTR_NODE_ID)."}],output:[{name:"node_id",type:"string",required:!0,constraint:"The promoted node."},{name:"kind",type:"string",required:!0,constraint:"The kind it now orchestrates as."},{name:"model",type:"string",required:!1,constraint:"The model tier it now runs on, present only when you changed it with --model."},{name:"lifecycle",type:"string",required:!0,constraint:'"resident" if you passed --resident, else unchanged (typically "terminal").'},{name:"roadmap_written",type:"boolean",required:!0,constraint:"True if the `roadmap` document was created by this call."},{name:"roadmap_ref",type:"string",required:!0,constraint:"Your roadmap document (`<node>/roadmap`) \u2014 author your plan with `crtr doc edit roadmap`."},{name:"goal_path",type:"string",required:!0,constraint:"Absolute path to your goal doc (context/initial-prompt.md) \u2014 the mandate you were spawned with."}],outputKind:"object",effects:["Flips mode\u2192orchestrator + kind\u2192chosen (lifecycle unchanged unless --resident, which also flips lifecycle\u2192resident); --model durably sets the model; rewrites the launch spec to that kind's orchestrator persona; creates your document `roadmap` (rule `on: boot`, `deliver: content`) if absent.","The next model request is gated on appending and persisting your new-role guidance. If a fresh context starts first, its rebuilt baseline carries the new role."]},helpState:{leaf:_},run:l(async e=>{const t=e.node??k();if(t===void 0||t==="")throw new d({error:"no_node",message:"no node to promote (set CRTR_NODE_ID or pass --node)",next:"Run from inside a node, or pass --node <id>."});const n=e.kind,i=e.model,s=e.resident===!0,o=await f().promoteNode(t,{...n!==void 0?{kind:n}:{},...s?{resident:!0}:{},...i!==void 0?{model:i}:{}}).catch(a=>p(a));return{node_id:o.node_id,kind:o.kind,lifecycle:o.lifecycle,...i!==void 0&&o.model_override!=null?{model:o.model_override}:{},roadmap_written:o.roadmap_written??!1,roadmap_ref:o.roadmap_ref??"",goal_path:o.goal_path??""}},"run")});async function E(e,t){let n=e.node;if((n===void 0||n==="")&&(n=x(t)),(n===void 0||n==="")&&(n=k()),n===void 0||n==="")throw new d({error:"no_node",message:"no node (set CRTR_NODE_ID, pass --node, or run from the agent's pane)",next:"Run from inside a node, pass --node <id>, or --pane <pane>."});if(await P(n)===null)throw new d({error:"not_found",message:`no node: ${n}`,next:"List nodes with `crtr node inspect list`."});return n}l(E,"resolveLifecycleNode");const W=y({name:"demote",description:"demote a node to terminal in place; it stays focused and running but now owes a final; --detach also closes its viewer (broker keeps running off-screen)",whenToUse:"you are watching a resident/interactive node and want to put it on a finishing track WITHOUT disturbing it: flip it terminal IN PLACE \u2014 it keeps its viewer and your focus, keeps running, but now owes a final report up the spine and reaps when done. The friendly counterpart to `node promote`. You can also let go entirely and close its viewer so the agent finishes off-screen on its broker. Use `node lifecycle recycle` instead to FINISH it now and reboot a fresh root in its pane, and `node config --lifecycle` for the orthogonal low-level flip (incl. terminal\u2192resident)",help:{name:"node lifecycle demote",summary:"demote a node to terminal IN PLACE \u2014 it stays focused and running but now owes a final up the spine and reaps when done. Pairs with `node promote` (mode\u2191); `--detach` also closes its viewer (the broker engine keeps running off-screen)",params:[{kind:"flag",name:"node",type:"string",required:!1,constraint:"Node to demote. Defaults to the node in --pane, else the caller (CRTR_NODE_ID)."},{kind:"flag",name:"pane",type:"string",required:!1,constraint:"tmux pane id whose node to demote, when --node is omitted. Defaults to $TMUX_PANE. The Alt+C menu passes this for you."},{kind:"flag",name:"detach",type:"bool",required:!1,constraint:"After flipping terminal, stop foregrounding the agent by closing its viewer pane. The broker engine keeps generating off-screen and \u2014 now terminal \u2014 pushes a final up the spine when done; a later `focus` reattaches a viewer. The Alt+C \u2192 D move."}],output:[{name:"node_id",type:"string",required:!0,constraint:"The demoted node."},{name:"detached",type:"boolean",required:!1,constraint:"True when --detach closed the agent's viewer pane (broker keeps running)."}],outputKind:"object",effects:["Flips the node's lifecycle\u2192terminal and rebuilds its launch spec so a future revive boots terminal \u2014 it stays focused and running, now owing a final up the spine.","The next model request is gated on appending and persisting the role transition. If a fresh context starts first, its rebuilt baseline carries the new lifecycle.","With --detach: closes the agent's viewer pane WITHOUT touching the broker engine \u2014 it keeps generating off-screen, reconnectable by a later `focus`."]},run:l(async e=>{const t=e.pane??process.env.TMUX_PANE,n=await E(e,t),i=await f().patchConfig(n,{lifecycle:"terminal"}).catch(o=>p(o));let s=!1;if(e.detach===!0){const o=M(n)??t;o!==void 0&&o!==""&&U(o)&&(s=R(o))}return{node_id:i.node_id,detached:s}},"run"),render:l(e=>`Demoted ${e.node_id} \u2014 lifecycle now terminal (in place)${e.detached===!0?", viewer closed (broker still running off-screen)":""}.`,"render")}),te=$({name:"lifecycle",description:"lifecycle actions \u2014 recycle, close, demote, or revive a node",whenToUse:"you are ending, transitioning, or bringing back one node you can name: finish the agent in your pane and reboot a fresh root (`recycle`), tear a node + its exclusive subtree down without finishing (`close`), flip a node onto a finishing track in place (`demote`), or bring a dormant node back (`revive`). To merely SET the terminal/resident attribute headlessly use `node config --lifecycle`; to FINISH from inside a node's own turn use `crtr push final`; to mass-reconnect EVERY disconnected node at once use `canvas revive --all`.",help:{name:"node lifecycle",summary:"end, transition, or revive a node \u2014 recycle the pane, close + cascade-cancel a subtree, demote to terminal in place, or revive a dormant node",model:"Lifecycle actions preserve or replace an existing node identity. Finishing is permanent, closing is revivable, demotion changes the completion obligation in place, and revival relaunches a dormant broker. Teardown cascades only through descendants exclusively managed by the affected subtree."},children:[F,H,W,I]}),oe=y({name:"yield",description:"refresh your context, same mode, against your roadmap or goal",whenToUse:"your context is filling but the mandate remains; refresh in the same mode, or combine the refresh with promotion when meaningful independent work can run in parallel and coordinating it should become your primary job",help:{name:"node yield",summary:"request a fresh context window against your roadmap or original goal; mode stays unchanged unless --promote is passed",guide:"The yield aborts your turn the instant this command returns: no text or tool call you planned after it will run. So anything the user is owed \u2014 an answer, a status \u2014 must already be a visible message in the conversation before you call it; never plan to reply after yielding. Before invoking: (1) If you hold a document `roadmap`, bring it current (`crtr doc edit roadmap`). It, not the yield note, carries your task into the fresh window. It has four sections: Goal (its first line is also the roadmap\u2019s preview, so set it with `--preview` whenever the goal changes), Exit criteria, Where things stand, and Working set. Keep it at your own level of abstraction. Detail below your level belongs to the node or document that holds it: link to it, don\u2019t copy it. Where things stand is present tense; delete what is done instead of marking it. Name units by what they are and how they fit, never by child node id or status: your bearings already list your children with their status. Working set holds `[[links]]`. Their previews load with the roadmap, so write no note beside a link. When a unit lands, delete its links: its evidence belongs in its report. (2) Give content its own document when it lives on a different timescale from the roadmap (exploration that outlives the current work), goes to a different reader (a brief for a child), or matters only when something specific happens. Link each such document from the Working set. Anything you had to re-derive in this window that a future window will need again belongs in a document: one you own for this task, `--owner repo` for lessons that would help others in the repo. A document that only points at other documents is not worth the extra hop. (3) Actions waiting on one specific event (\u201Cwhen X reports, send Y first\u201D) go in the yield note, not the roadmap. They are spent once they fire. (4) Send any pending human question; asks already in flight survive the refresh.",params:[{kind:"flag",name:"promote",type:"bool",required:!1,constraint:"Also become an orchestrator as you refresh. Pass only when (1) the remaining work contains independent units that can run in parallel and (2) the task is large enough that parallel execution will materially improve intelligence, productivity, or elapsed throughput after coordination and synthesis. Task length, context exhaustion, sequential phases, or one helper are not enough; keep hands-on work in base mode and yield again as needed. Promote when coordinating and integrating children becomes your primary job. Already an orchestrator? This is not an error: your mode stays orchestrator and a missing roadmap is seeded."},{kind:"flag",name:"kind",type:"string",required:!1,constraint:"Respecialize as you refresh. The <kinds> list below names every top-level available kind and when to choose it; a registered sub-kind is valid too by exact path but not listed here. Defaults to your current kind. With --promote it sets the orchestrator kind you become; without --promote it changes the kind while preserving your current mode."},{kind:"flag",name:"model",type:"enum",choices:["ultra","strong"],required:!1,constraint:"Change the model your fresh revive runs on, by capability tier: `ultra` (frontier \u2014 reserve for high-taste judgment or enormous work: speccing, planning something large, e2e-testing something hard to test) or `strong` (opus \u2014 regular dev work). Omit to keep your current model. A node steering work is never weaker than opus, so these are the only two choices; the change is durable across future revives."},{kind:"stdin",name:"message",required:!0,constraint:"A note to your future self \u2014 what you just tried, where things stand, the first move on waking, and any action waiting on a specific event: enough to continue efficiently, no more. Never describe a reply as sent unless it is visible in the conversation above this call. Your task and its state belong in the roadmap. Surfaced as <yield-message> in the next revive. Pass as a positional or pipe via heredoc."}],output:[{name:"node_id",type:"string",required:!0,constraint:"The yielding node."},{name:"model",type:"string",required:!1,constraint:"The model tier your fresh revive will run on, present only when you changed it with --model."}],outputKind:"object",effects:["Aborts your current turn as soon as this command returns \u2014 nothing after it runs \u2014 then the daemon revives the node fresh.","--kind and/or --model durably reshape the node (kind respecialization, model change) before the refresh-revive; --promote additionally flips mode\u2192orchestrator and creates your document `roadmap` \u2014 mode is otherwise left exactly as it was."]},helpState:{leaf:_},run:l(async e=>{const t=k();if(t===void 0||t==="")throw new d({error:"no_node",message:"no node to yield (CRTR_NODE_ID unset)",next:"Run from inside a node."});const n=e.kind,i=e.model,s=e.promote===!0,o=typeof e.message=="string"?e.message.trim():"";if(o==="")throw new d({error:"empty_message",message:"a yield message is required (stdin or positional)",next:"Pass a note to your future self as an argument or pipe it on stdin."});const a=await f().yieldNode(t,{note:o,...n!==void 0?{kind:n}:{},...i!==void 0?{model:i}:{},...s?{promote:!0}:{}}).catch(c=>p(c));return{node_id:a.node_id,...i!==void 0&&a.model_override!=null?{model:a.model_override}:{}}},"run")});export{ee as nodeConfig,te as nodeLifecycle,ne as nodePromote,oe as nodeYield};
@@ -1,4 +1,4 @@
1
1
  var l=Object.defineProperty;var n=(e,t)=>l(e,"name",{value:t,configurable:!0});import{isSafeNodeId as c}from"../../api/index.js";import{defineBranch as m,defineLeaf as a}from"../../core/command.js";import{InputError as i}from"../../core/io.js";import{cliClient as d,rethrowAsCliError as s}from"../api-client.js";function u(e){const t=typeof e.node=="string"?e.node.trim():"";if(!c(t))throw new i({error:"invalid_node",message:`invalid node id: ${t===""?"(empty)":t}`,field:"node",next:"Pass the exact node id returned when the node was created."});return t}n(u,"nodeId");function o(e){return e==null?"none":JSON.stringify(e)}n(o,"renderedJson");const p=a({name:"read",description:"read or briefly await a node\u2019s durable terminal outcome",whenToUse:"an external caller needs the durable result or failure reason for one node, optionally blocking for its terminal outcome without consuming or claiming it",help:{name:"node outcome read",summary:"read a node\u2019s durable outcome, optionally awaiting it for up to 25 seconds",params:[{kind:"positional",name:"node",required:!0,constraint:"Node id whose durable outcome to read."},{kind:"flag",name:"wait",type:"int",required:!1,default:0,constraint:"Whole seconds to await settlement: 0\u201325. Omit or use 0 to return the current pending/settled state immediately."}],output:[{name:"node_id",type:"string",required:!0,constraint:"The requested node id."},{name:"state",type:"string",required:!0,constraint:"`pending` while the node has no terminal outcome, otherwise `settled`."},{name:"outcome",type:"object",required:!0,constraint:"The durable result/failure record when settled \u2014 kind/reason, revision, settled time, report basename/path, structured result, failure detail, and `declined` ({ reason, code, retryable } for a failure with reason `declined`, else null) \u2014 otherwise null."},{name:"node_status",type:"string",required:!0,constraint:"The node status observed with the outcome."},{name:"deadline_at",type:"string",required:!0,constraint:"The absolute spawn deadline, or null when unbounded."}],outputKind:"object",effects:["Read-only: calls the daemon\u2019s replay-safe GET outcome route. With --wait, the daemon polls the durable row for no more than 25 seconds."]},run:n(async e=>{const t=u(e),r=e.wait;if(typeof r!="number"||!Number.isInteger(r)||r<0||r>25)throw new i({error:"invalid_wait",message:"--wait must be an integer between 0 and 25 seconds",field:"wait",received:r,next:"Pass a whole number from 0 through 25, or omit --wait to poll immediately."});return{...await d().getNodeOutcome(t,{waitSeconds:r}).catch(s)}},"run"),render:n(e=>{const t=e.outcome;return t===null?[`<outcome state="pending" node_id="${e.node_id}">`,`- node status: ${e.node_status}`,`- deadline: ${e.deadline_at??"none"}`,"</outcome>"].join(`
2
- `):[`<outcome kind="${t.kind}" reason="${t.reason}" state="settled" node_id="${e.node_id}" revision="${t.revision}">`,`- final report: ${t.final_report??"none"}`,`- report path: ${t.final_report_path??"none"}`,`- structured result: ${o(t.structured_result)}`,`- declined: ${o(t.declined)}`,`- failure detail: ${o(t.detail)}`,`- settled at: ${t.settled_at}`,`- node status: ${e.node_status}`,`- deadline: ${e.deadline_at??"none"}`,"</outcome>"].join(`
2
+ `):[`<outcome kind="${t.kind}" reason="${t.reason}" state="settled" node_id="${e.node_id}" revision="${t.revision}">`,`- final report: ${t.final_report===null||t.final_report===void 0?"none":`${t.final_report} (read with \`crtr canvas read ${t.final_report}\`)`}`,`- structured result: ${o(t.structured_result)}`,`- declined: ${o(t.declined)}`,`- failure detail: ${o(t.detail)}`,`- settled at: ${t.settled_at}`,`- node status: ${e.node_status}`,`- deadline: ${e.deadline_at??"none"}`,"</outcome>"].join(`
3
3
  `)},"render")}),h=a({name:"delivery",description:"inspect a node outcome-delivery registration",whenToUse:"you need the durable state of the local action target registered to receive a node\u2019s outcome",help:{name:"node outcome delivery",summary:"inspect the registered outcome-delivery target for one node",params:[{kind:"positional",name:"node",required:!0,constraint:"Node id whose outcome-delivery registration to inspect."}],output:[{name:"node_id",type:"string",required:!0,constraint:"The node whose outcome is delivered."},{name:"state",type:"string",required:!0,constraint:"`armed`, `pending`, `running`, `accepted`, or `permanent_failed`."},{name:"action",type:"string",required:!0,constraint:"The resolved configured action name."},{name:"attempt",type:"number",required:!0,constraint:"How many delivery attempts have been claimed."},{name:"next_attempt_at",type:"string",required:!0,constraint:"ISO-8601 time of the next retry, or null before a retry is scheduled (including while armed)."},{name:"accepted_at",type:"string",required:!0,constraint:"When the target accepted the document, or null."},{name:"permanent_failed_at",type:"string",required:!0,constraint:"When the target permanently rejected the document, or null."},{name:"last_failure",type:"object",required:!0,constraint:"The most recent retryable or permanent delivery failure, or null."},{name:"created_at",type:"string",required:!0,constraint:"When this delivery registration was created."},{name:"updated_at",type:"string",required:!0,constraint:"When this delivery registration was last updated."}],outputKind:"object",effects:["Read-only: calls the daemon outcome-delivery inspection route."]},run:n(async e=>{const t=u(e);return{...await d().getOutcomeDelivery(t).catch(s)}},"run"),render:n(e=>[`<outcome-delivery state="${e.state}" node_id="${e.node_id}">`,`- attempt: ${e.attempt}`,`- next retry: ${e.next_attempt_at??"none"}`,`- last failure: ${o(e.last_failure)}`,`- action: ${e.action}`,`- accepted at: ${e.accepted_at??"none"}`,`- permanently failed at: ${e.permanent_failed_at??"none"}`,`- registered at: ${e.created_at}`,`- updated at: ${e.updated_at}`,"</outcome-delivery>"].join(`
4
4
  `),"render")}),b=m({name:"outcome",description:"read a node\u2019s terminal outcome or its delivery state",whenToUse:"you need the durable terminal result/failure of one node, or the state of its registered outcome-delivery target",help:{name:"node outcome",summary:"read one node\u2019s durable terminal outcome or its delivery registration",model:"Outcome reads never consume the terminal record. Read returns the durable result or failure and can wait briefly for settlement; delivery inspects the separate action-registration attempt state."},children:[p,h]});export{b as nodeOutcome,h as nodeOutcomeDelivery};
@@ -1 +1 @@
1
- var b=Object.defineProperty;var r=(e,s)=>b(e,"name",{value:s,configurable:!0});import{envNodeId as p}from"../../shared/env.js";import{defineBranch as h,defineLeaf as o}from"../../core/command.js";import{InputError as n}from"../../core/io.js";import{cliClient as a,rethrowAsCliError as u}from"../api-client.js";function c(e){const s=e.subscriber??p();if(s===void 0||s==="")throw new n({error:"no_subscriber",message:"no subscriber (set CRTR_NODE_ID or pass --subscriber)",field:"subscriber",next:"Run from inside a node, or pass --subscriber <id>."});return s}r(c,"resolveSubscriber");const l=o({name:"add",description:"wire a subscribes_to edge between any pair (active or --passive)",whenToUse:"you want to make one node receive another node\u2019s pushes, actively or passively. Managed-child creation already adds its active edge; use this for other subscriber-to-publisher pairs",help:{name:"node subscription add",summary:"wire a subscribes_to edge so one node receives another's pushes \u2014 the subscriber can be you (default) or, with --subscriber, ANY node, to ANY publisher. Re-running flips an existing edge's active/passive mode.",params:[{kind:"positional",name:"publisher",required:!0,constraint:"The node to subscribe TO \u2014 whose pushes get delivered to the subscriber."},{kind:"flag",name:"subscriber",type:"string",required:!1,constraint:"Who receives the pushes. Defaults to the calling node (CRTR_NODE_ID). Pass any node id to wire a third party."},{kind:"flag",name:"passive",type:"bool",required:!1,constraint:"Passive subscription: pushes ACCUMULATE without waking the subscriber, then auto-inject as timestamped XML pre-text on its next message. Omit for an active (wake-on-push) subscription."}],output:[],outputKind:"object",effects:["Upserts a subscribes_to edge in canvas.db (active flag set from --passive).","Passive edges never wake the subscriber and do not hold it alive (excluded from the stop-guard)."]},run:r(async e=>{const s=e.publisher,i=c(e),t=e.passive===!0;if(i===s)throw new n({error:"self_subscribe",message:"a node cannot subscribe to itself",next:"Pick a different publisher."});return await a().subscribe(i,{target:s,active:!t}).catch(d=>u(d)),{}},"run"),render:r(()=>"Subscription added.","render")}),m=o({name:"remove",description:"drop a subscribes_to edge",whenToUse:"you want to stop one node receiving a publisher\u2019s pushes; removal is idempotent",help:{name:"node subscription remove",summary:"drop a subscribes_to edge \u2014 the subscriber (you by default, or any node via --subscriber) stops receiving the publisher's pushes.",params:[{kind:"positional",name:"publisher",required:!0,constraint:"The node to stop subscribing to."},{kind:"flag",name:"subscriber",type:"string",required:!1,constraint:"Who to detach. Defaults to the calling node (CRTR_NODE_ID)."}],output:[],outputKind:"object",effects:["Deletes the subscribes_to edge from canvas.db."]},run:r(async e=>{const s=e.publisher,i=c(e);return await a().unsubscribe(i,s).catch(t=>u(t)),{}},"run"),render:r(()=>"Subscription removed.","render")}),_=h({name:"subscription",description:"manage push-delivery edges between nodes",whenToUse:"you need an ongoing active or passive subscriber-to-publisher delivery edge that spawn did not create, or need to remove one",help:{name:"node subscription",summary:"add or remove subscriber-to-publisher push-delivery edges",model:"Subscriptions point from a subscriber to a publisher and control future push delivery. Active edges wake on each push; passive edges accumulate pushes for the subscriber\u2019s next turn. Removing either edge stops subsequent delivery without changing messages already received."},children:[l,m]});export{_ as nodeSubscription};
1
+ var b=Object.defineProperty;var r=(e,s)=>b(e,"name",{value:s,configurable:!0});import{envNodeId as p}from"../../shared/env.js";import{defineBranch as h,defineLeaf as o}from"../../core/command.js";import{InputError as n}from"../../core/io.js";import{cliClient as a,rethrowAsCliError as u}from"../api-client.js";function c(e){const s=e.subscriber??p();if(s===void 0||s==="")throw new n({error:"no_subscriber",message:"no subscriber (set CRTR_NODE_ID or pass --subscriber)",field:"subscriber",next:"Run from inside a node, or pass --subscriber <id>."});return s}r(c,"resolveSubscriber");const l=o({name:"add",description:"wire a subscribes_to edge between any pair (active or --passive)",whenToUse:"you want to make one node receive another node\u2019s pushes, actively or passively. Managed-child creation already adds its active edge; use this for other subscriber-to-publisher pairs",help:{name:"node subscription add",summary:"wire a subscribes_to edge so one node receives another's pushes \u2014 the subscriber can be you (default) or, with --subscriber, ANY node, to ANY publisher. Re-running flips an existing edge's active/passive mode.",params:[{kind:"positional",name:"publisher",required:!0,constraint:"The node to subscribe TO \u2014 whose pushes get delivered to the subscriber."},{kind:"flag",name:"subscriber",type:"string",required:!1,constraint:"Who receives the pushes. Defaults to the calling node (CRTR_NODE_ID). Pass any node id to wire a third party."},{kind:"flag",name:"passive",type:"bool",required:!1,constraint:"Passive subscription: pushes ACCUMULATE without waking the subscriber, then auto-inject as timestamped XML pre-text on its next message. Omit for an active (wake-on-push) subscription."}],output:[],outputKind:"object",effects:["Records an ask watch from the subscriber on the publisher in canvas.db; --passive sets its listening configuration to deliver on the next turn only.","Passive edges never wake the subscriber and do not hold it alive (excluded from the stop-guard)."]},run:r(async e=>{const s=e.publisher,i=c(e),t=e.passive===!0;if(i===s)throw new n({error:"self_subscribe",message:"a node cannot subscribe to itself",next:"Pick a different publisher."});return await a().subscribe(i,{target:s,active:!t}).catch(d=>u(d)),{}},"run"),render:r(()=>"Subscription added.","render")}),m=o({name:"remove",description:"drop a subscribes_to edge",whenToUse:"you want to stop one node receiving a publisher\u2019s pushes; removal is idempotent",help:{name:"node subscription remove",summary:"drop a subscribes_to edge \u2014 the subscriber (you by default, or any node via --subscriber) stops receiving the publisher's pushes.",params:[{kind:"positional",name:"publisher",required:!0,constraint:"The node to stop subscribing to."},{kind:"flag",name:"subscriber",type:"string",required:!1,constraint:"Who to detach. Defaults to the calling node (CRTR_NODE_ID)."}],output:[],outputKind:"object",effects:["Deletes the subscribes_to edge from canvas.db."]},run:r(async e=>{const s=e.publisher,i=c(e);return await a().unsubscribe(i,s).catch(t=>u(t)),{}},"run"),render:r(()=>"Subscription removed.","render")}),_=h({name:"subscription",description:"manage push-delivery edges between nodes",whenToUse:"you need an ongoing active or passive subscriber-to-publisher delivery edge that spawn did not create, or need to remove one",help:{name:"node subscription",summary:"add or remove subscriber-to-publisher push-delivery edges",model:"Subscriptions point from a subscriber to a publisher and control future push delivery. Active edges wake on each push; passive edges accumulate pushes for the subscriber\u2019s next turn. Removing either edge stops subsequent delivery without changing messages already received."},children:[l,m]});export{_ as nodeSubscription};