@north-light/cli 0.3.561 → 0.3.562

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 (573) hide show
  1. package/dist/api/client.d.ts +27 -4
  2. package/dist/api/client.js +3 -3
  3. package/dist/api/contract.d.ts +1 -1
  4. package/dist/api/contract.js +1 -1
  5. package/dist/api/dto/bash-jobs.d.ts +5 -1
  6. package/dist/api/dto/broker-ops.d.ts +25 -1
  7. package/dist/api/dto/charges.d.ts +9 -2
  8. package/dist/api/dto/delivery.d.ts +26 -2
  9. package/dist/api/dto/execution-start.d.ts +57 -0
  10. package/dist/api/dto/execution-start.js +0 -0
  11. package/dist/api/dto/modelauth.d.ts +5 -0
  12. package/dist/api/dto/node-records.d.ts +16 -0
  13. package/dist/api/dto/objects.d.ts +1 -1
  14. package/dist/api/dto/reports.d.ts +0 -3
  15. package/dist/api/dto/run-events.d.ts +7 -1
  16. package/dist/api/dto/settings.d.ts +55 -0
  17. package/dist/api/dto/settings.js +0 -0
  18. package/dist/api/index.d.ts +2 -0
  19. package/dist/api/index.js +1 -1
  20. package/dist/api/routes.d.ts +11 -0
  21. package/dist/api/routes.js +1 -1
  22. package/dist/builtin-memory/crouter-sdk/release-notes.md +4 -0
  23. package/dist/cli.js +7 -7
  24. package/dist/clients/attach/chrome/bash-jobs.d.ts +11 -1
  25. package/dist/clients/attach/chrome/bash-jobs.js +1 -1
  26. package/dist/clients/attach/chrome/canvas-panels.d.ts +1 -1
  27. package/dist/clients/attach/chrome/roster.js +6 -4
  28. package/dist/clients/attach/overlays/auth.js +2 -2
  29. package/dist/clients/attach/render/chat-view.js +1 -1
  30. package/dist/clients/attach/viewer.js +550 -567
  31. package/dist/commands/canvas-config.js +2 -2
  32. package/dist/commands/canvas-use.js +1 -1
  33. package/dist/commands/human.js +2 -2
  34. package/dist/commands/node/bash.js +3 -3
  35. package/dist/commands/node/create.js +2 -2
  36. package/dist/commands/person-settings.d.ts +1 -0
  37. package/dist/commands/person-settings.js +1 -0
  38. package/dist/commands/pkg/market-manage.d.ts +2 -2
  39. package/dist/commands/pkg/market-manage.js +1 -1
  40. package/dist/commands/pkg/plugin-manage.js +13 -13
  41. package/dist/commands/pkg/shared.js +1 -1
  42. package/dist/commands/profile/default.js +1 -1
  43. package/dist/commands/push.js +2 -3
  44. package/dist/commands/sys/config.js +1 -1
  45. package/dist/commands/sys/connect.js +1 -1
  46. package/dist/commands/sys/context/sysprompt.js +2 -2
  47. package/dist/commands/sys/doctor.js +1 -1
  48. package/dist/commands/sys/panels/broker-limits-panel.js +1 -1
  49. package/dist/commands/sys/panels/keybindings-panel.js +1 -1
  50. package/dist/commands/sys/panels/models-panel.d.ts +8 -2
  51. package/dist/commands/sys/panels/models-panel.js +1 -1
  52. package/dist/commands/sys/panels/prefs-panel.d.ts +1 -0
  53. package/dist/commands/sys/panels/prefs-panel.js +1 -1
  54. package/dist/commands/sys/panels/profiles-panel.js +1 -1
  55. package/dist/commands/sys/panels/provider-panel.d.ts +4 -0
  56. package/dist/commands/sys/panels/provider-panel.js +1 -1
  57. package/dist/commands/sys/provider-login.js +5 -5
  58. package/dist/commands/sys/setup-core.js +3 -3
  59. package/dist/commands/sys/setup-settings-runner.js +1 -1
  60. package/dist/commands/sys/spend.js +3 -3
  61. package/dist/commands/sys/update.js +1 -1
  62. package/dist/core/auth-file.d.ts +11 -19
  63. package/dist/core/auth-file.js +1 -2
  64. package/dist/core/auto-update.d.ts +1 -1
  65. package/dist/core/auto-update.js +4 -4
  66. package/dist/core/bash-jobs.d.ts +29 -71
  67. package/dist/core/bash-jobs.js +9 -10
  68. package/dist/core/blobs/directory.d.ts +3 -0
  69. package/dist/core/blobs/directory.js +1 -0
  70. package/dist/core/blobs/fault.d.ts +3 -0
  71. package/dist/core/blobs/fault.js +1 -0
  72. package/dist/core/blobs/index.d.ts +21 -0
  73. package/dist/core/blobs/index.js +1 -0
  74. package/dist/core/blobs/r2.d.ts +9 -0
  75. package/dist/core/blobs/r2.js +4 -0
  76. package/dist/core/bootstrap.d.ts +1 -1
  77. package/dist/core/bootstrap.js +2 -2
  78. package/dist/core/canvas/canvas.d.ts +9 -7
  79. package/dist/core/canvas/canvas.js +8 -8
  80. package/dist/core/canvas/crons.d.ts +3 -2
  81. package/dist/core/canvas/crons.js +3 -3
  82. package/dist/core/canvas/daemon-owner.d.ts +5 -0
  83. package/dist/core/canvas/daemon-owner.js +1 -1
  84. package/dist/core/canvas/db.d.ts +10 -9
  85. package/dist/core/canvas/db.js +4 -4
  86. package/dist/core/canvas/executions.d.ts +100 -4
  87. package/dist/core/canvas/executions.js +1 -1
  88. package/dist/core/canvas/export-sqlite.js +1 -1
  89. package/dist/core/canvas/focuses.d.ts +4 -0
  90. package/dist/core/canvas/focuses.js +1 -1
  91. package/dist/core/canvas/history.js +9 -9
  92. package/dist/core/canvas/managed-worktree-sweep-candidates.d.ts +2 -1
  93. package/dist/core/canvas/managed-worktree-sweep-candidates.js +3 -2
  94. package/dist/core/canvas/node-tokens.d.ts +1 -4
  95. package/dist/core/canvas/node-tokens.js +3 -3
  96. package/dist/core/canvas/paths.d.ts +0 -1
  97. package/dist/core/canvas/paths.js +1 -1
  98. package/dist/core/canvas/person-state.d.ts +26 -0
  99. package/dist/core/canvas/person-state.js +1 -0
  100. package/dist/core/canvas/person.d.ts +3 -10
  101. package/dist/core/canvas/person.js +7 -7
  102. package/dist/core/canvas/reconciler-leases.d.ts +12 -0
  103. package/dist/core/canvas/reconciler-leases.js +8 -0
  104. package/dist/core/canvas/recovery.d.ts +4 -0
  105. package/dist/core/canvas/recovery.js +1 -1
  106. package/dist/core/canvas/schema-pg/003-fleet-removals.sql +1 -1
  107. package/dist/core/canvas/schema-pg/005-fleet-blobs.sql +13 -0
  108. package/dist/core/canvas/schema-pg/006-job-rows.sql +20 -0
  109. package/dist/core/canvas/schema-pg/007-ledger-check.sql +117 -0
  110. package/dist/core/canvas/schema-version.d.ts +1 -1
  111. package/dist/core/canvas/schema-version.js +1 -1
  112. package/dist/core/canvas/sessions.d.ts +8 -11
  113. package/dist/core/canvas/sessions.js +1 -4
  114. package/dist/core/canvas/tree.d.ts +4 -2
  115. package/dist/core/canvas/tree.js +6 -6
  116. package/dist/core/canvas/types.d.ts +7 -0
  117. package/dist/core/command-hooks/transport/exec-lifecycle.js +1 -1
  118. package/dist/core/command-plugins/bundle.d.ts +1 -1
  119. package/dist/core/command-plugins/bundle.js +1 -1
  120. package/dist/core/command-plugins/compose.d.ts +1 -1
  121. package/dist/core/command-plugins/discovery.d.ts +0 -1
  122. package/dist/core/command-plugins/discovery.js +1 -1
  123. package/dist/core/command-plugins/help-addenda.js +1 -1
  124. package/dist/core/command-plugins/presence.d.ts +2 -0
  125. package/dist/core/command-plugins/presence.js +1 -1
  126. package/dist/core/config.d.ts +16 -15
  127. package/dist/core/config.js +1 -1
  128. package/dist/core/conversation-store/client-worker.js +1 -1
  129. package/dist/core/conversation-store/client.js +2 -2
  130. package/dist/core/conversation-store/index.d.ts +9 -11
  131. package/dist/core/conversation-store/index.js +1 -1
  132. package/dist/core/conversation-store/patch.js +1 -1
  133. package/dist/core/conversation-store/row-store.d.ts +14 -0
  134. package/dist/core/conversation-store/row-store.js +13 -0
  135. package/dist/core/directory/join.js +1 -1
  136. package/dist/core/env-name.d.ts +1 -0
  137. package/dist/core/env-name.js +1 -1
  138. package/dist/core/environment-recipe.d.ts +15 -0
  139. package/dist/core/environment-recipe.js +1 -0
  140. package/dist/core/environments.js +1 -1
  141. package/dist/core/events/emit.js +2 -2
  142. package/dist/core/events/run-event-chunks.d.ts +25 -0
  143. package/dist/core/events/run-event-chunks.js +5 -0
  144. package/dist/core/feed/messages.js +5 -5
  145. package/dist/core/grants/mirror.d.ts +15 -0
  146. package/dist/core/grants/mirror.js +15 -3
  147. package/dist/core/grants/on-sync.d.ts +9 -5
  148. package/dist/core/grants/on-sync.js +1 -1
  149. package/dist/core/grants/remove.d.ts +1 -6
  150. package/dist/core/grants/remove.js +1 -1
  151. package/dist/core/grants/sync.js +1 -1
  152. package/dist/core/grants/watch.d.ts +15 -9
  153. package/dist/core/grants/watch.js +1 -1
  154. package/dist/core/graph/custom.js +8 -8
  155. package/dist/core/graph/jobs.d.ts +83 -5
  156. package/dist/core/graph/jobs.js +11 -2
  157. package/dist/core/graph/kinds/job.js +2 -2
  158. package/dist/core/graph/names.js +7 -7
  159. package/dist/core/graph/objects.d.ts +5 -1
  160. package/dist/core/graph/objects.js +11 -11
  161. package/dist/core/graph/reader.d.ts +3 -0
  162. package/dist/core/graph/reader.js +3 -3
  163. package/dist/core/graph/repo-sync/identity.d.ts +3 -1
  164. package/dist/core/graph/repo-sync/identity.js +1 -1
  165. package/dist/core/graph/repo-sync/repos.d.ts +1 -1
  166. package/dist/core/graph/repo-sync/repos.js +2 -2
  167. package/dist/core/graph/repo-sync/sync.js +2 -2
  168. package/dist/core/installed-plugins.js +1 -1
  169. package/dist/core/keybindings/persistence.d.ts +1 -1
  170. package/dist/core/keybindings/persistence.js +1 -1
  171. package/dist/core/layout-migrate/loose-keys.d.ts +1 -1
  172. package/dist/core/layout-migrate/loose-keys.js +1 -1
  173. package/dist/core/ledger/account.d.ts +12 -0
  174. package/dist/core/ledger/account.js +2 -0
  175. package/dist/core/ledger/charges.d.ts +2 -2
  176. package/dist/core/ledger/charges.js +1 -1
  177. package/dist/core/ledger/check.d.ts +5 -1
  178. package/dist/core/ledger/check.js +1 -7
  179. package/dist/core/ledger/chunks.d.ts +8 -0
  180. package/dist/core/ledger/chunks.js +2 -0
  181. package/dist/core/ledger/index.d.ts +2 -1
  182. package/dist/core/ledger/index.js +1 -1
  183. package/dist/core/ledger/infra-allowance.d.ts +9 -0
  184. package/dist/core/ledger/infra-allowance.js +1 -0
  185. package/dist/core/ledger/limits.js +3 -3
  186. package/dist/core/ledger/payer.d.ts +1 -1
  187. package/dist/core/ledger/payer.js +1 -1
  188. package/dist/core/ledger/read.d.ts +14 -4
  189. package/dist/core/ledger/read.js +2 -3
  190. package/dist/core/ledger/totals.d.ts +5 -16
  191. package/dist/core/ledger/totals.js +4 -4
  192. package/dist/core/ledger/types.d.ts +12 -3
  193. package/dist/core/llm/run.js +2 -2
  194. package/dist/core/managed-providers.d.ts +72 -0
  195. package/dist/core/managed-providers.js +1 -0
  196. package/dist/core/model-auth/api-credential-store.d.ts +10 -0
  197. package/dist/core/model-auth/api-credential-store.js +1 -0
  198. package/dist/core/model-auth/daemon-login.d.ts +1 -1
  199. package/dist/core/model-auth/daemon-login.js +1 -1
  200. package/dist/core/model-auth/flows.d.ts +5 -4
  201. package/dist/core/model-auth/flows.js +1 -1
  202. package/dist/core/model-auth/handoff-jti.d.ts +3 -4
  203. package/dist/core/model-auth/handoff-jti.js +1 -1
  204. package/dist/core/model-auth/readiness.js +1 -1
  205. package/dist/core/model-config.d.ts +1 -1
  206. package/dist/core/model-config.js +1 -1
  207. package/dist/core/model-routes-pool.d.ts +14 -0
  208. package/dist/core/model-routes-pool.js +1 -0
  209. package/dist/core/model-routes.d.ts +11 -16
  210. package/dist/core/model-routes.js +1 -1
  211. package/dist/core/peer/ingest.d.ts +2 -0
  212. package/dist/core/peer/ingest.js +1 -1
  213. package/dist/core/peer/stubs.d.ts +8 -0
  214. package/dist/core/peer/stubs.js +1 -1
  215. package/dist/core/permissions/evaluator.d.ts +7 -2
  216. package/dist/core/permissions/evaluator.js +2 -2
  217. package/dist/core/plugin-env.js +1 -1
  218. package/dist/core/pool-credential-store.d.ts +2 -5
  219. package/dist/core/pool-credential-store.js +1 -1
  220. package/dist/core/profiles/app-env-store.d.ts +5 -5
  221. package/dist/core/profiles/app-env-store.js +1 -2
  222. package/dist/core/profiles/default-binding.d.ts +5 -5
  223. package/dist/core/profiles/default-binding.js +1 -1
  224. package/dist/core/profiles/env-store.d.ts +10 -21
  225. package/dist/core/profiles/env-store.js +1 -2
  226. package/dist/core/profiles/manifest.d.ts +14 -18
  227. package/dist/core/profiles/manifest.js +1 -1
  228. package/dist/core/profiles/relocate.js +1 -1
  229. package/dist/core/profiles/select.js +21 -21
  230. package/dist/core/provider-credential.d.ts +1 -1
  231. package/dist/core/provider-credential.js +1 -1
  232. package/dist/core/provider-management.d.ts +3 -4
  233. package/dist/core/provider-management.js +1 -1
  234. package/dist/core/providers/bundles.js +2 -2
  235. package/dist/core/providers/call.d.ts +3 -34
  236. package/dist/core/providers/description.d.ts +34 -0
  237. package/dist/core/providers/description.js +0 -0
  238. package/dist/core/providers/token-cache.d.ts +14 -14
  239. package/dist/core/providers/token-cache.js +1 -1
  240. package/dist/core/review/companion.js +1 -1
  241. package/dist/core/runs/activity.d.ts +1 -1
  242. package/dist/core/runs/activity.js +1 -1
  243. package/dist/core/runs/events.d.ts +7 -5
  244. package/dist/core/runs/events.js +2 -6
  245. package/dist/core/runs/operations.d.ts +1 -1
  246. package/dist/core/runs/operations.js +6 -6
  247. package/dist/core/runtime/bearings.js +1 -1
  248. package/dist/core/runtime/broker/attachments-fetch.d.ts +5 -0
  249. package/dist/core/runtime/broker/attachments-fetch.js +1 -0
  250. package/dist/core/runtime/broker/bash-job-launch.d.ts +4 -2
  251. package/dist/core/runtime/broker/bash-job-launch.js +1 -1
  252. package/dist/core/runtime/broker/boot-state.d.ts +9 -0
  253. package/dist/core/runtime/broker/boot-state.js +1 -0
  254. package/dist/core/runtime/broker/cli-relay.d.ts +2 -1
  255. package/dist/core/runtime/broker/cli-relay.js +1 -1
  256. package/dist/core/runtime/broker/client-registry.d.ts +3 -3
  257. package/dist/core/runtime/broker/daemon-ops.d.ts +10 -1
  258. package/dist/core/runtime/broker/daemon-ops.js +1 -1
  259. package/dist/core/runtime/broker/fault-retry.d.ts +2 -0
  260. package/dist/core/runtime/broker/fault-retry.js +1 -1
  261. package/dist/core/runtime/broker/frame-engine-control.js +1 -1
  262. package/dist/core/runtime/broker/held-deferred-inbox.d.ts +3 -0
  263. package/dist/core/runtime/broker/held-deferred-inbox.js +1 -1
  264. package/dist/core/runtime/broker/job-reporter.d.ts +18 -0
  265. package/dist/core/runtime/broker/job-reporter.js +2 -0
  266. package/dist/core/runtime/broker/model-adapter.d.ts +3 -0
  267. package/dist/core/runtime/broker/model-adapter.js +3 -2
  268. package/dist/core/runtime/broker/signal-stream.js +1 -1
  269. package/dist/core/runtime/broker/spilled-body.d.ts +5 -0
  270. package/dist/core/runtime/broker/spilled-body.js +1 -0
  271. package/dist/core/runtime/broker/summaries-post.d.ts +5 -0
  272. package/dist/core/runtime/broker/summaries-post.js +1 -0
  273. package/dist/core/runtime/broker/tool-groups.d.ts +3 -0
  274. package/dist/core/runtime/broker/tool-groups.js +1 -1
  275. package/dist/core/runtime/broker/turn-replies.d.ts +1 -0
  276. package/dist/core/runtime/broker/turn-replies.js +1 -1
  277. package/dist/core/runtime/broker-persona-guidance.js +2 -2
  278. package/dist/core/runtime/broker.js +1 -1
  279. package/dist/core/runtime/deliver-live.js +1 -1
  280. package/dist/core/runtime/first-boot-rows.d.ts +8 -0
  281. package/dist/core/runtime/first-boot-rows.js +2 -0
  282. package/dist/core/runtime/fleet.d.ts +13 -12
  283. package/dist/core/runtime/front-door-login.js +1 -1
  284. package/dist/core/runtime/front-door.js +6 -6
  285. package/dist/core/runtime/headless-pi.js +1 -1
  286. package/dist/core/runtime/host.d.ts +14 -3
  287. package/dist/core/runtime/host.js +3 -3
  288. package/dist/core/runtime/invocation.d.ts +6 -0
  289. package/dist/core/runtime/kickoff.d.ts +2 -14
  290. package/dist/core/runtime/kickoff.js +17 -19
  291. package/dist/core/runtime/launch-config-worker.js +1 -1
  292. package/dist/core/runtime/launch.js +2 -2
  293. package/dist/core/runtime/model-registry.js +1 -1
  294. package/dist/core/runtime/node-read.d.ts +4 -6
  295. package/dist/core/runtime/node-read.js +3 -3
  296. package/dist/core/runtime/nodes.js +1 -1
  297. package/dist/core/runtime/outcome-document.d.ts +0 -1
  298. package/dist/core/runtime/outcome-document.js +1 -1
  299. package/dist/core/runtime/output-result.d.ts +15 -0
  300. package/dist/core/runtime/output-result.js +5 -0
  301. package/dist/core/runtime/placement.d.ts +17 -1
  302. package/dist/core/runtime/placement.js +1 -1
  303. package/dist/core/runtime/provider-credential-gate.d.ts +6 -2
  304. package/dist/core/runtime/provider-credential-gate.js +2 -2
  305. package/dist/core/runtime/recovery-hold.d.ts +21 -2
  306. package/dist/core/runtime/recovery-hold.js +2 -1
  307. package/dist/core/runtime/recycle.js +1 -1
  308. package/dist/core/runtime/reset.js +1 -1
  309. package/dist/core/runtime/revive.d.ts +2 -2
  310. package/dist/core/runtime/revive.js +2 -2
  311. package/dist/core/runtime/runner/blaxel/client.d.ts +81 -0
  312. package/dist/core/runtime/runner/blaxel/client.js +1 -0
  313. package/dist/core/runtime/runner/blaxel/image-build.d.ts +74 -0
  314. package/dist/core/runtime/runner/blaxel/image-build.js +5 -0
  315. package/dist/core/runtime/runner/blaxel/runner.d.ts +41 -0
  316. package/dist/core/runtime/runner/blaxel/runner.js +3 -0
  317. package/dist/core/runtime/runner/index.d.ts +8 -0
  318. package/dist/core/runtime/runner/index.js +1 -0
  319. package/dist/core/runtime/runner/process.d.ts +11 -0
  320. package/dist/core/runtime/runner/process.js +1 -0
  321. package/dist/core/runtime/runner/types.d.ts +95 -0
  322. package/dist/core/runtime/runner/types.js +0 -0
  323. package/dist/core/runtime/situational-context.d.ts +3 -6
  324. package/dist/core/runtime/situational-context.js +2 -2
  325. package/dist/core/runtime/situational-live.js +1 -1
  326. package/dist/core/runtime/spawn-env-rows.d.ts +5 -0
  327. package/dist/core/runtime/spawn-env-rows.js +1 -0
  328. package/dist/core/runtime/spawn-env.d.ts +4 -18
  329. package/dist/core/runtime/spawn-env.js +1 -1
  330. package/dist/core/runtime/spawn.js +2 -2
  331. package/dist/core/runtime/stop-guard.d.ts +1 -1
  332. package/dist/core/runtime/stop-guard.js +2 -8
  333. package/dist/core/runtime/structured-output.d.ts +0 -67
  334. package/dist/core/runtime/structured-output.js +3 -8
  335. package/dist/core/runtime/tmux-driver.d.ts +8 -0
  336. package/dist/core/runtime/tmux-driver.js +6 -6
  337. package/dist/core/runtime/view-socket.d.ts +20 -0
  338. package/dist/core/runtime/view-socket.js +3 -1
  339. package/dist/core/runtime/warm-pool.d.ts +13 -1
  340. package/dist/core/runtime/warm-pool.js +1 -1
  341. package/dist/core/scoped-state/legacy-profiles.d.ts +11 -0
  342. package/dist/core/scoped-state/legacy-profiles.js +1 -0
  343. package/dist/core/scoped-state/materialized-plugins.d.ts +18 -0
  344. package/dist/core/scoped-state/materialized-plugins.js +1 -0
  345. package/dist/core/scoped-state/migrate.d.ts +8 -1
  346. package/dist/core/scoped-state/migrate.js +3 -3
  347. package/dist/core/scoped-state/person-rows.d.ts +10 -0
  348. package/dist/core/scoped-state/person-rows.js +6 -0
  349. package/dist/core/scoped-state/person-store.d.ts +72 -0
  350. package/dist/core/scoped-state/person-store.js +1 -0
  351. package/dist/core/secrets/blaxel-launch.d.ts +24 -0
  352. package/dist/core/secrets/blaxel-launch.js +4 -0
  353. package/dist/core/secrets/blaxel-register.d.ts +2 -0
  354. package/dist/core/secrets/blaxel-register.js +1 -0
  355. package/dist/core/secrets/env-rows.d.ts +17 -0
  356. package/dist/core/secrets/env-rows.js +4 -0
  357. package/dist/core/secrets/environment-owner.d.ts +8 -0
  358. package/dist/core/secrets/environment-owner.js +1 -0
  359. package/dist/core/secrets/environment-secrets.d.ts +14 -0
  360. package/dist/core/secrets/environment-secrets.js +1 -0
  361. package/dist/core/secrets/index.d.ts +2 -0
  362. package/dist/core/secrets/index.js +1 -0
  363. package/dist/core/secrets/key.d.ts +3 -0
  364. package/dist/core/secrets/key.js +2 -0
  365. package/dist/core/secrets/seal.d.ts +18 -0
  366. package/dist/core/secrets/seal.js +1 -0
  367. package/dist/core/spaces/mounts.d.ts +1 -1
  368. package/dist/core/spaces/mounts.js +1 -1
  369. package/dist/core/spaces/process.d.ts +1 -1
  370. package/dist/core/spaces/stop.d.ts +7 -5
  371. package/dist/core/spaces/stop.js +2 -2
  372. package/dist/core/spaces/users.d.ts +2 -1
  373. package/dist/core/spaces/users.js +1 -1
  374. package/dist/core/storage/attachments.d.ts +11 -8
  375. package/dist/core/storage/attachments.js +1 -1
  376. package/dist/core/storage/tables.d.ts +4 -0
  377. package/dist/core/storage/tables.js +3 -3
  378. package/dist/core/subscription-state.d.ts +34 -72
  379. package/dist/core/subscription-state.js +1 -4
  380. package/dist/core/substrate/delivery/plan.js +1 -1
  381. package/dist/core/user-settings.js +1 -1
  382. package/dist/daemon/api/app-listener.js +10 -10
  383. package/dist/daemon/api/auth/person-bearer.d.ts +14 -0
  384. package/dist/daemon/api/auth/person-bearer.js +1 -0
  385. package/dist/daemon/api/bridge.js +4 -4
  386. package/dist/daemon/api/document-listener.js +3 -3
  387. package/dist/daemon/api/environment-of.d.ts +18 -0
  388. package/dist/daemon/api/environment-of.js +5 -0
  389. package/dist/daemon/api/handlers/analytics.js +1 -1
  390. package/dist/daemon/api/handlers/app-profiles.d.ts +2 -2
  391. package/dist/daemon/api/handlers/app-profiles.js +1 -1
  392. package/dist/daemon/api/handlers/bash-jobs.js +1 -1
  393. package/dist/daemon/api/handlers/bash.js +1 -1
  394. package/dist/daemon/api/handlers/broker-ops.d.ts +3 -0
  395. package/dist/daemon/api/handlers/broker-ops.js +1 -1
  396. package/dist/daemon/api/handlers/broker-recovery.js +1 -1
  397. package/dist/daemon/api/handlers/broker-signals.d.ts +0 -3
  398. package/dist/daemon/api/handlers/broker-signals.js +2 -2
  399. package/dist/daemon/api/handlers/canvas.js +4 -4
  400. package/dist/daemon/api/handlers/charges.d.ts +1 -1
  401. package/dist/daemon/api/handlers/charges.js +1 -1
  402. package/dist/daemon/api/handlers/conversations.d.ts +2 -0
  403. package/dist/daemon/api/handlers/conversations.js +3 -0
  404. package/dist/daemon/api/handlers/crons.js +1 -1
  405. package/dist/daemon/api/handlers/daemon.js +1 -1
  406. package/dist/daemon/api/handlers/delivery.d.ts +1 -1
  407. package/dist/daemon/api/handlers/delivery.js +1 -1
  408. package/dist/daemon/api/handlers/docs.js +1 -1
  409. package/dist/daemon/api/handlers/exec.js +1 -1
  410. package/dist/daemon/api/handlers/execution-start.d.ts +2 -0
  411. package/dist/daemon/api/handlers/execution-start.js +2 -0
  412. package/dist/daemon/api/handlers/files.js +1 -1
  413. package/dist/daemon/api/handlers/git-credential.d.ts +11 -0
  414. package/dist/daemon/api/handlers/git-credential.js +1 -0
  415. package/dist/daemon/api/handlers/hook-exec.d.ts +1 -1
  416. package/dist/daemon/api/handlers/hook-exec.js +1 -1
  417. package/dist/daemon/api/handlers/human-requests.js +1 -1
  418. package/dist/daemon/api/handlers/inbox.js +1 -1
  419. package/dist/daemon/api/handlers/messages.js +1 -1
  420. package/dist/daemon/api/handlers/model-config.js +1 -1
  421. package/dist/daemon/api/handlers/modelauth.d.ts +1 -1
  422. package/dist/daemon/api/handlers/modelauth.js +1 -1
  423. package/dist/daemon/api/handlers/node-events.d.ts +6 -0
  424. package/dist/daemon/api/handlers/node-events.js +3 -3
  425. package/dist/daemon/api/handlers/node-outcomes.js +1 -1
  426. package/dist/daemon/api/handlers/node-records.js +3 -2
  427. package/dist/daemon/api/handlers/nodes.js +1 -1
  428. package/dist/daemon/api/handlers/objects.js +3 -3
  429. package/dist/daemon/api/handlers/profiles.js +1 -1
  430. package/dist/daemon/api/handlers/providers.js +1 -1
  431. package/dist/daemon/api/handlers/reports.js +9 -13
  432. package/dist/daemon/api/handlers/reviews.js +1 -1
  433. package/dist/daemon/api/handlers/runs.js +1 -1
  434. package/dist/daemon/api/handlers/settings.d.ts +2 -0
  435. package/dist/daemon/api/handlers/settings.js +1 -0
  436. package/dist/daemon/api/handlers/structured-requests.js +1 -1
  437. package/dist/daemon/api/handlers/vendor-status.d.ts +1 -1
  438. package/dist/daemon/api/handlers/vendor-status.js +1 -1
  439. package/dist/daemon/api/handlers/worktree.js +1 -1
  440. package/dist/daemon/api/map.d.ts +2 -2
  441. package/dist/daemon/api/map.js +2 -2
  442. package/dist/daemon/api/operations.js +1 -1
  443. package/dist/daemon/api/reader.d.ts +8 -1
  444. package/dist/daemon/api/reader.js +1 -1
  445. package/dist/daemon/api/router.d.ts +4 -2
  446. package/dist/daemon/api/router.js +1 -1
  447. package/dist/daemon/api/server.d.ts +15 -3
  448. package/dist/daemon/api/server.js +1 -1
  449. package/dist/daemon/api/vendor-signin.d.ts +2 -1
  450. package/dist/daemon/api/vendor-signin.js +19 -19
  451. package/dist/daemon/api/viewer-presence.d.ts +20 -6
  452. package/dist/daemon/api/viewer-presence.js +2 -1
  453. package/dist/daemon/broker-signals/bus.d.ts +48 -11
  454. package/dist/daemon/broker-signals/bus.js +1 -1
  455. package/dist/daemon/cron/armed-context.js +1 -1
  456. package/dist/daemon/cron/lease-recovery.js +1 -1
  457. package/dist/daemon/cron/live-runs.d.ts +6 -0
  458. package/dist/daemon/cron/live-runs.js +1 -1
  459. package/dist/daemon/cron/run-socket.js +1 -1
  460. package/dist/daemon/cron-run.js +2 -2
  461. package/dist/daemon/crtrd-cli.js +1 -1
  462. package/dist/daemon/crtrd.d.ts +10 -0
  463. package/dist/daemon/crtrd.js +18 -17
  464. package/dist/daemon/delivery/run-delivery.d.ts +1 -1
  465. package/dist/daemon/delivery/run-delivery.js +1 -1
  466. package/dist/daemon/due-sources.d.ts +1 -1
  467. package/dist/daemon/due-sources.js +1 -1
  468. package/dist/daemon/due.js +7 -6
  469. package/dist/daemon/fleet.d.ts +6 -6
  470. package/dist/daemon/fleet.js +9 -9
  471. package/dist/daemon/park-activity.d.ts +2 -1
  472. package/dist/daemon/park-activity.js +1 -1
  473. package/dist/daemon/park-pending.d.ts +20 -13
  474. package/dist/daemon/park-pending.js +3 -1
  475. package/dist/daemon/profile-delete.js +1 -1
  476. package/dist/daemon/reconcilers/bash-deadline.d.ts +15 -10
  477. package/dist/daemon/reconcilers/bash-deadline.js +1 -3
  478. package/dist/daemon/reconcilers/broker-supervision.d.ts +13 -3
  479. package/dist/daemon/reconcilers/broker-supervision.js +4 -4
  480. package/dist/daemon/reconcilers/cron-lane.d.ts +0 -1
  481. package/dist/daemon/reconcilers/cron-lane.js +1 -1
  482. package/dist/daemon/reconcilers/fault-retry-backstop.d.ts +2 -9
  483. package/dist/daemon/reconcilers/fault-retry-backstop.js +1 -1
  484. package/dist/daemon/reconcilers/grant-effects.d.ts +8 -0
  485. package/dist/daemon/reconcilers/grant-effects.js +1 -0
  486. package/dist/daemon/reconcilers/grove-instance-sweep.d.ts +1 -3
  487. package/dist/daemon/reconcilers/grove-instance-sweep.js +1 -1
  488. package/dist/daemon/reconcilers/ledger-r2.d.ts +27 -0
  489. package/dist/daemon/reconcilers/ledger-r2.js +7 -0
  490. package/dist/daemon/reconcilers/ledger-sandbox.d.ts +55 -0
  491. package/dist/daemon/reconcilers/ledger-sandbox.js +9 -0
  492. package/dist/daemon/reconcilers/ledger-storage.d.ts +22 -0
  493. package/dist/daemon/reconcilers/ledger-storage.js +5 -0
  494. package/dist/daemon/reconcilers/live-obligation.d.ts +3 -2
  495. package/dist/daemon/reconcilers/live-obligation.js +1 -1
  496. package/dist/daemon/reconcilers/managed-worktree-sweep.d.ts +0 -5
  497. package/dist/daemon/reconcilers/managed-worktree-sweep.js +1 -1
  498. package/dist/daemon/reconcilers/node-deadline.d.ts +2 -1
  499. package/dist/daemon/reconcilers/node-deadline.js +1 -1
  500. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.d.ts +1 -2
  501. package/dist/daemon/reconcilers/node-lifecycle/freeze-lane.js +1 -1
  502. package/dist/daemon/reconcilers/node-lifecycle/remote-wake.d.ts +31 -0
  503. package/dist/daemon/reconcilers/node-lifecycle/remote-wake.js +1 -0
  504. package/dist/daemon/reconcilers/node-lifecycle/requested-stops.d.ts +5 -0
  505. package/dist/daemon/reconcilers/node-lifecycle/requested-stops.js +2 -0
  506. package/dist/daemon/reconcilers/node-lifecycle/tick.js +1 -1
  507. package/dist/daemon/reconcilers/pending-review-submit.js +1 -1
  508. package/dist/daemon/reconcilers/recipe-image.d.ts +49 -0
  509. package/dist/daemon/reconcilers/recipe-image.js +7 -0
  510. package/dist/daemon/reconcilers/row-claim.d.ts +19 -0
  511. package/dist/daemon/reconcilers/row-claim.js +2 -0
  512. package/dist/daemon/reconcilers/storage-due-work.d.ts +18 -3
  513. package/dist/daemon/reconcilers/storage-due-work.js +2 -1
  514. package/dist/daemon/reconcilers/storage-maintenance.d.ts +5 -13
  515. package/dist/daemon/reconcilers/storage-maintenance.js +1 -1
  516. package/dist/daemon/reconcilers/structured-request-lane.js +1 -1
  517. package/dist/daemon/reconcilers/throttled-lane.d.ts +11 -0
  518. package/dist/daemon/reconcilers/throttled-lane.js +1 -0
  519. package/dist/daemon/review/finish.js +1 -1
  520. package/dist/daemon/run-notices.d.ts +3 -0
  521. package/dist/daemon/run-notices.js +1 -1
  522. package/dist/daemon/runs/append-section.d.ts +4 -1
  523. package/dist/daemon/runs/append-section.js +2 -2
  524. package/dist/daemon/runs/observer.d.ts +3 -2
  525. package/dist/daemon/runs/observer.js +3 -2
  526. package/dist/daemon/runs/view-transport.d.ts +19 -0
  527. package/dist/daemon/runs/view-transport.js +1 -0
  528. package/dist/daemon/structured-requests/boot.d.ts +2 -1
  529. package/dist/daemon/structured-requests/boot.js +1 -1
  530. package/dist/daemon/structured-requests/effects.d.ts +4 -3
  531. package/dist/daemon/structured-requests/effects.js +1 -1
  532. package/dist/daemon/structured-requests/notifier.d.ts +2 -0
  533. package/dist/daemon/structured-requests/notifier.js +1 -1
  534. package/dist/daemon/structured-requests/sidecar.d.ts +4 -3
  535. package/dist/daemon/structured-requests/sidecar.js +1 -1
  536. package/dist/launcher/validate.js +1 -1
  537. package/dist/migrations/004-canvas-documents/index.js +2 -2
  538. package/dist/pi-extensions/broker-local.d.ts +3 -3
  539. package/dist/pi-extensions/broker-local.js +1 -2
  540. package/dist/pi-extensions/canvas-bash-valve.js +6 -6
  541. package/dist/pi-extensions/canvas-context-intro.js +2 -2
  542. package/dist/pi-extensions/canvas-elapsed.js +2 -2
  543. package/dist/pi-extensions/canvas-goal-capture.js +1 -1
  544. package/dist/pi-extensions/canvas-inbox-watcher.js +2 -2
  545. package/dist/pi-extensions/canvas-review-boundary.d.ts +11 -1
  546. package/dist/pi-extensions/canvas-review-boundary.js +3 -3
  547. package/dist/pi-extensions/canvas-stophook.js +1 -1
  548. package/dist/pi-extensions/canvas-structured-output.d.ts +5 -3
  549. package/dist/pi-extensions/canvas-structured-output.js +6 -11
  550. package/dist/pi-extensions/canvas-tool-guide.d.ts +9 -2
  551. package/dist/pi-extensions/canvas-tool-guide.js +6 -6
  552. package/dist/shared/inbox-entry-body.js +7 -7
  553. package/dist/types.d.ts +12 -0
  554. package/docs/sdk/release-notes.md +4 -0
  555. package/package.json +7 -4
  556. package/packages/crouter-identity/package.json +1 -1
  557. package/runtime.lock.json +13 -13
  558. package/scripts/git-credential-crtr +33 -0
  559. package/scripts/sandbox-bootstrap.sh +54 -0
  560. package/scripts/sandbox-start.mjs +320 -0
  561. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +0 -78
  562. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +0 -1179
  563. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +0 -1406
  564. package/dist/core/conversation-store/file-store.d.ts +0 -37
  565. package/dist/core/conversation-store/file-store.js +0 -9
  566. package/dist/core/conversation-store/listener.d.ts +0 -4
  567. package/dist/core/conversation-store/listener.js +0 -8
  568. package/dist/core/scoped-state/profiles.d.ts +0 -27
  569. package/dist/core/scoped-state/profiles.js +0 -1
  570. package/dist/core/scoped-state/providers.d.ts +0 -24
  571. package/dist/core/scoped-state/providers.js +0 -1
  572. package/dist/core/scoped-state/settings.d.ts +0 -28
  573. package/dist/core/scoped-state/settings.js +0 -1
@@ -1,2 +1,2 @@
1
- var s=Object.defineProperty;var t=(e,r)=>s(e,"name",{value:r,configurable:!0});import{homedir as p}from"node:os";import{join as c,relative as f,sep as m}from"node:path";import{appsRoot as u}from"./layout.js";import{findProjectScopeRoot as S,userScopeRoot as n}from"./scope.js";import{ensureScopeInitialized as d}from"./config.js";import{initializeFreshScopedStore as h}from"./scoped-state/migrate.js";import{CRTR_DIR_NAME as l}from"../types.js";import{envDebug as R}from"../shared/env.js";const a=new Set(["help","--help","-h","--version","-v"]);function g(e){const r=e[2];return r===void 0||r==="sys"&&e[3]==="migrate"?!0:a.has(r)}t(g,"shouldSkipForArgv");function D(e){try{if(process.env.CRTR_NO_AUTO_INIT==="1"||g(e)||(h({kind:"user",root:n()}),S()!==null))return;const r=process.cwd();if(r===p())return;const o=f(u(),r).split(m);if(o.length===2&&o[1]==="home")return;const i=c(r,l);if(i===n())return;d("project",i)}catch(r){if(R()){const o=r instanceof Error?r.message:String(r);process.stderr.write(`crtr: project-init error: ${o}
2
- `)}}}t(D,"ensureProjectScope");export{D as ensureProjectScope};
1
+ var i=Object.defineProperty;var t=(e,r)=>i(e,"name",{value:r,configurable:!0});import{homedir as s}from"node:os";import{join as c,relative as p,sep as f}from"node:path";import{appsRoot as u}from"./layout.js";import{findProjectScopeRoot as m,userScopeRoot as a}from"./scope.js";import{ensureScopeInitialized as S}from"./config.js";import{CRTR_DIR_NAME as d}from"../types.js";import{envDebug as h}from"../shared/env.js";const l=new Set(["help","--help","-h","--version","-v"]);function R(e){const r=e[2];return r===void 0||r==="sys"&&e[3]==="migrate"?!0:l.has(r)}t(R,"shouldSkipForArgv");async function T(e){try{if(process.env.CRTR_NO_AUTO_INIT==="1"||R(e)||m()!==null)return;const r=process.cwd();if(r===s())return;const o=p(u(),r).split(f);if(o.length===2&&o[1]==="home")return;const n=c(r,d);if(n===a())return;await S("project",n)}catch(r){if(h()){const o=r instanceof Error?r.message:String(r);process.stderr.write(`crtr: project-init error: ${o}
2
+ `)}}}t(T,"ensureProjectScope");export{T as ensureProjectScope};
@@ -31,8 +31,10 @@ export declare function pruneExpiredCreateRequests(now?: number): Promise<number
31
31
  * durable so a retry after a daemon handover returns the original node. */
32
32
  export declare function recordCreateRequestNode(requestKey: string, nodeId: string): Promise<void>;
33
33
  /** Reserve a cold-birth node id for a key (state stays `reserved`) so a retry
34
- * after a daemon restart resumes the same identity instead of minting another. */
35
- export declare function reserveCreateRequestNode(requestKey: string, nodeId: string): Promise<void>;
34
+ * after a daemon restart resumes the same identity instead of minting another.
35
+ * Only the first caller binds: two API processes holding one unbound key race
36
+ * here, and each gets back the id the key actually names. */
37
+ export declare function reserveCreateRequestNode(requestKey: string, nodeId: string): Promise<string | null>;
36
38
  export declare function completeCreateRequest(requestKey: string, nodeId: string): Promise<void>;
37
39
  /** Forget an unserved reservation after a pre-birth failure. A record that
38
40
  * names a durable node is never removed by this path. */
@@ -301,12 +303,12 @@ export type TerminalGuardResult<T> = {
301
303
  * of it and the write that depends on that (for example, a deferred inbox
302
304
  * append). Fresh-reads `nodeId`'s
303
305
  * (status, final_report, frozen_at) and either runs `body` (a write) or short-circuits
304
- * with a `'terminal'`/`'missing'` outcome — the read and `body`'s write share
305
- * ONE canvas write-lock boundary (`withCanvasWrite`'s `BEGIN IMMEDIATE`), so
306
+ * with a `'terminal'`/`'missing'` outcome — the read takes the node row
307
+ * `FOR UPDATE` and holds it through `body`'s write in the same transaction, so
306
308
  * a concurrent finish/cancel/finalization/deletion either commits entirely
307
- * before this reads (observed here, `body` never runs) or entirely after
308
- * (`body` completes uncontested) — never in the gap a separate check-then-
309
- * write left open. Pass the shared `hasNoNaturalCycle` predicate rather than
309
+ * before this reads (observed here, `body` never runs) or waits on the row
310
+ * until `body` commits — never in the gap a separate check-then-write left
311
+ * open. Pass the shared `hasNoNaturalCycle` predicate rather than
310
312
  * a bespoke one so the two never drift.
311
313
  *
312
314
  * A dangling `nodeId` — the row deleted after the
@@ -1,8 +1,8 @@
1
- var ie=Object.defineProperty;var i=(e,n)=>ie(e,"name",{value:n,configurable:!0});import{envNodeId as W}from"../../shared/env.js";import{lstatSync as oe,realpathSync as b,rmdirSync as re,rmSync as se,statSync as de}from"node:fs";import{basename as ue,dirname as F,isAbsolute as le,join as S,relative as ce,resolve as _e,sep as pe}from"node:path";import{removeTreeBeneath as C}from"../spaces/open-beneath.js";import{emitEvent as fe}from"../events/emit.js";import{withCanvasWrite as p,openDb as s}from"./db.js";import{conversationStore as Ee,ConversationNotFoundError as we}from"../conversation-store/index.js";import{hasUnseenWakeMail as ye,UNSEEN_WAKE_MAIL_SQL as me}from"./deliveries.js";import{isAppRun as H}from"./app-run.js";import{registerOutcomeAction as M}from"./outcome-actions.js";import{publish as Ne}from"../graph/events.js";import{nodeObject as Re}from"../graph/objects.js";import{findNodeBySessionFileSql as Se}from"./sessions.js";import{hydrateFault as he}from"./recovery.js";import{hasUnreconciledWorktree as Oe,openWorktree as Te,readWorktree as Le,worktreeFromRow as De}from"./worktrees.js";import{isPidAlive as ve,isPidPresent as P,recordedPidLiveness as ge}from"./pid.js";import{ensureHome as Ae,jobDir as ke,nodeDir as xe,nodeRunDir as $}from"./paths.js";import{appForNode as Ue,ensureNodeDirs as je,nodeAgentDir as Ie}from"./node-agent-paths.js";import{appSpace as T}from"../layout.js";import{createObject as We,getById as L,ownerForGrantee as be,setObjectKind as Fe,setObjectName as Ce}from"../graph/objects.js";import{profileObject as He}from"../graph/reader.js";import{writeActsFor as q}from"../permissions/facts.js";import{CrtrError as Me}from"../errors.js";import{deleteNodeWithOwned as Pe}from"../graph/deletion.js";import{SUBTREE_ALL_IDS_SQL as $e,SUBTREE_IDS_SQL as qe}from"./tree.js";import{filterAsync as m,mapAsync as D}from"../async-array.js";function E(e,n){if(e==null)return null;const t=JSON.stringify(e);if(t===void 0)throw new TypeError(`${n} must be JSON-serializable`);return t}i(E,"jsonValue");async function vn(e,n){if((await e.prepare("SELECT deleting_at FROM nodes WHERE node_id = ?").get(n))?.deleting_at)throw new Error("run is deleting")}i(vn,"assertRunNotDeleting");async function B(e,n,t){try{return await He(e,n,t)}catch(a){if(a instanceof Me&&a.code==="not_found")return null;throw a}}i(B,"profileObjectFor");async function Be(e,n){const t=await be(n,e.grantee??"app:terminal"),a=e.profile_id?await B(n,e.profile_id,t):null;await We(n,{id:e.node_id,type:"node",kind:e.kind,owner:(a??t).id,privacy:"public",name:e.name,preview:""}),a===null&&e.sub&&await q(n,e.node_id,e.sub),await n.prepare(`INSERT INTO nodes (node_id, description, title, icon, cycles, mode, lifecycle, cwd, host_kind, profile_id, parent, spawned_by, creator, fork_from, fork_source_file, created, passive_default, cycle_pending, model_override, persona_ack_json, review_binding_json, launch_config_json, scopes_json, isolated, status, intent, final_report, finalized_at, deadline_at, "window", tmux_session, pane, sub, grant_id, client_id, metadata_json, run_id, event_cursor, usage_json, deleting_at, caused_by, app) VALUES (${Array(42).fill("?").join(", ")})`).run(e.node_id,e.description??null,e.title??null,e.icon??null,e.cycles??null,e.mode,e.lifecycle,e.cwd,e.host_kind??null,e.profile_id??null,e.parent??null,e.spawned_by??null,e.creator??null,e.fork_from??null,e.fork_source_file??null,e.created,e.passive_default===!0?1:0,e.cycle_pending===!0?1:0,e.model_override??null,E(e.persona_ack,"persona_ack"),E(e.review_binding,"review_binding"),E(e.launch,"launch"),E(e.scopes,"scopes"),e.isolated===!0?1:0,e.status??"active",e.intent??null,e.final_report??null,e.finalized_at??null,e.deadline_at??null,e.window??null,e.tmux_session??null,e.pane??null,e.sub??null,e.grant_id??null,e.client_id??null,e.metadata_json??null,e.run_id??null,e.event_cursor??null,e.usage_json??null,e.deleting_at??null,e.caused_by??null,t.id)}i(Be,"seedRow");function z(e){if(e.fault_id==null)return null;const n=Object.entries(e).filter(([t])=>t.startsWith("fault_")).map(([t,a])=>[t==="fault_id"?t:t.slice(6),a]);return he(Object.fromEntries(n))}i(z,"faultFrom");function f(e){const n={node_id:e.node_id,name:e.name,description:e.description??void 0,cycles:e.cycles??void 0,kind:e.kind,mode:e.mode,lifecycle:e.lifecycle,status:e.status,cwd:e.cwd,host_kind:e.host_kind??null,profile_id:e.profile_id??null,scopes:e.scopes_json==null?null:JSON.parse(e.scopes_json),isolated:e.isolated===1,grantee:e.grantee,sub:e.sub??null,grant_id:e.grant_id??null,client_id:e.client_id??null,metadata_json:e.metadata_json??null,run_id:e.run_id??null,event_cursor:e.event_cursor??null,last_activity_at:e.last_activity_at??null,usage_json:e.usage_json??null,caused_by:e.caused_by??null,last_turn_error_json:e.last_turn_error_json??null,deleting_at:e.deleting_at??null,parent:e.parent??null,created:e.created,intent:e.intent??null,frozen_at:e.frozen_at??null,respawn_failures:e.respawn_failures??0,respawn_not_before:e.respawn_not_before??null,launched_at:e.launched_at??null,last_settled_at:e.last_settled_at??null,terminal_reason:e.terminal_reason??null,final_report:e.final_report??null,finalized_at:e.finalized_at??null,outcome_kind:e.outcome_kind??null,outcome_reason:e.outcome_reason??null,outcome_at:e.outcome_at??null,outcome_revision:e.outcome_revision??0,outcome_detail_json:e.outcome_detail_json??null,deadline_at:e.deadline_at??null,telemetry_tokens_in:e.telemetry_tokens_in??null,telemetry_context_tokens:e.telemetry_context_tokens??null,telemetry_last_activity:e.telemetry_last_activity??null,telemetry_updated_at:e.telemetry_updated_at??null,pi_pid:e.pi_pid??null,pi_pid_identity:e.pi_pid_identity??null,window:e.window??null,tmux_session:e.tmux_session??null,pane:e.pane??null,fault:null,streaming:!1},t=P(n.pi_pid),a=z(e);return n.fault=a?.link==="pi\u2192provider"&&a.retry.by==="daemon"&&a.retry.disposition==="auto"&&!t?null:a,n.streaming=e.turn_open_at!==null&&e.turn_closed_at===null&&t,n}i(f,"rowFrom");async function J(e,n){return n??=await Le(e.node_id),{...f(e),title:e.title??void 0,icon:e.icon??void 0,spawned_by:e.spawned_by??null,creator:e.creator??null,fork_from:e.fork_from??null,fork_source_file:e.fork_source_file??null,passive_default:e.passive_default===1,cycle_pending:e.cycle_pending===1,model_override:e.model_override??null,scopes:e.scopes_json==null?null:JSON.parse(e.scopes_json),persona_ack:e.persona_ack_json==null?void 0:JSON.parse(e.persona_ack_json),review_binding:e.review_binding_json==null?void 0:JSON.parse(e.review_binding_json),launch:e.launch_config_json==null?void 0:JSON.parse(e.launch_config_json),pi_session_id:e.pi_session_id??null,pi_session_file:e.pi_session_file??null,...n===null?{}:{managed_worktree:n}}}i(J,"metaFrom");async function gn(e,n={}){return Ae(),await p(async t=>{await Be(e,t),e.managed_worktree!==void 0&&e.managed_worktree!==null&&await Te(e.node_id,e.managed_worktree,t),n.outcomeDelivery!==void 0&&await M(t,e.node_id,n.outcomeDelivery),await n.withinCreate?.(t,e)}),await je(e.node_id),await h(e.node_id)}i(gn,"createNode");async function h(e){const n=await(await s()).prepare("SELECT * FROM node_rows WHERE node_id = ?").get(e);return n===void 0?null:await J(n)}i(h,"getNode");const ze=1440*60*1e3;function Y(e){return{requestKey:e.request_key,nodeId:e.node_id,state:e.state}}i(Y,"createRequestRecord");async function An(e){const n=await(await s()).prepareUncached("SELECT request_key, node_id, state FROM create_requests WHERE request_key = ?").get(e);return n===void 0?null:Y(n)}i(An,"findCreateRequest");async function kn(e){return await p(async n=>{const t=Date.now();await n.prepareUncached("DELETE FROM create_requests WHERE expires_at <= ?").run(t);const a=await n.prepareUncached("SELECT request_key, node_id, state FROM create_requests WHERE request_key = ?").get(e);return a!==void 0?Y(a):(await n.prepareUncached("INSERT INTO create_requests (request_key, node_id, expires_at, state) VALUES (?, NULL, ?, 'reserved')").run(e,t+ze),{requestKey:e,nodeId:null,state:"reserved"})})}i(kn,"reserveCreateRequest");async function xn(e=Date.now()){return Number((await(await s()).prepareUncached("DELETE FROM create_requests WHERE expires_at <= ?").run(e)).changes)}i(xn,"pruneExpiredCreateRequests");async function Un(e,n){await(await s()).prepareUncached("UPDATE create_requests SET node_id = ?, state = 'complete' WHERE request_key = ?").run(n,e)}i(Un,"recordCreateRequestNode");async function jn(e,n){await(await s()).prepareUncached("UPDATE create_requests SET node_id = ?, state = 'reserved' WHERE request_key = ?").run(n,e)}i(jn,"reserveCreateRequestNode");async function In(e,n){await(await s()).prepareUncached("UPDATE create_requests SET state = 'complete' WHERE request_key = ? AND node_id = ?").run(e,n)}i(In,"completeCreateRequest");async function Wn(e,n){await p(async t=>{if(n===null){await t.prepareUncached("DELETE FROM create_requests WHERE request_key = ? AND node_id IS NULL").run(e);return}await t.prepareUncached("DELETE FROM create_requests WHERE request_key = ? AND node_id = ?").run(e,n)})}i(Wn,"abandonCreateRequest");function bn(e){return _e(e)}i(bn,"canonicalSessionFile");async function Fn(e){const n=await Se(e);return n===null?null:await h(n)}i(Fn,"findNodeBySessionFile");async function Cn(e){const n=await(await s()).prepare("SELECT * FROM node_rows WHERE node_id = ?").get(e);return n?f(n):null}i(Cn,"getRow");async function Hn(e){const n=await(await s()).prepare("SELECT * FROM node_rows WHERE pane = ?").get(e);return n?f(n):null}i(Hn,"getRowByPane");async function Mn(e){return(await(await s()).prepare(`SELECT * FROM node_rows
1
+ var ie=Object.defineProperty;var i=(e,n)=>ie(e,"name",{value:n,configurable:!0});import{envNodeId as W}from"../../shared/env.js";import{lstatSync as oe,realpathSync as b,rmdirSync as re,rmSync as se,statSync as de}from"node:fs";import{basename as ue,dirname as F,isAbsolute as le,join as S,relative as ce,resolve as _e,sep as pe}from"node:path";import{removeTreeBeneath as C}from"../spaces/open-beneath.js";import{emitEvent as fe}from"../events/emit.js";import{withCanvasWrite as p,openDb as s}from"./db.js";import{conversationStore as Ee,ConversationNotFoundError as we}from"../conversation-store/index.js";import{hasUnseenWakeMail as ye,UNSEEN_WAKE_MAIL_SQL as me}from"./deliveries.js";import{isAppRun as H}from"./app-run.js";import{registerOutcomeAction as M}from"./outcome-actions.js";import{publish as Ne}from"../graph/events.js";import{nodeObject as Re}from"../graph/objects.js";import{findNodeBySessionFileSql as Se}from"./sessions.js";import{hydrateFault as Oe}from"./recovery.js";import{hasUnreconciledWorktree as he,openWorktree as Te,readWorktree as Le,worktreeFromRow as De}from"./worktrees.js";import{isPidAlive as ve,isPidPresent as P,recordedPidLiveness as Ae}from"./pid.js";import{ensureHome as ge,jobDir as ke,nodeDir as Ue,nodeRunDir as $}from"./paths.js";import{appForNode as xe,ensureNodeDirs as Ie,nodeAgentDir as je}from"./node-agent-paths.js";import{appSpace as T}from"../layout.js";import{createObject as We,getById as L,ownerForGrantee as be,setObjectKind as Fe,setObjectName as Ce}from"../graph/objects.js";import{profileObject as He}from"../graph/reader.js";import{writeActsFor as q}from"../permissions/facts.js";import{CrtrError as Me}from"../errors.js";import{deleteNodeWithOwned as Pe}from"../graph/deletion.js";import{SUBTREE_ALL_IDS_SQL as $e,SUBTREE_IDS_SQL as qe}from"./tree.js";import{filterAsync as m,mapAsync as D}from"../async-array.js";function E(e,n){if(e==null)return null;const t=JSON.stringify(e);if(t===void 0)throw new TypeError(`${n} must be JSON-serializable`);return t}i(E,"jsonValue");async function vn(e,n){if((await e.prepare("SELECT deleting_at FROM nodes WHERE node_id = ?").get(n))?.deleting_at)throw new Error("run is deleting")}i(vn,"assertRunNotDeleting");async function B(e,n,t){try{return await He(e,n,t)}catch(a){if(a instanceof Me&&a.code==="not_found")return null;throw a}}i(B,"profileObjectFor");async function Be(e,n){const t=await be(n,e.grantee??"app:terminal"),a=e.profile_id?await B(n,e.profile_id,t):null;await We(n,{id:e.node_id,type:"node",kind:e.kind,owner:(a??t).id,privacy:"public",name:e.name,preview:""}),a===null&&e.sub&&await q(n,e.node_id,e.sub),await n.prepare(`INSERT INTO nodes (node_id, description, title, icon, cycles, mode, lifecycle, cwd, host_kind, profile_id, parent, spawned_by, creator, fork_from, fork_source_file, created, passive_default, cycle_pending, model_override, persona_ack_json, review_binding_json, launch_config_json, scopes_json, isolated, status, intent, final_report, finalized_at, deadline_at, "window", tmux_session, pane, sub, grant_id, client_id, metadata_json, run_id, event_cursor, usage_json, deleting_at, caused_by, app) VALUES (${Array(42).fill("?").join(", ")})`).run(e.node_id,e.description??null,e.title??null,e.icon??null,e.cycles??null,e.mode,e.lifecycle,e.cwd,e.host_kind??null,e.profile_id??null,e.parent??null,e.spawned_by??null,e.creator??null,e.fork_from??null,e.fork_source_file??null,e.created,e.passive_default===!0?1:0,e.cycle_pending===!0?1:0,e.model_override??null,E(e.persona_ack,"persona_ack"),E(e.review_binding,"review_binding"),E(e.launch,"launch"),E(e.scopes,"scopes"),e.isolated===!0?1:0,e.status??"active",e.intent??null,e.final_report??null,e.finalized_at??null,e.deadline_at??null,e.window??null,e.tmux_session??null,e.pane??null,e.sub??null,e.grant_id??null,e.client_id??null,e.metadata_json??null,e.run_id??null,e.event_cursor??null,e.usage_json??null,e.deleting_at??null,e.caused_by??null,t.id)}i(Be,"seedRow");function z(e){if(e.fault_id==null)return null;const n=Object.entries(e).filter(([t])=>t.startsWith("fault_")).map(([t,a])=>[t==="fault_id"?t:t.slice(6),a]);return Oe(Object.fromEntries(n))}i(z,"faultFrom");function f(e){const n={node_id:e.node_id,name:e.name,description:e.description??void 0,cycles:e.cycles??void 0,kind:e.kind,mode:e.mode,lifecycle:e.lifecycle,status:e.status,cwd:e.cwd,host_kind:e.host_kind??null,profile_id:e.profile_id??null,scopes:e.scopes_json==null?null:JSON.parse(e.scopes_json),isolated:e.isolated===1,grantee:e.grantee,sub:e.sub??null,grant_id:e.grant_id??null,client_id:e.client_id??null,metadata_json:e.metadata_json??null,run_id:e.run_id??null,event_cursor:e.event_cursor??null,last_activity_at:e.last_activity_at??null,usage_json:e.usage_json??null,caused_by:e.caused_by??null,last_turn_error_json:e.last_turn_error_json??null,deleting_at:e.deleting_at??null,parent:e.parent??null,created:e.created,intent:e.intent??null,frozen_at:e.frozen_at??null,respawn_failures:e.respawn_failures??0,respawn_not_before:e.respawn_not_before??null,launched_at:e.launched_at??null,last_settled_at:e.last_settled_at??null,terminal_reason:e.terminal_reason??null,final_report:e.final_report??null,finalized_at:e.finalized_at??null,outcome_kind:e.outcome_kind??null,outcome_reason:e.outcome_reason??null,outcome_at:e.outcome_at??null,outcome_revision:e.outcome_revision??0,outcome_detail_json:e.outcome_detail_json??null,deadline_at:e.deadline_at??null,telemetry_tokens_in:e.telemetry_tokens_in??null,telemetry_context_tokens:e.telemetry_context_tokens??null,telemetry_last_activity:e.telemetry_last_activity??null,telemetry_updated_at:e.telemetry_updated_at??null,pi_pid:e.pi_pid??null,pi_pid_identity:e.pi_pid_identity??null,window:e.window??null,tmux_session:e.tmux_session??null,pane:e.pane??null,fault:null,streaming:!1},t=P(n.pi_pid),a=z(e);return n.fault=a?.link==="pi\u2192provider"&&a.retry.by==="daemon"&&a.retry.disposition==="auto"&&!t?null:a,n.streaming=e.turn_open_at!==null&&e.turn_closed_at===null&&t,n}i(f,"rowFrom");async function J(e,n){return n??=await Le(e.node_id),{...f(e),title:e.title??void 0,icon:e.icon??void 0,spawned_by:e.spawned_by??null,creator:e.creator??null,fork_from:e.fork_from??null,fork_source_file:e.fork_source_file??null,passive_default:e.passive_default===1,cycle_pending:e.cycle_pending===1,model_override:e.model_override??null,scopes:e.scopes_json==null?null:JSON.parse(e.scopes_json),persona_ack:e.persona_ack_json==null?void 0:JSON.parse(e.persona_ack_json),review_binding:e.review_binding_json==null?void 0:JSON.parse(e.review_binding_json),launch:e.launch_config_json==null?void 0:JSON.parse(e.launch_config_json),pi_session_id:e.pi_session_id??null,pi_session_file:e.pi_session_file??null,...n===null?{}:{managed_worktree:n}}}i(J,"metaFrom");async function An(e,n={}){return ge(),await p(async t=>{await Be(e,t),e.managed_worktree!==void 0&&e.managed_worktree!==null&&await Te(e.node_id,e.managed_worktree,t),n.outcomeDelivery!==void 0&&await M(t,e.node_id,n.outcomeDelivery),await n.withinCreate?.(t,e)}),await Ie(e.node_id),await O(e.node_id)}i(An,"createNode");async function O(e){const n=await(await s()).prepare("SELECT * FROM node_rows WHERE node_id = ?").get(e);return n===void 0?null:await J(n)}i(O,"getNode");const ze=1440*60*1e3;function Y(e){return{requestKey:e.request_key,nodeId:e.node_id,state:e.state}}i(Y,"createRequestRecord");async function gn(e){const n=await(await s()).prepareUncached("SELECT request_key, node_id, state FROM create_requests WHERE request_key = ?").get(e);return n===void 0?null:Y(n)}i(gn,"findCreateRequest");async function kn(e){return await p(async n=>{const t=Date.now();await n.prepareUncached("DELETE FROM create_requests WHERE expires_at <= ?").run(t),await n.prepareUncached("INSERT INTO create_requests (request_key, node_id, expires_at, state) VALUES (?, NULL, ?, 'reserved') ON CONFLICT (request_key) DO NOTHING").run(e,t+ze);const a=await n.prepareUncached("SELECT request_key, node_id, state FROM create_requests WHERE request_key = ?").get(e);return Y(a)})}i(kn,"reserveCreateRequest");async function Un(e=Date.now()){return Number((await(await s()).prepareUncached("DELETE FROM create_requests WHERE expires_at <= ?").run(e)).changes)}i(Un,"pruneExpiredCreateRequests");async function xn(e,n){await(await s()).prepareUncached("UPDATE create_requests SET node_id = ?, state = 'complete' WHERE request_key = ?").run(n,e)}i(xn,"recordCreateRequestNode");async function In(e,n){const t=await s();return await t.prepareUncached("UPDATE create_requests SET node_id = ?, state = 'reserved' WHERE request_key = ? AND node_id IS NULL").run(n,e),(await t.prepareUncached("SELECT node_id FROM create_requests WHERE request_key = ?").get(e))?.node_id??null}i(In,"reserveCreateRequestNode");async function jn(e,n){await(await s()).prepareUncached("UPDATE create_requests SET state = 'complete' WHERE request_key = ? AND node_id = ?").run(e,n)}i(jn,"completeCreateRequest");async function Wn(e,n){await p(async t=>{if(n===null){await t.prepareUncached("DELETE FROM create_requests WHERE request_key = ? AND node_id IS NULL").run(e);return}await t.prepareUncached("DELETE FROM create_requests WHERE request_key = ? AND node_id = ?").run(e,n)})}i(Wn,"abandonCreateRequest");function bn(e){return _e(e)}i(bn,"canonicalSessionFile");async function Fn(e){const n=await Se(e);return n===null?null:await O(n)}i(Fn,"findNodeBySessionFile");async function Cn(e){const n=await(await s()).prepare("SELECT * FROM node_rows WHERE node_id = ?").get(e);return n?f(n):null}i(Cn,"getRow");async function Hn(e){const n=await(await s()).prepare("SELECT * FROM node_rows WHERE pane = ?").get(e);return n?f(n):null}i(Hn,"getRowByPane");async function Mn(e){return(await(await s()).prepare(`SELECT * FROM node_rows
2
2
  WHERE deadline_at IS NOT NULL AND outcome_at IS NULL
3
3
  AND deadline_at <= ? AND status IN ('active','idle') AND kind != 'human'
4
4
  ORDER BY deadline_at, created, node_id`).all(e)).map(f)}i(Mn,"dueNodeDeadlines");async function Pn(e,n){return Number((await(await s()).prepare("UPDATE nodes SET deadline_at = ? WHERE node_id = ?").run(n,e)).changes)===1}i(Pn,"setNodeDeadline");async function $n(e,n){return!Number.isFinite(n)||n<=0?0:await p(async t=>{const a=await t.prepare(`SELECT node_id, deadline_at FROM nodes
5
- WHERE profile_id = ? AND deadline_at IS NOT NULL AND status IN ('active', 'idle')`).all(e),o=t.prepare("UPDATE nodes SET deadline_at = ? WHERE node_id = ? AND deadline_at = ?");let r=0;for(const d of a){const l=Date.parse(d.deadline_at);Number.isNaN(l)||(r+=Number((await o.run(new Date(l+n).toISOString(),d.node_id,d.deadline_at)).changes))}return r})}i($n,"shiftProfileNodeDeadlines");function v(e,n){if(e==null)return null;if(typeof e=="string"||typeof e=="number"||typeof e=="bigint"||e instanceof Uint8Array)return e;throw new TypeError(`${n} must be a SQLite scalar`)}i(v,"sqlInput");async function qn(e,n){const t=[],a=i((r,d=r)=>{Object.prototype.hasOwnProperty.call(n,r)&&t.push([d,v(n[r],r)])},"add");if(a("description"),a("title"),a("icon"),a("cycles"),a("mode"),a("lifecycle"),a("cwd"),a("host_kind"),a("profile_id"),a("parent"),a("spawned_by"),a("creator"),a("fork_from"),a("fork_source_file"),a("model_override"),Object.prototype.hasOwnProperty.call(n,"passive_default")&&t.push(["passive_default",n.passive_default===!0?1:0]),Object.prototype.hasOwnProperty.call(n,"cycle_pending")&&t.push(["cycle_pending",n.cycle_pending===!0?1:0]),Object.prototype.hasOwnProperty.call(n,"persona_ack")&&t.push(["persona_ack_json",E(n.persona_ack,"persona_ack")]),Object.prototype.hasOwnProperty.call(n,"review_binding")&&t.push(["review_binding_json",E(n.review_binding,"review_binding")]),Object.prototype.hasOwnProperty.call(n,"isolated"))throw new TypeError("isolated cannot change after node creation");Object.prototype.hasOwnProperty.call(n,"scopes")&&t.push(["scopes_json",E(n.scopes,"scopes")]),Object.prototype.hasOwnProperty.call(n,"launch")&&t.push(["launch_config_json",E(n.launch,"launch")]);const o=i(r=>Object.prototype.hasOwnProperty.call(n,r),"has");return await p(async r=>{const d=await L(r,e);if(d===null||d.type!=="node")throw new Error(`unknown node: ${e}`);if(o("name")&&await Ce(r,d,v(n.name,"name")),o("kind")&&await Fe(r,d,v(n.kind,"kind")),t.length===0)return;if(o("profile_id")){const u=await r.prepare("SELECT app FROM nodes WHERE node_id = ?").get(e),_=u?.app?await L(r,u.app):null;if(_){const w=n.profile_id?await B(r,n.profile_id,_):null;await r.prepare("UPDATE objects SET owner = ? WHERE id = ?").run((w??_).id,e);const R=(await r.prepare("SELECT sub FROM nodes WHERE node_id = ?").get(e)).sub;w===null&&R&&await q(r,e,R)}}if((await r.prepareUncached(`UPDATE nodes SET ${t.map(([u])=>`${u} = ?`).join(", ")} WHERE node_id = ?`).run(...t.map(([,u])=>u),e)).changes!==1)throw new Error(`unknown node: ${e}`)}),await h(e)}i(qn,"updateNode");async function Bn(e,n){await(await s()).prepare("UPDATE nodes SET status = ? WHERE node_id = ?").run(n,e)}i(Bn,"setStatus");async function zn(e,n){await(await s()).prepare("UPDATE nodes SET intent = ? WHERE node_id = ?").run(n??null,e)}i(zn,"setIntent");async function Jn(e,n,t={}){if(t.outcome===void 0){const o=[],r=[];Object.prototype.hasOwnProperty.call(n,"status")&&(o.push("status = ?"),r.push(n.status??null)),Object.prototype.hasOwnProperty.call(n,"intent")&&(o.push("intent = ?"),r.push(n.intent??null)),Object.prototype.hasOwnProperty.call(n,"terminal_reason")&&(o.push("terminal_reason = ?"),r.push(n.terminal_reason??null));const d=["node_id = ?"];r.push(e),t.fromStatuses!==void 0&&(d.push(`status IN (${t.fromStatuses.map(()=>"?").join(", ")})`),r.push(...t.fromStatuses)),t.requireNoFinalReport===!0&&d.push("final_report IS NULL");const l=await(await s()).prepareUncached(`UPDATE nodes SET ${o.join(", ")} WHERE ${d.join(" AND ")}`).run(...r);return Number(l.changes)}const a=t.outcome;return await p(async o=>{const r=[],d=[];Object.prototype.hasOwnProperty.call(n,"status")&&(r.push("status = ?"),d.push(n.status??null)),Object.prototype.hasOwnProperty.call(n,"intent")&&(r.push("intent = ?"),d.push(n.intent??null)),Object.prototype.hasOwnProperty.call(n,"terminal_reason")&&(r.push("terminal_reason = ?"),d.push(n.terminal_reason??null));const l=i((R,y)=>a.supersede===!0?"?":`CASE WHEN outcome_at IS NULL THEN ? ELSE ${y} END`,"outcomeValue");r.push(`outcome_kind = ${l(a.kind,"outcome_kind")}`,`outcome_reason = ${l(a.reason,"outcome_reason")}`,`outcome_detail_json = ${l(a.detailJson,"outcome_detail_json")}`,`outcome_revision = ${a.supersede===!0?"outcome_revision + 1":"CASE WHEN outcome_at IS NULL THEN outcome_revision + 1 ELSE outcome_revision END"}`,`outcome_at = ${l(a.at,"outcome_at")}`),d.push(a.kind,a.reason,a.detailJson,a.at);const u=["node_id = ?"];d.push(e),t.fromStatuses!==void 0&&(u.push(`status IN (${t.fromStatuses.map(()=>"?").join(", ")})`),d.push(...t.fromStatuses)),t.requireNoFinalReport===!0&&u.push("final_report IS NULL");const _=await o.prepareUncached(`UPDATE nodes SET ${r.join(", ")} WHERE ${u.join(" AND ")}`).run(...d);if(Number(_.changes)===0)return 0;const w=await o.prepare("SELECT outcome_at FROM nodes WHERE node_id = ?").get(e);return(a.supersede===!0||w.outcome_at===a.at)&&a.documentJson!==null&&a.documentJson!==void 0&&await Ne(o,{event:"outcome",object:await Re(o,e),actor:null,attrs:{revision:Number((await o.prepare("SELECT outcome_revision FROM nodes WHERE node_id = ?").get(e)).outcome_revision)},messageBody:JSON.parse(a.documentJson)}),Number(_.changes)})}i(Jn,"transitionStatusIntent");async function Yn(e){return(await(await s()).prepare("SELECT final_report FROM nodes WHERE node_id = ?").get(e))?.final_report??null}i(Yn,"getFinalReport");async function Qn(e,n){return(await(await s()).prepare("UPDATE nodes SET final_report = NULL, finalized_at = NULL WHERE node_id = ? AND final_report = ?").run(e,n)).changes>0}i(Qn,"clearFinalReportIfMatches");async function Vn(e,n){await(await s()).prepare('UPDATE nodes SET tmux_session = ?, "window" = ?, pane = ? WHERE node_id = ?').run(n.tmux_session??null,n.window??null,n.pane??null,e)}i(Vn,"setPresence");async function Kn(e,n){await(await s()).prepare("UPDATE nodes SET launched_at = ?, frozen_at = NULL, respawn_not_before = NULL WHERE node_id = ?").run(n,e)}i(Kn,"recordLaunch");async function Gn(e,n){await(await s()).prepare("UPDATE nodes SET last_settled_at = ? WHERE node_id = ?").run(n,e)}i(Gn,"recordSettledTurn");async function Xn(e,n){await(await s()).prepare("UPDATE nodes SET frozen_at = ? WHERE node_id = ?").run(n,e)}i(Xn,"setFrozen");async function Zn(e){await(await s()).prepare("UPDATE nodes SET frozen_at = NULL WHERE node_id = ?").run(e)}i(Zn,"clearFrozen");async function et(e,n){await(await s()).prepare("UPDATE nodes SET respawn_failures = ?, respawn_not_before = ? WHERE node_id = ?").run(n.failures,n.notBefore,e)}i(et,"recordRespawnAttempt");const N="node_id NOT IN (SELECT node_id FROM warm_pool)",Q=["state","cleanup_state","path","branch","repo_root","base_ref","base_sha","created_at","closed_at","next_attempt_at","attempts","quarantined","sweep_reason","sweep_detail","sweep_last_at","sweep_observed_json","abandoned_json","land_intent_json","disposal_json"];function Je(e){return e.worktree_state===null?null:De(Object.fromEntries(Q.map(n=>[n,e[`worktree_${n}`]])))}i(Je,"worktreeFromProjection");async function nt(e){const n=Q.map(a=>`w.${a} AS worktree_${a}`).join(", "),t=await(await s()).prepareUncached(`SELECT n.*, ${n} FROM node_rows n LEFT JOIN worktrees w ON w.node_id = n.node_id WHERE n.profile_id = ? ORDER BY n.created`).all(e);return await D(t,async a=>await J(a,Je(a)))}i(nt,"listNodesByProfile");async function tt(){return(await(await s()).prepare(`SELECT
5
+ WHERE profile_id = ? AND deadline_at IS NOT NULL AND status IN ('active', 'idle')`).all(e),o=t.prepare("UPDATE nodes SET deadline_at = ? WHERE node_id = ? AND deadline_at = ?");let r=0;for(const d of a){const l=Date.parse(d.deadline_at);Number.isNaN(l)||(r+=Number((await o.run(new Date(l+n).toISOString(),d.node_id,d.deadline_at)).changes))}return r})}i($n,"shiftProfileNodeDeadlines");function v(e,n){if(e==null)return null;if(typeof e=="string"||typeof e=="number"||typeof e=="bigint"||e instanceof Uint8Array)return e;throw new TypeError(`${n} must be a SQLite scalar`)}i(v,"sqlInput");async function qn(e,n){const t=[],a=i((r,d=r)=>{Object.prototype.hasOwnProperty.call(n,r)&&t.push([d,v(n[r],r)])},"add");if(a("description"),a("title"),a("icon"),a("cycles"),a("mode"),a("lifecycle"),a("cwd"),a("host_kind"),a("profile_id"),a("parent"),a("spawned_by"),a("creator"),a("fork_from"),a("fork_source_file"),a("model_override"),Object.prototype.hasOwnProperty.call(n,"passive_default")&&t.push(["passive_default",n.passive_default===!0?1:0]),Object.prototype.hasOwnProperty.call(n,"cycle_pending")&&t.push(["cycle_pending",n.cycle_pending===!0?1:0]),Object.prototype.hasOwnProperty.call(n,"persona_ack")&&t.push(["persona_ack_json",E(n.persona_ack,"persona_ack")]),Object.prototype.hasOwnProperty.call(n,"review_binding")&&t.push(["review_binding_json",E(n.review_binding,"review_binding")]),Object.prototype.hasOwnProperty.call(n,"isolated"))throw new TypeError("isolated cannot change after node creation");Object.prototype.hasOwnProperty.call(n,"scopes")&&t.push(["scopes_json",E(n.scopes,"scopes")]),Object.prototype.hasOwnProperty.call(n,"launch")&&t.push(["launch_config_json",E(n.launch,"launch")]);const o=i(r=>Object.prototype.hasOwnProperty.call(n,r),"has");return await p(async r=>{const d=await L(r,e);if(d===null||d.type!=="node")throw new Error(`unknown node: ${e}`);if(o("name")&&await Ce(r,d,v(n.name,"name")),o("kind")&&await Fe(r,d,v(n.kind,"kind")),t.length===0)return;if(o("profile_id")){const u=await r.prepare("SELECT app FROM nodes WHERE node_id = ?").get(e),_=u?.app?await L(r,u.app):null;if(_){const w=n.profile_id?await B(r,n.profile_id,_):null;await r.prepare("UPDATE objects SET owner = ? WHERE id = ?").run((w??_).id,e);const R=(await r.prepare("SELECT sub FROM nodes WHERE node_id = ?").get(e)).sub;w===null&&R&&await q(r,e,R)}}if((await r.prepareUncached(`UPDATE nodes SET ${t.map(([u])=>`${u} = ?`).join(", ")} WHERE node_id = ?`).run(...t.map(([,u])=>u),e)).changes!==1)throw new Error(`unknown node: ${e}`)}),await O(e)}i(qn,"updateNode");async function Bn(e,n){await(await s()).prepare("UPDATE nodes SET status = ? WHERE node_id = ?").run(n,e)}i(Bn,"setStatus");async function zn(e,n){await(await s()).prepare("UPDATE nodes SET intent = ? WHERE node_id = ?").run(n??null,e)}i(zn,"setIntent");async function Jn(e,n,t={}){if(t.outcome===void 0){const o=[],r=[];Object.prototype.hasOwnProperty.call(n,"status")&&(o.push("status = ?"),r.push(n.status??null)),Object.prototype.hasOwnProperty.call(n,"intent")&&(o.push("intent = ?"),r.push(n.intent??null)),Object.prototype.hasOwnProperty.call(n,"terminal_reason")&&(o.push("terminal_reason = ?"),r.push(n.terminal_reason??null));const d=["node_id = ?"];r.push(e),t.fromStatuses!==void 0&&(d.push(`status IN (${t.fromStatuses.map(()=>"?").join(", ")})`),r.push(...t.fromStatuses)),t.requireNoFinalReport===!0&&d.push("final_report IS NULL");const l=await(await s()).prepareUncached(`UPDATE nodes SET ${o.join(", ")} WHERE ${d.join(" AND ")}`).run(...r);return Number(l.changes)}const a=t.outcome;return await p(async o=>{const r=[],d=[];Object.prototype.hasOwnProperty.call(n,"status")&&(r.push("status = ?"),d.push(n.status??null)),Object.prototype.hasOwnProperty.call(n,"intent")&&(r.push("intent = ?"),d.push(n.intent??null)),Object.prototype.hasOwnProperty.call(n,"terminal_reason")&&(r.push("terminal_reason = ?"),d.push(n.terminal_reason??null));const l=i((R,y)=>a.supersede===!0?"?":`CASE WHEN outcome_at IS NULL THEN ? ELSE ${y} END`,"outcomeValue");r.push(`outcome_kind = ${l(a.kind,"outcome_kind")}`,`outcome_reason = ${l(a.reason,"outcome_reason")}`,`outcome_detail_json = ${l(a.detailJson,"outcome_detail_json")}`,`outcome_revision = ${a.supersede===!0?"outcome_revision + 1":"CASE WHEN outcome_at IS NULL THEN outcome_revision + 1 ELSE outcome_revision END"}`,`outcome_at = ${l(a.at,"outcome_at")}`),d.push(a.kind,a.reason,a.detailJson,a.at);const u=["node_id = ?"];d.push(e),t.fromStatuses!==void 0&&(u.push(`status IN (${t.fromStatuses.map(()=>"?").join(", ")})`),d.push(...t.fromStatuses)),t.requireNoFinalReport===!0&&u.push("final_report IS NULL");const _=await o.prepareUncached(`UPDATE nodes SET ${r.join(", ")} WHERE ${u.join(" AND ")}`).run(...d);if(Number(_.changes)===0)return 0;const w=await o.prepare("SELECT outcome_at FROM nodes WHERE node_id = ?").get(e);return(a.supersede===!0||w.outcome_at===a.at)&&a.documentJson!==null&&a.documentJson!==void 0&&await Ne(o,{event:"outcome",object:await Re(o,e),actor:null,attrs:{revision:Number((await o.prepare("SELECT outcome_revision FROM nodes WHERE node_id = ?").get(e)).outcome_revision)},messageBody:JSON.parse(a.documentJson)}),Number(_.changes)})}i(Jn,"transitionStatusIntent");async function Yn(e){return(await(await s()).prepare("SELECT final_report FROM nodes WHERE node_id = ?").get(e))?.final_report??null}i(Yn,"getFinalReport");async function Qn(e,n){return(await(await s()).prepare("UPDATE nodes SET final_report = NULL, finalized_at = NULL WHERE node_id = ? AND final_report = ?").run(e,n)).changes>0}i(Qn,"clearFinalReportIfMatches");async function Vn(e,n){await(await s()).prepare('UPDATE nodes SET tmux_session = ?, "window" = ?, pane = ? WHERE node_id = ?').run(n.tmux_session??null,n.window??null,n.pane??null,e)}i(Vn,"setPresence");async function Kn(e,n){await(await s()).prepare("UPDATE nodes SET launched_at = ?, frozen_at = NULL, respawn_not_before = NULL WHERE node_id = ?").run(n,e)}i(Kn,"recordLaunch");async function Gn(e,n){await(await s()).prepare("UPDATE nodes SET last_settled_at = ? WHERE node_id = ?").run(n,e)}i(Gn,"recordSettledTurn");async function Xn(e,n){await(await s()).prepare("UPDATE nodes SET frozen_at = ? WHERE node_id = ?").run(n,e)}i(Xn,"setFrozen");async function Zn(e){await(await s()).prepare("UPDATE nodes SET frozen_at = NULL WHERE node_id = ?").run(e)}i(Zn,"clearFrozen");async function et(e,n){await(await s()).prepare("UPDATE nodes SET respawn_failures = ?, respawn_not_before = ? WHERE node_id = ?").run(n.failures,n.notBefore,e)}i(et,"recordRespawnAttempt");const N="node_id NOT IN (SELECT node_id FROM warm_pool)",Q=["state","cleanup_state","path","branch","repo_root","base_ref","base_sha","created_at","closed_at","next_attempt_at","attempts","quarantined","sweep_reason","sweep_detail","sweep_last_at","sweep_observed_json","abandoned_json","land_intent_json","disposal_json"];function Je(e){return e.worktree_state===null?null:De(Object.fromEntries(Q.map(n=>[n,e[`worktree_${n}`]])))}i(Je,"worktreeFromProjection");async function nt(e){const n=Q.map(a=>`w.${a} AS worktree_${a}`).join(", "),t=await(await s()).prepareUncached(`SELECT n.*, ${n} FROM node_rows n LEFT JOIN worktrees w ON w.node_id = n.node_id WHERE n.profile_id = ? ORDER BY n.created`).all(e);return await D(t,async a=>await J(a,Je(a)))}i(nt,"listNodesByProfile");async function tt(){return(await(await s()).prepare(`SELECT
6
6
  node_id, name, description, kind, mode, lifecycle, status, cwd, profile_id,
7
7
  grantee, parent, created, frozen_at, terminal_reason, final_report,
8
8
  telemetry_tokens_in, telemetry_updated_at, pi_pid, turn_open_at, turn_closed_at,
@@ -48,20 +48,20 @@ var ie=Object.defineProperty;var i=(e,n)=>ie(e,"name",{value:n,configurable:!0})
48
48
  ORDER BY v.created`;async function rt(){return(await(await s()).prepare(Ve).all()).map(f)}i(rt,"listNodesForSupervision");const V=i(e=>`(${e}.status = 'done' AND ${e}.intent = 'parked' AND ${e}.lifecycle = 'resident' AND ${e}.final_report IS NULL AND ${e}.deleting_at IS NULL)`,"PARKED_RESIDENT_SQL"),Ke=i(e=>`(${e}.status IN ('active', 'idle') OR ${V(e)} OR (${e}.status IN ('done', 'canceled', 'dead') AND ${e}.frozen_at IS NOT NULL AND ${e}.final_report IS NULL))`,"RESUMABLE_NODE_SQL");async function st(e){return await(await s()).prepare(`SELECT node_id, cwd FROM nodes WHERE ${Ke("nodes")} AND node_id != ?`).all(e)}i(st,"resumableNodeCwds");async function dt(){return(await(await s()).prepare(`SELECT v.* FROM node_rows v
49
49
  WHERE ${V("v")}
50
50
  AND EXISTS (SELECT 1 FROM deliveries d WHERE d.destination_id = v.node_id AND ${me("d")}) AND v.kind != 'human' AND v.node_id NOT IN (SELECT node_id FROM warm_pool)
51
- ORDER BY v.created`).all()).map(f)}i(dt,"listParkedResidentsWithWakeMail");async function ut(e,n){await(await s()).prepare("INSERT INTO warm_pool (node_id, recipe_key, created) VALUES (?, ?, ?) ON CONFLICT (node_id) DO UPDATE SET recipe_key = excluded.recipe_key, created = excluded.created").run(e,n,new Date().toISOString())}i(ut,"registerWarmSpare");async function lt(e){return(await(await s()).prepare("DELETE FROM warm_pool WHERE node_id = ?").run(e)).changes>0}i(lt,"unregisterWarmSpare");async function g(e,n){const t=`SELECT w.node_id AS node_id, w.recipe_key AS recipe_key, w.created AS created,
51
+ ORDER BY v.created`).all()).map(f)}i(dt,"listParkedResidentsWithWakeMail");async function ut(e,n){await(await s()).prepare("INSERT INTO warm_pool (node_id, recipe_key, created) VALUES (?, ?, ?) ON CONFLICT (node_id) DO UPDATE SET recipe_key = excluded.recipe_key, created = excluded.created").run(e,n,new Date().toISOString())}i(ut,"registerWarmSpare");async function lt(e){return(await(await s()).prepare("DELETE FROM warm_pool WHERE node_id = ?").run(e)).changes>0}i(lt,"unregisterWarmSpare");async function A(e,n){const t=`SELECT w.node_id AS node_id, w.recipe_key AS recipe_key, w.created AS created,
52
52
  n.status AS status, n.pi_pid AS pi_pid, n.pi_pid_identity AS pi_pid_identity
53
- FROM warm_pool w JOIN node_rows n ON n.node_id = w.node_id`+(n===void 0?"":" WHERE w.recipe_key = ?")+" ORDER BY w.created";return(n===void 0?await e.prepareUncached(t).all():await e.prepareUncached(t).all(n)).map(o=>({node_id:o.node_id,recipe_key:o.recipe_key,created:o.created,status:o.status,pi_pid:o.pi_pid??null,pi_pid_identity:o.pi_pid_identity??null}))}i(g,"warmSpareRowsFor");async function ct(){return await g(await s())}i(ct,"listWarmSpares");async function _t(e){return await(await s()).prepare("SELECT 1 FROM warm_pool WHERE node_id = ?").get(e)!==void 0}i(_t,"isWarmSpare");function K(e){return e.status!=="active"&&e.status!=="idle"?!1:ge(e.pi_pid,e.pi_pid_identity)==="alive"}i(K,"spareIsClaimable");async function pt(e,n,t={}){return await p(async a=>{const o=(await g(a,e)).find(K);return o===void 0||t.requestKey!==void 0&&(await a.prepareUncached("UPDATE create_requests SET node_id = ?, state = 'warm_claimed' WHERE request_key = ? AND node_id IS NULL").run(o.node_id,t.requestKey)).changes===0||(await a.prepare("DELETE FROM warm_pool WHERE node_id = ?").run(o.node_id)).changes===0?null:(await a.prepare("UPDATE nodes SET created = ?, deadline_at = ? WHERE node_id = ?").run(n,t.deadlineAt??null,o.node_id),t.outcomeDelivery!==void 0&&await M(a,o.node_id,t.outcomeDelivery),o.node_id)})}i(pt,"claimWarmSpare");async function ft(e){return(await g(await s(),e)).filter(K).length}i(ft,"warmSpareCount");async function Et(e){return(await(await s()).prepare("SELECT node_id FROM nodes WHERE parent = ? ORDER BY created").all(e)).map(n=>n.node_id)}i(Et,"childrenOf");async function wt(){return(await(await s()).prepareUncached(`SELECT node_id, name, kind, mode, lifecycle, status, cwd, host_kind, parent, created, description
53
+ FROM warm_pool w JOIN node_rows n ON n.node_id = w.node_id`+(n===void 0?"":" WHERE w.recipe_key = ?")+" ORDER BY w.created";return(n===void 0?await e.prepareUncached(t).all():await e.prepareUncached(t).all(n)).map(o=>({node_id:o.node_id,recipe_key:o.recipe_key,created:o.created,status:o.status,pi_pid:o.pi_pid??null,pi_pid_identity:o.pi_pid_identity??null}))}i(A,"warmSpareRowsFor");async function ct(){return await A(await s())}i(ct,"listWarmSpares");async function _t(e){return await(await s()).prepare("SELECT 1 FROM warm_pool WHERE node_id = ?").get(e)!==void 0}i(_t,"isWarmSpare");function K(e){return e.status!=="active"&&e.status!=="idle"?!1:Ae(e.pi_pid,e.pi_pid_identity)==="alive"}i(K,"spareIsClaimable");async function pt(e,n,t={}){return await p(async a=>{const o=(await A(a,e)).find(K);return o===void 0||t.requestKey!==void 0&&(await a.prepareUncached("UPDATE create_requests SET node_id = ?, state = 'warm_claimed' WHERE request_key = ? AND node_id IS NULL").run(o.node_id,t.requestKey)).changes===0||(await a.prepare("DELETE FROM warm_pool WHERE node_id = ?").run(o.node_id)).changes===0?null:(await a.prepare("UPDATE nodes SET created = ?, deadline_at = ? WHERE node_id = ?").run(n,t.deadlineAt??null,o.node_id),t.outcomeDelivery!==void 0&&await M(a,o.node_id,t.outcomeDelivery),o.node_id)})}i(pt,"claimWarmSpare");async function ft(e){return(await A(await s(),e)).filter(K).length}i(ft,"warmSpareCount");async function Et(e){return(await(await s()).prepare("SELECT node_id FROM nodes WHERE parent = ? ORDER BY created").all(e)).map(n=>n.node_id)}i(Et,"childrenOf");async function wt(){return(await(await s()).prepareUncached(`SELECT node_id, name, kind, mode, lifecycle, status, cwd, host_kind, parent, created, description
54
54
  FROM node_rows WHERE ${N} ORDER BY created`).all()).map(e=>({node_id:e.node_id,name:e.name,kind:e.kind,mode:e.mode,lifecycle:e.lifecycle,status:e.status,cwd:e.cwd,host_kind:e.host_kind??null,parent:e.parent??null,created:e.created,description:e.description??null}))}i(wt,"rosterNodes");async function yt(e){return(await(await s()).prepareUncached(`SELECT * FROM node_rows
55
55
  WHERE parent IS NULL AND kind != 'human'
56
56
  AND status IN ('active','idle') AND cwd = ?
57
57
  AND ${N}
58
- ORDER BY created DESC`).all(e)).map(f)}i(yt,"listResumableRoots");export*from"./tree.js";async function mt(e,n,t){return await p(async a=>{const o=await a.prepare("SELECT status, final_report, frozen_at FROM nodes WHERE node_id = ?").get(e);return o===void 0?{kind:"missing"}:n(o)?{kind:"terminal",status:o.status,final_report:o.final_report}:{kind:"ran",result:await t()}})}i(mt,"withFreshTerminalGuard");async function O(e){return await Oe(e)}i(O,"hasUnreconciledManagedWorktree");async function G(e,n={}){return!["done","dead","canceled"].includes(e.status)||e.pi_pid!==null||e.frozen_at!==null||await O(e.node_id)||e.node_id===W()||H(e)?!1:n.pastTtl===!0||!["crashed","stranded",null].includes(e.terminal_reason??null)}i(G,"isCollectible");async function A(e){if(e.length===0)return[];const n=e.map(()=>"?").join(","),t=await(await s()).prepareUncached(`SELECT s.session_id, s.transcript_path, s.node_id, n.grantee FROM sessions s JOIN node_rows n ON n.node_id = s.node_id WHERE s.node_id IN (${n}) AND s.session_id IS NOT NULL AND s.transcript_path IS NOT NULL ORDER BY s.created_at, s.seq`).all(...e);return[...new Map(t.map(a=>[a.session_id,{id:a.session_id,path:a.transcript_path,dir:S(T(a.grantee).conversations,a.node_id)}])).values()]}i(A,"nodeConversationIds");async function k(e){const n=await s(),t=n.prepare("SELECT 1 FROM sessions WHERE session_id = ? LIMIT 1"),a=n.prepare(`SELECT 1 FROM nodes n WHERE n.fork_source_file LIKE '%\\_' || ? || '.jsonl' ESCAPE '\\'
59
- AND NOT EXISTS (SELECT 1 FROM sessions s WHERE s.node_id = n.node_id) LIMIT 1`);for(const{id:o,path:r,dir:d}of e)if(!(await t.get(o)!==void 0||await a.get(o)!==void 0)&&oe(r,{throwIfNoEntry:!1})!==void 0){if(!Ge(d,r)){console.error(`[canvas] not deleting transcript outside its node's conversations directory: ${r}`);continue}try{await Ee().delete(o,r)}catch(l){if(!(l instanceof we))throw l}}}i(k,"deleteNodeConversations");function Ge(e,n){try{const t=ce(b(e),S(b(F(n)),ue(n)));return t!==""&&t!==".."&&!t.startsWith(`..${pe}`)&&!le(t)}catch(t){if(t.code==="ENOENT")return!1;throw t}}i(Ge,"isBeneath");async function x(e,n){const t=await L(e,n);return t===null||t.deleted_at!==null?!1:(await Pe(e,t),!0)}i(x,"deleteNodeObject");async function U(e){return{nodeId:e,app:await h(e)===null?null:await Ue(e)}}i(U,"nodeFiles");async function X({nodeId:e,app:n}){n!==null&&C(T(n).nodes,await Ie(e,n)),C(F($(e)),$(e))}i(X,"removeNodeAppTrees");function Z({nodeId:e}){return[xe(e)]}i(Z,"nodeOwnedDirs");function ee({nodeId:e,app:n}){if(n!==null)try{re(S(T(n).conversations,e))}catch(t){if(!["ENOENT","ENOTEMPTY"].includes(t.code??""))throw t}}i(ee,"removeNodeConversationDir");async function j(e){await X(e);for(const n of Z(e))se(n,{recursive:!0,force:!0});ee(e)}i(j,"removeNodeFiles");async function Nt(e){if(await O(e))return{deleted:!1};const n=await U(e),t=await A([e]),a=await p(async o=>(await x(o,e),{deleted:!0}));return await k(t),await j(n),a}i(Nt,"deleteNode");async function Rt(e){const n=[...new Set(e)];if(n.length===0)return[];const t=await m(n,O);if(t.length>0)throw new Error(`refusing to delete nodes with unreconciled managed worktrees: ${t.join(", ")}`);const a=await D(n,U),o=await A(n),r=n.map(()=>"?").join(","),d=(await(await s()).prepareUncached(`SELECT node_id FROM nodes WHERE node_id IN (${r}) ORDER BY created`).all(...n)).map(({node_id:u})=>u),l=await m(d,async u=>await p(async _=>await x(_,u)));await k(o);for(const u of l){const _=a[n.indexOf(u)];await I(_,async()=>await j(_))}return l}i(Rt,"deleteNodes");async function St(e){const n=e.dryRun??!1,t=e.includeStale??!1,a=Date.now()-e.ttlDays*864e5,o=new Date(a).toISOString(),r=W()??"",d=await s(),l=i(c=>ae(c)<a,"quiet"),u=i(c=>({node_id:c.node_id,status:c.status,created:c.created}),"toPruned"),_=await Xe(),w=(await m((await d.prepare(`SELECT * FROM node_rows
58
+ ORDER BY created DESC`).all(e)).map(f)}i(yt,"listResumableRoots");export*from"./tree.js";async function mt(e,n,t){return await p(async a=>{const o=await a.prepare("SELECT status, final_report, frozen_at FROM nodes WHERE node_id = ? FOR UPDATE").get(e);return o===void 0?{kind:"missing"}:n(o)?{kind:"terminal",status:o.status,final_report:o.final_report}:{kind:"ran",result:await t()}})}i(mt,"withFreshTerminalGuard");async function h(e){return await he(e)}i(h,"hasUnreconciledManagedWorktree");async function G(e,n={}){return!["done","dead","canceled"].includes(e.status)||e.pi_pid!==null||e.frozen_at!==null||await h(e.node_id)||e.node_id===W()||H(e)?!1:n.pastTtl===!0||!["crashed","stranded",null].includes(e.terminal_reason??null)}i(G,"isCollectible");async function g(e){if(e.length===0)return[];const n=e.map(()=>"?").join(","),t=await(await s()).prepareUncached(`SELECT s.session_id, s.transcript_path, s.node_id, n.grantee FROM sessions s JOIN node_rows n ON n.node_id = s.node_id WHERE s.node_id IN (${n}) AND s.session_id IS NOT NULL AND s.transcript_path IS NOT NULL ORDER BY s.created_at, s.seq`).all(...e);return[...new Map(t.map(a=>[a.session_id,{id:a.session_id,path:a.transcript_path,dir:S(T(a.grantee).conversations,a.node_id)}])).values()]}i(g,"nodeConversationIds");async function k(e){const n=await s(),t=n.prepare("SELECT 1 FROM sessions WHERE session_id = ? LIMIT 1"),a=n.prepare(`SELECT 1 FROM nodes n WHERE n.fork_source_file LIKE '%\\_' || ? || '.jsonl' ESCAPE '\\'
59
+ AND NOT EXISTS (SELECT 1 FROM sessions s WHERE s.node_id = n.node_id) LIMIT 1`);for(const{id:o,path:r,dir:d}of e)if(!(await t.get(o)!==void 0||await a.get(o)!==void 0)&&oe(r,{throwIfNoEntry:!1})!==void 0){if(!Ge(d,r)){console.error(`[canvas] not deleting transcript outside its node's conversations directory: ${r}`);continue}try{await Ee().delete(o,r)}catch(l){if(!(l instanceof we))throw l}}}i(k,"deleteNodeConversations");function Ge(e,n){try{const t=ce(b(e),S(b(F(n)),ue(n)));return t!==""&&t!==".."&&!t.startsWith(`..${pe}`)&&!le(t)}catch(t){if(t.code==="ENOENT")return!1;throw t}}i(Ge,"isBeneath");async function U(e,n){const t=await L(e,n);return t===null||t.deleted_at!==null?!1:(await Pe(e,t),!0)}i(U,"deleteNodeObject");async function x(e){return{nodeId:e,app:await O(e)===null?null:await xe(e)}}i(x,"nodeFiles");async function X({nodeId:e,app:n}){n!==null&&C(T(n).nodes,await je(e,n)),C(F($(e)),$(e))}i(X,"removeNodeAppTrees");function Z({nodeId:e}){return[Ue(e)]}i(Z,"nodeOwnedDirs");function ee({nodeId:e,app:n}){if(n!==null)try{re(S(T(n).conversations,e))}catch(t){if(!["ENOENT","ENOTEMPTY"].includes(t.code??""))throw t}}i(ee,"removeNodeConversationDir");async function I(e){await X(e);for(const n of Z(e))se(n,{recursive:!0,force:!0});ee(e)}i(I,"removeNodeFiles");async function Nt(e){if(await h(e))return{deleted:!1};const n=await x(e),t=await g([e]),a=await p(async o=>(await U(o,e),{deleted:!0}));return await k(t),await I(n),a}i(Nt,"deleteNode");async function Rt(e){const n=[...new Set(e)];if(n.length===0)return[];const t=await m(n,h);if(t.length>0)throw new Error(`refusing to delete nodes with unreconciled managed worktrees: ${t.join(", ")}`);const a=await D(n,x),o=await g(n),r=n.map(()=>"?").join(","),d=(await(await s()).prepareUncached(`SELECT node_id FROM nodes WHERE node_id IN (${r}) ORDER BY created`).all(...n)).map(({node_id:u})=>u),l=await m(d,async u=>await p(async _=>await U(_,u)));await k(o);for(const u of l){const _=a[n.indexOf(u)];await j(_,async()=>await I(_))}return l}i(Rt,"deleteNodes");async function St(e){const n=e.dryRun??!1,t=e.includeStale??!1,a=Date.now()-e.ttlDays*864e5,o=new Date(a).toISOString(),r=W()??"",d=await s(),l=i(c=>ae(c)<a,"quiet"),u=i(c=>({node_id:c.node_id,status:c.status,created:c.created}),"toPruned"),_=await Xe(),w=(await m((await d.prepare(`SELECT * FROM node_rows
60
60
  WHERE status IN ('dead', 'done', 'canceled') AND created < ?
61
61
  ORDER BY created`).all(o)).map(f),async c=>await G(c,{pastTtl:!0})&&l(c)&&!_.has(c.node_id)&&!await e.held(c.node_id))).map(u),R=t?(await m((await d.prepare(`SELECT * FROM node_rows
62
62
  WHERE status IN ('active', 'idle') AND created < ?
63
- ORDER BY created`).all(o)).map(f),async c=>c.node_id!==r&&!ve(c.pi_pid??null)&&(c.frozen_at??null)===null&&(c.respawn_not_before??null)===null&&c.intent!=="idle-release"&&!H(c)&&!await ye(c.node_id)&&!await O(c.node_id)&&l(c)&&!await e.held(c.node_id))).map(u):[],y=await ne([...w,...R]);return n||y.length===0?{pruned:y,dryRun:n}:(await te(y,e.removeDirs),{pruned:y,dryRun:n})}i(St,"pruneNodes");async function Xe(){const e=await(await s()).prepare(`SELECT DISTINCT d.owner AS owner FROM watches w
63
+ ORDER BY created`).all(o)).map(f),async c=>c.node_id!==r&&!ve(c.pi_pid??null)&&(c.frozen_at??null)===null&&(c.respawn_not_before??null)===null&&c.intent!=="idle-release"&&!H(c)&&!await ye(c.node_id)&&!await h(c.node_id)&&l(c)&&!await e.held(c.node_id))).map(u):[],y=await ne([...w,...R]);return n||y.length===0?{pruned:y,dryRun:n}:(await te(y,e.removeDirs),{pruned:y,dryRun:n})}i(St,"pruneNodes");async function Xe(){const e=await(await s()).prepare(`SELECT DISTINCT d.owner AS owner FROM watches w
64
64
  JOIN objects d ON d.id = w.object AND d.type = 'document' AND d.deleted_at IS NULL
65
65
  JOIN objects o ON o.id = w.watcher AND o.deleted_at IS NULL
66
66
  LEFT JOIN nodes n ON n.node_id = w.watcher
67
- WHERE w.watcher != d.owner AND d.owner IS NOT NULL AND (n.node_id IS NULL OR n.status IN ('active', 'idle'))`).all();return new Set(e.map(n=>n.owner))}i(Xe,"ownersOfWatchedDocuments");async function ne(e){const t=(await s()).prepare("SELECT node_id FROM nodes WHERE run_id = ? AND node_id != ?"),a=new Set(e.map(o=>o.node_id));return await m(e,async o=>(await t.all(o.node_id,o.node_id)).every(r=>a.has(r.node_id)))}i(ne,"retainRunRootsWithMembers");async function te(e,n){if(e.length===0)return;const t=await D(e,async o=>await U(o.node_id)),a=await A(e.map(o=>o.node_id));for(const o of e)await p(async r=>await x(r,o.node_id));if(await k(a),n!==void 0){for(const o of t)await I(o,async()=>{await X(o),ee(o)});n(t.flatMap(Z));return}for(const o of t)await I(o,async()=>await j(o))}i(te,"deletePrunedNodes");async function I(e,n){try{n()}catch(t){fe({level:"error",event:"canvas.node_files.remove_failed",error:t,fields:{node_id:e.nodeId}})}}i(I,"eachNodeBestEffort");function ae(e){let n=0;for(const t of[e.created,e.launched_at,e.last_settled_at,e.finalized_at,e.outcome_at,e.last_activity_at]){if(t==null)continue;const a=Date.parse(t);Number.isFinite(a)&&a>n&&(n=a)}try{const t=de(S(ke(e.node_id),"telemetry.json"),{throwIfNoEntry:!1});t!==void 0&&t.mtimeMs>n&&(n=t.mtimeMs)}catch{}return n}i(ae,"lastActivityMs");async function ht(e){const n=e.dryRun??!1,t=await Ye(),a=await m(t,async u=>await G(u)&&!await e.held(u.node_id)),o=t.length-a.length,r=Math.max(0,e.maxNodes-o),d=a.map(u=>({row:u,recency:ae(u)})).sort((u,_)=>_.recency-u.recency).slice(r).map(({row:u})=>({node_id:u.node_id,status:u.status,created:u.created})),l=await ne(d);return n||l.length===0?{pruned:l,dryRun:n}:(await te(l),{pruned:l,dryRun:n})}i(ht,"pruneToLimit");export{V as PARKED_RESIDENT_SQL,Ke as RESUMABLE_NODE_SQL,Wn as abandonCreateRequest,vn as assertRunNotDeleting,tt as browseNodes,bn as canonicalSessionFile,Et as childrenOf,pt as claimWarmSpare,Qn as clearFinalReportIfMatches,Zn as clearFrozen,In as completeCreateRequest,gn as createNode,Nt as deleteNode,Rt as deleteNodes,Mn as dueNodeDeadlines,An as findCreateRequest,Fn as findNodeBySessionFile,Yn as getFinalReport,h as getNode,Cn as getRow,Hn as getRowByPane,it as graphNodes,G as isCollectible,_t as isWarmSpare,ae as lastActivityMs,Ye as listNodes,nt as listNodesByProfile,rt as listNodesForSupervision,dt as listParkedResidentsWithWakeMail,yt as listResumableRoots,ot as listTerminalPlacementResidue,ct as listWarmSpares,U as nodeFiles,Ve as nodesForSupervisionSql,xn as pruneExpiredCreateRequests,St as pruneNodes,ht as pruneToLimit,Un as recordCreateRequestNode,Kn as recordLaunch,et as recordRespawnAttempt,Gn as recordSettledTurn,ut as registerWarmSpare,j as removeNodeFiles,kn as reserveCreateRequest,jn as reserveCreateRequestNode,st as resumableNodeCwds,wt as rosterNodes,Xn as setFrozen,zn as setIntent,Pn as setNodeDeadline,Vn as setPresence,Bn as setStatus,$n as shiftProfileNodeDeadlines,Qe as terminalPlacementResidueSql,Jn as transitionStatusIntent,lt as unregisterWarmSpare,qn as updateNode,at as updateNodeTelemetry,ft as warmSpareCount,mt as withFreshTerminalGuard};
67
+ WHERE w.watcher != d.owner AND d.owner IS NOT NULL AND (n.node_id IS NULL OR n.status IN ('active', 'idle'))`).all();return new Set(e.map(n=>n.owner))}i(Xe,"ownersOfWatchedDocuments");async function ne(e){const t=(await s()).prepare("SELECT node_id FROM nodes WHERE run_id = ? AND node_id != ?"),a=new Set(e.map(o=>o.node_id));return await m(e,async o=>(await t.all(o.node_id,o.node_id)).every(r=>a.has(r.node_id)))}i(ne,"retainRunRootsWithMembers");async function te(e,n){if(e.length===0)return;const t=await D(e,async o=>await x(o.node_id)),a=await g(e.map(o=>o.node_id));for(const o of e)await p(async r=>await U(r,o.node_id));if(await k(a),n!==void 0){for(const o of t)await j(o,async()=>{await X(o),ee(o)});n(t.flatMap(Z));return}for(const o of t)await j(o,async()=>await I(o))}i(te,"deletePrunedNodes");async function j(e,n){try{n()}catch(t){fe({level:"error",event:"canvas.node_files.remove_failed",error:t,fields:{node_id:e.nodeId}})}}i(j,"eachNodeBestEffort");function ae(e){let n=0;for(const t of[e.created,e.launched_at,e.last_settled_at,e.finalized_at,e.outcome_at,e.last_activity_at]){if(t==null)continue;const a=Date.parse(t);Number.isFinite(a)&&a>n&&(n=a)}try{const t=de(S(ke(e.node_id),"telemetry.json"),{throwIfNoEntry:!1});t!==void 0&&t.mtimeMs>n&&(n=t.mtimeMs)}catch{}return n}i(ae,"lastActivityMs");async function Ot(e){const n=e.dryRun??!1,t=await Ye(),a=await m(t,async u=>await G(u)&&!await e.held(u.node_id)),o=t.length-a.length,r=Math.max(0,e.maxNodes-o),d=a.map(u=>({row:u,recency:ae(u)})).sort((u,_)=>_.recency-u.recency).slice(r).map(({row:u})=>({node_id:u.node_id,status:u.status,created:u.created})),l=await ne(d);return n||l.length===0?{pruned:l,dryRun:n}:(await te(l),{pruned:l,dryRun:n})}i(Ot,"pruneToLimit");export{V as PARKED_RESIDENT_SQL,Ke as RESUMABLE_NODE_SQL,Wn as abandonCreateRequest,vn as assertRunNotDeleting,tt as browseNodes,bn as canonicalSessionFile,Et as childrenOf,pt as claimWarmSpare,Qn as clearFinalReportIfMatches,Zn as clearFrozen,jn as completeCreateRequest,An as createNode,Nt as deleteNode,Rt as deleteNodes,Mn as dueNodeDeadlines,gn as findCreateRequest,Fn as findNodeBySessionFile,Yn as getFinalReport,O as getNode,Cn as getRow,Hn as getRowByPane,it as graphNodes,G as isCollectible,_t as isWarmSpare,ae as lastActivityMs,Ye as listNodes,nt as listNodesByProfile,rt as listNodesForSupervision,dt as listParkedResidentsWithWakeMail,yt as listResumableRoots,ot as listTerminalPlacementResidue,ct as listWarmSpares,x as nodeFiles,Ve as nodesForSupervisionSql,Un as pruneExpiredCreateRequests,St as pruneNodes,Ot as pruneToLimit,xn as recordCreateRequestNode,Kn as recordLaunch,et as recordRespawnAttempt,Gn as recordSettledTurn,ut as registerWarmSpare,I as removeNodeFiles,kn as reserveCreateRequest,In as reserveCreateRequestNode,st as resumableNodeCwds,wt as rosterNodes,Xn as setFrozen,zn as setIntent,Pn as setNodeDeadline,Vn as setPresence,Bn as setStatus,$n as shiftProfileNodeDeadlines,Qe as terminalPlacementResidueSql,Jn as transitionStatusIntent,lt as unregisterWarmSpare,qn as updateNode,at as updateNodeTelemetry,ft as warmSpareCount,mt as withFreshTerminalGuard};
@@ -153,8 +153,9 @@ export declare function verifyLeasedCronRun(c: Cron): boolean;
153
153
  * bounded wait moves the deadline. This is the invariant the dropped
154
154
  * `triggers` partial unique index carried, and `idx_crons_deadline`
155
155
  * (`crons(anchor_node) WHERE cancel_on_wake = 1`) is still its structural
156
- * backstop; the displace-then-insert runs inside ONE canvas write boundary so
157
- * two concurrent arms cannot both pass. Only the anchor's own cancel-on-wake
156
+ * backstop; the displace-then-insert runs inside ONE transaction that first
157
+ * locks the anchor node row, so two concurrent arms replace one another in
158
+ * sequence. Only the anchor's own cancel-on-wake
158
159
  * rows are displaced — a standing declarative cron on the same anchor is
159
160
  * untouched. Any displaced in-flight run's process group is killed first (the
160
161
  * cancelCron discipline). */
@@ -1,8 +1,8 @@
1
- var w=Object.defineProperty;var a=(n,r)=>w(n,"name",{value:r,configurable:!0});import{withCanvasWrite as c,openDb as i}from"./db.js";import{capturePidIdentities as l,identitiesMatch as R,killProcessGroup as m}from"./pid.js";import{isProfilePaused as h}from"../profiles/manifest.js";import{stopAppProcess as D}from"../spaces/process.js";import{emitEvent as S}from"../events/emit.js";import{personId as p}from"./person.js";const N=20;function O(n){if(typeof n!="string")return null;try{const r=JSON.parse(n);return Array.isArray(r)?r.filter(e=>typeof e=="string"):null}catch{return null}}a(O,"scopesFrom");function s(n){return{cron_id:n.cron_id,name:n.name,created_by:n.created_by??null,grantee:n.grantee,sub:n.sub??p(),grant_id:n.grant_id??null,scopes:O(n.scopes_json),origin_run_id:n.origin_run_id??null,run_execution_id:n.run_execution_id??null,armed_from_exec:Number(n.armed_from_exec??0)!==0,isolated:Number(n.isolated)!==0,command:n.command,fire_at:n.fire_at,recur:n.recur??null,tz:n.tz??null,expires_at:n.expires_at??null,anchor_node:n.anchor_node??null,cancel_on_wake:Number(n.cancel_on_wake)!==0,cwd:n.cwd,env_json:n.env_json??null,profile:n.profile??null,scope:n.scope,run_timeout_s:Number(n.run_timeout_s),overlap:n.overlap,on_output:n.on_output,sink:n.sink,tier:n.tier,last_output_hash:n.last_output_hash??null,held:Number(n.held)!==0,last_poke_at:n.last_poke_at??null,state:n.state,run_state:n.run_state,run_pid:n.run_pid??null,run_pid_identity:n.run_pid_identity??null,run_started_at:n.run_started_at??null,run_lease_owner:n.run_lease_owner??null,run_out_of_band:Number(n.run_out_of_band)!==0,created:n.created,updated:n.updated}}a(s,"cronFrom");function E(n){try{const r=JSON.parse(n);if(typeof r.node=="string")return{kind:"node",node:r.node};if(typeof r.spawn=="string")return{kind:"spawn",spawn:r.spawn};if(r.human===!0)return{kind:"human"}}catch{}return null}a(E,"parseCronSink");function F(n){const r=E(n);if(r===null)return null;switch(r.kind){case"node":return`node:${r.node}`;case"spawn":return`spawn:${r.spawn}`;case"human":return"human"}}a(F,"cronSinkDisplay");async function y(n,r="SIGTERM"){if(!d(n))return!1;if(process.platform!=="linux")return m(n.run_pid,r),!0;try{return await D(n.run_pid,r,n.anchor_node??void 0),!0}catch(e){if(!d(n))return!1;throw e}}a(y,"stopLeasedCronRun");function _(n,r="SIGTERM"){return d(n)?(y(n,r).catch(e=>{S({level:"error",event:"cron.run.stop_failed",error:e,fields:{cron_id:n.cron_id,pid:n.run_pid}})}),!0):!1}a(_,"killLeasedCronRun");function d(n){const r=n.run_pid;if(r==null||n.run_pid_identity==null)return!1;const e=l([r]);if(e===null)return!1;const t=e.get(r);return t===void 0?!1:R(t,n.run_pid_identity)}a(d,"verifyLeasedCronRun");async function U(n){return await c(async r=>{const e=[];if(n.cancel_on_wake&&n.anchor_node!==null){const t=(await r.prepare("SELECT * FROM crons WHERE anchor_node = ? AND cancel_on_wake = 1").all(n.anchor_node)).map(s);for(const o of t)o.run_state==="running"&&_(o),await r.prepare("DELETE FROM crons WHERE cron_id = ?").run(o.cron_id),e.push(o.cron_id)}return await L(r,n),{cron_id:n.cron_id,replaced:e}})}a(U,"armCron");async function L(n,r){const e=new Date().toISOString();await n.prepare(`INSERT INTO crons (
1
+ var w=Object.defineProperty;var a=(n,r)=>w(n,"name",{value:r,configurable:!0});import{withCanvasWrite as c,openDb as i}from"./db.js";import{capturePidIdentities as l,identitiesMatch as R,killProcessGroup as m}from"./pid.js";import{isProfilePaused as h}from"../profiles/manifest.js";import{stopAppProcess as D}from"../spaces/process.js";import{emitEvent as S}from"../events/emit.js";import{personId as p}from"./person.js";const N=20;function O(n){if(typeof n!="string")return null;try{const r=JSON.parse(n);return Array.isArray(r)?r.filter(e=>typeof e=="string"):null}catch{return null}}a(O,"scopesFrom");function s(n){return{cron_id:n.cron_id,name:n.name,created_by:n.created_by??null,grantee:n.grantee,sub:n.sub??p(),grant_id:n.grant_id??null,scopes:O(n.scopes_json),origin_run_id:n.origin_run_id??null,run_execution_id:n.run_execution_id??null,armed_from_exec:Number(n.armed_from_exec??0)!==0,isolated:Number(n.isolated)!==0,command:n.command,fire_at:n.fire_at,recur:n.recur??null,tz:n.tz??null,expires_at:n.expires_at??null,anchor_node:n.anchor_node??null,cancel_on_wake:Number(n.cancel_on_wake)!==0,cwd:n.cwd,env_json:n.env_json??null,profile:n.profile??null,scope:n.scope,run_timeout_s:Number(n.run_timeout_s),overlap:n.overlap,on_output:n.on_output,sink:n.sink,tier:n.tier,last_output_hash:n.last_output_hash??null,held:Number(n.held)!==0,last_poke_at:n.last_poke_at??null,state:n.state,run_state:n.run_state,run_pid:n.run_pid??null,run_pid_identity:n.run_pid_identity??null,run_started_at:n.run_started_at??null,run_lease_owner:n.run_lease_owner??null,run_out_of_band:Number(n.run_out_of_band)!==0,created:n.created,updated:n.updated}}a(s,"cronFrom");function E(n){try{const r=JSON.parse(n);if(typeof r.node=="string")return{kind:"node",node:r.node};if(typeof r.spawn=="string")return{kind:"spawn",spawn:r.spawn};if(r.human===!0)return{kind:"human"}}catch{}return null}a(E,"parseCronSink");function F(n){const r=E(n);if(r===null)return null;switch(r.kind){case"node":return`node:${r.node}`;case"spawn":return`spawn:${r.spawn}`;case"human":return"human"}}a(F,"cronSinkDisplay");async function L(n,r="SIGTERM"){if(!d(n))return!1;if(process.platform!=="linux")return m(n.run_pid,r),!0;try{return await D(n.run_pid,r,n.anchor_node??void 0),!0}catch(e){if(!d(n))return!1;throw e}}a(L,"stopLeasedCronRun");function _(n,r="SIGTERM"){return d(n)?(L(n,r).catch(e=>{S({level:"error",event:"cron.run.stop_failed",error:e,fields:{cron_id:n.cron_id,pid:n.run_pid}})}),!0):!1}a(_,"killLeasedCronRun");function d(n){const r=n.run_pid;if(r==null||n.run_pid_identity==null)return!1;const e=l([r]);if(e===null)return!1;const t=e.get(r);return t===void 0?!1:R(t,n.run_pid_identity)}a(d,"verifyLeasedCronRun");async function U(n){return await c(async r=>{const e=[];if(n.cancel_on_wake&&n.anchor_node!==null){await r.prepare("SELECT 1 FROM nodes WHERE node_id = ? FOR UPDATE").get(n.anchor_node);const t=(await r.prepare("SELECT * FROM crons WHERE anchor_node = ? AND cancel_on_wake = 1").all(n.anchor_node)).map(s);for(const o of t)o.run_state==="running"&&_(o),await r.prepare("DELETE FROM crons WHERE cron_id = ?").run(o.cron_id),e.push(o.cron_id)}return await y(r,n),{cron_id:n.cron_id,replaced:e}})}a(U,"armCron");async function y(n,r){const e=new Date().toISOString();await n.prepare(`INSERT INTO crons (
2
2
  cron_id, name, created_by, grantee, sub, grant_id, scopes_json, origin_run_id, isolated, command, fire_at, recur, tz, expires_at,
3
3
  anchor_node, cancel_on_wake, cwd, env_json, profile, scope,
4
4
  run_timeout_s, overlap, on_output, sink, tier, created, updated, armed_from_exec
5
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(r.cron_id,r.name,r.created_by,r.grantee??"app:terminal",r.sub??p(),r.grant_id??null,r.scopes==null?null:JSON.stringify(r.scopes),r.origin_run_id??null,r.isolated?1:0,r.command,r.fire_at,r.recur,r.tz,r.expires_at,r.anchor_node,r.cancel_on_wake?1:0,r.cwd,r.env_json,r.profile,r.scope,r.run_timeout_s,r.overlap,r.on_output,r.sink,r.tier,e,e,r.armed_from_exec?1:0)}a(L,"insertCron");async function M(n){const r=await(await i()).prepare("SELECT * FROM crons WHERE cron_id = ?").get(n);return r===void 0?null:s(r)}a(M,"getCron");async function v(n){const r=await i();let e;return"profile"in n?e=await r.prepare("SELECT * FROM crons WHERE scope = 'profile' AND profile = ? ORDER BY fire_at").all(n.profile):"global"in n?e=await r.prepare("SELECT * FROM crons WHERE scope = 'global' ORDER BY fire_at").all():e=await r.prepare("SELECT * FROM crons ORDER BY fire_at").all(),e.map(s)}a(v,"listCrons");async function I(n,r){return await c(async e=>{const t=await e.prepare("SELECT * FROM crons WHERE cron_id = ?").get(n);if(t===void 0)return!1;const o=s(t);return o.run_state==="running"&&r?.preserveRunningProcess!==!0&&_(o),await e.prepare("DELETE FROM crons WHERE cron_id = ?").run(n),!0})}a(I,"cancelCron");function f(n,r){const e=[...new Set(r)];return e.length===0?{sql:"profile = ?",params:[n]}:{sql:`profile = ? OR anchor_node IN (${e.map(()=>"?").join(",")})`,params:[n,...e]}}a(f,"profileCronWhere");async function b(n,r){const e=f(n,r);return(await(await i()).prepareUncached(`SELECT cron_id FROM crons WHERE ${e.sql} ORDER BY created, cron_id`).all(...e.params)).map(({cron_id:t})=>t)}a(b,"cronIdsForProfileDeletion");async function P(n,r){return await c(async e=>{const t=f(n,r),o=(await e.prepareUncached(`SELECT * FROM crons WHERE ${t.sql} ORDER BY created, cron_id`).all(...t.params)).map(s);for(const u of o)u.run_state==="running"&&_(u);return await e.prepareUncached(`DELETE FROM crons WHERE ${t.sql}`).run(...t.params),o.map(({cron_id:u})=>u)})}a(P,"deleteCronsForProfile");async function B(n){return(await(await i()).prepare("SELECT cron_id FROM crons WHERE profile = ? ORDER BY created, cron_id").all(n)).map(({cron_id:r})=>r)}a(B,"cronIdsForProfileDetach");async function Y(n){return await c(async r=>{const e=(await r.prepare("SELECT cron_id FROM crons WHERE profile = ? ORDER BY created, cron_id").all(n)).map(({cron_id:t})=>t);return e.length>0&&await r.prepare("UPDATE crons SET profile = NULL, scope = 'global', updated = ? WHERE profile = ?").run(new Date().toISOString(),n),e})}a(Y,"globalizeCronsForProfile");async function $(n){return(await(await i()).prepare("DELETE FROM crons WHERE cron_id = ?").run(n)).changes>0}a($,"consumeCron");async function j(n,r){return(await(await i()).prepare("UPDATE crons SET state = ?, updated = ? WHERE cron_id = ?").run(r,new Date().toISOString(),n)).changes>0}a(j,"setCronState");async function q(n,r){await(await i()).prepare("UPDATE crons SET last_output_hash = ?, updated = ? WHERE cron_id = ?").run(r,new Date().toISOString(),n)}a(q,"setCronLastOutputHash");async function z(n){const r=await(await i()).prepare(`SELECT anchor_node, cancel_on_wake, sink
5
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(r.cron_id,r.name,r.created_by,r.grantee??"app:terminal",r.sub??p(),r.grant_id??null,r.scopes==null?null:JSON.stringify(r.scopes),r.origin_run_id??null,r.isolated?1:0,r.command,r.fire_at,r.recur,r.tz,r.expires_at,r.anchor_node,r.cancel_on_wake?1:0,r.cwd,r.env_json,r.profile,r.scope,r.run_timeout_s,r.overlap,r.on_output,r.sink,r.tier,e,e,r.armed_from_exec?1:0)}a(y,"insertCron");async function M(n){const r=await(await i()).prepare("SELECT * FROM crons WHERE cron_id = ?").get(n);return r===void 0?null:s(r)}a(M,"getCron");async function v(n){const r=await i();let e;return"profile"in n?e=await r.prepare("SELECT * FROM crons WHERE scope = 'profile' AND profile = ? ORDER BY fire_at").all(n.profile):"global"in n?e=await r.prepare("SELECT * FROM crons WHERE scope = 'global' ORDER BY fire_at").all():e=await r.prepare("SELECT * FROM crons ORDER BY fire_at").all(),e.map(s)}a(v,"listCrons");async function I(n,r){return await c(async e=>{const t=await e.prepare("SELECT * FROM crons WHERE cron_id = ?").get(n);if(t===void 0)return!1;const o=s(t);return o.run_state==="running"&&r?.preserveRunningProcess!==!0&&_(o),await e.prepare("DELETE FROM crons WHERE cron_id = ?").run(n),!0})}a(I,"cancelCron");function f(n,r){const e=[...new Set(r)];return e.length===0?{sql:"profile = ?",params:[n]}:{sql:`profile = ? OR anchor_node IN (${e.map(()=>"?").join(",")})`,params:[n,...e]}}a(f,"profileCronWhere");async function P(n,r){const e=f(n,r);return(await(await i()).prepareUncached(`SELECT cron_id FROM crons WHERE ${e.sql} ORDER BY created, cron_id`).all(...e.params)).map(({cron_id:t})=>t)}a(P,"cronIdsForProfileDeletion");async function b(n,r){return await c(async e=>{const t=f(n,r),o=(await e.prepareUncached(`SELECT * FROM crons WHERE ${t.sql} ORDER BY created, cron_id`).all(...t.params)).map(s);for(const u of o)u.run_state==="running"&&_(u);return await e.prepareUncached(`DELETE FROM crons WHERE ${t.sql}`).run(...t.params),o.map(({cron_id:u})=>u)})}a(b,"deleteCronsForProfile");async function B(n){return(await(await i()).prepare("SELECT cron_id FROM crons WHERE profile = ? ORDER BY created, cron_id").all(n)).map(({cron_id:r})=>r)}a(B,"cronIdsForProfileDetach");async function Y(n){return await c(async r=>{const e=(await r.prepare("SELECT cron_id FROM crons WHERE profile = ? ORDER BY created, cron_id").all(n)).map(({cron_id:t})=>t);return e.length>0&&await r.prepare("UPDATE crons SET profile = NULL, scope = 'global', updated = ? WHERE profile = ?").run(new Date().toISOString(),n),e})}a(Y,"globalizeCronsForProfile");async function $(n){return(await(await i()).prepare("DELETE FROM crons WHERE cron_id = ?").run(n)).changes>0}a($,"consumeCron");async function j(n,r){return(await(await i()).prepare("UPDATE crons SET state = ?, updated = ? WHERE cron_id = ?").run(r,new Date().toISOString(),n)).changes>0}a(j,"setCronState");async function q(n,r){await(await i()).prepare("UPDATE crons SET last_output_hash = ?, updated = ? WHERE cron_id = ?").run(r,new Date().toISOString(),n)}a(q,"setCronLastOutputHash");async function z(n){const r=await(await i()).prepare(`SELECT anchor_node, cancel_on_wake, sink
6
6
  FROM crons
7
7
  WHERE (anchor_node = ? AND cancel_on_wake = 1 AND state = 'active')
8
8
  OR (
@@ -33,4 +33,4 @@ var w=Object.defineProperty;var a=(n,r)=>w(n,"name",{value:r,configurable:!0});i
33
33
  WHERE cron_id = ? AND run_id NOT IN (
34
34
  SELECT run_id FROM cron_runs WHERE cron_id = ?
35
35
  ORDER BY started DESC, run_id DESC LIMIT ?
36
- )`).run(r.cron_id,r.cron_id,N),!0)}a(g,"recordCronRunLocked");async function an(n){return await c(async r=>await g(r,n))}a(an,"recordCronRun");export{N as CRON_RUN_RING,K as acquireCronRunLease,J as advanceCron,U as armCron,Q as attachCronRunPid,I as cancelCron,G as cancelCronsOnWake,$ as consumeCron,b as cronIdsForProfileDeletion,B as cronIdsForProfileDetach,F as cronSinkDisplay,P as deleteCronsForProfile,V as dueClockCrons,nn as dueOverlapCrons,rn as expiredIdleCrons,M as getCron,Y as globalizeCronsForProfile,z as hasPendingCronWake,_ as killLeasedCronRun,en as listCronRuns,v as listCrons,E as parseCronSink,Z as pokeHeldCrons,an as recordCronRun,g as recordCronRunLocked,X as releaseCronRunLease,tn as runningCrons,q as setCronLastOutputHash,j as setCronState,y as stopLeasedCronRun,d as verifyLeasedCronRun};
36
+ )`).run(r.cron_id,r.cron_id,N),!0)}a(g,"recordCronRunLocked");async function an(n){return await c(async r=>await g(r,n))}a(an,"recordCronRun");export{N as CRON_RUN_RING,K as acquireCronRunLease,J as advanceCron,U as armCron,Q as attachCronRunPid,I as cancelCron,G as cancelCronsOnWake,$ as consumeCron,P as cronIdsForProfileDeletion,B as cronIdsForProfileDetach,F as cronSinkDisplay,b as deleteCronsForProfile,V as dueClockCrons,nn as dueOverlapCrons,rn as expiredIdleCrons,M as getCron,Y as globalizeCronsForProfile,z as hasPendingCronWake,_ as killLeasedCronRun,en as listCronRuns,v as listCrons,E as parseCronSink,Z as pokeHeldCrons,an as recordCronRun,g as recordCronRunLocked,X as releaseCronRunLease,tn as runningCrons,q as setCronLastOutputHash,j as setCronState,L as stopLeasedCronRun,d as verifyLeasedCronRun};
@@ -11,6 +11,11 @@ export interface DaemonOwnerClaim {
11
11
  readonly pid_identity: string;
12
12
  readonly token: string;
13
13
  }
14
+ /** Whether a daemon of this role takes the `daemon_owner` claim. A Mac install has one owner (`role` undefined: the single
15
+ * process running both roles); the cloud's `--role api` and `--role reconcile` processes are several per database by design, so
16
+ * they never claim, never release, and never read the row. The caller (`crtrd.ts`) checks this before
17
+ * `claimDaemonOwnership` / `releaseDaemonOwnership` / `daemonOwnershipClaimed`. */
18
+ export declare function daemonOwnershipApplies(role: 'api' | 'reconcile' | undefined): boolean;
14
19
  /** Every terminal, non-retriable way `claimDaemonOwnership` can lose. A loss
15
20
  * never deletes or otherwise mutates the incumbent row — only a positively
16
21
  * DEAD incumbent is ever CAS-replaced. */
@@ -1 +1 @@
1
- var m=Object.defineProperty;var i=(e,n)=>m(e,"name",{value:n,configurable:!0});import{openDb as d}from"./db.js";import{capturePidIdentities as f,recordedPidLiveness as E}from"./pid.js";const s="daemon_owner";function l(e){return JSON.stringify({pid:e.pid,pid_identity:e.pid_identity,token:e.token})}i(l,"serializeClaim");function y(e){let n;try{n=JSON.parse(e)}catch{return null}if(typeof n!="object"||n===null)return null;const{pid:a,pid_identity:t,token:r}=n;return!Number.isInteger(a)||a<=0||typeof t!="string"||t.length===0||typeof r!="string"||r.length===0?null:{pid:a,pid_identity:t,token:r}}i(y,"parseClaim");async function p(){return(await(await d()).prepare("SELECT value FROM canvas_meta WHERE key = ?").get(s))?.value}i(p,"readSnapshot");async function O(e){const n=f([process.pid]),a=n?.get(process.pid);if(n===null||a===void 0)return{claimed:!1,reason:"own_identity_indeterminate"};const t={pid:process.pid,pid_identity:a,token:e},r=l(t);for(;;){const u=await p();if(u===void 0){if((await(await d()).prepare("INSERT INTO canvas_meta (key, value) SELECT ?, ? WHERE NOT EXISTS (SELECT 1 FROM canvas_meta WHERE key = ?) ON CONFLICT (key) DO NOTHING").run(s,r,s)).changes===1)return{claimed:!0,claim:t};continue}const c=y(u);if(c===null)return{claimed:!1,reason:"incumbent_malformed"};const o=E(c.pid,c.pid_identity);if(o==="alive"||o==="indeterminate")return{claimed:!1,reason:o==="alive"?"incumbent_alive":"incumbent_indeterminate",incumbentPid:c.pid,verdict:o};if((await(await d()).prepare("UPDATE canvas_meta SET value = ? WHERE key = ? AND value = ?").run(r,s,u)).changes===1)return{claimed:!0,claim:t}}}i(O,"claimDaemonOwnership");async function N(e){return{released:(await(await d()).prepare("DELETE FROM canvas_meta WHERE key = ? AND value = ?").run(s,l(e))).changes===1}}i(N,"releaseDaemonOwnership");async function T(){return await p()!==void 0}i(T,"daemonOwnershipClaimed");export{s as DAEMON_OWNER_META_KEY,O as claimDaemonOwnership,T as daemonOwnershipClaimed,N as releaseDaemonOwnership};
1
+ var m=Object.defineProperty;var t=(e,n)=>m(e,"name",{value:n,configurable:!0});import{openDb as d}from"./db.js";import{capturePidIdentities as f,recordedPidLiveness as E}from"./pid.js";const s="daemon_owner";function p(e){return JSON.stringify({pid:e.pid,pid_identity:e.pid_identity,token:e.token})}t(p,"serializeClaim");function y(e){let n;try{n=JSON.parse(e)}catch{return null}if(typeof n!="object"||n===null)return null;const{pid:a,pid_identity:i,token:r}=n;return!Number.isInteger(a)||a<=0||typeof i!="string"||i.length===0||typeof r!="string"||r.length===0?null:{pid:a,pid_identity:i,token:r}}t(y,"parseClaim");async function l(){return(await(await d()).prepare("SELECT value FROM canvas_meta WHERE key = ?").get(s))?.value}t(l,"readSnapshot");function v(e){return e===void 0}t(v,"daemonOwnershipApplies");async function N(e){const n=f([process.pid]),a=n?.get(process.pid);if(n===null||a===void 0)return{claimed:!1,reason:"own_identity_indeterminate"};const i={pid:process.pid,pid_identity:a,token:e},r=p(i);for(;;){const u=await l();if(u===void 0){if((await(await d()).prepare("INSERT INTO canvas_meta (key, value) SELECT ?, ? WHERE NOT EXISTS (SELECT 1 FROM canvas_meta WHERE key = ?) ON CONFLICT (key) DO NOTHING").run(s,r,s)).changes===1)return{claimed:!0,claim:i};continue}const o=y(u);if(o===null)return{claimed:!1,reason:"incumbent_malformed"};const c=E(o.pid,o.pid_identity);if(c==="alive"||c==="indeterminate")return{claimed:!1,reason:c==="alive"?"incumbent_alive":"incumbent_indeterminate",incumbentPid:o.pid,verdict:c};if((await(await d()).prepare("UPDATE canvas_meta SET value = ? WHERE key = ? AND value = ?").run(r,s,u)).changes===1)return{claimed:!0,claim:i}}}t(N,"claimDaemonOwnership");async function T(e){return{released:(await(await d()).prepare("DELETE FROM canvas_meta WHERE key = ? AND value = ?").run(s,p(e))).changes===1}}t(T,"releaseDaemonOwnership");async function b(){return await l()!==void 0}t(b,"daemonOwnershipClaimed");export{s as DAEMON_OWNER_META_KEY,N as claimDaemonOwnership,v as daemonOwnershipApplies,b as daemonOwnershipClaimed,T as releaseDaemonOwnership};
@@ -27,19 +27,17 @@ export interface CanvasDb {
27
27
  /** True when the calling async context is inside a `withCanvasWrite` body (its own open transaction). */
28
28
  readonly inTransaction: boolean;
29
29
  }
30
- /** True only for a statement that starts with SELECT and cannot write or lock. Anything else — WITH, VALUES, SHOW,
31
- * a comment-led statement, an unrecognised shape — is not a read, so a wrong guess lands on the writer. */
32
- export declare function isReadStatement(sql: string): boolean;
30
+ export declare function canvasPoolSize(env?: Record<string, string | undefined>): number;
33
31
  /** The URL of the database for the current `CRTR_HOME`. In a test it creates the home's database on first use. */
34
32
  export declare function canvasDatabaseUrl(): Promise<string>;
35
33
  /** Open (or reuse) the canvas db handle for the current `CRTR_HOME`. Keyed by database URL, so tests with distinct
36
- * homes get independent handles. The first open connects and applies the schema. */
34
+ * homes get independent handles. The first open applies the schema. */
37
35
  export declare function openDb(): Promise<CanvasDb>;
38
36
  /** Close and forget the connection for the current home. Mainly for tests. */
39
37
  export declare function closeDb(): Promise<void>;
40
38
  /** Close every connection this process holds (daemon shutdown). */
41
39
  export declare function closeAllDbs(): Promise<void>;
42
- /** Run `hook` once the calling context's outermost transaction has committed (and released the write lock); drop it if
40
+ /** Run `hook` once the calling context's outermost transaction has committed (and returned its connection to the pool); drop it if
43
41
  * the transaction or the savepoint holding it rolls back. Outside a transaction it runs in the next microtask. Use it for anything
44
42
  * that tells another context about a write, so that context never reads uncommitted rows or hears of a rolled-back one. */
45
43
  export declare function afterCommit(hook: () => void): void;
@@ -49,10 +47,13 @@ export interface CanvasWriteOptions {
49
47
  /** Always undo the body's writes, even when it returns (a dry run). Nested, it runs in a SAVEPOINT rolled back at the end. */
50
48
  rollback?: boolean;
51
49
  }
52
- /** Run `body` in one transaction, the only transaction entry point for the canvas database.
53
- * A call from inside the body's own async context joins it and only the outermost call commits; a call
54
- * from any other context waits its turn. A throwing body rolls back (with `savepoint`, a nested body
55
- * rolls back only its own writes). With `rollback` the body's writes are always undone.
50
+ /** Run `body` in one transaction on its own pooled connection, the only transaction entry point for the canvas database.
51
+ * A call from inside the body's own async context joins it and only the outermost call commits. A call from any other
52
+ * context opens a separate transaction that runs concurrently (READ COMMITTED): a read-modify-write that must not lose an
53
+ * update takes its row with `SELECT … FOR UPDATE` or writes conditionally. A throwing body rolls back (with `savepoint`, a
54
+ * nested body rolls back only its own writes). With `rollback` the body's writes are always undone.
55
+ *
56
+ * The body holds its connection until it returns: keep network, subprocess and filesystem waits out of it.
56
57
  *
57
58
  * Postgres aborts the whole transaction when a statement fails, so a body that catches a failed statement and
58
59
  * carries on must run that statement inside a nested `withCanvasWrite(…, { savepoint: true })`. */
@@ -1,4 +1,4 @@
1
- var C=Object.defineProperty;var a=(n,t)=>C(n,"name",{value:t,configurable:!0});import{AsyncLocalStorage as R}from"node:async_hooks";import{randomBytes as x}from"node:crypto";import{appendFileSync as $,mkdirSync as P,readFileSync as k}from"node:fs";import{join as L}from"node:path";import m from"pg";import{resolveDatabaseUrl as D}from"../../daemon/postgres/supervisor.js";import{publishFileExclusive as M}from"../fs-utils.js";import{initializeEmptyLayout as N,runtimeRoot as h}from"../layout.js";import{translatePlaceholders as B,translateOnce as q}from"../statements.js";import{ensureHome as U}from"./paths.js";import{emitEvent as F}from"../events/emit.js";import{ensureSchema as T}from"./schema-chain.js";import{canvasDatabaseSchema as I,canvasSchemaDatabaseUrl as K,canvasSearchPathOptions as b}from"./schema-version.js";const G="crtr_test_template";class H extends Error{static{a(this,"CanvasConstraintError")}constraint;code="23505";constructor(t,e,r){super(t,r),this.name="CanvasConstraintError",this.constraint=e}}function _(n){const t=process.env.CRTR_DB_OPEN_LEDGER;if(t)try{const e=`${new Date().toISOString()} ${process.pid} ${process.argv[1]??"<unknown>"} ${n.replace(/\/\/([^:@/]*):[^@/]*@/,"//$1@")}
2
- `;$(t,e)}catch{}}a(_,"recordOpenLedger");const Y=4,z=/\b(?:FOR\s+(?:NO\s+KEY\s+)?(?:UPDATE|SHARE)|FOR\s+KEY\s+SHARE|INTO|nextval|setval|set_config|pg_notify|pg_advisory\w*|pg_try_advisory\w*|lo_\w+)\b/i;function J(n){return/^\s*SELECT\b/i.test(n)&&!z.test(n)}a(J,"isReadStatement");const V=1e3,f=new R;class W{static{a(this,"FifoLock")}tail=Promise.resolve();pending=0;get busy(){return this.pending>0}drained(){return this.tail}async acquire(){this.pending++;const t=this.tail;let e;return this.tail=new Promise(r=>{e=r}),await t,()=>{this.pending--,e()}}}const j=new Set([114,3802]),O={getTypeParser:a(((n,t)=>j.has(n)?e=>e:n===20||n===1700?e=>{const r=Number(e);if(n===20&&!Number.isSafeInteger(r))throw new Error(`canvas db: bigint ${e} does not fit a JS number`);return r}:m.types.getTypeParser(n,t)),"getTypeParser")};class X{static{a(this,"Handle")}url;lock=new W;db;client=null;pool=null;connecting=null;schemaChecked=!1;active=0;inFlight=new Set;constructor(t){this.url=t;const e=this;this.db={prepare:a(r=>this.statement(r,!0),"prepare"),prepareUncached:a(r=>this.statement(r,!1),"prepareUncached"),exec:a(r=>this.gated(async()=>{await this.send({text:r})}),"exec"),get inTransaction(){return w(e)}}}async connected(){return this.client!==null?this.client:(this.connecting??=this.connect().finally(()=>{this.connecting=null}),this.connecting)}async connect(){_(this.url);const t=new m.Client({connectionString:this.url,types:O,application_name:`crtr-${process.pid}`,connectionTimeoutMillis:1e4,options:b()});t.on("error",e=>{console.warn(`[canvas db] connection error: ${e.message}`),this.drop(t)}),t.on("end",()=>this.drop(t)),await t.connect();try{this.schemaChecked||(await Q(t),this.schemaChecked=!0)}catch(e){throw await t.end().catch(()=>{}),e}return t.connection.stream.unref(),this.client=t,t}readPool(){if(this.pool!==null)return this.pool;_(this.url);const t=new m.Pool({connectionString:this.url,types:O,application_name:`crtr-${process.pid}`,connectionTimeoutMillis:1e4,options:b(),max:Y,idleTimeoutMillis:1e4,allowExitOnIdle:!0});return t.on("error",e=>console.warn(`[canvas db] read connection error: ${e.message}`)),this.pool=t}async read(t){await this.lock.drained(),this.inFlight.size>0&&await Promise.all([...this.inFlight]);try{return await this.readPool().query(t)}catch(e){throw this.mapError(e,null)}}drop(t){this.client===t&&(this.client=null)}async send(t){const e=await this.connected(),r=f.getStore();if(r!==void 0&&r.open&&r.handle===this&&r.client!==e)throw new Error("canvas db: the connection was lost during this transaction; it was rolled back");return this.sendOn(e,t)}async sendOn(t,e){const r=t.connection.stream;this.active++===0&&r.ref();const o=t.query(e),i=o.then(()=>{},()=>{});this.inFlight.add(i),i.then(()=>this.inFlight.delete(i));try{return await o}catch(s){throw this.mapError(s,t)}finally{--this.active===0&&r.unref()}}mapError(t,e){const r=t;return r.code==="23505"?new H(r.message??"unique constraint violation",r.constraint,{cause:t}):((r.code==="57P01"||r.code==="ECONNRESET"||r.code==="EPIPE"||/Connection terminated|connection error|Client was closed/i.test(r.message??""))&&e!==null&&(this.drop(e),e.end().catch(()=>{})),t)}async gated(t){if(w(this)){const r=f.getStore(),o=performance.now();try{return await t()}finally{r.statementMs+=performance.now()-o}}if(!this.lock.busy)return t();const e=await this.lock.acquire();try{return await t()}finally{e()}}statement(t,e){const r=e?(()=>{const s=B(t);return{text:s.text,name:s.name}})():{text:q(t).text},o=J(r.text),i=a(s=>{const u={...r,values:s.map(Z)};return o&&!w(this)?this.read(u):this.gated(()=>this.send(u))},"query");return{get:a(async(...s)=>(await i(s)).rows[0],"get"),all:a(async(...s)=>(await i(s)).rows,"all"),run:a(async(...s)=>({changes:(await i(s)).rowCount??0}),"run")}}async close(){const t=this.pool;this.pool=null,await t?.end().catch(()=>{}),(await this.lock.acquire())();const e=this.client;this.client=null,e!==null&&(e.connection.stream.ref(),await e.end().catch(()=>{}))}}function Z(n){return ArrayBuffer.isView(n)&&!Buffer.isBuffer(n)?Buffer.from(n.buffer,n.byteOffset,n.byteLength):n}a(Z,"bindable");function w(n){const t=f.getStore();return t!==void 0&&t.open&&t.handle===n}a(w,"ownsTransaction");async function Q(n){const t=a((i,s,u)=>{F({level:i,event:s,fields:u})},"emit"),e=I(),r=K();if(r===void 0){await T(n,{schema:e,emit:t});return}const o=new m.Client({connectionString:r,application_name:`crtr-schema-${process.pid}`,connectionTimeoutMillis:1e4});await o.connect();try{await T(o,{schema:e,emit:t})}finally{await o.end().catch(()=>{})}}a(Q,"applySchema");function tt(){return process.env.NODE_TEST_CONTEXT!==void 0||process.execArgv.includes("--test")}a(tt,"runningUnderNodeTest");function y(n,t){const e=/^(postgres(?:ql)?:\/\/[^/?#]*)(\/[^?#]*)?(.*)$/s.exec(n);if(e===null)throw new Error(`not a postgres URL: ${n.replace(/\/\/[^@/]*@/,"//")}`);return`${e[1]}/${t}${e[3]}`}a(y,"withDatabase");async function et(n){const t=L(h(),"test-db"),e=a(()=>{try{return k(t,"utf8").trim()||void 0}catch(s){if(s.code==="ENOENT")return;throw s}},"recorded"),r=e();if(r!==void 0)return y(n,r);P(h(),{recursive:!0});const o=`t_${x(8).toString("hex")}`,i=new m.Client({connectionString:y(n,"postgres")});await i.connect();try{if(await i.query(`CREATE DATABASE ${o} TEMPLATE ${G} STRATEGY file_copy`),!M(t,`${o}
3
- `,384))return await i.query(`DROP DATABASE ${o}`),y(n,e())}finally{await i.end()}return y(n,o)}a(et,"testDatabaseUrl");async function nt(){const n=process.env.CRTR_TEST_PG_URL;if(n)return et(n);if(tt())throw new Error("CRTR_TEST_PG_URL is not set: a test must reach its own Postgres (run the suite through the lane runner); refusing to open a real canvas database");return D(h())}a(nt,"canvasDatabaseUrl");const p=new Map,g=new Map;async function v(){N(),U();const n=await nt();g.set(h(),n);let t=p.get(n);return t===void 0&&p.set(n,t=new X(n)),await t.connected(),t}a(v,"openHandle");async function gt(){return(await v()).db}a(gt,"openDb");async function Et(){const n=g.get(h());if(n===void 0)return;g.delete(h());const t=p.get(n);t!==void 0&&(p.delete(n),await t.close())}a(Et,"closeDb");async function St(){const n=[...p.values()];p.clear(),g.clear(),await Promise.all(n.map(t=>t.close()))}a(St,"closeAllDbs");function Tt(n){const t=f.getStore();if(t===void 0||!t.open){queueMicrotask(n);return}t.afterCommit.push(n)}a(Tt,"afterCommit");let rt=0;async function bt(n,t={}){const e=await v(),r=e.db;if(w(e)){if(!t.savepoint&&!t.rollback)return n(r);const c=`canvas_write_${++rt}`;await e.send({text:`SAVEPOINT ${c}`});const l=f.getStore()?.afterCommit,E=l?.length??0;let S;try{S=await n(r)}catch(A){throw await e.send({text:`ROLLBACK TO ${c}; RELEASE ${c}`}),l?.splice(E),A}return await e.send({text:t.rollback?`ROLLBACK TO ${c}; RELEASE ${c}`:`RELEASE ${c}`}),t.rollback&&l?.splice(E),S}const o=await e.lock.acquire(),i=performance.now(),s=new Error("canvas write transaction entered here");let u=!1;const d={handle:e,client:void 0,open:!0,afterCommit:[],statementMs:0};try{const c=await e.connected();await e.sendOn(c,{text:"BEGIN"}),d.client=c;try{const l=await f.run(d,()=>n(r));return await e.sendOn(c,{text:t.rollback?"ROLLBACK":"COMMIT"}),u=!t.rollback,l}catch(l){try{await e.sendOn(c,{text:"ROLLBACK"})}catch{}throw l}}finally{d.open=!1;const c=performance.now()-i;if(c-d.statementMs>V&&console.warn(`[canvas db] write transaction held ${Math.round(c)}ms (${Math.round(d.statementMs)}ms in its statements); its body did non-db work while every other db caller waited
4
- ${s.stack}`),o(),u)for(const l of d.afterCommit)queueMicrotask(l)}}a(bt,"withCanvasWrite");export{H as CanvasConstraintError,Tt as afterCommit,nt as canvasDatabaseUrl,St as closeAllDbs,Et as closeDb,J as isReadStatement,gt as openDb,bt as withCanvasWrite};
1
+ var v=Object.defineProperty;var o=(e,t)=>v(e,"name",{value:t,configurable:!0});import{AsyncLocalStorage as P}from"node:async_hooks";import{randomBytes as D}from"node:crypto";import{appendFileSync as M,mkdirSync as B,readFileSync as k}from"node:fs";import{join as N}from"node:path";import d from"pg";import{resolveDatabaseUrl as I}from"../../daemon/postgres/supervisor.js";import{publishFileExclusive as U}from"../fs-utils.js";import{initializeEmptyLayout as q,runtimeRoot as l}from"../layout.js";import{translatePlaceholders as G,translateOnce as K}from"../statements.js";import{ensureHome as X}from"./paths.js";import{emitEvent as F}from"../events/emit.js";import{ensureSchema as H}from"./schema-chain.js";import{canvasDatabaseSchema as J,canvasSchemaDatabaseUrl as V,canvasSearchPathOptions as b}from"./schema-version.js";const W="crtr_test_template";class Z extends Error{static{o(this,"CanvasConstraintError")}constraint;code="23505";constructor(t,n,a){super(t,a),this.name="CanvasConstraintError",this.constraint=n}}function A(e){const t=process.env.CRTR_DB_OPEN_LEDGER;if(t)try{const n=`${new Date().toISOString()} ${process.pid} ${process.argv[1]??"<unknown>"} ${e.replace(/\/\/([^:@/]*):[^@/]*@/,"//$1@")}
2
+ `;M(t,n)}catch{}}o(A,"recordOpenLedger");const z=6,j=12,O=2;function $(e=process.env){const t=e.CRTR_DATABASE_POOL_MAX?.trim();if(t){const n=Number(t);if(!Number.isInteger(n)||n<O)throw new Error(`CRTR_DATABASE_POOL_MAX must be an integer of at least ${O}; got ${JSON.stringify(t)}`);return n}return e.CRTR_DATABASE_URL?j:z}o($,"canvasPoolSize");const R=3e4,Y=1e3,p=new P,Q=new Set([114,3802]),tt={getTypeParser:o(((e,t)=>Q.has(e)?n=>n:e===20||e===1700?n=>{const a=Number(n);if(e===20&&!Number.isSafeInteger(a))throw new Error(`canvas db: bigint ${n} does not fit a JS number`);return a}:d.types.getTypeParser(e,t)),"getTypeParser")};class et{static{o(this,"Handle")}url;db;pool=null;schema=null;constructor(t){this.url=t;const n=this;this.db={prepare:o(a=>this.statement(a,!0),"prepare"),prepareUncached:o(a=>this.statement(a,!1),"prepareUncached"),exec:o(a=>this.run({text:a}).then(()=>{}),"exec"),get inTransaction(){return L(n)}}}ready(){return this.schema??=rt(this.url).catch(t=>{throw this.schema=null,t}),this.schema}connections(){if(this.pool!==null)return this.pool;const t=new d.Pool({connectionString:this.url,types:tt,application_name:`crtr-${process.pid}`,connectionTimeoutMillis:R,options:b(),max:$(),idleTimeoutMillis:1e4,allowExitOnIdle:!0});return t.on("connect",()=>A(this.url)),t.on("error",n=>console.warn(`[canvas db] pooled connection error: ${n.message}`)),this.pool=t}async checkout(){try{return await this.connections().connect()}catch(t){throw this.mapError(t)}}async run(t){const n=p.getStore();try{if(n!==void 0&&n.open&&n.handle===this){const a=performance.now();try{return await n.client.query(t)}finally{n.statementMs+=performance.now()-a}}return await this.connections().query(t)}catch(a){throw this.mapError(a)}}mapError(t){const n=t;return n.code==="23505"?new Z(n.message??"unique constraint violation",n.constraint,{cause:t}):/timeout exceeded when trying to connect/i.test(n.message??"")?new Error(`canvas db: no pooled connection became free within ${R} ms (pool of ${$()}); a transaction body is holding connections across a slow await`,{cause:t}):t}statement(t,n){const a=n?(()=>{const r=G(t);return{text:r.text,name:r.name}})():{text:K(t).text},s=o(r=>this.run({...a,values:r.map(nt)}),"query");return{get:o(async(...r)=>(await s(r)).rows[0],"get"),all:o(async(...r)=>(await s(r)).rows,"all"),run:o(async(...r)=>({changes:(await s(r)).rowCount??0}),"run")}}async close(){const t=this.pool;this.pool=null,await t?.end().catch(()=>{})}}function nt(e){return ArrayBuffer.isView(e)&&!Buffer.isBuffer(e)?Buffer.from(e.buffer,e.byteOffset,e.byteLength):e}o(nt,"bindable");function L(e){const t=p.getStore();return t!==void 0&&t.open&&t.handle===e}o(L,"ownsTransaction");async function rt(e){const t=o((u,h,y)=>{F({level:u,event:h,fields:y})},"emit"),n=J(),a=V(),s=a??e;A(s);const r=new d.Client({connectionString:s,application_name:`crtr-schema-${process.pid}`,connectionTimeoutMillis:1e4,...a===void 0?{options:b()}:{}});r.on("error",()=>{}),await r.connect();try{await H(r,{schema:n,emit:t})}finally{await r.end().catch(()=>{})}}o(rt,"applySchema");function ot(){return process.env.NODE_TEST_CONTEXT!==void 0||process.execArgv.includes("--test")}o(ot,"runningUnderNodeTest");function w(e,t){const n=/^(postgres(?:ql)?:\/\/[^/?#]*)(\/[^?#]*)?(.*)$/s.exec(e);if(n===null)throw new Error(`not a postgres URL: ${e.replace(/\/\/[^@/]*@/,"//")}`);return`${n[1]}/${t}${n[3]}`}o(w,"withDatabase");async function at(e){const t=N(l(),"test-db"),n=o(()=>{try{return k(t,"utf8").trim()||void 0}catch(u){if(u.code==="ENOENT")return;throw u}},"recorded"),a=n();if(a!==void 0)return w(e,a);B(l(),{recursive:!0});const s=`t_${D(8).toString("hex")}`,r=new d.Client({connectionString:w(e,"postgres")});r.on("error",()=>{}),await r.connect();try{if(await r.query(`CREATE DATABASE ${s} TEMPLATE ${W} STRATEGY file_copy`),!U(t,`${s}
3
+ `,384))return await r.query(`DROP DATABASE ${s}`),w(e,n())}finally{await r.end()}return w(e,s)}o(at,"testDatabaseUrl");async function st(){const e=process.env.CRTR_TEST_PG_URL;if(e)return at(e);if(ot())throw new Error("CRTR_TEST_PG_URL is not set: a test must reach its own Postgres (run the suite through the lane runner); refusing to open a real canvas database");return I(l())}o(st,"canvasDatabaseUrl");const f=new Map,E=new Map;async function C(){q(),X();const e=await st();E.set(l(),e);let t=f.get(e);return t===void 0&&f.set(e,t=new et(e)),await t.ready(),t}o(C,"openHandle");async function _t(){return(await C()).db}o(_t,"openDb");async function bt(){const e=E.get(l());if(e===void 0)return;E.delete(l());const t=f.get(e);t!==void 0&&(f.delete(e),await t.close())}o(bt,"closeDb");async function At(){const e=[...f.values()];f.clear(),E.clear(),await Promise.all(e.map(t=>t.close()))}o(At,"closeAllDbs");function Ot(e){const t=p.getStore();if(t===void 0||!t.open){queueMicrotask(e);return}t.afterCommit.push(e)}o(Ot,"afterCommit");let ct=0;async function $t(e,t={}){const n=await C(),a=n.db;if(L(n)){if(!t.savepoint&&!t.rollback)return e(a);const c=p.getStore(),i=`canvas_write_${++ct}`;await n.run({text:`SAVEPOINT ${i}`});const S=c.afterCommit,T=S.length;let _;try{_=await e(a)}catch(x){throw await n.run({text:`ROLLBACK TO ${i}; RELEASE ${i}`}),S.splice(T),x}return await n.run({text:t.rollback?`ROLLBACK TO ${i}; RELEASE ${i}`:`RELEASE ${i}`}),t.rollback&&S.splice(T),_}const s=new Error("canvas write transaction entered here"),r=await n.checkout();let u;const h=o(c=>{u??=c},"onConnectionError");r.on("error",h);const y=performance.now();let g=!1;const m={handle:n,client:r,open:!0,afterCommit:[],statementMs:0};try{await r.query("BEGIN");try{const c=await p.run(m,()=>e(a));return await r.query(t.rollback?"ROLLBACK":"COMMIT"),g=!t.rollback,c}catch(c){try{await r.query("ROLLBACK")}catch(i){u??=i}throw n.mapError(c)}}finally{m.open=!1;const c=performance.now()-y;if(c-m.statementMs>Y&&console.warn(`[canvas db] write transaction held its connection ${Math.round(c)}ms (${Math.round(m.statementMs)}ms in its statements); its body did non-db work while holding a pooled connection and its row locks
4
+ ${s.stack}`),r.removeListener("error",h),r.release(u),g)for(const i of m.afterCommit)queueMicrotask(i)}}o($t,"withCanvasWrite");export{Z as CanvasConstraintError,Ot as afterCommit,st as canvasDatabaseUrl,$ as canvasPoolSize,At as closeAllDbs,bt as closeDb,_t as openDb,$t as withCanvasWrite};
@@ -8,22 +8,118 @@ export interface Execution {
8
8
  startedAt: string;
9
9
  endedAt: string | null;
10
10
  exitReason: string | null;
11
+ runner: 'process' | 'blaxel';
12
+ sandboxName: string | null;
13
+ attempt: number;
14
+ /** The release the wake accepted. */
15
+ release: string | null;
16
+ heartbeatAt: string | null;
17
+ awakeSince: string | null;
18
+ idleAt: string | null;
19
+ exitedAt: string | null;
20
+ /** The launch recipe the wake wrote: invocation argv and env, config snapshot, system prompt text, scopes. */
21
+ launchJson: string | null;
22
+ stopRequestedAt: string | null;
23
+ stopAfterTurn: boolean;
24
+ stopReason: string | null;
11
25
  }
26
+ /** Reasons that end a launch the broker never reported `running` for; nothing the broker owned needs cleanup. */
27
+ export type ExecutionEndReason = 'launch_lost' | 'launch_timeout' | (string & {});
12
28
  export declare function liveExecutionForNode(nodeId: string): Promise<Execution | null>;
13
- export declare function beginExecution(nodeId: string, input: {
29
+ export interface BeginExecutionInput {
14
30
  daemonEpoch: string | null;
15
- }): Promise<string>;
31
+ runner?: Execution['runner'];
32
+ /** Defaults to one above the node's highest attempt. */
33
+ attempt?: number;
34
+ sandboxName?: string | null;
35
+ release?: string | null;
36
+ launchJson?: string | null;
37
+ /** The broker cap. The count of other nodes' live rows and this insert are one step under a database-wide lock,
38
+ * so two processes beginning at cap-1 cannot both take the last slot. */
39
+ cap?: number;
40
+ }
41
+ /** Thrown by `beginExecution` when `cap` other nodes already hold a live execution row. */
42
+ export declare class ExecutionCapReached extends Error {
43
+ readonly cap: number;
44
+ readonly live: number;
45
+ constructor(cap: number, live: number);
46
+ }
47
+ /** Thrown by `beginExecution` when the node already holds a live execution another caller owns: a remote one, or a process
48
+ * one this daemon epoch began. The holder keeps its row and token; the caller adopts it or backs off, never replaces it. */
49
+ export declare class ExecutionAlreadyLive extends Error {
50
+ readonly nodeId: string;
51
+ readonly executionId: string;
52
+ constructor(nodeId: string, executionId: string);
53
+ }
54
+ /** Inserts the `launching` row. It mints no token: the runner mints inside `start` (design D74). */
55
+ export declare function beginExecution(nodeId: string, input: BeginExecutionInput): Promise<string>;
56
+ /** Persist the complete launch recipe before handing the execution to a runner. */
57
+ export declare function setExecutionLaunchJson(executionId: string, launchJson: string): Promise<void>;
58
+ /** The broker went idle with nothing holding it (design D18): stamps `idle_at` on its running execution, once per awake period. */
59
+ export declare function recordExecutionIdle(executionId: string): Promise<boolean>;
60
+ /** A thawed resident broker starts a new awake period: `awake_since` is now and `idle_at` is cleared. */
61
+ export declare function recordExecutionAwake(executionId: string): Promise<void>;
16
62
  /** Returns shutdown when a late broker references an execution no longer live. */
17
63
  export declare function recordExecutionPid(executionId: string, nodeId: string, pid: number): Promise<'recorded' | 'shutdown'>;
64
+ /**
65
+ * Ends the execution, revokes its token and clears the node's supervision marks in one transaction.
66
+ * `launch_lost` and `launch_timeout` are the recovery ends of a `launching` row (design, Execution token hand-off).
67
+ */
18
68
  export declare function endExecution(executionId: string, input: {
19
- reason: string;
69
+ reason: ExecutionEndReason;
70
+ }): Promise<boolean>;
71
+ /** Ends the execution only while it is still `launching`: false when a `session.bound` already made it `running`, so a stale
72
+ * timeout decision never kills a broker that bound. The caller stops the external process only on true. */
73
+ export declare function endLaunchingExecution(executionId: string, input: {
74
+ reason: ExecutionEndReason;
20
75
  }): Promise<boolean>;
21
76
  export declare function abortExecution(executionId: string, input: {
22
77
  reason: string;
23
78
  }): Promise<boolean>;
24
79
  export declare function endLiveExecution(nodeId: string, input: {
25
- reason: string;
80
+ reason: ExecutionEndReason;
26
81
  }): Promise<boolean>;
27
82
  export declare function migrateLegacyExecutionIdentities(captureIdentity?: (pid: number) => string | undefined): Promise<{
28
83
  migrated: string[];
29
84
  }>;
85
+ /** Records a stop request on the node's live execution; the first request's time and reason stand. */
86
+ export declare function requestExecutionStop(nodeId: string, input: {
87
+ afterTurn: boolean;
88
+ reason: string;
89
+ }): Promise<boolean>;
90
+ /** Live executions with a stop requested at or before `requestedBefore` (ISO time). A request this old has outlived the
91
+ * process that queued it, whose own teardown runs within seconds. */
92
+ export declare function executionsWithStopRequestedBefore(requestedBefore: string): Promise<Execution[]>;
93
+ /** The node's most recent execution row, live or ended. */
94
+ export declare function latestExecutionForNode(nodeId: string): Promise<Execution | null>;
95
+ /** True when the node has a `running` execution on a runner other than the process runner: the row is its only liveness record. */
96
+ export declare function hasRunningRemoteExecution(nodeId: string): Promise<boolean>;
97
+ /** True when a `launching` execution is in flight: any runner's, or a process runner's started by this daemon epoch. */
98
+ export declare function hasLaunchingExecution(nodeId: string, epoch: string): Promise<boolean>;
99
+ /** Node ids with a live process-runner execution begun by this daemon epoch. */
100
+ export declare function liveProcessExecutionNodeIds(epoch: string): Promise<Set<string>>;
101
+ /** Every node with a live (not exited) execution row, whichever process or runner holds it. */
102
+ export declare function liveExecutionNodeIds(): Promise<Set<string>>;
103
+ export declare const PROFILE_PAUSE_STOP_REASON = "profile_pause";
104
+ /** Marks the node's live execution as stopped by a profile pause, so the exit it causes is not read as a crash. */
105
+ export declare function markProfilePauseStop(nodeId: string): Promise<void>;
106
+ export declare function hasProfilePauseStop(nodeId: string): Promise<boolean>;
107
+ /** Consumes the pause stop once: true for the single dead-row pass that must absorb it. */
108
+ export declare function absorbProfilePauseStop(nodeId: string): Promise<boolean>;
109
+ /** Whether the latest execution's profile-pause stop was absorbed by a policy pass, the exit it caused already forgiven. */
110
+ export declare function hasAbsorbedProfilePauseStop(nodeId: string): Promise<boolean>;
111
+ /** Records a child's observed exit status on its execution without ending it; the next dead-row pass reads it once. The first recorded reason stands. */
112
+ export declare function recordExecutionExit(executionId: string, status: {
113
+ code: number | null;
114
+ signal: string | null;
115
+ oomKilled?: boolean;
116
+ }): Promise<void>;
117
+ export interface ObservedExit {
118
+ code: number | null;
119
+ signal: NodeJS.Signals | null;
120
+ oomKilled?: boolean;
121
+ }
122
+ /** Whether the latest execution holds an observed exit no dead-row pass has consumed yet. */
123
+ export declare function hasObservedExit(nodeId: string): Promise<boolean>;
124
+ /** Reads and consumes the latest execution's unhandled observed exit, once. */
125
+ export declare function takeObservedExit(nodeId: string): Promise<ObservedExit | undefined>;