@north-light/crouter 0.3.348 → 0.3.350

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 (634) hide show
  1. package/bin/crtr-launcher +4 -0
  2. package/bin/runtime-generation-manifest.mjs +12 -2
  3. package/dist/api/client.d.ts +50 -7
  4. package/dist/api/client.js +2 -2
  5. package/dist/api/command-manifest/schema.js +1 -1
  6. package/dist/api/dto/analytics.d.ts +184 -0
  7. package/dist/api/dto/analytics.js +0 -0
  8. package/dist/api/dto/broker-ops.d.ts +43 -1
  9. package/dist/api/dto/config.d.ts +11 -0
  10. package/dist/api/dto/grants.d.ts +14 -0
  11. package/dist/api/dto/grants.js +0 -0
  12. package/dist/api/dto/human-requests.d.ts +14 -0
  13. package/dist/api/dto/memory.d.ts +6 -0
  14. package/dist/api/dto/modelauth.d.ts +35 -0
  15. package/dist/api/dto/node-records.d.ts +35 -0
  16. package/dist/api/dto/node-records.js +0 -0
  17. package/dist/api/dto/nodes.d.ts +5 -0
  18. package/dist/api/dto/profiles.d.ts +38 -3
  19. package/dist/api/dto/worktree.d.ts +11 -0
  20. package/dist/api/errors.d.ts +23 -1
  21. package/dist/api/errors.js +1 -1
  22. package/dist/api/index.d.ts +2 -0
  23. package/dist/api/index.js +1 -1
  24. package/dist/api/node-transport.js +1 -1
  25. package/dist/api/routes.d.ts +22 -0
  26. package/dist/api/routes.js +1 -1
  27. package/dist/build-root.d.ts +6 -1
  28. package/dist/build-root.js +1 -1
  29. package/dist/builtin-memory/crouter-sdk/client.md +34 -0
  30. package/dist/builtin-memory/crouter-sdk/errors.md +2 -0
  31. package/dist/builtin-memory/crouter-sdk/memory.md +2 -2
  32. package/dist/builtin-memory/crouter-sdk/streaming.md +21 -0
  33. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/claude-plugin-commands.ts +8 -4
  34. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +39 -118
  35. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +6 -1
  36. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +41 -27
  37. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +52 -29
  38. package/dist/cli.js +9 -8
  39. package/dist/clients/attach/chrome/inbox-strip.js +1 -1
  40. package/dist/clients/attach/chrome/roster.js +1 -1
  41. package/dist/clients/attach/config.js +1 -1
  42. package/dist/clients/attach/overlays/auth.js +2 -2
  43. package/dist/clients/attach/overlays/graph.js +1 -1
  44. package/dist/clients/attach/render/tool-calls.js +3 -3
  45. package/dist/clients/attach/session/connection.js +1 -1
  46. package/dist/clients/attach/session/profile-files.js +1 -1
  47. package/dist/clients/attach/viewer.js +1569 -816
  48. package/dist/commands/api-client.js +3 -3
  49. package/dist/commands/broker-permissions.d.ts +2 -0
  50. package/dist/commands/broker-permissions.js +1 -0
  51. package/dist/commands/canvas-analytics/chart.d.ts +52 -0
  52. package/dist/commands/canvas-analytics/chart.js +1 -0
  53. package/dist/commands/canvas-analytics/drilldown.d.ts +34 -0
  54. package/dist/commands/canvas-analytics/drilldown.js +1 -0
  55. package/dist/commands/canvas-analytics/format.d.ts +29 -0
  56. package/dist/commands/canvas-analytics/format.js +1 -0
  57. package/dist/commands/canvas-analytics/graph-detail.d.ts +2 -0
  58. package/dist/commands/canvas-analytics/graph-detail.js +2 -0
  59. package/dist/commands/canvas-analytics/graphs.d.ts +9 -0
  60. package/dist/commands/canvas-analytics/graphs.js +1 -0
  61. package/dist/commands/canvas-analytics/memory.d.ts +2 -0
  62. package/dist/commands/canvas-analytics/memory.js +1 -0
  63. package/dist/commands/canvas-analytics/overview.d.ts +2 -0
  64. package/dist/commands/canvas-analytics/overview.js +1 -0
  65. package/dist/commands/canvas-analytics/shell.d.ts +60 -0
  66. package/dist/commands/canvas-analytics/shell.js +2 -0
  67. package/dist/commands/canvas-analytics/time.d.ts +2 -0
  68. package/dist/commands/canvas-analytics/time.js +1 -0
  69. package/dist/commands/canvas-analytics/usage.d.ts +2 -0
  70. package/dist/commands/canvas-analytics/usage.js +1 -0
  71. package/dist/commands/canvas-analytics/view.d.ts +85 -0
  72. package/dist/commands/canvas-analytics/view.js +1 -0
  73. package/dist/commands/canvas-analytics.d.ts +2 -0
  74. package/dist/commands/canvas-analytics.js +1 -0
  75. package/dist/commands/canvas-prune.js +1 -1
  76. package/dist/commands/canvas.js +1 -1
  77. package/dist/commands/cron.js +5 -5
  78. package/dist/commands/human/components.d.ts +2 -0
  79. package/dist/commands/human/components.js +2 -2
  80. package/dist/commands/human/prompts.js +1 -1
  81. package/dist/commands/human/queue.js +1 -1
  82. package/dist/commands/memory/client.d.ts +11 -0
  83. package/dist/commands/memory/client.js +1 -0
  84. package/dist/commands/memory/delete.js +1 -1
  85. package/dist/commands/memory/edit.js +3 -3
  86. package/dist/commands/memory/find.js +1 -1
  87. package/dist/commands/memory/history.js +4 -4
  88. package/dist/commands/memory/lint.js +1 -1
  89. package/dist/commands/memory/list.d.ts +0 -2
  90. package/dist/commands/memory/list.js +1 -1
  91. package/dist/commands/memory/move.js +1 -1
  92. package/dist/commands/memory/origin.js +1 -1
  93. package/dist/commands/memory/read.d.ts +0 -5
  94. package/dist/commands/memory/read.js +3 -3
  95. package/dist/commands/memory/shared.d.ts +0 -178
  96. package/dist/commands/memory/shared.js +1 -12
  97. package/dist/commands/memory/write.js +5 -5
  98. package/dist/commands/node/bash.js +5 -5
  99. package/dist/commands/node/create.d.ts +0 -17
  100. package/dist/commands/node/create.js +2 -3
  101. package/dist/commands/node/inspect.js +6 -6
  102. package/dist/commands/node/lifecycle.js +2 -2
  103. package/dist/commands/node-context.js +2 -2
  104. package/dist/commands/node-inspect-artifacts.js +6 -6
  105. package/dist/commands/pkg/browse/catalog.js +1 -1
  106. package/dist/commands/pkg/browse/model.d.ts +1 -1
  107. package/dist/commands/pkg/market-manage.js +1 -1
  108. package/dist/commands/pkg/plugin-manage.js +10 -10
  109. package/dist/commands/profile/daemon-profiles.d.ts +13 -0
  110. package/dist/commands/profile/daemon-profiles.js +4 -0
  111. package/dist/commands/profile/delete.js +1 -1
  112. package/dist/commands/profile/env.d.ts +6 -0
  113. package/dist/commands/profile/env.js +2 -2
  114. package/dist/commands/profile/kind.js +1 -2
  115. package/dist/commands/profile/list.js +1 -1
  116. package/dist/commands/profile/meta.js +1 -1
  117. package/dist/commands/profile/new.js +1 -1
  118. package/dist/commands/profile/pause.js +1 -1
  119. package/dist/commands/profile/project.js +1 -1
  120. package/dist/commands/profile/rename.js +1 -1
  121. package/dist/commands/profile/show.js +1 -1
  122. package/dist/commands/profile.js +1 -1
  123. package/dist/commands/push.js +3 -3
  124. package/dist/commands/surface-inspect.js +1 -1
  125. package/dist/commands/sys/branch.js +1 -1
  126. package/dist/commands/sys/config.js +1 -1
  127. package/dist/commands/sys/connect.js +3 -3
  128. package/dist/commands/sys/context/admin/actions.js +1 -1
  129. package/dist/commands/sys/context/admin/model.d.ts +1 -0
  130. package/dist/commands/sys/context/admin/model.js +1 -1
  131. package/dist/commands/sys/context/admin.js +1 -1
  132. package/dist/commands/sys/context/expose.js +2 -1
  133. package/dist/commands/sys/context/prompt-review.js +2 -2
  134. package/dist/commands/sys/context/resolve.js +7 -7
  135. package/dist/commands/sys/context/sysprompt.js +1 -1
  136. package/dist/commands/sys/join.d.ts +1 -0
  137. package/dist/commands/sys/join.js +1 -0
  138. package/dist/commands/sys/migrate.js +1 -1
  139. package/dist/commands/sys/panels/models-panel.js +1 -1
  140. package/dist/commands/sys/panels/profiles-panel.d.ts +0 -1
  141. package/dist/commands/sys/panels/profiles-panel.js +1 -1
  142. package/dist/commands/sys/provider-login.js +6 -6
  143. package/dist/commands/sys/setup-core.d.ts +1 -1
  144. package/dist/commands/sys/setup-core.js +6 -6
  145. package/dist/commands/sys/setup-settings-runner.js +1 -1
  146. package/dist/commands/sys/setup.js +1 -1
  147. package/dist/commands/sys/spaces.d.ts +1 -0
  148. package/dist/commands/sys/spaces.js +2 -0
  149. package/dist/commands/sys/sync-deps.js +2 -2
  150. package/dist/commands/sys/sync-project-guidance.js +4 -4
  151. package/dist/commands/sys/sync-skills.js +2 -2
  152. package/dist/commands/sys/worktrees.d.ts +1 -0
  153. package/dist/commands/sys/worktrees.js +1 -1
  154. package/dist/commands/sys.js +1 -1
  155. package/dist/core/auth-file.js +2 -2
  156. package/dist/core/bash-job-supervisor.d.ts +6 -0
  157. package/dist/core/bash-job-supervisor.js +4 -0
  158. package/dist/core/bash-jobs.d.ts +13 -6
  159. package/dist/core/bash-jobs.js +8 -7
  160. package/dist/core/broker-client/transport-relay.d.ts +6 -3
  161. package/dist/core/broker-client/transport-relay.js +1 -1
  162. package/dist/core/broker-client/transport-socket.d.ts +6 -1
  163. package/dist/core/broker-client/transport-socket.js +1 -1
  164. package/dist/core/canvas/canvas.d.ts +39 -18
  165. package/dist/core/canvas/canvas.js +21 -20
  166. package/dist/core/canvas/context-strip.d.ts +20 -0
  167. package/dist/core/canvas/context-strip.js +5 -0
  168. package/dist/core/canvas/crons.d.ts +12 -20
  169. package/dist/core/canvas/crons.js +15 -13
  170. package/dist/core/canvas/db.js +2 -2
  171. package/dist/core/canvas/deliveries.d.ts +4 -1
  172. package/dist/core/canvas/deliveries.js +1 -1
  173. package/dist/core/canvas/executions.js +1 -1
  174. package/dist/core/canvas/history.d.ts +2 -1
  175. package/dist/core/canvas/history.js +17 -18
  176. package/dist/core/canvas/memory-reads.d.ts +19 -0
  177. package/dist/core/canvas/memory-reads.js +1 -0
  178. package/dist/core/canvas/migrations.d.ts +0 -4
  179. package/dist/core/canvas/migrations.js +99 -32
  180. package/dist/core/canvas/nav-render.js +1 -1
  181. package/dist/core/canvas/node-agent-paths.d.ts +8 -0
  182. package/dist/core/canvas/node-agent-paths.js +1 -0
  183. package/dist/core/canvas/node-recap.d.ts +2 -2
  184. package/dist/core/canvas/node-recap.js +2 -2
  185. package/dist/core/canvas/node-tokens.d.ts +24 -0
  186. package/dist/core/canvas/node-tokens.js +5 -0
  187. package/dist/core/canvas/paths.d.ts +16 -19
  188. package/dist/core/canvas/paths.js +1 -1
  189. package/dist/core/canvas/render-source.d.ts +2 -2
  190. package/dist/core/canvas/render-source.js +6 -9
  191. package/dist/core/canvas/sessions.d.ts +18 -0
  192. package/dist/core/canvas/sessions.js +4 -1
  193. package/dist/core/canvas/types.d.ts +22 -6
  194. package/dist/core/command-hooks/compose.js +1 -1
  195. package/dist/core/command-hooks/discovery.d.ts +15 -1
  196. package/dist/core/command-hooks/discovery.js +1 -1
  197. package/dist/core/command-hooks/help.d.ts +2 -2
  198. package/dist/core/command-hooks/lifecycle-create.d.ts +1 -1
  199. package/dist/core/command-hooks/lifecycle-create.js +1 -1
  200. package/dist/core/command-hooks/transport/exec-invoke.d.ts +2 -0
  201. package/dist/core/command-hooks/transport/exec-invoke.js +1 -1
  202. package/dist/core/command-hooks/transport/exec-lifecycle.d.ts +1 -0
  203. package/dist/core/command-hooks/transport/exec-lifecycle.js +1 -1
  204. package/dist/core/command-hooks/transport/plan.d.ts +3 -0
  205. package/dist/core/command-hooks/transport/plan.js +1 -0
  206. package/dist/core/command-plugins/discovery.js +1 -1
  207. package/dist/core/command-plugins/transport/http-invoke.js +2 -2
  208. package/dist/core/command.d.ts +6 -2
  209. package/dist/core/command.js +6 -6
  210. package/dist/core/config.d.ts +8 -5
  211. package/dist/core/config.js +1 -1
  212. package/dist/core/conversation-store/client-worker.d.ts +1 -0
  213. package/dist/core/conversation-store/client-worker.js +1 -0
  214. package/dist/core/conversation-store/client.d.ts +2 -0
  215. package/dist/core/conversation-store/client.js +2 -0
  216. package/dist/core/conversation-store/file-store.d.ts +37 -0
  217. package/dist/core/conversation-store/file-store.js +9 -0
  218. package/dist/core/conversation-store/index.d.ts +50 -0
  219. package/dist/core/conversation-store/index.js +1 -0
  220. package/dist/core/conversation-store/listener.d.ts +4 -0
  221. package/dist/core/conversation-store/listener.js +3 -0
  222. package/dist/core/conversation-store/patch.d.ts +1 -0
  223. package/dist/core/conversation-store/patch.js +1 -0
  224. package/dist/core/directory/join.d.ts +27 -0
  225. package/dist/core/directory/join.js +1 -0
  226. package/dist/core/disk-usage.d.ts +4 -0
  227. package/dist/core/disk-usage.js +1 -0
  228. package/dist/core/events/emit.d.ts +2 -0
  229. package/dist/core/events/emit.js +3 -1
  230. package/dist/core/feed/feed.js +4 -4
  231. package/dist/core/feed/messages.d.ts +3 -0
  232. package/dist/core/feed/messages.js +6 -6
  233. package/dist/core/fs-utils.d.ts +2 -0
  234. package/dist/core/fs-utils.js +3 -3
  235. package/dist/core/grants/local.d.ts +4 -0
  236. package/dist/core/grants/local.js +1 -0
  237. package/dist/core/grants/mirror.d.ts +44 -0
  238. package/dist/core/grants/mirror.js +4 -0
  239. package/dist/core/grants/on-sync.d.ts +22 -0
  240. package/dist/core/grants/on-sync.js +1 -0
  241. package/dist/core/grants/remove.d.ts +7 -0
  242. package/dist/core/grants/remove.js +1 -0
  243. package/dist/core/grants/sandboxes.d.ts +7 -0
  244. package/dist/core/grants/sandboxes.js +1 -0
  245. package/dist/core/grants/sync.d.ts +12 -0
  246. package/dist/core/grants/sync.js +1 -0
  247. package/dist/core/help/memory-extensions.d.ts +3 -0
  248. package/dist/core/help/memory-extensions.js +2 -0
  249. package/dist/core/help.d.ts +14 -0
  250. package/dist/core/help.js +7 -7
  251. package/dist/core/host-exports/builtins.js +3 -3
  252. package/dist/core/host-exports/export.js +3 -3
  253. package/dist/core/human/action-binding.d.ts +2 -0
  254. package/dist/core/human/action-binding.js +1 -1
  255. package/dist/core/human/feedback-companion.d.ts +0 -1
  256. package/dist/core/human/feedback-companion.js +1 -1
  257. package/dist/core/human/page-schema.d.ts +1 -1
  258. package/dist/core/inspector/core.d.ts +2 -0
  259. package/dist/core/inspector/core.js +1 -1
  260. package/dist/core/inspector/tui.js +2 -2
  261. package/dist/core/installed-plugins.js +1 -1
  262. package/dist/core/io.js +6 -6
  263. package/dist/core/keybindings/catalog.d.ts +1 -1
  264. package/dist/core/keybindings/catalog.js +1 -1
  265. package/dist/core/layout-migrate/index.d.ts +15 -0
  266. package/dist/core/layout-migrate/index.js +4 -0
  267. package/dist/core/layout-migrate/paths.d.ts +26 -0
  268. package/dist/core/layout-migrate/paths.js +1 -0
  269. package/dist/core/layout-migrate/stored-paths.d.ts +2 -0
  270. package/dist/core/layout-migrate/stored-paths.js +11 -0
  271. package/dist/core/layout-migrate/stores.d.ts +17 -0
  272. package/dist/core/layout-migrate/stores.js +4 -0
  273. package/dist/core/layout.d.ts +26 -0
  274. package/dist/core/layout.js +2 -0
  275. package/dist/core/llm/run.d.ts +60 -0
  276. package/dist/core/llm/run.js +4 -0
  277. package/dist/core/memory/cli-selector.d.ts +13 -0
  278. package/dist/core/memory/cli-selector.js +1 -0
  279. package/dist/core/memory/history.d.ts +5 -4
  280. package/dist/core/memory/history.js +6 -6
  281. package/dist/core/memory/inline-ref-inventory.d.ts +2 -1
  282. package/dist/core/memory/inline-ref-inventory.js +1 -1
  283. package/dist/core/memory/lint.js +2 -2
  284. package/dist/core/memory/mutation-domain.d.ts +2 -2
  285. package/dist/core/memory/mutation-domain.js +8 -7
  286. package/dist/core/memory/mutations.js +1 -1
  287. package/dist/core/memory/service.d.ts +5 -0
  288. package/dist/core/memory/service.js +2 -2
  289. package/dist/core/memory-resolver.d.ts +11 -7
  290. package/dist/core/memory-resolver.js +2 -2
  291. package/dist/core/model-auth/daemon-login.d.ts +13 -0
  292. package/dist/core/model-auth/daemon-login.js +1 -0
  293. package/dist/core/model-auth/flows.d.ts +45 -0
  294. package/dist/core/model-auth/flows.js +1 -0
  295. package/dist/core/model-auth/handoff-jti.d.ts +4 -0
  296. package/dist/core/model-auth/handoff-jti.js +1 -0
  297. package/dist/core/model-routes.js +1 -1
  298. package/dist/core/nested-stores.d.ts +2 -1
  299. package/dist/core/nested-stores.js +1 -2
  300. package/dist/core/preview-registry.js +2 -2
  301. package/dist/core/profiles/env-store.js +2 -2
  302. package/dist/core/profiles/manifest.d.ts +16 -5
  303. package/dist/core/profiles/manifest.js +1 -1
  304. package/dist/core/provider-management.js +1 -1
  305. package/dist/core/review/birth.js +1 -1
  306. package/dist/core/review/companion.d.ts +0 -2
  307. package/dist/core/review/companion.js +1 -1
  308. package/dist/core/review/fork.js +2 -2
  309. package/dist/core/review/realize.js +1 -1
  310. package/dist/core/review/stage.d.ts +1 -1
  311. package/dist/core/review/stage.js +1 -1
  312. package/dist/core/review/types.d.ts +1 -1
  313. package/dist/core/runs/events.d.ts +34 -0
  314. package/dist/core/runs/events.js +1 -0
  315. package/dist/core/runs/operations.d.ts +43 -0
  316. package/dist/core/runs/operations.js +12 -0
  317. package/dist/core/runtime/bearings-render.d.ts +2 -2
  318. package/dist/core/runtime/bearings-render.js +11 -11
  319. package/dist/core/runtime/bearings.js +2 -2
  320. package/dist/core/runtime/broker/cli-relay.d.ts +5 -0
  321. package/dist/core/runtime/broker/cli-relay.js +1 -0
  322. package/dist/core/runtime/broker/client-registry.d.ts +0 -5
  323. package/dist/core/runtime/broker/client-registry.js +1 -1
  324. package/dist/core/runtime/broker/daemon-ops.d.ts +28 -1
  325. package/dist/core/runtime/broker/daemon-ops.js +1 -1
  326. package/dist/core/runtime/broker/engine-drive.d.ts +0 -2
  327. package/dist/core/runtime/broker/engine-drive.js +1 -1
  328. package/dist/core/runtime/broker/fault-retry.d.ts +9 -0
  329. package/dist/core/runtime/broker/fault-retry.js +1 -1
  330. package/dist/core/runtime/broker/frame-dispatch.d.ts +5 -9
  331. package/dist/core/runtime/broker/frame-dispatch.js +1 -1
  332. package/dist/core/runtime/broker/frame-engine-control.d.ts +3 -2
  333. package/dist/core/runtime/broker/frame-engine-control.js +1 -1
  334. package/dist/core/runtime/broker/frame-memory-refs.d.ts +4 -3
  335. package/dist/core/runtime/broker/frame-memory-refs.js +1 -1
  336. package/dist/core/runtime/broker/frame-read.js +1 -1
  337. package/dist/core/runtime/broker/frame-session.js +1 -1
  338. package/dist/core/runtime/broker/held-deferred-inbox.d.ts +3 -1
  339. package/dist/core/runtime/broker/held-deferred-inbox.js +1 -1
  340. package/dist/core/runtime/broker/inbox.d.ts +1 -2
  341. package/dist/core/runtime/broker/inbox.js +7 -10
  342. package/dist/core/runtime/broker/model-adapter.d.ts +9 -0
  343. package/dist/core/runtime/broker/model-adapter.js +2 -0
  344. package/dist/core/runtime/broker/node-token.d.ts +3 -0
  345. package/dist/core/runtime/broker/node-token.js +1 -0
  346. package/dist/core/runtime/broker/read-ops.js +1 -1
  347. package/dist/core/runtime/broker/rebind.d.ts +2 -0
  348. package/dist/core/runtime/broker/rebind.js +1 -1
  349. package/dist/core/runtime/broker/tool-groups.js +1 -1
  350. package/dist/core/runtime/broker/turn-ignition.d.ts +3 -3
  351. package/dist/core/runtime/broker/turn-ignition.js +1 -1
  352. package/dist/core/runtime/broker-cli.js +3 -3
  353. package/dist/core/runtime/broker-extension-render.d.ts +2 -5
  354. package/dist/core/runtime/broker-extension-render.js +4 -4
  355. package/dist/core/runtime/broker-persona-guidance.js +3 -3
  356. package/dist/core/runtime/broker-protocol.d.ts +7 -8
  357. package/dist/core/runtime/broker.js +1 -1
  358. package/dist/core/runtime/canvas-extensions.d.ts +1 -0
  359. package/dist/core/runtime/canvas-extensions.js +1 -1
  360. package/dist/core/runtime/close.d.ts +9 -0
  361. package/dist/core/runtime/close.js +1 -1
  362. package/dist/core/runtime/deliver-live.js +1 -1
  363. package/dist/core/runtime/fleet.d.ts +1 -1
  364. package/dist/core/runtime/front-door-login.js +1 -1
  365. package/dist/core/runtime/front-door.js +2 -2
  366. package/dist/core/runtime/headless-pi.d.ts +1 -1
  367. package/dist/core/runtime/headless-pi.js +1 -1
  368. package/dist/core/runtime/host.d.ts +1 -1
  369. package/dist/core/runtime/host.js +3 -1
  370. package/dist/core/runtime/invocation.d.ts +3 -0
  371. package/dist/core/runtime/kickoff.d.ts +1 -0
  372. package/dist/core/runtime/kickoff.js +16 -16
  373. package/dist/core/runtime/launch-target.d.ts +1 -1
  374. package/dist/core/runtime/launch-target.js +1 -1
  375. package/dist/core/runtime/launch.d.ts +10 -35
  376. package/dist/core/runtime/launch.js +1 -1
  377. package/dist/core/runtime/lifecycle.js +2 -2
  378. package/dist/core/runtime/memory.d.ts +1 -1
  379. package/dist/core/runtime/memory.js +1 -1
  380. package/dist/core/runtime/model-registry.js +1 -1
  381. package/dist/core/runtime/model-swap.d.ts +17 -19
  382. package/dist/core/runtime/model-swap.js +1 -1
  383. package/dist/core/runtime/node-read.js +3 -2
  384. package/dist/core/runtime/nodes.d.ts +7 -0
  385. package/dist/core/runtime/nodes.js +1 -1
  386. package/dist/core/runtime/outcome-document.js +1 -1
  387. package/dist/core/runtime/package-health.js +2 -2
  388. package/dist/core/runtime/persona.js +2 -2
  389. package/dist/core/runtime/pi-vendored.js +1 -1
  390. package/dist/core/runtime/placement-tmux.d.ts +1 -1
  391. package/dist/core/runtime/placement-tmux.js +1 -1
  392. package/dist/core/runtime/placement.js +1 -1
  393. package/dist/core/runtime/promote.js +1 -1
  394. package/dist/core/runtime/provider-credential-gate.js +2 -2
  395. package/dist/core/runtime/recovery-hold.d.ts +2 -0
  396. package/dist/core/runtime/recovery-hold.js +1 -0
  397. package/dist/core/runtime/recycle.js +1 -1
  398. package/dist/core/runtime/reopen.js +1 -1
  399. package/dist/core/runtime/reset.js +1 -1
  400. package/dist/core/runtime/revive.d.ts +1 -1
  401. package/dist/core/runtime/revive.js +2 -3
  402. package/dist/core/runtime/roadmap.js +4 -4
  403. package/dist/core/runtime/run-command.d.ts +6 -0
  404. package/dist/core/runtime/run-command.js +1 -1
  405. package/dist/core/runtime/session-branch.d.ts +2 -8
  406. package/dist/core/runtime/session-branch.js +1 -1
  407. package/dist/core/runtime/session-list-cache.d.ts +6 -22
  408. package/dist/core/runtime/session-list-cache.js +1 -1
  409. package/dist/core/runtime/situational-context.js +2 -2
  410. package/dist/core/runtime/spawn-env.d.ts +6 -6
  411. package/dist/core/runtime/spawn-env.js +1 -1
  412. package/dist/core/runtime/spawn.d.ts +5 -0
  413. package/dist/core/runtime/spawn.js +2 -2
  414. package/dist/core/runtime/stop-guard.js +2 -2
  415. package/dist/core/runtime/stop-signals.d.ts +2 -2
  416. package/dist/core/runtime/stop-signals.js +1 -1
  417. package/dist/core/runtime/structured-output.d.ts +10 -4
  418. package/dist/core/runtime/structured-output.js +4 -4
  419. package/dist/core/runtime/tmux-bindings.js +3 -3
  420. package/dist/core/runtime/tmux-driver.d.ts +4 -0
  421. package/dist/core/runtime/tmux-driver.js +7 -7
  422. package/dist/core/runtime/view-socket.d.ts +9 -2
  423. package/dist/core/runtime/view-socket.js +1 -1
  424. package/dist/core/runtime/warm-pool.js +1 -1
  425. package/dist/core/scope.d.ts +3 -2
  426. package/dist/core/scope.js +1 -1
  427. package/dist/core/scoped-state/db.js +1 -1
  428. package/dist/core/scoped-state/migrate.js +3 -3
  429. package/dist/core/scoped-state/profiles.d.ts +2 -1
  430. package/dist/core/scoped-state/profiles.js +1 -1
  431. package/dist/core/scoped-state/schema.d.ts +2 -1
  432. package/dist/core/scoped-state/schema.js +4 -4
  433. package/dist/core/scopes.d.ts +2 -2
  434. package/dist/core/scopes.js +1 -1
  435. package/dist/core/secrets.d.ts +1 -0
  436. package/dist/core/secrets.js +2 -2
  437. package/dist/core/spaces/mounts.d.ts +32 -0
  438. package/dist/core/spaces/mounts.js +1 -0
  439. package/dist/core/spaces/open-beneath.d.ts +71 -0
  440. package/dist/core/spaces/open-beneath.js +1 -0
  441. package/dist/core/spaces/permissions.d.ts +46 -0
  442. package/dist/core/spaces/permissions.js +1 -0
  443. package/dist/core/spaces/process.d.ts +27 -0
  444. package/dist/core/spaces/process.js +1 -0
  445. package/dist/core/spaces/space.d.ts +8 -0
  446. package/dist/core/spaces/space.js +2 -0
  447. package/dist/core/spaces/stop.d.ts +8 -0
  448. package/dist/core/spaces/stop.js +2 -0
  449. package/dist/core/spaces/users.d.ts +19 -0
  450. package/dist/core/spaces/users.js +1 -0
  451. package/dist/core/subscription-state.d.ts +2 -0
  452. package/dist/core/subscription-state.js +3 -3
  453. package/dist/core/substrate/frontmatter-validation.js +1 -1
  454. package/dist/core/substrate/injected-store.d.ts +28 -3
  455. package/dist/core/substrate/injected-store.js +2 -1
  456. package/dist/core/substrate/listings.js +1 -1
  457. package/dist/core/substrate/memory-events.d.ts +6 -0
  458. package/dist/core/substrate/memory-events.js +1 -0
  459. package/dist/core/substrate/on-read-node.js +1 -1
  460. package/dist/core/substrate/on-read.d.ts +6 -6
  461. package/dist/core/substrate/on-read.js +5 -5
  462. package/dist/core/substrate/plan.js +1 -1
  463. package/dist/core/substrate/render-node.js +1 -1
  464. package/dist/core/substrate/render.d.ts +4 -3
  465. package/dist/core/substrate/render.js +38 -38
  466. package/dist/core/substrate/schema.d.ts +6 -0
  467. package/dist/core/substrate/schema.js +1 -1
  468. package/dist/core/tui/panel.js +1 -1
  469. package/dist/core/user-settings.d.ts +12 -0
  470. package/dist/core/user-settings.js +1 -1
  471. package/dist/core/worktree-mutation-async.js +5 -5
  472. package/dist/core/worktree-quarantine.js +1 -1
  473. package/dist/core/worktree-remove.d.ts +15 -0
  474. package/dist/core/worktree-remove.js +1 -0
  475. package/dist/core/worktree-sweep.js +6 -6
  476. package/dist/daemon/analytics/compute.d.ts +37 -0
  477. package/dist/daemon/analytics/compute.js +1 -0
  478. package/dist/daemon/analytics/scan-worker.d.ts +1 -0
  479. package/dist/daemon/analytics/scan-worker.js +2 -0
  480. package/dist/daemon/analytics/scan.d.ts +37 -0
  481. package/dist/daemon/analytics/scan.js +1 -0
  482. package/dist/daemon/api/app-listener.d.ts +11 -0
  483. package/dist/daemon/api/app-listener.js +21 -0
  484. package/dist/daemon/api/bridge.js +5 -5
  485. package/dist/daemon/api/handlers/analytics.d.ts +2 -0
  486. package/dist/daemon/api/handlers/analytics.js +1 -0
  487. package/dist/daemon/api/handlers/attach.js +1 -1
  488. package/dist/daemon/api/handlers/bash-jobs.js +2 -2
  489. package/dist/daemon/api/handlers/bash.js +1 -1
  490. package/dist/daemon/api/handlers/broker-ops.js +1 -1
  491. package/dist/daemon/api/handlers/canvas.d.ts +1 -1
  492. package/dist/daemon/api/handlers/canvas.js +6 -6
  493. package/dist/daemon/api/handlers/crons.js +1 -1
  494. package/dist/daemon/api/handlers/files.d.ts +1 -1
  495. package/dist/daemon/api/handlers/files.js +1 -1
  496. package/dist/daemon/api/handlers/health.js +2 -2
  497. package/dist/daemon/api/handlers/hook-exec.d.ts +2 -0
  498. package/dist/daemon/api/handlers/hook-exec.js +1 -0
  499. package/dist/daemon/api/handlers/human-requests.js +1 -1
  500. package/dist/daemon/api/handlers/inbox.js +1 -1
  501. package/dist/daemon/api/handlers/llm.d.ts +2 -0
  502. package/dist/daemon/api/handlers/llm.js +3 -0
  503. package/dist/daemon/api/handlers/memory-reads.d.ts +4 -0
  504. package/dist/daemon/api/handlers/memory-reads.js +1 -0
  505. package/dist/daemon/api/handlers/memory.js +5 -1
  506. package/dist/daemon/api/handlers/messages.js +2 -2
  507. package/dist/daemon/api/handlers/modelauth.js +1 -1
  508. package/dist/daemon/api/handlers/node-outcomes.js +1 -1
  509. package/dist/daemon/api/handlers/node-records.d.ts +2 -0
  510. package/dist/daemon/api/handlers/node-records.js +3 -0
  511. package/dist/daemon/api/handlers/nodes.d.ts +1 -1
  512. package/dist/daemon/api/handlers/nodes.js +1 -1
  513. package/dist/daemon/api/handlers/profiles.d.ts +4 -1
  514. package/dist/daemon/api/handlers/profiles.js +1 -1
  515. package/dist/daemon/api/handlers/reports.js +5 -3
  516. package/dist/daemon/api/handlers/reviews.d.ts +1 -1
  517. package/dist/daemon/api/handlers/reviews.js +1 -1
  518. package/dist/daemon/api/handlers/run-routes.d.ts +2 -0
  519. package/dist/daemon/api/handlers/run-routes.js +1 -0
  520. package/dist/daemon/api/handlers/runs.d.ts +9 -0
  521. package/dist/daemon/api/handlers/runs.js +1 -0
  522. package/dist/daemon/api/handlers/subscriptions.js +1 -1
  523. package/dist/daemon/api/handlers/worktree.js +1 -1
  524. package/dist/daemon/api/map.d.ts +10 -4
  525. package/dist/daemon/api/map.js +2 -2
  526. package/dist/daemon/api/operations.d.ts +18 -0
  527. package/dist/daemon/api/operations.js +1 -0
  528. package/dist/daemon/api/principal.d.ts +42 -0
  529. package/dist/daemon/api/principal.js +1 -0
  530. package/dist/daemon/api/router.d.ts +27 -3
  531. package/dist/daemon/api/router.js +1 -1
  532. package/dist/daemon/api/server.d.ts +4 -0
  533. package/dist/daemon/api/server.js +1 -1
  534. package/dist/daemon/api/vendor-signin.d.ts +7 -0
  535. package/dist/daemon/api/vendor-signin.js +77 -0
  536. package/dist/daemon/api/viewer-presence.d.ts +3 -0
  537. package/dist/daemon/api/viewer-presence.js +1 -0
  538. package/dist/daemon/cron/armed-context.d.ts +4 -0
  539. package/dist/daemon/cron/armed-context.js +1 -1
  540. package/dist/daemon/cron/escalation.js +3 -3
  541. package/dist/daemon/cron/lease-recovery.js +1 -1
  542. package/dist/daemon/cron/sinks.js +2 -2
  543. package/dist/daemon/cron-run.d.ts +5 -5
  544. package/dist/daemon/cron-run.js +1 -1
  545. package/dist/daemon/crtrd-cli.js +1 -1
  546. package/dist/daemon/crtrd.js +8 -7
  547. package/dist/daemon/delivery/run-delivery.d.ts +4 -0
  548. package/dist/daemon/delivery/run-delivery.js +1 -1
  549. package/dist/daemon/fleet.d.ts +1 -1
  550. package/dist/daemon/fleet.js +6 -6
  551. package/dist/daemon/http-dispatcher.d.ts +1 -0
  552. package/dist/daemon/http-dispatcher.js +1 -0
  553. package/dist/daemon/human/finish.js +7 -7
  554. package/dist/daemon/joined-owner.d.ts +5 -0
  555. package/dist/daemon/joined-owner.js +1 -0
  556. package/dist/daemon/manage.js +2 -2
  557. package/dist/daemon/messaging/node-message.js +1 -1
  558. package/dist/daemon/park-activity.js +1 -1
  559. package/dist/daemon/reconcilers/bash-deadline.js +1 -1
  560. package/dist/daemon/reconcilers/broker-supervision.d.ts +1 -1
  561. package/dist/daemon/reconcilers/broker-supervision.js +4 -4
  562. package/dist/daemon/reconcilers/delivery-lane.js +1 -1
  563. package/dist/daemon/reconcilers/live-obligation.d.ts +7 -0
  564. package/dist/daemon/reconcilers/live-obligation.js +1 -1
  565. package/dist/daemon/reconcilers/node-lifecycle/tick.js +1 -1
  566. package/dist/daemon/reconcilers/storage-maintenance.d.ts +20 -1
  567. package/dist/daemon/reconcilers/storage-maintenance.js +1 -1
  568. package/dist/daemon/runs/append-section.d.ts +6 -0
  569. package/dist/daemon/runs/append-section.js +2 -0
  570. package/dist/daemon/runs/observer.d.ts +5 -0
  571. package/dist/daemon/runs/observer.js +4 -0
  572. package/dist/launcher/bwrap.d.ts +4 -0
  573. package/dist/launcher/bwrap.js +2 -0
  574. package/dist/launcher/client.d.ts +2 -0
  575. package/dist/launcher/client.js +3 -0
  576. package/dist/launcher/main.d.ts +1 -0
  577. package/dist/launcher/main.js +3 -0
  578. package/dist/launcher/netrule.d.ts +1 -0
  579. package/dist/launcher/netrule.js +1 -0
  580. package/dist/launcher/seccomp.d.ts +1 -0
  581. package/dist/launcher/seccomp.js +1 -0
  582. package/dist/launcher/spec.d.ts +88 -0
  583. package/dist/launcher/spec.js +1 -0
  584. package/dist/launcher/users.d.ts +18 -0
  585. package/dist/launcher/users.js +7 -0
  586. package/dist/launcher/validate.d.ts +22 -0
  587. package/dist/launcher/validate.js +1 -0
  588. package/dist/migrations/corpus.js +3 -3
  589. package/dist/migrations/runner.js +1 -1
  590. package/dist/native/linux/addon.node +0 -0
  591. package/dist/native/linux.d.ts +29 -0
  592. package/dist/native/linux.js +1 -0
  593. package/dist/pi-extensions/broker-local.js +2 -2
  594. package/dist/pi-extensions/canvas-bash-valve.d.ts +1 -3
  595. package/dist/pi-extensions/canvas-bash-valve.js +4 -7
  596. package/dist/pi-extensions/canvas-context-intro.d.ts +1 -2
  597. package/dist/pi-extensions/canvas-context-intro.js +2 -2
  598. package/dist/pi-extensions/canvas-doc-substrate.js +6 -6
  599. package/dist/pi-extensions/canvas-inbox-watcher.d.ts +2 -1
  600. package/dist/pi-extensions/canvas-inbox-watcher.js +2 -1
  601. package/dist/pi-extensions/canvas-passive-context.d.ts +1 -1
  602. package/dist/pi-extensions/canvas-passive-context.js +11 -11
  603. package/dist/pi-extensions/canvas-recap.js +6 -6
  604. package/dist/pi-extensions/canvas-stophook.js +1 -1
  605. package/dist/pi-extensions/canvas-structured-output.js +4 -4
  606. package/dist/pi-extensions/canvas-tool-guide.js +1 -1
  607. package/dist/pi-extensions/system-prompt-entry.d.ts +15 -0
  608. package/dist/pi-extensions/system-prompt-entry.js +1 -0
  609. package/dist/shared/env.d.ts +5 -1
  610. package/dist/shared/env.js +1 -1
  611. package/dist/shared/strip-cd-prefix.d.ts +1 -0
  612. package/dist/shared/strip-cd-prefix.js +1 -0
  613. package/dist/types.d.ts +31 -15
  614. package/docs/cli/memory-and-preferences.md +3 -1
  615. package/docs/sdk/client.md +34 -0
  616. package/docs/sdk/errors.md +2 -0
  617. package/docs/sdk/memory.md +2 -2
  618. package/docs/sdk/streaming.md +21 -0
  619. package/package.json +19 -4
  620. package/packages/crouter-identity/dist/index.d.ts +54 -0
  621. package/packages/crouter-identity/dist/index.js +166 -0
  622. package/packages/crouter-identity/package.json +33 -0
  623. package/runtime.lock.json +44 -8
  624. package/scripts/build-native-linux.mjs +16 -0
  625. package/scripts/install-runtime.mjs +9 -7
  626. package/scripts/lib/isolation-env.mjs +42 -0
  627. package/scripts/runtime-smoke.mjs +49 -0
  628. package/src/native/linux/addon.c +249 -0
  629. package/dist/core/profiles/state-block.d.ts +0 -4
  630. package/dist/core/profiles/state-block.js +0 -3
  631. package/dist/core/runtime/broker/auth-reload.d.ts +0 -41
  632. package/dist/core/runtime/broker/auth-reload.js +0 -1
  633. package/dist/core/substrate/cli-context-delivery.d.ts +0 -22
  634. package/dist/core/substrate/cli-context-delivery.js +0 -1
@@ -1,4 +1,4 @@
1
- var F=Object.defineProperty;var p=(e,t)=>F(e,"name",{value:t,configurable:!0});import{envProfileId as O}from"../../shared/env.js";import{existsSync as b,readdirSync as W,statSync as S}from"node:fs";import{basename as _,join as g,resolve as $}from"node:path";import{defineLeaf as z}from"../../core/command.js";import{parseFrontmatterGeneric as v}from"../../core/frontmatter.js";import{usage as w}from"../../core/errors.js";import{pathExists as E,readText as j,realpathOrSelf as y,walkFiles as A,writeText as U}from"../../core/fs-utils.js";import{loadProfileManifest as q}from"../../core/profiles/manifest.js";import{CRTR_DIR_NAME as k}from"../../types.js";import{memoryFilePath as J,serializeMemoryDoc as X}from"../memory/shared.js";import{normalizedName as B,routeFromDescription as K,scalarString as x}from"./sync-shared.js";import{deriveProjectNamespace as H}from"../../core/memory/project-namespace.js";import{associateRepository as C}from"../../core/memory/repository-association.js";import{openProjectMemoryStore as P}from"../../core/memory-resolver.js";import{composeCanonicalName as T,localNameFromPath as Q,resolveLocalName as V,validateExplicitLocalName as Y}from"../../core/memory/identity.js";const Z=new Set(["node_modules",".git",".venv","venv","dist","build",".next",".cache",".yalc",".sisyphus",k]),ee=["AGENTS.md","CLAUDE.md"];function te(e){if(e!==void 0)return[$(e)];const t=[$(process.cwd())],o=O();if(o!==void 0&&o!=="")try{const{manifest:a}=q(o);for(const r of a.projects){const n=$(r.path);t.includes(n)||t.push(n)}}catch{}return t}p(te,"projectDocRoots");function oe(e){const t=[];if(!b(e)||!S(e).isDirectory())return t;const o=$(e),a=[o];for(;a.length>0;){const r=a.pop();for(const i of ee){const d=g(r,i);if(b(d)&&S(d).isFile()){t.push({kind:"agents",path:d,sourceDir:r,walkRoot:o});break}}const n=g(r,".claude","rules");if(b(n))for(const i of A(n,d=>d.endsWith(".md")))t.push({kind:"rule",path:i,sourceDir:r,walkRoot:o});let c;try{c=W(r,{withFileTypes:!0})}catch{continue}for(const i of c)i.isDirectory()&&!Z.has(i.name)&&a.push(g(r,i.name))}return t}p(oe,"collectProjectDocCandidates");function N(e){for(const t of e.split(`
2
- `)){const o=t.replace(/^#+\s*/,"").replace(/\s+/g," ").trim();if(o!=="")return o.length>200?`${o.slice(0,197)}...`:o}return"Migrated project guidance."}p(N,"synthesizeShortForm");function L(e){const t=[];let o=0,a="";for(const r of e){if(r==="{"?o+=1:r==="}"&&(o=Math.max(0,o-1)),r===","&&o===0){t.push(a),a="";continue}a+=r}return t.push(a),t.map(r=>r.trim()).filter(r=>r!=="")}p(L,"splitTopLevelCommas");function re(e){return typeof e=="string"?L(e):Array.isArray(e)?e.filter(t=>typeof t=="string").flatMap(L):[]}p(re,"normalizeRulePaths");function ne(e,t,o,a){const r=C(e);let n;if(r.ok)n=r.association.repositoryRoot;else if(r.ok===!1&&r.code==="no-project-root")n=y(t);else{const s=r.ok===!1?r.reason:"unknown repository association failure";throw w(`cannot associate project guidance at ${e}: ${s}`,{next:`Run \`crtr sys migrate --dir ${e}\`.`})}n=y(n);const c=a.get(n);if(c!==void 0)return c;const i=P(n);if(i.mountStatus==="ready"){const u={namespace:i.repositoryNamespace??i.namespace,repositoryRoot:n,initialize:!1};return a.set(n,u),u}const d=g(n,k,"memory");if((E(d)?A(d,s=>s.endsWith(".md")):[]).length>0)throw w(`project memory store at ${d} is undeclared or invalid`,{next:`Run \`crtr sys migrate --dir ${n}\`.`});const f=H({ownerDir:n,rootBody:o.get(n)});if(f.ok===!1)throw w(`cannot initialize project memory store at ${d}: ${f.reason}`,{next:`Run \`crtr sys migrate --dir ${n}\`.`});const h={namespace:f.namespace,repositoryRoot:n,initialize:!0};return a.set(n,h),h}p(ne,"resolveRepositoryNamespace");function se(){return[{on:"workspace-open",at:"content"},{on:"read",match:"./**",at:"content"}]}p(se,"frontDoorSurfaces");function ae(e){const t=e.trim();return!t.includes("/")||t.startsWith("/")||t.startsWith("**")||t.startsWith("./")?t:`./${t}`}p(ae,"anchorReadGlob");function ie(e,t){const o=v(j(e.path)),a=y(e.sourceDir)===t.repositoryRoot;return M(e,t.namespace,o.body,a,o.data??{})}p(ie,"prepareAgentsCandidate");function ce(e){return Array.isArray(e.surfaces)?e.surfaces.filter(t=>{if(t===null||typeof t!="object"||Array.isArray(t))return!1;const o=t;if(o.on!=="read"||!["name","preview","content"].includes(String(o.at)))return!1;const a=o.match,r=typeof a=="string"?a.trim()!=="":Array.isArray(a)&&a.length>0&&a.every(c=>typeof c=="string"&&c.trim()!==""),n=o["match-frontmatter"];return r||n!==null&&typeof n=="object"&&!Array.isArray(n)}):[]}p(ce,"sourceReadRoute");function M(e,t,o,a,r={}){if(!a){const c=ce(r),i=x(r["when-and-why-to-read"])?.trim(),d={kind:"knowledge","when-and-why-to-read":i!==void 0&&i!==""?i:`When working under ${t}, this knowledge should be read because it carries the nested project guidance.`,"short-form":N(o),...c.length>0?{surfaces:c}:{}};return{candidate:e,name:"",target:g(e.sourceDir,k,"memory","INDEX.md"),frontmatter:d,body:o}}const n={kind:"knowledge",namespace:t,"when-and-why-to-read":`When working in ${t}, this knowledge should be read because it is the project's operating guide.`,"short-form":N(o),surfaces:se()};return{candidate:e,name:t,target:g(e.sourceDir,k,"memory","INDEX.md"),frontmatter:n,body:o}}p(M,"prepareRootCandidate");function de(e,t){const o=C(e.sourceDir);if(!o.ok)throw w(`cannot associate rule guidance at ${e.path}: ${o.reason}`,{next:`Run \`crtr sys migrate --dir ${e.sourceDir}\`.`});const a=T(t.namespace,o.association.ownerRelativePath),r=j(e.path),n=v(r),c=n.data??{},i=B(_(e.path)),d=Q(`${i}.md`),l=V(c,d),f=Y(l,a);if(f.ok===!1)throw w(`invalid local rule name in ${e.path}: ${f.reason}`);const h=x(c.description)??"",s=T(a,l),u={kind:"knowledge",name:l,"when-and-why-to-read":h!==""?K(h,"knowledge"):`When working under ${a}, this knowledge should be read because it is migrated project rule guidance.`,"short-form":h!==""?h.replace(/\s+/g," ").trim():N(n.body)},m=re(c.paths).map(ae);return u.surfaces=m.length>0?[{on:"read",match:m.length===1?m[0]:m,at:"content"}]:[{on:"read",match:"./**",at:"content"}],{candidate:e,name:s,target:J(g(e.sourceDir,k,"memory"),l),frontmatter:u,body:n.body}}p(de,"prepareRuleCandidate");function I(e,t){return E(e.target)&&!t.overwrite?{source:e.candidate.path,target:e.target,name:e.name,scope:"project",status:"skipped",reason:"target memory doc already exists; re-run with --overwrite to replace it"}:(t.dryRun||U(e.target,X(e.frontmatter,e.body)),{source:e.candidate.path,target:e.target,name:e.name,scope:"project",status:t.dryRun?"would-import":"imported"})}p(I,"convertProjectDoc");function ue(e,t){if(e.kind!=="agents")return;let o;try{o=v(j(e.path)).data?.namespace}catch{return}if(o===void 0)return;const a=y(e.sourceDir)===t.repositoryRoot,r=g(t.repositoryRoot,k,"memory","INDEX.md");if(!a)throw w(`project guidance at ${e.path} declares a namespace, but ${r} is the only declaration for this repository`,{next:`Run \`crtr sys migrate --dir ${t.repositoryRoot}\`.`});if(o!==t.namespace)throw w(`project guidance namespace ${JSON.stringify(o)} conflicts with the declared store namespace ${JSON.stringify(t.namespace)}`,{next:`Run \`crtr sys migrate --dir ${t.repositoryRoot}\` for a repository-wide identity change.`})}p(ue,"validateAuthoredNamespace");function pe(e){const t=e.filter(i=>i.status==="imported").length,o=e.filter(i=>i.status==="would-import").length,a=e.filter(i=>i.status==="skipped").length,r=o>0?`crtr sys sync project-guidance \u2014 dry run: ${o} would import, ${a} skipped`:`crtr sys sync project-guidance \u2014 ${t} imported, ${a} skipped`,n=e.map(i=>{const d=i.reason?` (${i.reason})`:"";return`| ${i.status} | ${i.scope}/${i.name||"\u2014"} | ${i.target} | ${i.source}${d} |`}),c=n.length===0?["No sync candidates."]:["| status | memory doc | target | source |","| --- | --- | --- | --- |",...n];return[r,"",...c].join(`
3
- `)}p(pe,"renderSummary");const Ee=z({name:"project-guidance",description:"migrate repository guidance into repository-root and nested crouter memory docs",whenToUse:"migrating repository CLAUDE.md/AGENTS.md/.claude/rules into the crouter memory-doc model: one-way copy into the repository root's or nested store's .crouter/memory/. Only the repository-root guide becomes the workspace front door; nested guides and rules keep read routes. Never exports memory docs back to these source files.",help:{name:"sys sync project-guidance",summary:"one-way migration: CLAUDE.md/AGENTS.md/.claude/rules \u2192 repository-root and nested crouter memory docs",params:[{kind:"flag",name:"source",type:"string",required:!1,constraint:"The root to walk recursively for CLAUDE.md/AGENTS.md/.claude/rules files. Default: cwd plus every project in the current profile's purview (when one is selected)."},{kind:"flag",name:"dry-run",type:"bool",required:!1,constraint:"Show what would be migrated without writing memory docs."},{kind:"flag",name:"overwrite",type:"bool",required:!1,constraint:"Replace an existing target memory doc. Default: skip existing docs to avoid clobbering user-authored memory."}],output:[{name:"results",type:"object[]",required:!0,constraint:"Each: {source,target,name,scope,status,reason?}; status is one of imported, skipped, would-import."}],outputKind:"object",effects:["Copies the repository-root CLAUDE.md/AGENTS.md into the repository-root .crouter/memory/INDEX.md with the namespace declaration and workspace front-door surfaces. Copies nested guides into nested .crouter/memory/INDEX.md files without namespace, name, or workspace-open metadata. Copies each .claude/rules/*.md into its nested store with a local name and a content read entry over the source paths (\u2018./\u2019-anchored when repo-relative), or a plain read route when no paths are declared. Originals are left untouched.","Skips existing memory docs unless --overwrite is present.","With --dry-run: read-only; writes nothing."]},run:p(async e=>{const t=e.source,o=e.dryRun??!1,a=e.overwrite??!1,r=[],n=new Set,c=[];for(const s of te(t))for(const u of oe(s))n.has(u.path)||(n.add(u.path),c.push(u));const i=new Map;for(const s of c)if(s.kind==="agents")try{i.set(y(s.sourceDir),v(j(s.path)).body)}catch{i.set(y(s.sourceDir),void 0)}const d=new Map,l=new Map;for(const s of c){const u=ne(s.sourceDir,s.walkRoot,i,d);l.set(s.path,u),ue(s,u)}const f=[];for(const s of c){const u=l.get(s.path);try{const m=s.kind==="agents"?ie(s,u):de(s,u);f.push({candidate:s,prepared:m})}catch(m){const R=(m instanceof Error?m.message:String(m)).split(`
4
- `)[0];r.push({source:s.path,target:"",name:"",scope:"project",status:"skipped",reason:`unparseable source frontmatter: ${R} \u2014 fix the source file and re-run`})}}for(const s of d.values()){if(c.some(D=>D.kind==="agents"&&l.get(D.path)?.repositoryRoot===s.repositoryRoot&&y(D.sourceDir)===s.repositoryRoot)||P(s.repositoryRoot).mountStatus==="ready")continue;const m=c.find(D=>l.get(D.path)?.repositoryRoot===s.repositoryRoot);if(m===void 0)continue;const R={...m,kind:"agents",sourceDir:s.repositoryRoot},G=M(R,s.namespace,"Migrated project guidance.",!0);r.push(I(G,{dryRun:o,overwrite:a}))}const h=[...f].sort((s,u)=>{const m=y(s.candidate.sourceDir)===l.get(s.candidate.path).repositoryRoot,R=y(u.candidate.sourceDir)===l.get(u.candidate.path).repositoryRoot;return Number(R)-Number(m)});for(const{prepared:s}of h)r.push(I(s,{dryRun:o,overwrite:a}));return{results:r}},"run"),render:p(e=>pe(e.results),"render")});export{Ee as sysSyncProjectGuidanceLeaf};
1
+ var F=Object.defineProperty;var p=(e,t)=>F(e,"name",{value:t,configurable:!0});import{envProfileId as O}from"../../shared/env.js";import{existsSync as j,readdirSync as W,statSync as E}from"node:fs";import{basename as _,join as g,resolve as b}from"node:path";import{defineLeaf as z}from"../../core/command.js";import{parseFrontmatterGeneric as D}from"../../core/frontmatter.js";import{usage as w}from"../../core/errors.js";import{pathExists as N,readText as $,realpathOrSelf as y,walkFiles as A,writeText as U}from"../../core/fs-utils.js";import{loadProfileManifest as q}from"../../core/profiles/manifest.js";import{CRTR_DIR_NAME as k}from"../../types.js";import{memoryFilePath as J,serializeMemoryDoc as X}from"../../core/memory/mutation-domain.js";import{normalizedName as B,routeFromDescription as K,scalarString as x}from"./sync-shared.js";import{deriveProjectNamespace as H}from"../../core/memory/project-namespace.js";import{associateRepository as C}from"../../core/memory/repository-association.js";import{openProjectMemoryStore as P}from"../../core/memory-resolver.js";import{composeCanonicalName as T,localNameFromPath as Q,resolveLocalName as V,validateExplicitLocalName as Y}from"../../core/memory/identity.js";const Z=new Set(["node_modules",".git",".venv","venv","dist","build",".next",".cache",".yalc",".sisyphus",k]),ee=["AGENTS.md","CLAUDE.md"];function te(e){if(e!==void 0)return[b(e)];const t=[b(process.cwd())],o=O();if(o!==void 0&&o!=="")try{const{manifest:a}=q(o);for(const r of a.projects){const n=b(r.path);t.includes(n)||t.push(n)}}catch{}return t}p(te,"projectDocRoots");function oe(e){const t=[];if(!j(e)||!E(e).isDirectory())return t;const o=b(e),a=[o];for(;a.length>0;){const r=a.pop();for(const i of ee){const u=g(r,i);if(j(u)&&E(u).isFile()){t.push({kind:"agents",path:u,sourceDir:r,walkRoot:o});break}}const n=g(r,".claude","rules");if(j(n))for(const i of A(n,u=>u.endsWith(".md")))t.push({kind:"rule",path:i,sourceDir:r,walkRoot:o});let c;try{c=W(r,{withFileTypes:!0})}catch{continue}for(const i of c)i.isDirectory()&&!Z.has(i.name)&&a.push(g(r,i.name))}return t}p(oe,"collectProjectDocCandidates");function S(e){for(const t of e.split(`
2
+ `)){const o=t.replace(/^#+\s*/,"").replace(/\s+/g," ").trim();if(o!=="")return o.length>200?`${o.slice(0,197)}...`:o}return"Migrated project guidance."}p(S,"synthesizeShortForm");function L(e){const t=[];let o=0,a="";for(const r of e){if(r==="{"?o+=1:r==="}"&&(o=Math.max(0,o-1)),r===","&&o===0){t.push(a),a="";continue}a+=r}return t.push(a),t.map(r=>r.trim()).filter(r=>r!=="")}p(L,"splitTopLevelCommas");function re(e){return typeof e=="string"?L(e):Array.isArray(e)?e.filter(t=>typeof t=="string").flatMap(L):[]}p(re,"normalizeRulePaths");function ne(e,t,o,a){const r=C(e);let n;if(r.ok)n=r.association.repositoryRoot;else if(r.ok===!1&&r.code==="no-project-root")n=y(t);else{const s=r.ok===!1?r.reason:"unknown repository association failure";throw w(`cannot associate project guidance at ${e}: ${s}`,{next:`Run \`crtr sys migrate --dir ${e}\`.`})}n=y(n);const c=a.get(n);if(c!==void 0)return c;const i=P(n);if(i.mountStatus==="ready"){const d={namespace:i.repositoryNamespace??i.namespace,repositoryRoot:n,initialize:!1};return a.set(n,d),d}const u=g(n,k,"memory");if((N(u)?A(u,s=>s.endsWith(".md")):[]).length>0)throw w(`project memory store at ${u} is undeclared or invalid`,{next:`Run \`crtr sys migrate --dir ${n}\`.`});const f=H({ownerDir:n,rootBody:o.get(n)});if(f.ok===!1)throw w(`cannot initialize project memory store at ${u}: ${f.reason}`,{next:`Run \`crtr sys migrate --dir ${n}\`.`});const h={namespace:f.namespace,repositoryRoot:n,initialize:!0};return a.set(n,h),h}p(ne,"resolveRepositoryNamespace");function se(){return[{on:"workspace-open",at:"content"},{on:"read",match:"./**",at:"content"}]}p(se,"frontDoorSurfaces");function ae(e){const t=e.trim();return!t.includes("/")||t.startsWith("/")||t.startsWith("**")||t.startsWith("./")?t:`./${t}`}p(ae,"anchorReadGlob");function ie(e,t){const o=D($(e.path)),a=y(e.sourceDir)===t.repositoryRoot;return M(e,t.namespace,o.body,a,o.data??{})}p(ie,"prepareAgentsCandidate");function ce(e){return Array.isArray(e.surfaces)?e.surfaces.filter(t=>{if(t===null||typeof t!="object"||Array.isArray(t))return!1;const o=t;if(o.on!=="read"||!["name","preview","content"].includes(String(o.at)))return!1;const a=o.match,r=typeof a=="string"?a.trim()!=="":Array.isArray(a)&&a.length>0&&a.every(c=>typeof c=="string"&&c.trim()!==""),n=o["match-frontmatter"];return r||n!==null&&typeof n=="object"&&!Array.isArray(n)}):[]}p(ce,"sourceReadRoute");function M(e,t,o,a,r={}){if(!a){const c=ce(r),i=x(r["when-and-why-to-read"])?.trim(),u={kind:"knowledge",privacy:"public","when-and-why-to-read":i!==void 0&&i!==""?i:`When working under ${t}, this knowledge should be read because it carries the nested project guidance.`,"short-form":S(o),...c.length>0?{surfaces:c}:{}};return{candidate:e,name:"",target:g(e.sourceDir,k,"memory","INDEX.md"),frontmatter:u,body:o}}const n={kind:"knowledge",privacy:"public",namespace:t,"when-and-why-to-read":`When working in ${t}, this knowledge should be read because it is the project's operating guide.`,"short-form":S(o),surfaces:se()};return{candidate:e,name:t,target:g(e.sourceDir,k,"memory","INDEX.md"),frontmatter:n,body:o}}p(M,"prepareRootCandidate");function ue(e,t){const o=C(e.sourceDir);if(!o.ok)throw w(`cannot associate rule guidance at ${e.path}: ${o.reason}`,{next:`Run \`crtr sys migrate --dir ${e.sourceDir}\`.`});const a=T(t.namespace,o.association.ownerRelativePath),r=$(e.path),n=D(r),c=n.data??{},i=B(_(e.path)),u=Q(`${i}.md`),l=V(c,u),f=Y(l,a);if(f.ok===!1)throw w(`invalid local rule name in ${e.path}: ${f.reason}`);const h=x(c.description)??"",s=T(a,l),d={kind:"knowledge",privacy:"public",name:l,"when-and-why-to-read":h!==""?K(h,"knowledge"):`When working under ${a}, this knowledge should be read because it is migrated project rule guidance.`,"short-form":h!==""?h.replace(/\s+/g," ").trim():S(n.body)},m=re(c.paths).map(ae);return d.surfaces=m.length>0?[{on:"read",match:m.length===1?m[0]:m,at:"content"}]:[{on:"read",match:"./**",at:"content"}],{candidate:e,name:s,target:J(g(e.sourceDir,k,"memory"),l),frontmatter:d,body:n.body}}p(ue,"prepareRuleCandidate");function I(e,t){if(N(e.target)&&!t.overwrite)return{source:e.candidate.path,target:e.target,name:e.name,scope:"project",status:"skipped",reason:"target memory doc already exists; re-run with --overwrite to replace it"};if(!t.dryRun){const o=N(e.target)?D($(e.target)).data?.privacy??"private":"public";U(e.target,X({...e.frontmatter,privacy:o},e.body))}return{source:e.candidate.path,target:e.target,name:e.name,scope:"project",status:t.dryRun?"would-import":"imported"}}p(I,"convertProjectDoc");function de(e,t){if(e.kind!=="agents")return;let o;try{o=D($(e.path)).data?.namespace}catch{return}if(o===void 0)return;const a=y(e.sourceDir)===t.repositoryRoot,r=g(t.repositoryRoot,k,"memory","INDEX.md");if(!a)throw w(`project guidance at ${e.path} declares a namespace, but ${r} is the only declaration for this repository`,{next:`Run \`crtr sys migrate --dir ${t.repositoryRoot}\`.`});if(o!==t.namespace)throw w(`project guidance namespace ${JSON.stringify(o)} conflicts with the declared store namespace ${JSON.stringify(t.namespace)}`,{next:`Run \`crtr sys migrate --dir ${t.repositoryRoot}\` for a repository-wide identity change.`})}p(de,"validateAuthoredNamespace");function pe(e){const t=e.filter(i=>i.status==="imported").length,o=e.filter(i=>i.status==="would-import").length,a=e.filter(i=>i.status==="skipped").length,r=o>0?`crtr sys sync project-guidance \u2014 dry run: ${o} would import, ${a} skipped`:`crtr sys sync project-guidance \u2014 ${t} imported, ${a} skipped`,n=e.map(i=>{const u=i.reason?` (${i.reason})`:"";return`| ${i.status} | ${i.scope}/${i.name||"\u2014"} | ${i.target} | ${i.source}${u} |`}),c=n.length===0?["No sync candidates."]:["| status | memory doc | target | source |","| --- | --- | --- | --- |",...n];return[r,"",...c].join(`
3
+ `)}p(pe,"renderSummary");const Ee=z({name:"project-guidance",description:"migrate repository guidance into repository-root and nested crouter memory docs",whenToUse:"migrating repository CLAUDE.md/AGENTS.md/.claude/rules into the crouter memory-doc model: one-way copy into the repository root's or nested store's .crouter/memory/. Only the repository-root guide becomes the workspace front door; nested guides and rules keep read routes. Never exports memory docs back to these source files.",help:{name:"sys sync project-guidance",summary:"one-way migration: CLAUDE.md/AGENTS.md/.claude/rules \u2192 repository-root and nested crouter memory docs",params:[{kind:"flag",name:"source",type:"string",required:!1,constraint:"The root to walk recursively for CLAUDE.md/AGENTS.md/.claude/rules files. Default: cwd plus every project in the current profile's purview (when one is selected)."},{kind:"flag",name:"dry-run",type:"bool",required:!1,constraint:"Show what would be migrated without writing memory docs."},{kind:"flag",name:"overwrite",type:"bool",required:!1,constraint:"Replace an existing target memory doc. Default: skip existing docs to avoid clobbering user-authored memory."}],output:[{name:"results",type:"object[]",required:!0,constraint:"Each: {source,target,name,scope,status,reason?}; status is one of imported, skipped, would-import."}],outputKind:"object",effects:["Copies the repository-root CLAUDE.md/AGENTS.md into the repository-root .crouter/memory/INDEX.md with the namespace declaration and workspace front-door surfaces. Copies nested guides into nested .crouter/memory/INDEX.md files without namespace, name, or workspace-open metadata. Copies each .claude/rules/*.md into its nested store with a local name and a content read entry over the source paths (\u2018./\u2019-anchored when repo-relative), or a plain read route when no paths are declared. Originals are left untouched.","Skips existing memory docs unless --overwrite is present.","With --dry-run: read-only; writes nothing."]},run:p(async e=>{const t=e.source,o=e.dryRun??!1,a=e.overwrite??!1,r=[],n=new Set,c=[];for(const s of te(t))for(const d of oe(s))n.has(d.path)||(n.add(d.path),c.push(d));const i=new Map;for(const s of c)if(s.kind==="agents")try{i.set(y(s.sourceDir),D($(s.path)).body)}catch{i.set(y(s.sourceDir),void 0)}const u=new Map,l=new Map;for(const s of c){const d=ne(s.sourceDir,s.walkRoot,i,u);l.set(s.path,d),de(s,d)}const f=[];for(const s of c){const d=l.get(s.path);try{const m=s.kind==="agents"?ie(s,d):ue(s,d);f.push({candidate:s,prepared:m})}catch(m){const R=(m instanceof Error?m.message:String(m)).split(`
4
+ `)[0];r.push({source:s.path,target:"",name:"",scope:"project",status:"skipped",reason:`unparseable source frontmatter: ${R} \u2014 fix the source file and re-run`})}}for(const s of u.values()){if(c.some(v=>v.kind==="agents"&&l.get(v.path)?.repositoryRoot===s.repositoryRoot&&y(v.sourceDir)===s.repositoryRoot)||P(s.repositoryRoot).mountStatus==="ready")continue;const m=c.find(v=>l.get(v.path)?.repositoryRoot===s.repositoryRoot);if(m===void 0)continue;const R={...m,kind:"agents",sourceDir:s.repositoryRoot},G=M(R,s.namespace,"Migrated project guidance.",!0);r.push(I(G,{dryRun:o,overwrite:a}))}const h=[...f].sort((s,d)=>{const m=y(s.candidate.sourceDir)===l.get(s.candidate.path).repositoryRoot,R=y(d.candidate.sourceDir)===l.get(d.candidate.path).repositoryRoot;return Number(R)-Number(m)});for(const{prepared:s}of h)r.push(I(s,{dryRun:o,overwrite:a}));return{results:r}},"run"),render:p(e=>pe(e.results),"render")});export{Ee as sysSyncProjectGuidanceLeaf};
@@ -1,2 +1,2 @@
1
- var _=Object.defineProperty;var s=(e,o)=>_(e,"name",{value:o,configurable:!0});import{existsSync as $}from"node:fs";import{homedir as v}from"node:os";import{basename as j,dirname as h,join as g,relative as E,resolve as y,sep as C}from"node:path";import{defineLeaf as N}from"../../core/command.js";import{usage as I}from"../../core/errors.js";import{parseFrontmatterGeneric as q}from"../../core/frontmatter.js";import{pathExists as T,readJsonIfExists as M,readText as O,walkFiles as P,writeJson as D,writeText as W}from"../../core/fs-utils.js";import{composeCanonicalName as J}from"../../core/memory/identity.js";import{findProjectScopeRoot as G}from"../../core/scope.js";import{CRTR_DIR_NAME as z}from"../../types.js";import{memoryFilePath as H,requireMountedStore as U,resolveWriteSelector as B,serializeMemoryDoc as X}from"../memory/shared.js";import{normalizedName as R,routeFromDescription as Y,scalarString as x}from"./sync-shared.js";const Q="<!-- crtr-boot-skill v",L="SKILL.md",w="skill-import-ignore.json",V=new Set(["node_modules",".git",".venv","venv","dist","build",".next",".cache",".yalc",".sisyphus",z]);function Z(){const e=G();return e?h(e):null}s(Z,"projectDir");function k(){return g(v(),".crouter",w)}s(k,"ignoreFilePath");function ee(){const e=[{label:"claude-user",root:g(v(),".claude","skills"),defaultScope:"user"},{label:"pi-user",root:g(v(),".pi","agent","skills"),defaultScope:"user"}],o=Z();return o&&(e.push({label:"claude-project",root:g(o,".claude","skills"),defaultScope:"project"}),e.push({label:"pi-project",root:g(o,".pi","agent","skills"),defaultScope:"project"})),e}s(ee,"defaultSourceRoots");function te(e,o){if(e===void 0)return ee();const r=y(e);if(o!==void 0&&o!=="user"&&o!=="project")throw I(`invalid --scope: ${o} (expected user|project)`);return[{label:"source",root:r,defaultScope:o??"user"}]}s(te,"sourceRootsFromArg");function oe(e){const o=e.root;if(!$(o))return[];const r=y(o),t=[];if(j(r)===L)return t.push({path:r,root:h(r),source:e.label,defaultScope:e.defaultScope}),t;const i=g(r,L);if($(i))return t.push({path:i,root:h(r),source:e.label,defaultScope:e.defaultScope}),t;for(const a of P(r,c=>c===L,c=>V.has(c)))t.push({path:a,root:r,source:e.label,defaultScope:e.defaultScope});return t}s(oe,"collectCandidates");function re(e,o){const r=x(o?.name);if(r&&r.trim()!=="")return R(r);const t=E(e.root,h(e.path)).split(C).join("/");return R(t||j(h(e.path)))}s(re,"nameForCandidate");function ne(){const e=new Map;return o=>{const r=e.get(o);if(r!==void 0)return r;const{memoryDir:t,store:i}=B({scope:o});U(i);const a={memoryDir:t,namespace:i.namespace};return e.set(o,a),a}}s(ne,"storeResolver");function se(){let e;try{e=M(k())}catch(r){throw I(`invalid JSON in ${k()}: ${r instanceof Error?r.message:String(r)}`)}if(e===null)return{version:1,ignored:[]};if(typeof e!="object"||e===null||e.version!==1||!Array.isArray(e.ignored))throw I(`malformed ${k()}: expected {"version":1,"ignored":[...]}`);const o=[];for(const r of e.ignored){if(typeof r!="object"||r===null)continue;const t=r;typeof t.source=="string"&&typeof t.name=="string"&&(t.scope==="user"||t.scope==="project")&&typeof t.ignoredAt=="string"&&o.push({source:y(t.source),name:t.name,scope:t.scope,ignoredAt:t.ignoredAt})}return{version:1,ignored:o}}s(se,"readIgnoreState");function ie(e){D(k(),e)}s(ie,"writeIgnoreState");function K(e,o){const r=y(e.candidate.path);return o.ignored.some(t=>t.source===r||t.scope===e.scope&&t.name===e.name)}s(K,"isIgnored");function ae(e,o){return K(e,o)?!1:(o.ignored.push({source:y(e.candidate.path),name:e.name,scope:e.scope,ignoredAt:new Date().toISOString()}),o.ignored.sort((r,t)=>`${r.scope}/${r.name}`.localeCompare(`${t.scope}/${t.name}`)),!0)}s(ae,"rememberIgnored");function F(e,o,r){return{source:e.path,target:"",name:"",scope:o,status:"skipped",reason:r}}s(F,"skipResult");function ce(e,o,r){const t=O(e.path),i=r??e.defaultScope;if(t.includes(Q))return F(e,i,"legacy generated crtr boot skill is pruned by host exports, not imported");const a=q(t),c=a.data??{},u=c.kind==="preference"?"preference":"knowledge",d=re(e,c);if(d==="")return F(e,i,"could not derive memory document name");const m=o(i),n=H(m.memoryDir,d),p=J(m.namespace,d);return{candidate:e,raw:t,parsed:a,sourceFm:c,kind:u,name:p,scope:i,target:n}}s(ce,"prepareCandidate");function le(e,o){if(T(e.target)&&!o.overwrite)return{source:e.candidate.path,target:e.target,name:e.name,scope:e.scope,status:"skipped",reason:"target memory doc already exists; re-run with --overwrite to replace it"};const r=x(e.sourceFm.description)??"",t={...e.sourceFm};return delete t.name,delete t.description,delete t.type,delete t.keywords,delete t["system-prompt-visibility"],delete t["file-read-visibility"],delete t["applies-to"],delete t["read-when"],t.kind=e.kind,(typeof t["when-and-why-to-read"]!="string"||t["when-and-why-to-read"].trim()==="")&&(t["when-and-why-to-read"]=Y(r,e.kind)),(typeof t["short-form"]!="string"||t["short-form"].trim()==="")&&(t["short-form"]=r.replace(/\s+/g," ").trim()),Array.isArray(t.surfaces)||(t.surfaces=[{on:"boot",at:"preview"}]),o.dryRun||W(e.target,X(t,e.parsed.body)),{source:e.candidate.path,target:e.target,name:e.name,scope:e.scope,status:o.dryRun?"would-import":"imported"}}s(le,"convertPrepared");function A(e,o,r){return{source:e.candidate.path,target:e.target,name:e.name,scope:e.scope,status:r?"would-ignore":"ignored",reason:o}}s(A,"ignoreResult");function de(e,o){const r=e.filter(n=>n.status==="imported").length,t=e.filter(n=>n.status==="would-import").length,i=e.filter(n=>n.status==="skipped").length,a=e.filter(n=>n.status==="ignored").length,c=e.filter(n=>n.status==="would-ignore").length;let u;o?u=`crtr sys sync skills \u2014 ${c>0?`${c} would ignore`:`${a} ignored`}, ${i} skipped`:t>0?u=`crtr sys sync skills \u2014 dry run: ${t} would import, ${i} skipped, ${a} ignored`:u=`crtr sys sync skills \u2014 ${r} imported, ${i} skipped, ${a} ignored`;const d=e.map(n=>{const p=n.target||"\u2014",f=n.reason?` (${n.reason})`:"";return`| ${n.status} | ${n.scope}/${n.name||"\u2014"} | ${p} | ${n.source}${f} |`}),m=d.length===0?["No sync candidates."]:["| status | memory doc | target | source |","| --- | --- | --- | --- |",...d];return[u,"",...m].join(`
2
- `)}s(de,"renderSummary");const Le=N({name:"skills",description:"import host Agent Skill (SKILL.md) bundles as crouter memory docs",whenToUse:"migrating legacy host-native Agent Skill bundles (SKILL.md \u2014 Claude/pi user or project skill roots, or an explicit bundle/root via --source) into the crouter memory-doc model. One-way: crouter never re-exports memory docs back to SKILL.md, and stops reading the originals unconditionally once migrated.",help:{name:"sys sync skills",summary:"one-way import: host SKILL.md bundles \u2192 crouter memory docs",params:[{kind:"flag",name:"source",type:"string",required:!1,constraint:"A SKILL.md file, a single skill bundle dir containing SKILL.md, or a skills root to scan recursively. Default: the user/project Claude and pi skill roots that exist."},{kind:"flag",name:"scope",type:"enum",choices:["user","project"],required:!1,constraint:"Target memory scope for the selected sources. Default: user for user host roots and project for project host roots; an explicit value applies to every selected source. `project` writes into the nearest project store walking up from cwd, which must already declare its `namespace:` \u2014 this lane imports documents into a store, never the declaration itself."},{kind:"flag",name:"dry-run",type:"bool",required:!1,constraint:"Show what would be imported without writing memory docs."},{kind:"flag",name:"overwrite",type:"bool",required:!1,constraint:"Replace an existing target memory doc. Default: skip existing docs to avoid clobbering user-authored memory."},{kind:"flag",name:"ignore",type:"bool",required:!1,constraint:`Permanently ignore every selected SKILL.md source by recording it in ~/.crouter/${w}. Honors --source/--scope; with no --source, ignores every currently discovered default host skill candidate.`},{kind:"flag",name:"include-ignored",type:"bool",required:!1,constraint:"Include ignored candidates in the report without making sync read-only. Pair with --dry-run to audit the full candidate set without importing non-ignored skills."}],output:[{name:"results",type:"object[]",required:!0,constraint:"Each: {source,target,name,scope,status,reason?}; status is one of imported, skipped, would-import, ignored, would-ignore. `name` is the full canonical name the imported doc answers at \u2014 a user import\u2019s bundle name, a project import\u2019s `<namespace>/<bundle name>`."}],outputKind:"object",effects:["Writes memory/<bundle name>.md in the selected crouter scope with substrate frontmatter and the SKILL.md body. The file stays store-local; a project import answers at `<namespace>/<bundle name>`.","Refuses a project store that declares no namespace, naming the `crtr sys migrate --dir` (legacy store) or `crtr memory write <namespace> --dir` (empty store) remedy; it never declares one itself.","Skips existing memory docs unless --overwrite is present.","Skips marker-bearing generated crtr boot skills; host exports prune those legacy artifacts instead.",`With --ignore: writes ~/.crouter/${w} and does not import the selected sources.`,"With --dry-run: read-only; writes nothing."]},run:s(async e=>{const o=e.source,r=e.scope,t=e.dryRun??!1,i=e.overwrite??!1,a=e.ignore??!1,c=e.includeIgnored??!1,u=te(o,r),d=new Set,m=[];for(const b of u)for(const l of oe(b))d.has(l.path)||(d.add(l.path),m.push(l));const n=ne(),p=se(),f=[];let S=!1;for(const b of m){const l=ce(b,n,r);if("status"in l){f.push(l);continue}if(a){t||(S=ae(l,p)||S),f.push(A(l,t?"would record permanent ignore":`recorded in ~/.crouter/${w}`,t));continue}if(K(l,p)){c&&f.push(A(l,`matched ~/.crouter/${w}`,!1));continue}f.push(le(l,{dryRun:t,overwrite:i}))}return S&&ie(p),{results:f,ignoreFile:k(),ignoreMode:a}},"run"),render:s(e=>de(e.results,e.ignoreMode===!0),"render")});export{Le as sysSyncSkillsLeaf};
1
+ var C=Object.defineProperty;var s=(e,o)=>C(e,"name",{value:o,configurable:!0});import{existsSync as L}from"node:fs";import{homedir as N}from"node:os";import{basename as $,dirname as h,join as g,relative as q,resolve as y,sep as T}from"node:path";import{defineLeaf as D}from"../../core/command.js";import{usage as v}from"../../core/errors.js";import{parseFrontmatterGeneric as j}from"../../core/frontmatter.js";import{pathExists as R,readJsonIfExists as M,readText as x,walkFiles as O,writeJson as P,writeText as W}from"../../core/fs-utils.js";import{composeCanonicalName as J}from"../../core/memory/identity.js";import{findProjectScopeRoot as G}from"../../core/scope.js";import{runtimeRoot as z}from"../../core/layout.js";import{piAgentDir as H}from"../../core/runtime/package-health.js";import{CRTR_DIR_NAME as U}from"../../types.js";import{memoryFilePath as B,requireMountedStore as X,serializeMemoryDoc as Y}from"../../core/memory/mutation-domain.js";import{resolveWriteSelector as Q}from"../../core/memory/cli-selector.js";import{normalizedName as K,routeFromDescription as V,scalarString as A}from"./sync-shared.js";const Z="<!-- crtr-boot-skill v",I="SKILL.md",w="skill-import-ignore.json",ee=new Set(["node_modules",".git",".venv","venv","dist","build",".next",".cache",".yalc",".sisyphus",U]);function te(){const e=G();return e?h(e):null}s(te,"projectDir");function k(){return g(z(),w)}s(k,"ignoreFilePath");function oe(){const e=[{label:"claude-user",root:g(N(),".claude","skills"),defaultScope:"user"},{label:"pi-user",root:g(H(),"skills"),defaultScope:"user"}],o=te();return o&&(e.push({label:"claude-project",root:g(o,".claude","skills"),defaultScope:"project"}),e.push({label:"pi-project",root:g(o,".pi","agent","skills"),defaultScope:"project"})),e}s(oe,"defaultSourceRoots");function re(e,o){if(e===void 0)return oe();const r=y(e);if(o!==void 0&&o!=="user"&&o!=="project")throw v(`invalid --scope: ${o} (expected user|project)`);return[{label:"source",root:r,defaultScope:o??"user"}]}s(re,"sourceRootsFromArg");function ne(e){const o=e.root;if(!L(o))return[];const r=y(o),t=[];if($(r)===I)return t.push({path:r,root:h(r),source:e.label,defaultScope:e.defaultScope}),t;const i=g(r,I);if(L(i))return t.push({path:i,root:h(r),source:e.label,defaultScope:e.defaultScope}),t;for(const a of O(r,c=>c===I,c=>ee.has(c)))t.push({path:a,root:r,source:e.label,defaultScope:e.defaultScope});return t}s(ne,"collectCandidates");function se(e,o){const r=A(o?.name);if(r&&r.trim()!=="")return K(r);const t=q(e.root,h(e.path)).split(T).join("/");return K(t||$(h(e.path)))}s(se,"nameForCandidate");function ie(){const e=new Map;return o=>{const r=e.get(o);if(r!==void 0)return r;const{memoryDir:t,store:i}=Q({scope:o});X(i);const a={memoryDir:t,namespace:i.namespace};return e.set(o,a),a}}s(ie,"storeResolver");function ae(){let e;try{e=M(k())}catch(r){throw v(`invalid JSON in ${k()}: ${r instanceof Error?r.message:String(r)}`)}if(e===null)return{version:1,ignored:[]};if(typeof e!="object"||e===null||e.version!==1||!Array.isArray(e.ignored))throw v(`malformed ${k()}: expected {"version":1,"ignored":[...]}`);const o=[];for(const r of e.ignored){if(typeof r!="object"||r===null)continue;const t=r;typeof t.source=="string"&&typeof t.name=="string"&&(t.scope==="user"||t.scope==="project")&&typeof t.ignoredAt=="string"&&o.push({source:y(t.source),name:t.name,scope:t.scope,ignoredAt:t.ignoredAt})}return{version:1,ignored:o}}s(ae,"readIgnoreState");function ce(e){P(k(),e)}s(ce,"writeIgnoreState");function F(e,o){const r=y(e.candidate.path);return o.ignored.some(t=>t.source===r||t.scope===e.scope&&t.name===e.name)}s(F,"isIgnored");function le(e,o){return F(e,o)?!1:(o.ignored.push({source:y(e.candidate.path),name:e.name,scope:e.scope,ignoredAt:new Date().toISOString()}),o.ignored.sort((r,t)=>`${r.scope}/${r.name}`.localeCompare(`${t.scope}/${t.name}`)),!0)}s(le,"rememberIgnored");function _(e,o,r){return{source:e.path,target:"",name:"",scope:o,status:"skipped",reason:r}}s(_,"skipResult");function de(e,o,r){const t=x(e.path),i=r??e.defaultScope;if(t.includes(Z))return _(e,i,"legacy generated crtr boot skill is pruned by host exports, not imported");const a=j(t),c=a.data??{},u=c.kind==="preference"?"preference":"knowledge",d=se(e,c);if(d==="")return _(e,i,"could not derive memory document name");const m=o(i),n=B(m.memoryDir,d),p=J(m.namespace,d);return{candidate:e,raw:t,parsed:a,sourceFm:c,kind:u,name:p,scope:i,target:n}}s(de,"prepareCandidate");function ue(e,o){if(R(e.target)&&!o.overwrite)return{source:e.candidate.path,target:e.target,name:e.name,scope:e.scope,status:"skipped",reason:"target memory doc already exists; re-run with --overwrite to replace it"};const r=A(e.sourceFm.description)??"",t={...e.sourceFm};return delete t.name,delete t.description,delete t.type,delete t.keywords,delete t["system-prompt-visibility"],delete t["file-read-visibility"],delete t["applies-to"],delete t["read-when"],t.kind=e.kind,(typeof t["when-and-why-to-read"]!="string"||t["when-and-why-to-read"].trim()==="")&&(t["when-and-why-to-read"]=V(r,e.kind)),(typeof t["short-form"]!="string"||t["short-form"].trim()==="")&&(t["short-form"]=r.replace(/\s+/g," ").trim()),Array.isArray(t.surfaces)||(t.surfaces=[{on:"boot",at:"preview"}]),o.dryRun||(t.privacy=R(e.target)?j(x(e.target)).data?.privacy??"private":t.privacy??"public",W(e.target,Y(t,e.parsed.body))),{source:e.candidate.path,target:e.target,name:e.name,scope:e.scope,status:o.dryRun?"would-import":"imported"}}s(ue,"convertPrepared");function E(e,o,r){return{source:e.candidate.path,target:e.target,name:e.name,scope:e.scope,status:r?"would-ignore":"ignored",reason:o}}s(E,"ignoreResult");function me(e,o){const r=e.filter(n=>n.status==="imported").length,t=e.filter(n=>n.status==="would-import").length,i=e.filter(n=>n.status==="skipped").length,a=e.filter(n=>n.status==="ignored").length,c=e.filter(n=>n.status==="would-ignore").length;let u;o?u=`crtr sys sync skills \u2014 ${c>0?`${c} would ignore`:`${a} ignored`}, ${i} skipped`:t>0?u=`crtr sys sync skills \u2014 dry run: ${t} would import, ${i} skipped, ${a} ignored`:u=`crtr sys sync skills \u2014 ${r} imported, ${i} skipped, ${a} ignored`;const d=e.map(n=>{const p=n.target||"\u2014",f=n.reason?` (${n.reason})`:"";return`| ${n.status} | ${n.scope}/${n.name||"\u2014"} | ${p} | ${n.source}${f} |`}),m=d.length===0?["No sync candidates."]:["| status | memory doc | target | source |","| --- | --- | --- | --- |",...d];return[u,"",...m].join(`
2
+ `)}s(me,"renderSummary");const Ke=D({name:"skills",description:"import host Agent Skill (SKILL.md) bundles as crouter memory docs",whenToUse:"migrating legacy host-native Agent Skill bundles (SKILL.md \u2014 Claude/pi user or project skill roots, or an explicit bundle/root via --source) into the crouter memory-doc model. One-way: crouter never re-exports memory docs back to SKILL.md, and stops reading the originals unconditionally once migrated.",help:{name:"sys sync skills",summary:"one-way import: host SKILL.md bundles \u2192 crouter memory docs",params:[{kind:"flag",name:"source",type:"string",required:!1,constraint:"A SKILL.md file, a single skill bundle dir containing SKILL.md, or a skills root to scan recursively. Default: the user/project Claude and pi skill roots that exist."},{kind:"flag",name:"scope",type:"enum",choices:["user","project"],required:!1,constraint:"Target memory scope for the selected sources. Default: user for user host roots and project for project host roots; an explicit value applies to every selected source. `project` writes into the nearest project store walking up from cwd, which must already declare its `namespace:` \u2014 this lane imports documents into a store, never the declaration itself."},{kind:"flag",name:"dry-run",type:"bool",required:!1,constraint:"Show what would be imported without writing memory docs."},{kind:"flag",name:"overwrite",type:"bool",required:!1,constraint:"Replace an existing target memory doc. Default: skip existing docs to avoid clobbering user-authored memory."},{kind:"flag",name:"ignore",type:"bool",required:!1,constraint:`Permanently ignore every selected SKILL.md source by recording it in ~/.crouter/${w}. Honors --source/--scope; with no --source, ignores every currently discovered default host skill candidate.`},{kind:"flag",name:"include-ignored",type:"bool",required:!1,constraint:"Include ignored candidates in the report without making sync read-only. Pair with --dry-run to audit the full candidate set without importing non-ignored skills."}],output:[{name:"results",type:"object[]",required:!0,constraint:"Each: {source,target,name,scope,status,reason?}; status is one of imported, skipped, would-import, ignored, would-ignore. `name` is the full canonical name the imported doc answers at \u2014 a user import\u2019s bundle name, a project import\u2019s `<namespace>/<bundle name>`."}],outputKind:"object",effects:["Writes memory/<bundle name>.md in the selected crouter scope with substrate frontmatter and the SKILL.md body. The file stays store-local; a project import answers at `<namespace>/<bundle name>`.","Refuses a project store that declares no namespace, naming the `crtr sys migrate --dir` (legacy store) or `crtr memory write <namespace> --dir` (empty store) remedy; it never declares one itself.","Skips existing memory docs unless --overwrite is present.","Skips marker-bearing generated crtr boot skills; host exports prune those legacy artifacts instead.",`With --ignore: writes ~/.crouter/${w} and does not import the selected sources.`,"With --dry-run: read-only; writes nothing."]},run:s(async e=>{const o=e.source,r=e.scope,t=e.dryRun??!1,i=e.overwrite??!1,a=e.ignore??!1,c=e.includeIgnored??!1,u=re(o,r),d=new Set,m=[];for(const b of u)for(const l of ne(b))d.has(l.path)||(d.add(l.path),m.push(l));const n=ie(),p=ae(),f=[];let S=!1;for(const b of m){const l=de(b,n,r);if("status"in l){f.push(l);continue}if(a){t||(S=le(l,p)||S),f.push(E(l,t?"would record permanent ignore":`recorded in ~/.crouter/${w}`,t));continue}if(F(l,p)){c&&f.push(E(l,`matched ~/.crouter/${w}`,!1));continue}f.push(ue(l,{dryRun:t,overwrite:i}))}return S&&ce(p),{results:f,ignoreFile:k(),ignoreMode:a}},"run"),render:s(e=>me(e.results,e.ignoreMode===!0),"render")});export{Ke as sysSyncSkillsLeaf};
@@ -1 +1,2 @@
1
+ export declare function formatSize(bytes: number | null): string;
1
2
  export declare const sysWorktreesLeaf: import("../../core/command.js").LeafDef;
@@ -2,4 +2,4 @@ var i=Object.defineProperty;var r=(t,n)=>i(t,"name",{value:n,configurable:!0});i
2
2
 
3
3
  ${n.map(e=>{const a=[`- \`${e.node_id}\` \u2014 ${e.reason} \xB7 ${u(e.size_bytes)} \xB7 ${e.attempts} attempt${e.attempts===1?"":"s"} \xB7 ${e.last_attempt}`,` - path: \`${e.path}\``,` - branch: \`${e.branch}\``,` - repo root: \`${e.repo_root}\``];return e.detail!==void 0&&a.push(` - detail: ${e.detail}`),a.join(`
4
4
  `)}).join(`
5
- `)}`},"render")});export{p as sysWorktreesLeaf};
5
+ `)}`},"render")});export{u as formatSize,p as sysWorktreesLeaf};
@@ -1 +1 @@
1
- var t=Object.defineProperty;var r=(o,m)=>t(o,"name",{value:m,configurable:!0});import{defineSysBranch as s}from"./sys/branch.js";import{configBranch as f}from"./sys/config.js";import{daemonBranch as e}from"./sys/daemon.js";import{sysDoctorLeaf as i}from"./sys/doctor.js";import{sysFeedbackLeaf as a}from"./sys/feedback.js";import{sysLogsLeaf as p}from"./sys/logs.js";import{sysWorktreesLeaf as n}from"./sys/worktrees.js";import{sysSettingsLeaf as c}from"./sys/settings.js";import{sysConnectLeaf as y}from"./sys/connect.js";import{sysSetupLeaf as L}from"./sys/setup.js";import{tutorialBranch as h}from"./sys/tutorial/branch.js";import{supportBranch as B}from"./sys/support.js";import{contextBranch as g}from"./sys/context.js";import{sysSyncBranch as u}from"./sys/sync.js";import{sysMigrateLeaf as S}from"./sys/migrate.js";import{sysUpdateLeaf as d,sysVersionLeaf as k}from"./sys/update.js";function G(){return s([c,f,y,L,h,i,g,a,p,n,B,d,k,S,u,e])}r(G,"registerSys");export{G as registerSys};
1
+ var s=Object.defineProperty;var r=(o,m)=>s(o,"name",{value:m,configurable:!0});import{defineSysBranch as t}from"./sys/branch.js";import{configBranch as f}from"./sys/config.js";import{daemonBranch as e}from"./sys/daemon.js";import{sysDoctorLeaf as i}from"./sys/doctor.js";import{sysFeedbackLeaf as a}from"./sys/feedback.js";import{sysLogsLeaf as p}from"./sys/logs.js";import{sysWorktreesLeaf as n}from"./sys/worktrees.js";import{sysSpacesLeaf as y}from"./sys/spaces.js";import{sysSettingsLeaf as c}from"./sys/settings.js";import{sysConnectLeaf as L}from"./sys/connect.js";import{sysSetupLeaf as h}from"./sys/setup.js";import{tutorialBranch as B}from"./sys/tutorial/branch.js";import{supportBranch as S}from"./sys/support.js";import{contextBranch as g}from"./sys/context.js";import{sysSyncBranch as u}from"./sys/sync.js";import{sysMigrateLeaf as d}from"./sys/migrate.js";import{sysJoinLeaf as k}from"./sys/join.js";import{sysUpdateLeaf as x,sysVersionLeaf as b}from"./sys/update.js";function K(){return t([c,f,L,h,B,i,g,a,p,n,y,S,x,b,d,k,u,e])}r(K,"registerSys");export{K as registerSys};
@@ -1,2 +1,2 @@
1
- var m=Object.defineProperty;var a=(e,t)=>m(e,"name",{value:t,configurable:!0});import{execSync as w}from"node:child_process";import{homedir as A}from"node:os";import{join as k}from"node:path";import{LockedJsonFile as d}from"./locked-file.js";function c(){return k(A(),".pi","agent","auth.json")}a(c,"defaultAuthPath");function l(e){if(e===void 0||e.trim()==="")return{};const t=JSON.parse(e);return t!==null&&typeof t=="object"?t:{}}a(l,"parse");function p(e){return`${JSON.stringify(e,null,2)}
2
- `}a(p,"serialize");const _=/^[A-Za-z_][A-Za-z0-9_]*/,C=/^[A-Za-z_][A-Za-z0-9_]*$/,x=new Map;function O(e){if(x.has(e))return x.get(e);let t;try{t=w(e,{encoding:"utf-8",timeout:1e4,stdio:["ignore","pipe","ignore"]}).trim()||void 0}catch{t=void 0}return x.set(e,t),t}a(O,"runValueCommand");function N(e,t){if(e.startsWith("!"))return O(e.slice(1));let n="",i=0;for(;i<e.length;){const r=e.indexOf("$",i);if(r<0){n+=e.slice(i);break}n+=e.slice(i,r);const s=e[r+1];if(s==="$"||s==="!"){n+=s,i=r+2;continue}let u,f=r+1;if(s==="{"){const o=e.indexOf("}",r+2),y=o<0?void 0:e.slice(r+2,o);y!==void 0&&C.test(y)&&(u=y,f=o+1)}else{const o=_.exec(e.slice(r+1));o!==null&&(u=o[0],f=r+1+u.length)}if(u===void 0){n+="$",i=r+1;continue}const h=t?.[u]||process.env[u];if(h===void 0||h==="")return;n+=h,i=f}return n}a(N,"resolveCredentialValue");async function j(e=c()){return l(new d(e).readUnlocked())}a(j,"readAuthCredentials");async function z(e,t=c()){return(await j(t))[e]}a(z,"readAuthCredential");async function L(e,t,n=c()){await new d(n).withLockAsync(async i=>{const r=l(i);return r[e]=t,{result:void 0,next:p(r)}})}a(L,"setAuthCredential");async function E(e,t=c()){return new d(t).withLockAsync(async n=>{const i=l(n),r=Object.hasOwn(i,e);return delete i[e],{result:r,next:p(i)}})}a(E,"deleteAuthCredential");class S{static{a(this,"CrouterCredentialStore")}authPath;file;constructor(t=c()){this.authPath=t,this.file=new d(t)}async read(t){const n=await z(t,this.authPath);return n?.type!=="api_key"||n.key===void 0?n:{...n,key:N(n.key,n.env)}}async list(){return Object.entries(l(this.file.readUnlocked())).map(([t,n])=>({providerId:t,type:n.type}))}async modify(t,n){return this.file.withLockAsync(async i=>{const r=l(i),s=await n(r[t]);return s===void 0?{result:r[t]}:(r[t]=s,{result:s,next:p(r)})})}async delete(t){await E(t,this.authPath)}}export{S as CrouterCredentialStore,c as defaultAuthPath,E as deleteAuthCredential,z as readAuthCredential,j as readAuthCredentials,N as resolveCredentialValue,L as setAuthCredential};
1
+ var w=Object.defineProperty;var s=(e,t)=>w(e,"name",{value:t,configurable:!0});import{execSync as m}from"node:child_process";import{join as A}from"node:path";import{secretsDir as k}from"./layout.js";import{LockedJsonFile as d}from"./locked-file.js";function o(){return A(k(),"auth.json")}s(o,"defaultAuthPath");function l(e){if(e===void 0||e.trim()==="")return{};const t=JSON.parse(e);return t!==null&&typeof t=="object"?t:{}}s(l,"parse");function p(e){return`${JSON.stringify(e,null,2)}
2
+ `}s(p,"serialize");const _=/^[A-Za-z_][A-Za-z0-9_]*/,C=/^[A-Za-z_][A-Za-z0-9_]*$/,x=new Map;function O(e){if(x.has(e))return x.get(e);let t;try{t=m(e,{encoding:"utf-8",timeout:1e4,stdio:["ignore","pipe","ignore"]}).trim()||void 0}catch{t=void 0}return x.set(e,t),t}s(O,"runValueCommand");function N(e,t){if(e.startsWith("!"))return O(e.slice(1));let n="",i=0;for(;i<e.length;){const r=e.indexOf("$",i);if(r<0){n+=e.slice(i);break}n+=e.slice(i,r);const a=e[r+1];if(a==="$"||a==="!"){n+=a,i=r+2;continue}let u,f=r+1;if(a==="{"){const c=e.indexOf("}",r+2),y=c<0?void 0:e.slice(r+2,c);y!==void 0&&C.test(y)&&(u=y,f=c+1)}else{const c=_.exec(e.slice(r+1));c!==null&&(u=c[0],f=r+1+u.length)}if(u===void 0){n+="$",i=r+1;continue}const h=t?.[u]||process.env[u];if(h===void 0||h==="")return;n+=h,i=f}return n}s(N,"resolveCredentialValue");async function j(e=o()){return l(new d(e).readUnlocked())}s(j,"readAuthCredentials");async function z(e,t=o()){return(await j(t))[e]}s(z,"readAuthCredential");async function L(e,t,n=o()){await new d(n).withLockAsync(async i=>{const r=l(i);return r[e]=t,{result:void 0,next:p(r)}})}s(L,"setAuthCredential");async function E(e,t=o()){return new d(t).withLockAsync(async n=>{const i=l(n),r=Object.hasOwn(i,e);return delete i[e],{result:r,next:p(i)}})}s(E,"deleteAuthCredential");class S{static{s(this,"CrouterCredentialStore")}authPath;file;constructor(t=o()){this.authPath=t,this.file=new d(t)}async read(t){const n=await z(t,this.authPath);return n?.type!=="api_key"||n.key===void 0?n:{...n,key:N(n.key,n.env)}}async list(){return Object.entries(l(this.file.readUnlocked())).map(([t,n])=>({providerId:t,type:n.type}))}async modify(t,n){return this.file.withLockAsync(async i=>{const r=l(i),a=await n(r[t]);return a===void 0?{result:r[t]}:(r[t]=a,{result:a,next:p(r)})})}async delete(t){await E(t,this.authPath)}}export{S as CrouterCredentialStore,o as defaultAuthPath,E as deleteAuthCredential,z as readAuthCredential,j as readAuthCredentials,N as resolveCredentialValue,L as setAuthCredential};
@@ -0,0 +1,6 @@
1
+ export declare const MAX_BACKGROUND_BASH_LOG_BYTES: number;
2
+ export declare const BACKGROUND_BASH_LOG_TRUNCATION_MARKER = "\n[background bash job output truncated at 10 MiB; remaining output discarded]\n";
3
+ /** File-backed command supervisor. The daemon receives completed Linux jobs
4
+ * from the durable file state; on a Mac the existing CLI completion notice
5
+ * remains the owner of delivery. $12 selects that direct Mac path. */
6
+ export declare const BASH_JOB_SUPERVISOR_SCRIPT: string;
@@ -0,0 +1,4 @@
1
+ const t=10485760,e=`
2
+ [background bash job output truncated at 10 MiB; remaining output discarded]
3
+ `,n=['const fs = require("node:fs");',"const cap = 10485760;",`const marker = Buffer.from(${JSON.stringify(e)});`,"const prefixCap = cap - marker.length;",'const fd = fs.openSync(process.argv[1], "w");',"let written = 0;","let truncated = false;",'process.stdin.on("data", (chunk) => {'," if (truncated) return;"," const available = prefixCap - written;"," if (available > 0) {"," const prefix = chunk.subarray(0, available);"," fs.writeSync(fd, prefix);"," written += prefix.length;"," if (prefix.length === chunk.length) return;"," }"," fs.writeSync(fd, marker);"," truncated = true;","});",'process.stdin.on("end", () => fs.closeSync(fd));'].join(" "),r=["SECONDS=0","n=$(command -v nice)",'${n:+"$n" -n "${10}"} "${9}" "$1" 2>&1 | "${11}" -e '+JSON.stringify(n)+' "$2"',"ec=${PIPESTATUS[0]}","d=$SECONDS",'echo "$ec" > "$3"','if [ "$d" -lt 60 ]; then',' el="${d}s"','elif [ "$d" -lt 3600 ]; then',' el="$((d / 60))m $(printf "%02d" "$((d % 60))")s"',"else",' el="$((d / 3600))h $(printf "%02d" "$((d / 60 % 60))")m"',"fi",'if mv "$4" "$5" 2>/dev/null; then'," :",'elif [ -f "$6" ] && [ "${12}" = direct ]; then',' crtr node message send "Background bash job $7 finished (exit $ec) after $el. Log: $2 Command: $1" --to "$8" --tier urgent || true',"fi"].join(`
4
+ `);export{e as BACKGROUND_BASH_LOG_TRUNCATION_MARKER,r as BASH_JOB_SUPERVISOR_SCRIPT,t as MAX_BACKGROUND_BASH_LOG_BYTES};
@@ -1,5 +1,8 @@
1
1
  export interface BashJobPaths {
2
2
  jobId: string;
3
+ /** The node's agent folder (the parent of its context folder): every job
4
+ * file resolves beneath it, so a symlink the agent plants is never followed. */
5
+ root: string;
3
6
  dir: string;
4
7
  cmdSh: string;
5
8
  jobLog: string;
@@ -75,10 +78,9 @@ export declare function writeJobDeadlineMs(paths: BashJobPaths, deadlineMs: numb
75
78
  /** Drop a job's cancel deadline so it runs to completion — the node has claimed
76
79
  * it. Best-effort: a job with no deadline file is already in this state. */
77
80
  export declare function clearJobDeadline(paths: BashJobPaths): void;
78
- /** Whether a persisted job supervisor process group is still present. EPERM
79
- * means the group exists but cannot be signaled by this process; it is still
80
- * live for the status surface. */
81
- export declare function isBashJobProcessGroupAlive(pgid: number): boolean;
81
+ /** Launcher jobs have a root-owned bwrap pid, not a process group; direct
82
+ * jobs keep their detached supervisor process group. */
83
+ export declare function isBashJobProcessGroupAlive(pgid: number, identity?: string): boolean;
82
84
  /** Best-effort cancellation for a detached bash supervisor. It snapshots the
83
85
  * observable descendant tree while the supervisor is still alive, signals its
84
86
  * leaves before their parents, then uses the shared identity-guarded KILL
@@ -108,7 +110,7 @@ export type StopBackgroundBashJobResult = {
108
110
  * supervisor exits, so a retired, still-foreground, or already-stopped job
109
111
  * would otherwise have its recorded pgid signaled long after the OS could have
110
112
  * reused that id for an unrelated process group. */
111
- export declare function stopBackgroundBashJob(contextDir: string, jobId: string): Promise<StopBackgroundBashJobResult>;
113
+ export declare function stopBackgroundBashJob(contextDir: string, jobId: string, allowForeground?: boolean, nodeId?: string, reason?: string): Promise<StopBackgroundBashJobResult>;
112
114
  /** Last `count` lines of a job log, cheaply: read at most the trailing 64 KiB
113
115
  * rather than the whole file, which for a long-running job can be enormous. */
114
116
  export declare function tailJobLog(logPath: string, count: number): string[];
@@ -146,10 +148,15 @@ export interface BashJobStatus {
146
148
  * absent — whether handed off by the menu, the valve deadline, or broker
147
149
  * termination. Read-only; never mutates. Skips malformed/partially-written
148
150
  * directories. Returns oldest-first by startedAtMs. */
149
- export declare function activeBackgroundBashJobs(contextDir: string): BashJobStatus[];
151
+ export declare function activeBackgroundBashJobs(contextDir: string, includeForeground?: boolean): BashJobStatus[];
150
152
  /** Every live background job for this node whose auto-cancel deadline has
151
153
  * passed at `now`. The daemon's deadline lane stops exactly these. A job with
152
154
  * no deadline (deliberate handoff, or one the node claimed) never appears. */
155
+ export declare function completedBackgroundBashJobs(contextDir: string): Array<{
156
+ jobId: string;
157
+ exitCode: number;
158
+ startedAtMs: number;
159
+ }>;
153
160
  export declare function expiredAutoBackgroundBashJobs(contextDir: string, now: number): BashJobStatus[];
154
161
  /** Credit every armed auto-cancel deadline for an interval the node was not
155
162
  * permitted to run. A profile pause stops the owning node, so without this the
@@ -1,7 +1,8 @@
1
- var A=Object.defineProperty;var o=(t,n)=>A(t,"name",{value:n,configurable:!0});import{randomBytes as J}from"node:crypto";import{closeSync as g,existsSync as d,mkdirSync as E,openSync as m,readdirSync as y,readFileSync as f,readSync as N,renameSync as k,rmSync as L,statSync as w,writeFileSync as h}from"node:fs";import{join as s}from"node:path";import{captureTeardownSnapshot as $,isAnyPidAlive as T,killProcessTreePids as S}from"./canvas/pid.js";const _=280;function I(t){if(typeof t!="string"||/[\p{Cc}\p{Cf}]/u.test(t))return null;const n=t.trim();return n===""?null:Buffer.byteLength(n,"utf8")<=_?n:null}o(I,"normalizeBashJobPurpose");function l(t){return s(t,"jobs")}o(l,"bashJobsDir");function Y(t){return typeof t=="string"&&t!==""&&t!=="."&&t!==".."&&/^[A-Za-z0-9._~-]+$/u.test(t)&&Buffer.byteLength(t,"utf8")<=128}o(Y,"isSafeBashJobId");function a(t,n){const e=s(l(t),n);return{jobId:n,dir:e,cmdSh:s(e,"cmd.sh"),jobLog:s(e,"job.log"),jobExit:s(e,"job.exit"),jobRun:s(e,"job.run"),jobBg:s(e,"job.bg"),jobDone:s(e,"job.done"),jobPgid:s(e,"job.pgid"),jobPgidIdentity:s(e,"job.pgid-identity"),jobPurpose:s(e,"purpose"),jobDeadline:s(e,"job.deadline")}}o(a,"bashJobPaths");function R(t,n){try{const e=Number(f(a(t,n).jobPgid,"utf8").trim());return Number.isInteger(e)&&e>1?e:void 0}catch{return}}o(R,"readJobPgid");function D(t,n){try{const e=f(a(t,n).jobPgidIdentity,"utf8").trim();return e===""?void 0:e}catch{return}}o(D,"readJobPgidIdentity");function F(t,n){try{return I(f(a(t,n).jobPurpose,"utf8"))}catch{return null}}o(F,"readJobPurpose");const j=10,O=19;function x(t){return s(l(t),"niceness")}o(x,"bashNicenessPath");function Z(t){try{const n=Number(f(x(t),"utf8").trim());return!Number.isInteger(n)||n<0||n>O?j:n}catch{return j}}o(Z,"readBashNiceness");function q(t,n){E(l(t),{recursive:!0}),h(x(t),`${Math.trunc(n)}
2
- `)}o(q,"writeBashNiceness");function C(t,n){try{const e=Number(f(a(t,n).jobDeadline,"utf8").trim());return Number.isFinite(e)?e:void 0}catch{return}}o(C,"readJobDeadlineMs");function z(t,n){h(t.jobDeadline,`${Math.trunc(n)}
3
- `)}o(z,"writeJobDeadlineMs");function Q(t){try{L(t.jobDeadline,{force:!0})}catch{}}o(Q,"clearJobDeadline");function V(t){try{return process.kill(-t,0),!0}catch(n){return n.code==="EPERM"}}o(V,"isBashJobProcessGroupAlive");async function v(t,n){const e=$(t,n);if(e.reused||e.tree.length===0)return!1;const r=[...e.tree].reverse(),i=T(r);return S(r,"SIGTERM",e.identities),await new Promise(c=>setTimeout(c,400)),S(r,"SIGKILL",e.identities),i}o(v,"cancelBashJobProcessTree");async function tt(t,n){const e=a(t,n);if(!d(e.dir))return{kind:"missing"};if(!d(e.jobBg)||d(e.jobExit))return{kind:"retired",logPath:e.jobLog};const r=R(t,n),i=D(t,n);if(r===void 0||i===void 0)return{kind:"not-stoppable",logPath:e.jobLog};try{const u=m(e.jobExit,"wx");try{h(u,`143
4
- `)}finally{g(u)}}catch(u){if(u.code==="EEXIST")return{kind:"retired",logPath:e.jobLog};throw u}return{kind:"stopped",signaled:await v(r,i),pgid:r,logPath:e.jobLog}}o(tt,"stopBackgroundBashJob");function nt(t,n){if(n<=0)return[];const e=64*1024;let r;try{const i=w(t).size,c=Math.max(0,i-e),u=Buffer.alloc(Math.min(i,e));r=m(t,"r"),N(r,u,0,u.length,c);const p=u.toString("utf8"),b=(c>0?p.slice(p.indexOf(`
5
- `)+1):p).split(`
6
- `);return b[b.length-1]===""&&b.pop(),b.slice(-n)}catch{return[]}finally{r!==void 0&&g(r)}}o(nt,"tailJobLog");function et(){return`${Date.now()}-${J(4).toString("hex")}`}o(et,"newBashJobId");function B(t){const n=t.indexOf("-");if(n<=0||n===t.length-1)return;const e=Number(t.slice(0,n));return Number.isFinite(e)?e:void 0}o(B,"jobStartedAtMs");function rt(t){const n=Math.max(0,Math.floor(t/1e3));if(n<60)return`${n}s`;const e=Math.floor(n/60),r=n%60;if(e<60)return`${e}m ${String(r).padStart(2,"0")}s`;const i=Math.floor(e/60),c=e%60;return`${i}h ${String(c).padStart(2,"0")}m`}o(rt,"formatBashElapsed");function ot(t){return(t.split(`
7
- `).map(e=>e.trim()).find(e=>e!=="")??"").replace(/\s+/g," ")}o(ot,"bashJobCommandLine");function M(t){let n;try{n=y(l(t))}catch{return[]}const e=[];for(const r of n){const i=a(t,r);if(!d(i.jobBg)||d(i.jobExit))continue;const c=B(r);if(c===void 0)continue;let u;try{u=f(i.cmdSh,"utf8")}catch{continue}e.push({jobId:r,command:u,purpose:F(t,r),startedAtMs:c,logPath:i.jobLog,deadlineAtMs:C(t,r)??null})}return e.sort((r,i)=>r.startedAtMs-i.startedAtMs)}o(M,"activeBackgroundBashJobs");function it(t,n){return M(t).filter(e=>e.deadlineAtMs!==null&&n>=e.deadlineAtMs)}o(it,"expiredAutoBackgroundBashJobs");function st(t,n){if(!Number.isFinite(n)||n<=0)return 0;let e=0;for(const r of M(t))if(r.deadlineAtMs!==null)try{z(a(t,r.jobId),r.deadlineAtMs+n),e+=1}catch{}return e}o(st,"shiftAutoBackgroundBashDeadlines");function G(t){try{return k(t.jobRun,t.jobBg),"backgrounded"}catch(n){if(n.code!=="ENOENT")throw n;return d(t.jobBg)?"already-backgrounded":d(t.jobDone)?"finished":"missing"}}o(G,"backgroundBashJob");function P(t){let n;try{n=y(l(t))}catch{return[]}return n.flatMap(e=>{const r=a(t,e);return d(r.cmdSh)&&d(r.jobLog)&&d(r.jobRun)?[r]:[]})}o(P,"runningBashJobs");function ut(t){let n;for(const e of P(t)){const r=B(e.jobId);r!==void 0&&(n===void 0||r<n)&&(n=r)}return n}o(ut,"oldestRunningBashJobStartedAtMs");function ct(t){const n=[];for(const e of P(t))G(e)==="backgrounded"&&n.push(e);return n}o(ct,"backgroundRunningBashJobs");export{j as DEFAULT_BASH_NICENESS,O as MAX_BASH_NICENESS,M as activeBackgroundBashJobs,G as backgroundBashJob,ct as backgroundRunningBashJobs,ot as bashJobCommandLine,a as bashJobPaths,l as bashJobsDir,x as bashNicenessPath,v as cancelBashJobProcessTree,Q as clearJobDeadline,it as expiredAutoBackgroundBashJobs,rt as formatBashElapsed,V as isBashJobProcessGroupAlive,Y as isSafeBashJobId,B as jobStartedAtMs,et as newBashJobId,I as normalizeBashJobPurpose,ut as oldestRunningBashJobStartedAtMs,Z as readBashNiceness,C as readJobDeadlineMs,R as readJobPgid,D as readJobPgidIdentity,F as readJobPurpose,st as shiftAutoBackgroundBashDeadlines,tt as stopBackgroundBashJob,nt as tailJobLog,q as writeBashNiceness,z as writeJobDeadlineMs};
1
+ var N=Object.defineProperty;var o=(t,e)=>N(t,"name",{value:e,configurable:!0});import{randomBytes as k}from"node:crypto";import{closeSync as w,openSync as L,readSync as T,statSync as $}from"node:fs";import{dirname as g,join as c}from"node:path";import{captureTeardownSnapshot as I,isAnyPidAlive as R,isPidAlive as _,killProcessTreePids as y}from"./canvas/pid.js";import{launcherRequest as O}from"../launcher/client.js";import{LaunchRefused as F}from"../launcher/spec.js";import{createFileBeneath as S,existsBeneath as C,readdirBeneath as v,readFileBeneath as z,renameBeneath as X,unlinkBeneath as G,writeFileBeneath as x}from"./spaces/open-beneath.js";const H=280;function q(t){if(typeof t!="string"||/[\p{Cc}\p{Cf}]/u.test(t))return null;const e=t.trim();return e===""?null:Buffer.byteLength(e,"utf8")<=H?e:null}o(q,"normalizeBashJobPurpose");function m(t){return c(t,"jobs")}o(m,"bashJobsDir");function p(t,e){return z(g(t),e).toString("utf8")}o(p,"readJobText");function j(t){return v(g(t),m(t)).map(e=>e.name)}o(j,"jobIds");function a(t,e){return C(t.root,e)}o(a,"has");function E(t,e){try{G(t,e)}catch(n){if(n.code!=="ENOENT")throw n}}o(E,"unlinkIfPresent");function U(t){return typeof t=="string"&&t!==""&&t!=="."&&t!==".."&&/^[A-Za-z0-9._~-]+$/u.test(t)&&Buffer.byteLength(t,"utf8")<=128}o(U,"isSafeBashJobId");function l(t,e){const n=c(m(t),e);return{jobId:e,root:g(t),dir:n,cmdSh:c(n,"cmd.sh"),jobLog:c(n,"job.log"),jobExit:c(n,"job.exit"),jobRun:c(n,"job.run"),jobBg:c(n,"job.bg"),jobDone:c(n,"job.done"),jobPgid:c(n,"job.pgid"),jobPgidIdentity:c(n,"job.pgid-identity"),jobPurpose:c(n,"purpose"),jobDeadline:c(n,"job.deadline")}}o(l,"bashJobPaths");function W(t,e){try{const n=Number(p(t,l(t,e).jobPgid).trim());return Number.isInteger(n)&&n>1?n:void 0}catch{return}}o(W,"readJobPgid");function K(t,e){try{const n=p(t,l(t,e).jobPgidIdentity).trim();return n===""?void 0:n}catch{return}}o(K,"readJobPgidIdentity");function Y(t,e){try{return q(p(t,l(t,e).jobPurpose))}catch{return null}}o(Y,"readJobPurpose");const P=10,Z=19;function M(t){return c(m(t),"niceness")}o(M,"bashNicenessPath");function at(t){try{const e=Number(p(t,M(t)).trim());return!Number.isInteger(e)||e<0||e>Z?P:e}catch{return P}}o(at,"readBashNiceness");function dt(t,e){x(g(t),M(t),`${Math.trunc(e)}
2
+ `)}o(dt,"writeBashNiceness");function Q(t,e){try{const n=Number(p(t,l(t,e).jobDeadline).trim());return Number.isFinite(n)?n:void 0}catch{return}}o(Q,"readJobDeadlineMs");function V(t,e){x(t.root,t.jobDeadline,`${Math.trunc(e)}
3
+ `)}o(V,"writeJobDeadlineMs");function ft(t){try{E(t.root,t.jobDeadline)}catch{}}o(ft,"clearJobDeadline");function lt(t,e){if(e===`launcher:${t}`)return _(t);try{return process.kill(-t,0),!0}catch(n){return n.code==="EPERM"}}o(lt,"isBashJobProcessGroupAlive");async function D(t,e){const n=I(t,e);if(n.reused||n.tree.length===0)return!1;const r=[...n.tree].reverse(),s=R(r);return y(r,"SIGTERM",n.identities),await new Promise(i=>setTimeout(i,400)),y(r,"SIGKILL",n.identities),s}o(D,"cancelBashJobProcessTree");async function ht(t,e,n=!1,r,s){const i=l(t,e);if(!a(i,i.dir))return{kind:"missing"};if(!a(i,i.jobBg)&&!(n&&a(i,i.jobRun))||a(i,i.jobExit))return{kind:"retired",logPath:i.jobLog};const u=W(t,e),d=K(t,e);if(u===void 0||d===void 0)return{kind:"not-stoppable",logPath:i.jobLog};const h=c(i.dir,"job.stop");try{S(i.root,h,s===void 0?"":`${s}
4
+ `)}catch(f){if(f.code==="EEXIST")return{kind:"retired",logPath:i.jobLog};throw f}try{let f;if(d===`launcher:${u}`){if(r===void 0)throw new Error("launcher job stop requires its owning node");try{if(!("ok"in await O({operation:"stop",pid:u,nodeId:r})))throw new Error("launcher did not stop job");f=!0}catch(b){if(!(b instanceof F&&b.refusal==="invalid_spec"))throw b;f=!1}}else f=await D(u,d);try{S(i.root,i.jobExit,`143
5
+ `)}catch(b){if(b.code!=="EEXIST")throw b}return{kind:"stopped",signaled:f,pgid:u,logPath:i.jobLog}}catch(f){throw E(i.root,h),f}}o(ht,"stopBackgroundBashJob");function bt(t,e){if(e<=0)return[];const n=64*1024;let r;try{const s=$(t).size,i=Math.max(0,s-n),u=Buffer.alloc(Math.min(s,n));r=L(t,"r"),T(r,u,0,u.length,i);const d=u.toString("utf8"),h=(i>0?d.slice(d.indexOf(`
6
+ `)+1):d).split(`
7
+ `);return h[h.length-1]===""&&h.pop(),h.slice(-e)}catch{return[]}finally{r!==void 0&&w(r)}}o(bt,"tailJobLog");function pt(){return`${Date.now()}-${k(4).toString("hex")}`}o(pt,"newBashJobId");function B(t){const e=t.indexOf("-");if(e<=0||e===t.length-1)return;const n=Number(t.slice(0,e));return Number.isFinite(n)?n:void 0}o(B,"jobStartedAtMs");function gt(t){const e=Math.max(0,Math.floor(t/1e3));if(e<60)return`${e}s`;const n=Math.floor(e/60),r=e%60;if(n<60)return`${n}m ${String(r).padStart(2,"0")}s`;const s=Math.floor(n/60),i=n%60;return`${s}h ${String(i).padStart(2,"0")}m`}o(gt,"formatBashElapsed");function mt(t){return(t.split(`
8
+ `).map(n=>n.trim()).find(n=>n!=="")??"").replace(/\s+/g," ")}o(mt,"bashJobCommandLine");function A(t,e=!1){let n;try{n=j(t)}catch{return[]}const r=[];for(const s of n){const i=B(s);if(i===void 0)continue;const u=l(t,s);let d;try{if(!a(u,u.jobBg)&&!(e&&a(u,u.jobRun))||a(u,u.jobExit))continue;d=p(t,u.cmdSh)}catch{continue}r.push({jobId:s,command:d,purpose:Y(t,s),startedAtMs:i,logPath:u.jobLog,deadlineAtMs:Q(t,s)??null})}return r.sort((s,i)=>s.startedAtMs-i.startedAtMs)}o(A,"activeBackgroundBashJobs");function jt(t){let e;try{e=j(t)}catch(r){if(r.code==="ENOENT")return[];throw r}const n=[];for(const r of e){if(!U(r))continue;const s=l(t,r);if(!a(s,s.jobBg)||!a(s,s.jobExit)||a(s,c(s.dir,"job.stop")))continue;const i=B(r);if(i===void 0)continue;const u=Number(p(t,s.jobExit).trim());Number.isSafeInteger(u)&&n.push({jobId:r,exitCode:u,startedAtMs:i})}return n}o(jt,"completedBackgroundBashJobs");function Bt(t,e){return A(t).filter(n=>n.deadlineAtMs!==null&&e>=n.deadlineAtMs)}o(Bt,"expiredAutoBackgroundBashJobs");function yt(t,e){if(!Number.isFinite(e)||e<=0)return 0;let n=0;for(const r of A(t))if(r.deadlineAtMs!==null)try{V(l(t,r.jobId),r.deadlineAtMs+e),n+=1}catch{}return n}o(yt,"shiftAutoBackgroundBashDeadlines");function tt(t){try{return X(t.root,t.jobRun,t.jobBg),"backgrounded"}catch(e){if(e.code!=="ENOENT")throw e;return a(t,t.jobBg)?"already-backgrounded":a(t,t.jobDone)?"finished":"missing"}}o(tt,"backgroundBashJob");function J(t){let e;try{e=j(t)}catch{return[]}return e.flatMap(n=>{const r=l(t,n);try{return a(r,r.cmdSh)&&a(r,r.jobLog)&&a(r,r.jobRun)?[r]:[]}catch{return[]}})}o(J,"runningBashJobs");function St(t){let e;for(const n of J(t)){const r=B(n.jobId);r!==void 0&&(e===void 0||r<e)&&(e=r)}return e}o(St,"oldestRunningBashJobStartedAtMs");function xt(t){const e=[];for(const n of J(t))tt(n)==="backgrounded"&&e.push(n);return e}o(xt,"backgroundRunningBashJobs");export{P as DEFAULT_BASH_NICENESS,Z as MAX_BASH_NICENESS,A as activeBackgroundBashJobs,tt as backgroundBashJob,xt as backgroundRunningBashJobs,mt as bashJobCommandLine,l as bashJobPaths,m as bashJobsDir,M as bashNicenessPath,D as cancelBashJobProcessTree,ft as clearJobDeadline,jt as completedBackgroundBashJobs,Bt as expiredAutoBackgroundBashJobs,gt as formatBashElapsed,lt as isBashJobProcessGroupAlive,U as isSafeBashJobId,B as jobStartedAtMs,pt as newBashJobId,q as normalizeBashJobPurpose,St as oldestRunningBashJobStartedAtMs,at as readBashNiceness,Q as readJobDeadlineMs,W as readJobPgid,K as readJobPgidIdentity,Y as readJobPurpose,yt as shiftAutoBackgroundBashDeadlines,ht as stopBackgroundBashJob,bt as tailJobLog,dt as writeBashNiceness,V as writeJobDeadlineMs};
@@ -13,17 +13,20 @@ export interface RelayTarget {
13
13
  /** The raw bearer token. NEVER logged, NEVER echoed. */
14
14
  relayToken: string;
15
15
  }
16
+ export interface LocalRelayTarget {
17
+ /** The owner's daemon API socket. The viewer has already called ensureAttach. */
18
+ socketPath: string;
19
+ }
16
20
  export declare class RelayTransport extends EventEmitter implements Transport {
17
21
  private readonly nodeId;
18
22
  private readonly target;
19
23
  private ws;
20
24
  private closeEmitted;
21
- constructor(nodeId: string, target: RelayTarget);
25
+ constructor(nodeId: string, target: RelayTarget | LocalRelayTarget);
22
26
  private get url();
23
27
  private dial;
24
28
  connect(): void;
25
- /** Re-dial the same crtrd attach endpoint — the preview endpoint is the
26
- * stable remote anchor, as `view.sock` is locally. */
29
+ /** Re-dial the same daemon attach endpoint after a broker replacement. */
27
30
  redial(_options?: RedialOptions): Promise<void>;
28
31
  send(frame: ClientToBroker): void;
29
32
  destroy(): void;
@@ -1 +1 @@
1
- var l=Object.defineProperty;var a=(i,t)=>l(i,"name",{value:t,configurable:!0});import{EventEmitter as d}from"node:events";import{WebSocket as h}from"ws";import{classify as m}from"../fault-classifier.js";import{BrokerUnavailableError as f}from"./transport.js";function u(i,t){const e=new URL(i.previewEndpoint);return e.protocol=e.protocol==="http:"?"ws:":"wss:",e.pathname=`/v1/nodes/${encodeURIComponent(t)}/attach`,e.search="?wakeParked=1",e.hash="",e.toString()}a(u,"relayUrl");class S extends d{static{a(this,"RelayTransport")}nodeId;target;ws;closeEmitted=!1;constructor(t,e){super(),this.nodeId=t,this.target=e}get url(){return u(this.target,this.nodeId)}dial(){const t=new h(this.url,{headers:{authorization:`Bearer ${this.target.relayToken}`},perMessageDeflate:!1});return this.ws=t,t}connect(){const t=this.dial();t.on("open",()=>{this.emit("connect")}),t.on("message",e=>this.onMessage(e)),t.on("error",e=>this.onError(e)),t.on("close",(e,s)=>this.onClose(e,s.toString()))}redial(t){return this.destroy(),this.closeEmitted=!1,new Promise((e,s)=>{const r=this.dial();let n=!1;r.on("open",()=>{n||(n=!0,this.emit("connect"),e())}),r.on("message",o=>this.onMessage(o)),r.on("error",o=>{if(n){this.onError(o);return}n=!0;try{r.terminate()}catch{}s(o)}),r.on("close",(o,c)=>{n&&this.onClose(o,c.toString())})})}send(t){const e=this.ws;if(!(e===void 0||e.readyState!==h.OPEN))try{e.send(JSON.stringify(t))}catch{}}destroy(){const t=this.ws;if(t!==void 0&&t.readyState<=h.OPEN)try{t.close()}catch{}}onMessage(t){let e;try{e=JSON.parse(t.toString())}catch{return}this.emit("frame",e)}onError(t){this.emitError(new Error(`relay transport error: ${t.message}`))}onClose(t,e){if(this.closeEmitted)return;this.closeEmitted=!0;const s=m("viewer\u2194broker",{code:t,reason:e});(t===1008||s.kind==="protocol"||t===1011&&(/^no running broker/.test(e)||/^no node /.test(e)))&&this.emitError(new f(this.nodeId)),this.emit("close")}emitError(t){this.listenerCount("error")>0&&this.emit("error",t)}}export{S as RelayTransport};
1
+ var l=Object.defineProperty;var a=(r,t)=>l(r,"name",{value:t,configurable:!0});import{EventEmitter as d}from"node:events";import{WebSocket as h}from"ws";import{classify as m}from"../fault-classifier.js";import{BrokerUnavailableError as f}from"./transport.js";function u(r,t){if("socketPath"in r)return`ws+unix://${r.socketPath}:/v1/nodes/${encodeURIComponent(t)}/attach?revive=0`;const e=new URL(r.previewEndpoint);return e.protocol=e.protocol==="http:"?"ws:":"wss:",e.pathname=`/v1/nodes/${encodeURIComponent(t)}/attach`,e.search="?wakeParked=1",e.hash="",e.toString()}a(u,"relayUrl");class k extends d{static{a(this,"RelayTransport")}nodeId;target;ws;closeEmitted=!1;constructor(t,e){super(),this.nodeId=t,this.target=e}get url(){return u(this.target,this.nodeId)}dial(){const t=new h(this.url,{..."relayToken"in this.target?{headers:{authorization:`Bearer ${this.target.relayToken}`}}:{},perMessageDeflate:!1});return this.ws=t,t}connect(){const t=this.dial();t.on("open",()=>{this.emit("connect")}),t.on("message",e=>this.onMessage(e)),t.on("error",e=>this.onError(e)),t.on("close",(e,n)=>this.onClose(e,n.toString()))}redial(t){return this.destroy(),this.closeEmitted=!1,new Promise((e,n)=>{const o=this.dial();let i=!1;o.on("open",()=>{i||(i=!0,this.emit("connect"),e())}),o.on("message",s=>this.onMessage(s)),o.on("error",s=>{if(i){this.onError(s);return}i=!0;try{o.terminate()}catch{}n(s)}),o.on("close",(s,c)=>{i&&this.onClose(s,c.toString())})})}send(t){const e=this.ws;if(!(e===void 0||e.readyState!==h.OPEN))try{e.send(JSON.stringify(t))}catch{}}destroy(){const t=this.ws;if(t!==void 0&&t.readyState<=h.OPEN)try{t.close()}catch{}}onMessage(t){let e;try{e=JSON.parse(t.toString())}catch{return}this.emit("frame",e)}onError(t){this.emitError(new Error(`relay transport error: ${t.message}`))}onClose(t,e){if(this.closeEmitted)return;this.closeEmitted=!0;const n=m("viewer\u2194broker",{code:t,reason:e});(t===1008||n.kind==="protocol"||t===1011&&(/^no running broker/.test(e)||/^no node /.test(e)))&&this.emitError(new f(this.nodeId)),this.emit("close")}emitError(t){this.listenerCount("error")>0&&this.emit("error",t)}}export{k as RelayTransport};
@@ -3,13 +3,18 @@ import { type ClientToBroker } from '../runtime/broker-protocol.js';
3
3
  import { type RedialOptions, type Transport } from './transport.js';
4
4
  export declare class SocketTransport extends EventEmitter implements Transport {
5
5
  private readonly nodeId;
6
+ private readonly options;
6
7
  private socket;
7
8
  private decoder;
8
9
  private closeEmitted;
9
10
  /** One redial fault line per reconnect EPISODE, not per attempt — see
10
11
  * {@link recordSocketFault}. */
11
12
  private redialFaultRecorded;
12
- constructor(nodeId: string);
13
+ /** `recordsViewerFaults: false` is for an internal daemon reader: its
14
+ * connect outcome says nothing about any person's viewer. */
15
+ constructor(nodeId: string, options?: {
16
+ recordsViewerFaults?: boolean;
17
+ });
13
18
  /** The broker binds `viewSocketPath(id)` — resolve it the same way. */
14
19
  get socketPath(): string;
15
20
  private recordSocketFault;
@@ -1 +1 @@
1
- var f=Object.defineProperty;var a=(n,e)=>f(n,"name",{value:e,configurable:!0});import{EventEmitter as k}from"node:events";import{createConnection as d}from"node:net";import{viewSocketPath as u}from"../canvas/paths.js";import{classify as E}from"../fault-classifier.js";import{clearNodeFault as v,recordNodeFault as F}from"../runtime/broker/daemon-ops.js";import{emitEvent as c}from"../events/emit.js";import{CLIENT_READ_CAPS as l,encodeFrame as p,FrameDecoder as h,FrameOverflowError as w}from"../runtime/broker-protocol.js";import{BrokerUnavailableError as b}from"./transport.js";class P extends k{static{a(this,"SocketTransport")}nodeId;socket;decoder=new h(l);closeEmitted=!1;redialFaultRecorded=!1;constructor(e){super(),this.nodeId=e}get socketPath(){return u(this.nodeId)}recordSocketFault(e,t){if(e==="redial"){if(this.redialFaultRecorded)return;this.redialFaultRecorded=!0}const o=E("viewer\u2194broker",t);F(this.nodeId,{link:"viewer\u2194broker",op:e,kind:o.kind,retry:e==="redial"?{disposition:"auto",by:"client"}:{disposition:"manual"},message:t instanceof Error?t.message:String(t)}).catch(r=>{c({event:"broker.daemon.operation.failed",level:"error",node_id:this.nodeId,error:r,fields:{operation:"viewer_fault_record"}})})}connect(){const e=d(this.socketPath);this.socket=e,e.on("connect",()=>{this.redialFaultRecorded=!1,this.clearSocketFault(),this.emit("connect")}),e.on("data",t=>this.onData(t)),e.on("error",t=>this.onError(t,"connect")),e.on("close",()=>this.onClose())}redial(e={}){return this.destroy(),this.decoder=new h(l),this.closeEmitted=!1,e.brokerUnavailableIsExpected&&(this.redialFaultRecorded=!1,this.clearSocketFault()),new Promise((t,o)=>{const r=d(this.socketPath);this.socket=r;let s=!1;r.on("connect",()=>{s||(s=!0,this.redialFaultRecorded=!1,this.clearSocketFault(),this.emit("connect"),t())}),r.on("data",i=>this.onData(i)),r.on("error",i=>{if(s){this.onError(i,"redial");return}s=!0;const m=i.code==="ECONNREFUSED"||i.code==="ENOENT";(!e.brokerUnavailableIsExpected||!m)&&this.recordSocketFault("redial",i);try{r.destroy()}catch{}o(i)}),r.on("close",()=>this.onClose())})}send(e){const t=this.socket;if(!(t===void 0||t.destroyed))try{t.write(p(e))}catch{}}destroy(){const e=this.socket;if(e!==void 0&&!e.destroyed)try{e.destroy()}catch{}}clearSocketFault(){v(this.nodeId,{link:"viewer\u2194broker"}).catch(e=>{c({event:"broker.daemon.operation.failed",level:"error",node_id:this.nodeId,error:e,fields:{operation:"viewer_fault_clear"}})})}onData(e){let t;try{t=this.decoder.push(e)}catch(o){const r=o instanceof w?`broker sent an oversized frame (${o.message}) \u2014 disconnecting`:`failed to decode a broker frame: ${String(o)}`;this.emitError(new Error(r)),this.destroy();return}for(const o of t)this.emit("frame",o)}onError(e,t){this.recordSocketFault(t,e),e.code==="ECONNREFUSED"||e.code==="ENOENT"?this.emitError(new b(this.nodeId)):this.emitError(new Error(`view socket error: ${e.message}`))}onClose(){this.closeEmitted||(this.closeEmitted=!0,this.emit("close"))}emitError(e){this.listenerCount("error")>0&&this.emit("error",e)}}export{P as SocketTransport};
1
+ var m=Object.defineProperty;var d=(n,e)=>m(n,"name",{value:e,configurable:!0});import{EventEmitter as u}from"node:events";import{viewSocketPath as k}from"../canvas/paths.js";import{connectViewSocket as a}from"../runtime/view-socket.js";import{classify as E}from"../fault-classifier.js";import{clearNodeFault as F,recordNodeFault as p}from"../runtime/broker/daemon-ops.js";import{emitEvent as c}from"../events/emit.js";import{CLIENT_READ_CAPS as l,encodeFrame as v,FrameDecoder as h,FrameOverflowError as w}from"../runtime/broker-protocol.js";import{BrokerUnavailableError as b}from"./transport.js";class U extends u{static{d(this,"SocketTransport")}nodeId;options;socket;decoder=new h(l);closeEmitted=!1;redialFaultRecorded=!1;constructor(e,t={}){super(),this.nodeId=e,this.options=t}get socketPath(){return k(this.nodeId)}recordSocketFault(e,t){if(this.options.recordsViewerFaults===!1)return;if(e==="redial"){if(this.redialFaultRecorded)return;this.redialFaultRecorded=!0}const o=E("viewer\u2194broker",t);p(this.nodeId,{link:"viewer\u2194broker",op:e,kind:o.kind,retry:e==="redial"?{disposition:"auto",by:"client"}:{disposition:"manual"},message:t instanceof Error?t.message:String(t)}).catch(r=>{c({event:"broker.daemon.operation.failed",level:"error",node_id:this.nodeId,error:r,fields:{operation:"viewer_fault_record"}})})}connect(){const e=a(this.nodeId);this.socket=e,e.on("connect",()=>{this.redialFaultRecorded=!1,this.clearSocketFault(),this.emit("connect")}),e.on("data",t=>this.onData(t)),e.on("error",t=>this.onError(t,"connect")),e.on("close",()=>this.onClose())}redial(e={}){return this.destroy(),this.decoder=new h(l),this.closeEmitted=!1,e.brokerUnavailableIsExpected&&(this.redialFaultRecorded=!1,this.clearSocketFault()),new Promise((t,o)=>{const r=a(this.nodeId);this.socket=r;let s=!1;r.on("connect",()=>{s||(s=!0,this.redialFaultRecorded=!1,this.clearSocketFault(),this.emit("connect"),t())}),r.on("data",i=>this.onData(i)),r.on("error",i=>{if(s){this.onError(i,"redial");return}s=!0;const f=i.code==="ECONNREFUSED"||i.code==="ENOENT";(!e.brokerUnavailableIsExpected||!f)&&this.recordSocketFault("redial",i);try{r.destroy()}catch{}o(i)}),r.on("close",()=>this.onClose())})}send(e){const t=this.socket;if(!(t===void 0||t.destroyed))try{t.write(v(e))}catch{}}destroy(){const e=this.socket;if(e!==void 0&&!e.destroyed)try{e.destroy()}catch{}}clearSocketFault(){this.options.recordsViewerFaults!==!1&&F(this.nodeId,{link:"viewer\u2194broker"}).catch(e=>{c({event:"broker.daemon.operation.failed",level:"error",node_id:this.nodeId,error:e,fields:{operation:"viewer_fault_clear"}})})}onData(e){let t;try{t=this.decoder.push(e)}catch(o){const r=o instanceof w?`broker sent an oversized frame (${o.message}) \u2014 disconnecting`:`failed to decode a broker frame: ${String(o)}`;this.emitError(new Error(r)),this.destroy();return}for(const o of t)this.emit("frame",o)}onError(e,t){this.recordSocketFault(t,e),e.code==="ECONNREFUSED"||e.code==="ENOENT"?this.emitError(new b(this.nodeId)):this.emitError(new Error(`view socket error: ${e.message}`))}onClose(){this.closeEmitted||(this.closeEmitted=!0,this.emit("close"))}emitError(e){this.listenerCount("error")>0&&this.emit("error",e)}}export{U as SocketTransport};
@@ -1,3 +1,4 @@
1
+ import type { DatabaseSync } from 'node:sqlite';
1
2
  import type { ExitIntent, Lifecycle, Mode, NodeIdentity, NodeMemoryTarget, NodeMeta, NodeOutcomeKind, NodeRow, NodeStatus, SubscriptionRef, TerminalReason } from './types.js';
2
3
  export interface OutcomeDeliveryBinding {
3
4
  actionName: string;
@@ -6,9 +7,11 @@ export interface OutcomeDeliveryBinding {
6
7
  configOrigin: string;
7
8
  payload?: unknown;
8
9
  }
10
+ export declare function assertRunNotDeleting(db: DatabaseSync, runId: string): void;
9
11
  /** Create a SQL-authoritative node, then ensure its artifact directories exist. */
10
12
  export declare function createNode(meta: NodeMeta, opts?: {
11
13
  outcomeDelivery?: OutcomeDeliveryBinding;
14
+ withinCreate?: (db: DatabaseSync, row: NodeMeta) => void;
12
15
  }): NodeMeta;
13
16
  /** The canonical node record, hydrated from SQL. Null if unknown. */
14
17
  export declare function getNode(nodeId: string): NodeMeta | null;
@@ -28,6 +31,12 @@ export declare function dueNodeDeadlines(nowIso: string): NodeRow[];
28
31
  export declare function setNodeDeadline(nodeId: string, deadlineAt: string | null): boolean;
29
32
  /** Shift active/idle profile deadlines by a completed profile pause duration. */
30
33
  export declare function shiftProfileNodeDeadlines(profileId: string, pauseDurationMs: number): number;
34
+ /** OWN-11 for a joined runtime: a `user:local` subject becomes the owner, a
35
+ * `user:local` grantee becomes `app:terminal` (no node has the owner as grantee),
36
+ * a terminal node born before the local grant mirror (no subject) gets the owner,
37
+ * and a reference to the dropped local grant is cleared so the node resolves the
38
+ * synced grant by grantee and subject. */
39
+ export declare function rewriteLocalNodeIdentity(db: DatabaseSync, owner: string, localGrantId: string): number;
31
40
  type NodeUpdate = Partial<Omit<NodeIdentity, 'node_id' | 'pi_session_id' | 'pi_session_file' | 'managed_worktree'>>;
32
41
  /** Update node identity/configuration columns. Sessions and worktrees have named stores. */
33
42
  export declare function updateNode(nodeId: string, patch: NodeUpdate): NodeMeta;
@@ -56,7 +65,7 @@ export declare function transitionStatusIntent(nodeId: string, fields: {
56
65
  reason: TerminalReason;
57
66
  detailJson: string | null;
58
67
  at: string;
59
- /** Canonical-final only: replace a prior failure outcome. */
68
+ /** Replace a prior outcome after a run resumes or when its final is committed. */
60
69
  supersede?: boolean;
61
70
  /** The document composed at outcome time for an armed delivery. */
62
71
  documentJson?: string | null;
@@ -328,6 +337,15 @@ export declare function isCollectible(row: NodeRow, opts?: {
328
337
  export interface DeleteNodeResult {
329
338
  deleted: boolean;
330
339
  }
340
+ /** A node's files, located while its row still names its app. */
341
+ export interface NodeFiles {
342
+ nodeId: string;
343
+ app: string | null;
344
+ }
345
+ export declare function nodeFiles(nodeId: string): NodeFiles;
346
+ /** Remove every directory a deleted node owned. A conversation another node
347
+ * still references keeps its file, and so its conversations directory. */
348
+ export declare function removeNodeFiles(files: NodeFiles): void;
331
349
  /** Hard-delete ONE node: drop its row (the edges→nodes FK, ON DELETE CASCADE,
332
350
  * GCs its edges) and remove its on-disk `nodes/<id>/` dir. The single-node
333
351
  * analogue of {@link pruneNodes}, for IMMEDIATE reaping — e.g. an empty node the
@@ -342,18 +360,17 @@ export declare function deleteNode(nodeId: string): DeleteNodeResult;
342
360
  * their directories. The caller must tear down engines and viewers first. */
343
361
  export declare function deleteNodes(nodeIds: readonly string[]): string[];
344
362
  /** Retention sweep: remove TERMINAL nodes (status dead | done | canceled) whose
345
- * `created` is older than `ttlDays`, bounding the otherwise-unbounded growth of
346
- * node rows + dirs. Open managed-worktree nodes are retained so their checkout
347
- * remains owned until `node worktree close`. The edges→nodes FK (`ON DELETE
363
+ * last activity ({@link lastActivityMs}) is older than `ttlDays`, bounding the
364
+ * otherwise-unbounded growth of node rows + dirs. Only {@link isCollectible}
365
+ * rows qualify, and never one `held` claims. The edges→nodes FK (`ON DELETE
348
366
  * CASCADE`, migration v4) GCs each pruned node's edges automatically; the
349
367
  * on-disk `nodes/<id>/` dir is removed too.
350
368
  *
351
369
  * With `includeStale`, ALSO prunes nominally-live (active | idle) nodes past the
352
- * TTL whose process is provably gone — `pi_pid` is NULL or no longer alive. This
353
- * reaps stale roots (a bare `crtr` whose pi died without the row transitioning),
354
- * which the daemon's supervision never reconciled. A genuinely-running node keeps
355
- * a live `pi_pid`, so it is protected, as is the CALLER ($CRTR_NODE_ID). Without
356
- * the flag, active | idle are NEVER touched (the daemon's domain).
370
+ * TTL whose process is provably gone and that nothing will bring back: no live
371
+ * `pi_pid`, not sleeping until a wake (`idle-release`), no unseen wake mail,
372
+ * not frozen, not in respawn backoff, not an app run, not held, and never the CALLER
373
+ * ($CRTR_NODE_ID). Without the flag, active | idle are NEVER touched.
357
374
  *
358
375
  * Row deletes run in ONE transaction, so the sweep is all-or-nothing. Dir
359
376
  * removals follow after COMMIT — the fs isn't transactional, and by then
@@ -361,24 +378,28 @@ export declare function deleteNodes(nodeIds: readonly string[]): string[];
361
378
  * reports the candidate set and deletes NOTHING. */
362
379
  export declare function pruneNodes(opts: {
363
380
  ttlDays: number;
381
+ held: (nodeId: string) => boolean;
364
382
  dryRun?: boolean;
365
383
  includeStale?: boolean;
384
+ removeDirs?: (dirs: string[]) => void;
366
385
  }): PruneResult;
367
- /** Count-based retention sweep: retain the `maxNodes` MOST-RECENTLY-ACTIVE
368
- * nodes plus every row the collection authority retains, so the real retained
369
- * floor is `max(maxNodes, retainedCount)`. Bounds the otherwise-unbounded growth
370
- * the TTL sweep can't (a canvas that never crosses the TTL still grows without
371
- * limit).
372
- *
373
- * Algorithm: order ALL nodes by recency DESC (telemetry.json mtime, falling back
374
- * to `created`); `target = max(maxNodes, retainedCount)`; delete every node
375
- * ranked beyond `target` (0-indexed rank >= target) that is collectible.
386
+ /** A node's most recent activity (epoch ms): the latest of its lifecycle
387
+ * timestamps (created, launched, settled, finalized, outcome, last run event)
388
+ * and the mtime of `job/telemetry.json`, which every turn_end rewrites. 0 when
389
+ * none is readable. The single clock every retention decision uses. Never throws. */
390
+ export declare function lastActivityMs(row: Pick<NodeRow, 'node_id' | 'created' | 'launched_at' | 'last_settled_at' | 'finalized_at' | 'outcome_at' | 'last_activity_at'>): number;
391
+ /** Count-based retention sweep: keep every row that is not collectible or that
392
+ * `held` claims, plus the most recently active collectible rows up to
393
+ * `maxNodes` in total, so the real retained floor is `max(maxNodes,
394
+ * retainedCount)`. Bounds the growth the TTL sweep can't (a canvas that never
395
+ * crosses the TTL still grows without limit). Recency is {@link lastActivityMs}.
376
396
  *
377
397
  * Mirrors {@link pruneNodes} exactly for the delete (shared `deletePrunedNodes`:
378
398
  * transactioned row sweep + FK edge cascade + best-effort dir removal). `dryRun`
379
399
  * reports the candidate set and deletes NOTHING. */
380
400
  export declare function pruneToLimit(opts: {
381
401
  maxNodes: number;
402
+ held: (nodeId: string) => boolean;
382
403
  dryRun?: boolean;
383
404
  }): PruneResult;
384
405
  export {};