@north-light/cli 0.3.560 → 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 +20 -20
  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
@@ -17,7 +17,7 @@ export interface AppProcessOptions {
17
17
  grantId: string | null;
18
18
  scopes: string[] | null;
19
19
  };
20
- /** A cron execution: launch id `cron-<executionId>`, no node, and no `api.sock`/`store.sock` — its only API path is
20
+ /** A cron execution: launch id `cron-<executionId>`, no node, and no `api.sock` — its only API path is
21
21
  * its own execution folder's `cli.sock`. `anchorDir` is an anchored cron's anchor agent folder (rw). Needs `principal`. */
22
22
  cron?: {
23
23
  executionId: string;
@@ -9,14 +9,16 @@ export declare function killBashJobObject(jobId: string, killedBy: (db: CanvasDb
9
9
  * daemon adopting a job the broker backgrounded while exiting. Daemon-only. */
10
10
  export declare function recordBashJobObject(nodeId: string, jobId: string, spec: {
11
11
  command: string;
12
- jobDir: string;
13
- logPath: string;
12
+ purpose?: string | null;
13
+ deadlineAtMs?: number | null;
14
14
  }): Promise<ObjectRow>;
15
- /** Stop the broker first so it cannot start another job during the job scan. */
16
- export declare function stopNodeSandboxes(nodeId: string, reason: string, options?: {
15
+ type StopOptions = {
17
16
  excludePid?: number;
18
17
  startAfter?: Promise<unknown>;
19
18
  after?: () => Promise<void>;
20
19
  deleting?: boolean;
21
20
  holdRecovery?: boolean;
22
- }): Promise<TeardownResult>;
21
+ };
22
+ /** Stop the broker first so it cannot start another job during the job scan. */
23
+ export declare function stopNodeSandboxes(nodeId: string, reason: string, options?: StopOptions): Promise<TeardownResult>;
24
+ export {};
@@ -1,2 +1,2 @@
1
- var _=Object.defineProperty;var s=(t,e)=>_(t,"name",{value:e,configurable:!0});import{randomUUID as g}from"node:crypto";import{activeBackgroundBashJobs as x,stopBackgroundBashJob as R}from"../bash-jobs.js";import{getNode as T}from"../canvas/index.js";import{withCanvasWrite as b,openDb as D}from"../canvas/db.js";import{finishJob as S,startJob as J}from"../graph/jobs.js";import{getById as h,nodeObject as O}from"../graph/objects.js";import{readLiveSession as A}from"../canvas/sessions.js";import{contextDir as M}from"../canvas/node-agent-paths.js";import{conversationStore as N}from"../conversation-store/index.js";import{requestBrokerTeardown as W}from"../runtime/host.js";import{holdRecovery as $}from"../runtime/recovery-hold.js";async function C(t,e,r){return await b(async n=>{const o=await h(n,t);return o===null||o.type!=="job"?!1:(await S(n,o,{killedBy:await e(n),...r===void 0?{}:{reason:r}}),!0)})}s(C,"killBashJobObject");async function X(t,e,r){return await b(async n=>{const o=await h(n,e);if(o!==null){if(o.type!=="job")throw new Error(`${e} names a ${o.type}`);return o}return await J(n,await O(n,t),{id:e,...r})})}s(X,"recordBashJobObject");const d=new Map;async function Z(t,e,r={}){const n=d.get(t);if(n)return n;const o=await T(t);if(!o)return Promise.resolve({confirmed:!1,error:new Error(`missing node ${t}`)});const l=(await A(t))?.sessionId,m=s(async()=>await(await D()).prepare(`SELECT type, body_json FROM run_events WHERE node_id = ?
2
- AND type IN ('node.turn.started', 'node.turn.completed') ORDER BY sequence_number DESC LIMIT 1`).get(t),"latestTurn"),v=o.run_id!==null&&o.pi_pid!==null&&(await m())?.type==="node.turn.started",E=r.holdRecovery?$(t):()=>{},j=W(t,{excludePid:r.excludePid,...r.startAfter?{startAfter:r.startAfter}:{}}),c=(async()=>{const u=await j;if(!u.confirmed)return u;try{const a=await M(t);for(const p of x(a,!0)){const i=await R(a,p.jobId,!0,t,e);if(i.kind==="not-stoppable")throw new Error(`background bash job ${p.jobId} could not be stopped`);i.kind!=="stopped"||r.deleting||await C(p.jobId,async()=>null,e)}if(v&&l&&!r.deleting){if((await m())?.type!=="node.turn.started")return await r.after?.(),u;const i=N(),{entries:k}=await i.read(l),B=k.at(-1),f=new Date().toISOString(),w=e==="permission_revoked",y={type:"message",id:g(),parentId:B?.id??null,timestamp:f,message:{role:"assistant",content:[],api:"openai-completions",provider:"crouter",model:"",usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:w?"error":"aborted",errorMessage:e,timestamp:Date.now()}};await i.append(l,[y,{type:"custom",id:g(),parentId:y.id,timestamp:f,customType:"crtr-turn-end",data:w?{stop_reason:"error",error:{code:"model_error",message:e}}:{stop_reason:"aborted"}}])}return await r.after?.(),u}catch(a){return{confirmed:!1,error:a instanceof Error?a:new Error(String(a))}}})();return d.set(t,c),c.finally(()=>{E(),d.get(t)===c&&d.delete(t)}),c}s(Z,"stopNodeSandboxes");export{C as killBashJobObject,X as recordBashJobObject,Z as stopNodeSandboxes};
1
+ var D=Object.defineProperty;var c=(t,r)=>D(t,"name",{value:r,configurable:!0});import{randomUUID as x}from"node:crypto";import{readJobSupervisorState as J,signalBashJob as k,unfinishedBashJobIds as O}from"../bash-jobs.js";import{contextDir as M}from"../canvas/node-agent-paths.js";import{getNode as P}from"../canvas/index.js";import{withCanvasWrite as R,openDb as h}from"../canvas/db.js";import{finishJob as $,ownerRunsRemotely as A,runningJobs as N,startJob as W}from"../graph/jobs.js";import{getById as S,nodeObject as C}from"../graph/objects.js";import{readLiveSession as H}from"../canvas/sessions.js";import{conversationStore as L}from"../conversation-store/index.js";import{requestBrokerTeardown as _}from"../runtime/host.js";import{holdRecovery as q,waitForRecoveryHold as F}from"../runtime/recovery-hold.js";async function T(t,r,e){return await R(async n=>{const a=await S(n,t);return a===null||a.type!=="job"?!1:(await $(n,a,{killedBy:await r(n),...e===void 0?{}:{reason:e}}),!0)})}c(T,"killBashJobObject");async function nt(t,r,e){return await R(async n=>{const a=await S(n,r);if(a!==null){if(a.type!=="job")throw new Error(`${r} names a ${a.type}`);return a}return await W(n,await C(n,t),{id:r,...e})})}c(nt,"recordBashJobObject");const y=new Map;async function U(t,r,e){return await F(t)==="lapsed"?await Y(t,r,e):await _(t,{excludePid:e.excludePid})}c(U,"stopHeldElsewhere");async function Y(t,r,e={}){const n=y.get(t);if(n)return n;const a=await P(t);if(!a)return Promise.resolve({confirmed:!1,error:new Error(`missing node ${t}`)});const g=(await H(t))?.sessionId,b=c(async()=>await(await h()).prepare(`SELECT type, body_json FROM run_events WHERE node_id = ?
2
+ AND type IN ('node.turn.started', 'node.turn.completed') ORDER BY sequence_number DESC LIMIT 1`).get(t),"latestTurn"),j=a.run_id!==null&&a.pi_pid!==null&&(await b())?.type==="node.turn.started",u=await q(t);if(u===null)return await U(t,r,e);const B=_(t,{excludePid:e.excludePid,...e.startAfter?{startAfter:e.startAfter}:{}}),w=(async()=>{const f=await B;if(!f.confirmed)return f;try{const o=c(()=>({confirmed:!1,error:new Error(`the recovery hold on ${t} lapsed during its stop`)}),"lost");if(!await u.renew())return o();const l=new Map((await N(await h(),t)).map(s=>[s.jobId,s]));if(await A(await h(),t)){if(!e.deleting)for(const s of l.keys())await T(s,async()=>null,r)}else{const s=await M(t);for(const i of new Set([...l.keys(),...O(s)])){const d=J(s,i),m=d.pgid??l.get(i)?.pgid??null,p=d.pgidIdentity??l.get(i)?.pgidIdentity??null;if(m===null||p===null)throw new Error(`bash job ${i} could not be stopped`);await k(m,p,t),!e.deleting&&l.has(i)&&await T(i,async()=>null,r)}}if(!await u.renew())return o();if(j&&g&&!e.deleting){if((await b())?.type!=="node.turn.started")return await u.renew()?(await e.after?.(),f):o();const i=L(),{entries:d}=await i.read(g),m=d.at(-1),p=new Date().toISOString(),v=r==="permission_revoked",E={type:"message",id:x(),parentId:m?.id??null,timestamp:p,message:{role:"assistant",content:[],api:"openai-completions",provider:"crouter",model:"",usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:v?"error":"aborted",errorMessage:r,timestamp:Date.now()}};await i.append(g,[E,{type:"custom",id:x(),parentId:E.id,timestamp:p,customType:"crtr-turn-end",data:v?{stop_reason:"error",error:{code:"model_error",message:r}}:{stop_reason:"aborted"}}])}return await u.renew()?(await e.after?.(),f):o()}catch(o){return{confirmed:!1,error:o instanceof Error?o:new Error(String(o))}}})();return y.set(t,w),w.finally(()=>{u.release().catch(()=>{}),y.get(t)===w&&y.delete(t)}),w}c(Y,"stopNodeSandboxes");export{T as killBashJobObject,nt as recordBashJobObject,Y as stopNodeSandboxes};
@@ -7,7 +7,8 @@ export interface AppUser {
7
7
  /** The launcher is absent on macOS; there is no app uid on that host. */
8
8
  export declare function uidForApp(app: string): Promise<number>;
9
9
  /** An app has at most one active row. A removed app that reconnects gets a new
10
- * row above every uid ever allocated, so a uid is never reused. */
10
+ * row above every uid ever allocated, so a uid is never reused. The lock makes the
11
+ * read-then-insert safe when two first starts for one app, or two apps, race. */
11
12
  export declare function allocateAppUser(app: string): Promise<AppUser>;
12
13
  export declare function activeAppUsers(): Promise<Array<{
13
14
  app: string;
@@ -1 +1 @@
1
- var o=Object.defineProperty;var e=(t,a)=>o(t,"name",{value:a,configurable:!0});import{existsSync as c}from"node:fs";import{withCanvasWrite as E,openDb as p}from"../canvas/db.js";import{APP_UID_MAX as d,APP_UID_MIN as u,LAUNCHER_SOCKET as w}from"../../launcher/spec.js";async function l(t){return process.platform!=="linux"||!c(w)?process.getuid?.()??0:(await A(t)).uid}e(l,"uidForApp");async function A(t){if(process.platform!=="linux")throw new Error("app OS users exist only on Linux");return await E(async a=>{const s=await a.prepare("SELECT app, uid, created, status FROM app_users WHERE app = ? AND status = 'active'").get(t);if(s)return s;const n=(await a.prepare("SELECT MAX(uid) AS uid FROM app_users").get()).uid,r=Math.max(u,(n??u-1)+1);if(r>d)throw new Error("app OS user range exhausted");const i=new Date().toISOString();return await a.prepare("INSERT INTO app_users (app, uid, created, status) VALUES (?, ?, ?, ?)").run(t,r,i,"active"),{app:t,uid:r,created:i,status:"active"}})}e(A,"allocateAppUser");async function v(){return(await(await p()).prepare("SELECT app, uid FROM app_users WHERE status = 'active'").all()).map(a=>({app:a.app.startsWith("app:")?a.app.slice(4):a.app,uid:a.uid}))}e(v,"activeAppUsers");async function _(t){return await(await p()).prepare("SELECT app, uid, created, status FROM app_users WHERE app = ? AND status = 'active'").get(t)??null}e(_,"activeAppUser");async function R(t){await(await p()).prepare("UPDATE app_users SET status = 'removed', removed_at = ? WHERE uid = ? AND status = 'active'").run(new Date().toISOString(),t)}e(R,"markAppUserRemoved");export{_ as activeAppUser,v as activeAppUsers,A as allocateAppUser,R as markAppUserRemoved,l as uidForApp};
1
+ var o=Object.defineProperty;var e=(t,a)=>o(t,"name",{value:a,configurable:!0});import{existsSync as c}from"node:fs";import{withCanvasWrite as E,openDb as p}from"../canvas/db.js";import{APP_UID_MAX as _,APP_UID_MIN as u,LAUNCHER_SOCKET as d}from"../../launcher/spec.js";async function l(t){return process.platform!=="linux"||!c(d)?process.getuid?.()??0:(await w(t)).uid}e(l,"uidForApp");const A=7340002;async function w(t){if(process.platform!=="linux")throw new Error("app OS users exist only on Linux");return await E(async a=>{await a.prepare(`SELECT pg_advisory_xact_lock(${A})`).get();const s=await a.prepare("SELECT app, uid, created, status FROM app_users WHERE app = ? AND status = 'active'").get(t);if(s)return s;const n=(await a.prepare("SELECT MAX(uid) AS uid FROM app_users").get()).uid,r=Math.max(u,(n??u-1)+1);if(r>_)throw new Error("app OS user range exhausted");const i=new Date().toISOString();return await a.prepare("INSERT INTO app_users (app, uid, created, status) VALUES (?, ?, ?, ?)").run(t,r,i,"active"),{app:t,uid:r,created:i,status:"active"}})}e(w,"allocateAppUser");async function v(){return(await(await p()).prepare("SELECT app, uid FROM app_users WHERE status = 'active'").all()).map(a=>({app:a.app.startsWith("app:")?a.app.slice(4):a.app,uid:a.uid}))}e(v,"activeAppUsers");async function O(t){return await(await p()).prepare("SELECT app, uid, created, status FROM app_users WHERE app = ? AND status = 'active'").get(t)??null}e(O,"activeAppUser");async function R(t){await(await p()).prepare("UPDATE app_users SET status = 'removed', removed_at = ? WHERE uid = ? AND status = 'active'").run(new Date().toISOString(),t)}e(R,"markAppUserRemoved");export{O as activeAppUser,v as activeAppUsers,w as allocateAppUser,R as markAppUserRemoved,l as uidForApp};
@@ -1,33 +1,36 @@
1
+ import { type BlobStore } from '../blobs/index.js';
1
2
  import { type StorageClient, type StorageConfig } from './client.js';
2
3
  import type { CanvasDb } from '../canvas/db.js';
3
4
  /** The fetch step of one `run.start` or `run.message` is bounded to 110 s in
4
5
  * total (the hosting router waits 120 s for its headers). */
5
6
  export declare const ATTACHMENT_FETCH_BUDGET_MS = 110000;
6
- /** One fetched upload; `url` is the storage URL the text named (for refusals). */
7
+ /** One fetched upload; `url` is the storage URL the text named (for refusals). `path` is where the node's broker puts
8
+ * the file; `blob_key` (`attachments/<run root node>/<sha256>`) is where its bytes are in the blob store. */
7
9
  export interface AttachmentUse {
8
10
  upload_id: string;
9
11
  path: string;
10
12
  url: string;
13
+ blob_key: string;
11
14
  }
12
15
  export interface ResolvedAttachments {
13
16
  /** The text with each storage URL replaced by its runtime path. */
14
17
  text: string;
15
18
  uses: AttachmentUse[];
16
- /** Remove every file (and directory) this call created. Idempotent. */
17
- cleanup: () => void;
18
19
  }
19
20
  export interface ResolveOptions {
20
21
  config?: () => StorageConfig | null;
21
22
  client?: () => StorageClient;
22
23
  budgetMs?: number;
24
+ blobs?: () => Promise<BlobStore>;
25
+ /** Paths another call of the same request has already chosen, which no file of this call may take. */
26
+ taken?: readonly string[];
23
27
  }
24
28
  /** Re-check, inside the accepting write, that every used upload is still this
25
29
  * grantee's and not pending delete (the due-work tick or an app removal may
26
30
  * have released it while its bytes were fetched). */
27
31
  export declare function assertUploadsUsable(grantee: string, uses: readonly AttachmentUse[], db: CanvasDb): Promise<void>;
28
- /** Fetch every upload a storage URL in `text` names into
29
- * `appSpace(grantee).nodes/<rootId>/attachments/` and replace exactly each
30
- * URL's span with the file's absolute runtime path. Without configured
31
- * storage the text passes through unchanged. On any failure every file this
32
- * call wrote is removed before the error propagates. */
32
+ /** Fetch every upload a storage URL in `text` names into the blob store at `attachments/<rootId>/<sha256>` and replace
33
+ * exactly each URL's span with the path the node's broker downloads that file to (`<app space>/nodes/<rootId>/attachments/<name>`).
34
+ * The control plane writes no file there. Without configured storage the text passes through unchanged. A failure leaves
35
+ * at most content-keyed blobs, which are harmless and are deleted with the node (D100). */
33
36
  export declare function resolveStorageUrls(grantee: string, rootId: string, text: string, options?: ResolveOptions): Promise<ResolvedAttachments>;
@@ -1 +1 @@
1
- var N=Object.defineProperty;var i=(e,o)=>N(e,"name",{value:o,configurable:!0});import{constants as Z,createWriteStream as j}from"node:fs";import{extname as q,join as E}from"node:path";import{Transform as G}from"node:stream";import{pipeline as X}from"node:stream/promises";import{appSpace as Y}from"../layout.js";import{RunError as v}from"../runs/operations.js";import{createFileBeneath as J,existsBeneath as z,openBeneath as K,removeTreeBeneath as O,setModeBeneath as P,unlinkBeneath as Q,withDirBeneath as V}from"../spaces/open-beneath.js";import{ensureAppSpace as ee}from"../spaces/space.js";import{storageClient as te,storageConfig as ne,StorageRefused as oe}from"./client.js";import{getUpload as k}from"./tables.js";const re=11e4,p=i((e,o)=>new v(400,"invalid_request",o,void 0,{url:e}),"refused"),y=i(e=>new v(503,"storage_unavailable",e,void 0,void 0,void 0,!0),"unavailable");function x(e,o){return/^[^\s<>"'`)\]]*/.exec(e.slice(o))[0].replace(/[.,;:!?]+$/,"")}i(x,"shownUrl");async function ae(e,o,r,c,d){const l=`${o}/`,_=[];let h=0;for(let u=e.indexOf(l,h);u!==-1;u=e.indexOf(l,h)){const t=x(e,u),f=/^([A-Za-z0-9_-]+)\/([A-Za-z0-9_-]+)\//.exec(e.slice(u+l.length));if(!f)throw p(t,`storage URL ${t} does not name an upload`);if(f[1]!==r)throw p(t,`storage URL ${t} names another runtime`);const s=await k(f[2],d);if(!s||s.grantee!==c)throw p(t,`storage URL ${t} names an unknown upload`);if(s.delete_pending_since)throw p(t,`storage URL ${t} names a deleted upload`);const $=u+l.length+f[0].length,m=encodeURIComponent(s.name);if(!e.startsWith(m,$))throw p(t,`storage URL ${t} does not match the upload's name`);_.push({start:u,end:$+m.length,upload:s}),h=$+m.length}return _}i(ae,"matchStorageUrls");async function _e(e,o,r){for(const c of o){const d=await k(c.upload_id,r);if(!d||d.grantee!==e||d.delete_pending_since)throw p(c.url,`storage URL ${c.url} names an upload deleted while it was fetched`)}}i(_e,"assertUploadsUsable");function se(e,o){if(o===1)return e;const r=q(e);return r&&r!==e?`${e.slice(0,-r.length)}-${o}${r}`:`${e}-${o}`}i(se,"candidate");async function $e(e,o,r,c={}){const d={text:r,uses:[],cleanup:i(()=>{},"cleanup")},l=(c.config??ne)();if(!l||!r.includes(`${l.base_url}/`))return d;const _=(c.client??te)(),h=await ae(r,l.base_url,_.runtimeId,e);if(!h.length)return d;const u=await ee(e),t=Y(e).nodes,f=E(t,o),s=E(f,"attachments"),$={uid:process.getuid?.()??0,gid:u.gid},m=!z(t,f),I=!m&&!z(t,s),C=[];let D=!1;const M=i(()=>{if(!D){D=!0;for(const n of C)try{Q(t,n)}catch{}try{m?O(t,f):I&&O(t,s)}catch{}}},"cleanup"),U=AbortSignal.timeout(c.budgetMs??re),b=new Map;try{V(t,s,()=>{},!0);for(const{upload:n,start:B}of h){if(b.has(n.upload_id))continue;const g=x(r,B);let L;try{L=await _.fetchUpload(n.upload_id,U)}catch(a){throw a instanceof oe?p(g,a.status===409?`storage URL ${g} names an upload that was never finished`:`storage URL ${g} names an unknown upload`):U.aborted?y("attachments could not be fetched within 110 s"):a}let w="";for(let a=1;;a++){w=E(s,se(n.name,a));try{J(t,w,Buffer.alloc(0),432);break}catch(A){if(A.code!=="EEXIST")throw L.body.destroy(),A}}C.push(w);let T=0;const W=new G({transform(a,A,H){T+=a.length,H(null,a)}}),F=K(t,w,Z.O_WRONLY);try{await X(L.body,W,j("",{fd:F,autoClose:!0}),{signal:U})}catch(a){throw U.aborted?y("attachments could not be fetched within 110 s"):a instanceof v?a:y(`storage stopped sending ${n.name}: ${a instanceof Error?a.message:String(a)}`)}if(T!==n.size)throw y(`storage sent ${T} of ${n.size} bytes of ${n.name}`);P(t,w,432,$),b.set(n.upload_id,{path:w,url:g})}}catch(n){throw M(),n}let R="",S=0;for(const n of h)R+=r.slice(S,n.start)+b.get(n.upload.upload_id).path,S=n.end;return R+=r.slice(S),{text:R,uses:[...b].map(([n,{path:B,url:g}])=>({upload_id:n,path:B,url:g})),cleanup:M}}i($e,"resolveStorageUrls");export{re as ATTACHMENT_FETCH_BUDGET_MS,_e as assertUploadsUsable,$e as resolveStorageUrls};
1
+ var M=Object.defineProperty;var h=(e,t)=>M(e,"name",{value:t,configurable:!0});import{createHash as x}from"node:crypto";import{extname as H,join as v}from"node:path";import{Writable as B}from"node:stream";import{pipeline as O}from"node:stream/promises";import{blobStore as W}from"../blobs/index.js";import{appSpace as Z}from"../layout.js";import{RunError as A}from"../runs/operations.js";import{storageClient as j,storageConfig as q,StorageRefused as D}from"./client.js";import{getUpload as E,runUsesPath as F}from"./tables.js";const G=11e4,m=h((e,t)=>new A(400,"invalid_request",t,void 0,{url:e}),"refused"),b=h(e=>new A(503,"storage_unavailable",e,void 0,void 0,void 0,!0),"unavailable");function T(e,t){return/^[^\s<>"'`)\]]*/.exec(e.slice(t))[0].replace(/[.,;:!?]+$/,"")}h(T,"shownUrl");async function N(e,t,o,r,i){const c=`${t}/`,w=[];let f=0;for(let d=e.indexOf(c,f);d!==-1;d=e.indexOf(c,f)){const a=T(e,d),g=/^([A-Za-z0-9_-]+)\/([A-Za-z0-9_-]+)\//.exec(e.slice(d+c.length));if(!g)throw m(a,`storage URL ${a} does not name an upload`);if(g[1]!==o)throw m(a,`storage URL ${a} names another runtime`);const l=await E(g[2],i);if(!l||l.grantee!==r)throw m(a,`storage URL ${a} names an unknown upload`);if(l.delete_pending_since)throw m(a,`storage URL ${a} names a deleted upload`);const u=d+c.length+g[0].length,p=encodeURIComponent(l.name);if(!e.startsWith(p,u))throw m(a,`storage URL ${a} does not match the upload's name`);w.push({start:d,end:u+p.length,upload:l}),f=u+p.length}return w}h(N,"matchStorageUrls");async function ae(e,t,o){for(const r of t){const i=await E(r.upload_id,o);if(!i||i.grantee!==e||i.delete_pending_since)throw m(r.url,`storage URL ${r.url} names an upload deleted while it was fetched`)}}h(ae,"assertUploadsUsable");function P(e,t){if(t===1)return e;const o=H(e);return o&&o!==e?`${e.slice(0,-o.length)}-${t}${o}`:`${e}-${t}`}h(P,"candidate");async function se(e,t,o,r={}){const i={text:o,uses:[]},c=(r.config??q)();if(!c||!o.includes(`${c.base_url}/`))return i;const w=(r.client??j)(),f=await N(o,c.base_url,w.runtimeId,e);if(!f.length)return i;const d=v(Z(e).nodes,t,"attachments"),a=AbortSignal.timeout(r.budgetMs??G),g=await(r.blobs??W)(),l=new Set(r.taken),u=new Map;for(const{upload:s,start:C}of f){if(u.has(s.upload_id))continue;const _=T(o,C);let y;try{y=await w.fetchUpload(s.upload_id,a)}catch(n){throw n instanceof D?m(_,n.status===409?`storage URL ${_} names an upload that was never finished`:`storage URL ${_} names an unknown upload`):a.aborted?b("attachments could not be fetched within 110 s"):n}const S=[],L=x("sha256");try{await O(y.body,new B({write(n,I,z){L.update(n),S.push(n),z()}}),{signal:a})}catch(n){throw a.aborted?b("attachments could not be fetched within 110 s"):b(`storage stopped sending ${s.name}: ${n instanceof Error?n.message:String(n)}`)}const R=Buffer.concat(S);if(R.length!==s.size)throw b(`storage sent ${R.length} of ${s.size} bytes of ${s.name}`);const k=`attachments/${t}/${L.digest("hex")}`;try{await g.put(k,R)}catch(n){throw b(`the attachment ${s.name} could not be stored: ${n instanceof Error?n.message:String(n)}`)}let $="";for(let n=1;$=v(d,P(s.name,n)),!(!l.has($)&&!await F(t,$));n++);l.add($),u.set(s.upload_id,{upload_id:s.upload_id,path:$,url:_,blob_key:k})}let p="",U=0;for(const s of f)p+=o.slice(U,s.start)+u.get(s.upload.upload_id).path,U=s.end;return p+=o.slice(U),{text:p,uses:[...u.values()]}}h(se,"resolveStorageUrls");export{G as ATTACHMENT_FETCH_BUDGET_MS,ae as assertUploadsUsable,se as resolveStorageUrls};
@@ -10,10 +10,12 @@ export interface UploadRow {
10
10
  created_at: string;
11
11
  delete_pending_since: string | null;
12
12
  }
13
+ /** `blob_key` is where the use's bytes are in the blob store (D98); null for a use recorded before the blob store held them. */
13
14
  export interface UploadUseRow {
14
15
  upload_id: string;
15
16
  run_id: string;
16
17
  path: string;
18
+ blob_key?: string | null;
17
19
  }
18
20
  export interface ShareRow {
19
21
  share_id: string;
@@ -35,6 +37,8 @@ export declare function pendingUploadDeletes(db?: CanvasDb): Promise<UploadRow[]
35
37
  /** Mark uploads for deletion (idempotent: keeps the first mark). */
36
38
  export declare function markUploadsDeletePending(uploadIds: readonly string[], db?: CanvasDb, at?: string): Promise<void>;
37
39
  export declare function insertUploadUses(uses: readonly UploadUseRow[], db?: CanvasDb): Promise<void>;
40
+ /** Whether `runId` already has an upload recorded at `path`. */
41
+ export declare function runUsesPath(runId: string, path: string, db?: CanvasDb): Promise<boolean>;
38
42
  /** Remove one recorded use (a file result undone after it was recorded). */
39
43
  export declare function deleteUploadUse(use: UploadUseRow, db?: CanvasDb): Promise<void>;
40
44
  /** Record the size storage actually holds for an upload created with `max_size`. */
@@ -1,3 +1,3 @@
1
- var E=Object.defineProperty;var t=(a,e)=>E(a,"name",{value:e,configurable:!0});import{openDb as l}from"../canvas/db.js";import{filterAsync as _}from"../async-array.js";const n=t(async a=>a??await l(),"handle"),d=t(()=>new Date().toISOString(),"now");async function w(a,e){await(await n(e)).prepare("INSERT INTO uploads (upload_id, grantee, name, media_type, size, created_at, delete_pending_since) VALUES (?, ?, ?, ?, ?, ?, NULL)").run(a.upload_id,a.grantee,a.name,a.media_type,a.size,a.created_at)}t(w,"insertUpload");async function T(a,e){return await(await n(e)).prepare("SELECT * FROM uploads WHERE upload_id = ?").get(a)??null}t(T,"getUpload");async function f(a,e){await(await n(e)).prepare("DELETE FROM uploads WHERE upload_id = ?").run(a)}t(f,"deleteUploadRow");async function L(a){return await(await n(a)).prepare("SELECT * FROM uploads WHERE delete_pending_since IS NOT NULL ORDER BY delete_pending_since, upload_id").all()}t(L,"pendingUploadDeletes");async function u(a,e,r=d()){const i=(await n(e)).prepare("UPDATE uploads SET delete_pending_since = COALESCE(delete_pending_since, ?) WHERE upload_id = ?");for(const s of a)await i.run(r,s)}t(u,"markUploadsDeletePending");async function O(a,e){const r=(await n(e)).prepare("INSERT INTO upload_uses (upload_id, run_id, path) VALUES (?, ?, ?)");for(const i of a)await r.run(i.upload_id,i.run_id,i.path)}t(O,"insertUploadUses");async function D(a,e){await(await n(e)).prepare("DELETE FROM upload_uses WHERE upload_id = ? AND run_id = ? AND path = ?").run(a.upload_id,a.run_id,a.path)}t(D,"deleteUploadUse");async function U(a,e,r){await(await n(r)).prepare("UPDATE uploads SET size = ? WHERE upload_id = ?").run(e,a)}t(U,"setUploadSize");async function x(a,e){return await(await n(e)).prepare("SELECT upload_id, run_id, path FROM upload_uses WHERE path = ? ORDER BY run_id LIMIT 1").get(a)??null}t(x,"uploadUseAt");async function y(a,e){const r=await n(e);if(!a.length)return[];const i=a.map(()=>"?").join(","),s=(await r.prepareUncached(`SELECT DISTINCT upload_id FROM upload_uses WHERE run_id IN (${i})`).all(...a)).map(o=>o.upload_id);await r.prepareUncached(`DELETE FROM upload_uses WHERE run_id IN (${i})`).run(...a);const p=await _(s,async o=>!await r.prepare("SELECT 1 FROM upload_uses WHERE upload_id = ? LIMIT 1").get(o));return await u(p,r),p}t(y,"releaseRunUploads");async function N(a,e){return(await(await n(e)).prepare(`SELECT upload_id FROM uploads u WHERE delete_pending_since IS NULL AND created_at <= ?
2
- AND NOT EXISTS (SELECT 1 FROM upload_uses x WHERE x.upload_id = u.upload_id)`).all(a)).map(r=>r.upload_id)}t(N,"staleUnusedUploads");async function g(a,e){await(await n(e)).prepare(`INSERT INTO shares (share_id, source_space, source_path, storage_key, url, created_by, created_at, expires_at, status, object_deleted_at)
3
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'on', NULL)`).run(a.share_id,a.source_space,a.source_path,a.storage_key,a.url,a.created_by,a.created_at,a.expires_at)}t(g,"insertShare");async function A(a,e){return await(await n(e)).prepare("SELECT * FROM shares WHERE share_id = ?").get(a)??null}t(A,"getShare");async function C(a,e){return a===null?await(await n(e)).prepare("SELECT * FROM shares ORDER BY created_at DESC, share_id").all():await(await n(e)).prepare("SELECT * FROM shares WHERE created_by = ? ORDER BY created_at DESC, share_id").all(a)}t(C,"listShares");async function c(a,e){await(await n(e)).prepare("UPDATE shares SET status = 'off' WHERE share_id = ?").run(a)}t(c,"turnShareOff");async function H(a,e){const r=await n(e),i=(await r.prepare("SELECT share_id FROM shares WHERE created_by = ? AND status = 'on'").all(a)).map(s=>s.share_id);return await r.prepare("UPDATE shares SET status = 'off' WHERE created_by = ? AND status = 'on'").run(a),i}t(H,"turnSharesOffFor");async function W(a=d(),e){const r=await n(e),i=(await r.prepare("SELECT share_id FROM shares WHERE status = 'on' AND expires_at IS NOT NULL AND expires_at <= ?").all(a)).map(s=>s.share_id);for(const s of i)await c(s,r);return i}t(W,"expireShares");async function m(a){return await(await n(a)).prepare("SELECT * FROM shares WHERE status = 'off' AND object_deleted_at IS NULL ORDER BY created_at, share_id").all()}t(m,"sharesOwingObjectDelete");async function M(a,e,r=d()){await(await n(e)).prepare("UPDATE shares SET object_deleted_at = COALESCE(object_deleted_at, ?) WHERE share_id = ?").run(r,a)}t(M,"markShareObjectDeleted");async function F(a,e,r=d()){const i=await n(e),s=(await i.prepare("SELECT upload_id FROM uploads WHERE grantee = ? AND delete_pending_since IS NULL").all(a)).map(p=>p.upload_id);return await u(s,i,r),s}t(F,"markGranteeUploadsDeletePending");export{f as deleteUploadRow,D as deleteUploadUse,W as expireShares,A as getShare,T as getUpload,g as insertShare,w as insertUpload,O as insertUploadUses,C as listShares,F as markGranteeUploadsDeletePending,M as markShareObjectDeleted,u as markUploadsDeletePending,L as pendingUploadDeletes,y as releaseRunUploads,U as setUploadSize,m as sharesOwingObjectDelete,N as staleUnusedUploads,c as turnShareOff,H as turnSharesOffFor,x as uploadUseAt};
1
+ var E=Object.defineProperty;var r=(a,e)=>E(a,"name",{value:e,configurable:!0});import{openDb as l}from"../canvas/db.js";import{filterAsync as _}from"../async-array.js";const n=r(async a=>a??await l(),"handle"),d=r(()=>new Date().toISOString(),"now");async function w(a,e){await(await n(e)).prepare("INSERT INTO uploads (upload_id, grantee, name, media_type, size, created_at, delete_pending_since) VALUES (?, ?, ?, ?, ?, ?, NULL)").run(a.upload_id,a.grantee,a.name,a.media_type,a.size,a.created_at)}r(w,"insertUpload");async function f(a,e){return await(await n(e)).prepare("SELECT * FROM uploads WHERE upload_id = ?").get(a)??null}r(f,"getUpload");async function T(a,e){await(await n(e)).prepare("DELETE FROM uploads WHERE upload_id = ?").run(a)}r(T,"deleteUploadRow");async function L(a){return await(await n(a)).prepare("SELECT * FROM uploads WHERE delete_pending_since IS NOT NULL ORDER BY delete_pending_since, upload_id").all()}r(L,"pendingUploadDeletes");async function u(a,e,t=d()){const i=(await n(e)).prepare("UPDATE uploads SET delete_pending_since = COALESCE(delete_pending_since, ?) WHERE upload_id = ?");for(const s of a)await i.run(t,s)}r(u,"markUploadsDeletePending");async function O(a,e){const t=(await n(e)).prepare("INSERT INTO upload_uses (upload_id, run_id, path, blob_key) VALUES (?, ?, ?, ?)");for(const i of a)await t.run(i.upload_id,i.run_id,i.path,i.blob_key??null)}r(O,"insertUploadUses");async function D(a,e,t){return await(await n(t)).prepare("SELECT 1 AS used FROM upload_uses WHERE run_id = ? AND path = ?").get(a,e)!==void 0}r(D,"runUsesPath");async function y(a,e){await(await n(e)).prepare("DELETE FROM upload_uses WHERE upload_id = ? AND run_id = ? AND path = ?").run(a.upload_id,a.run_id,a.path)}r(y,"deleteUploadUse");async function U(a,e,t){await(await n(t)).prepare("UPDATE uploads SET size = ? WHERE upload_id = ?").run(e,a)}r(U,"setUploadSize");async function x(a,e){return await(await n(e)).prepare("SELECT upload_id, run_id, path FROM upload_uses WHERE path = ? ORDER BY run_id LIMIT 1").get(a)??null}r(x,"uploadUseAt");async function N(a,e){const t=await n(e);if(!a.length)return[];const i=a.map(()=>"?").join(","),s=(await t.prepareUncached(`SELECT DISTINCT upload_id FROM upload_uses WHERE run_id IN (${i})`).all(...a)).map(o=>o.upload_id);await t.prepareUncached(`DELETE FROM upload_uses WHERE run_id IN (${i})`).run(...a);const p=await _(s,async o=>!await t.prepare("SELECT 1 FROM upload_uses WHERE upload_id = ? LIMIT 1").get(o));return await u(p,t),p}r(N,"releaseRunUploads");async function g(a,e){return(await(await n(e)).prepare(`SELECT upload_id FROM uploads u WHERE delete_pending_since IS NULL AND created_at <= ?
2
+ AND NOT EXISTS (SELECT 1 FROM upload_uses x WHERE x.upload_id = u.upload_id)`).all(a)).map(t=>t.upload_id)}r(g,"staleUnusedUploads");async function A(a,e){await(await n(e)).prepare(`INSERT INTO shares (share_id, source_space, source_path, storage_key, url, created_by, created_at, expires_at, status, object_deleted_at)
3
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'on', NULL)`).run(a.share_id,a.source_space,a.source_path,a.storage_key,a.url,a.created_by,a.created_at,a.expires_at)}r(A,"insertShare");async function C(a,e){return await(await n(e)).prepare("SELECT * FROM shares WHERE share_id = ?").get(a)??null}r(C,"getShare");async function H(a,e){return a===null?await(await n(e)).prepare("SELECT * FROM shares ORDER BY created_at DESC, share_id").all():await(await n(e)).prepare("SELECT * FROM shares WHERE created_by = ? ORDER BY created_at DESC, share_id").all(a)}r(H,"listShares");async function c(a,e){await(await n(e)).prepare("UPDATE shares SET status = 'off' WHERE share_id = ?").run(a)}r(c,"turnShareOff");async function W(a,e){const t=await n(e),i=(await t.prepare("SELECT share_id FROM shares WHERE created_by = ? AND status = 'on'").all(a)).map(s=>s.share_id);return await t.prepare("UPDATE shares SET status = 'off' WHERE created_by = ? AND status = 'on'").run(a),i}r(W,"turnSharesOffFor");async function M(a=d(),e){const t=await n(e),i=(await t.prepare("SELECT share_id FROM shares WHERE status = 'on' AND expires_at IS NOT NULL AND expires_at <= ?").all(a)).map(s=>s.share_id);for(const s of i)await c(s,t);return i}r(M,"expireShares");async function m(a){return await(await n(a)).prepare("SELECT * FROM shares WHERE status = 'off' AND object_deleted_at IS NULL ORDER BY created_at, share_id").all()}r(m,"sharesOwingObjectDelete");async function F(a,e,t=d()){await(await n(e)).prepare("UPDATE shares SET object_deleted_at = COALESCE(object_deleted_at, ?) WHERE share_id = ?").run(t,a)}r(F,"markShareObjectDeleted");async function I(a,e,t=d()){const i=await n(e),s=(await i.prepare("SELECT upload_id FROM uploads WHERE grantee = ? AND delete_pending_since IS NULL").all(a)).map(p=>p.upload_id);return await u(s,i,t),s}r(I,"markGranteeUploadsDeletePending");export{T as deleteUploadRow,y as deleteUploadUse,M as expireShares,C as getShare,f as getUpload,A as insertShare,w as insertUpload,O as insertUploadUses,H as listShares,I as markGranteeUploadsDeletePending,F as markShareObjectDeleted,u as markUploadsDeletePending,L as pendingUploadDeletes,N as releaseRunUploads,D as runUsesPath,U as setUploadSize,m as sharesOwingObjectDelete,g as staleUnusedUploads,c as turnShareOff,W as turnSharesOffFor,x as uploadUseAt};
@@ -1,47 +1,26 @@
1
+ import { type CanvasDb } from './canvas/db.js';
1
2
  import { type RateLimitScope } from './rate-limit-scope.js';
2
- export declare const ANTHROPIC_PROVIDER_ID = "anthropic";
3
- export declare const OPENAI_CODEX_PROVIDER_ID = "openai-codex";
4
- /** pi's built-in `openai` provider signed in with a ChatGPT account (dynamic OAuth client). Distinct from Codex: its own pool, credential file and sign-in. */
5
- export declare const OPENAI_CHATGPT_PROVIDER_ID = "openai";
6
- /** Longest a recorded 429 keeps an account or model family cooling, whatever the provider's Retry-After or window reset says: after this the next real call probes the provider, and a success clears the row. */
7
- export declare const MAX_RATE_LIMIT_COOLDOWN_MS: number;
8
- export declare const INVALID_REFRESH_TOKEN_BACKOFF_MS: number;
9
- export type ManagedProviderId = typeof ANTHROPIC_PROVIDER_ID | typeof OPENAI_CODEX_PROVIDER_ID | typeof OPENAI_CHATGPT_PROVIDER_ID;
10
- export declare const MANAGED_PROVIDER_IDS: readonly ManagedProviderId[];
11
- export type ModelStrength = 'ultra' | 'strong' | 'medium' | 'light';
12
- export type RotationConfig = {
13
- defaultFallbackStrength?: ModelStrength;
14
- revertWhenAvailable?: boolean;
15
- };
16
- /** `rateLimitedUntil` cools the whole account; `modelCooldowns` cools one model family on it (family → epoch ms, stored rows including expired ones). Read both through `coolingUntilFor`. */
17
- export type SubscriptionCredential = {
3
+ import { type ManagedProviderId, type RotationConfig, type SubscriptionCredential } from './managed-providers.js';
4
+ export * from './managed-providers.js';
5
+ /** One `model_accounts` row without its sealed tokens. */
6
+ type ProviderRow = {
7
+ providerId: string;
8
+ id: string;
18
9
  label: string;
19
- refresh: string;
20
- access: string;
21
- expires: number;
22
- rateLimitedUntil: number;
10
+ labelKey: string;
11
+ position: number;
23
12
  lastAttemptAt: number;
24
13
  lastRateLimitedAt: number;
14
+ rateLimitedUntil: number;
25
15
  authFailure?: 'invalid_grant';
16
+ };
17
+ type StoredCredential = {
18
+ refresh: string;
19
+ access: string;
20
+ expires: number;
26
21
  accountId?: string;
27
22
  clientId?: string;
28
- modelCooldowns?: Record<string, number>;
29
23
  };
30
- export type ProviderCandidate = {
31
- providerId: string;
32
- modelId: string;
33
- thinkingLevel?: string;
34
- registered: boolean;
35
- authenticated: boolean;
36
- coolingUntil?: number;
37
- automaticFallback?: boolean;
38
- routeId?: string;
39
- credentialSource?: string;
40
- family?: string;
41
- strength?: ModelStrength;
42
- rank?: number;
43
- };
44
- export type ProviderCandidateRejection = 'unregistered' | 'non-managed-fallback' | 'unauthenticated' | 'cooling';
45
24
  /** Refreshed tokens; `clientId` is set when the vendor rotated the issued OAuth client id. */
46
25
  export type RefreshedTokens = {
47
26
  refresh: string;
@@ -49,39 +28,20 @@ export type RefreshedTokens = {
49
28
  expires: number;
50
29
  clientId?: string;
51
30
  };
52
- /** When this account can next serve `modelId`: the later of its account-wide cooldown and the cooldown on that model's family. Without a model only the account-wide cooldown applies. 0 or a past time means ready. */
53
- export declare function coolingUntilFor(entry: Pick<SubscriptionCredential, 'rateLimitedUntil' | 'modelCooldowns'>, modelId?: string): number;
54
- /** Every cooldown on an account still in force at `now`: `scope` is `account` or the model family. */
55
- export declare function activeCooldowns(entry: Pick<SubscriptionCredential, 'rateLimitedUntil' | 'modelCooldowns'>, now?: number): Array<{
56
- scope: string;
57
- until: number;
58
- }>;
59
- /** Human text for an account's active cooldowns (`cooldown 12m`, `opus cooldown 3d 4h`), or undefined when it is ready. */
60
- export declare function formatCooldowns(entry: Pick<SubscriptionCredential, 'rateLimitedUntil' | 'modelCooldowns'>, now?: number): string | undefined;
61
- /** Whether a successful call on `modelId` has stored cooldown or auth health to clear; callers skip the write otherwise (every provider write bumps provider_revision). */
62
- export declare function subscriptionNeedsSuccessWrite(entry: Pick<SubscriptionCredential, 'rateLimitedUntil' | 'authFailure' | 'modelCooldowns'>, modelId?: string): boolean;
63
- export declare function isManagedProvider(providerId: string | undefined): providerId is ManagedProviderId;
64
- /** Stable per-install UUID sent to OpenAI as `ext_agent_host_id=urn:uuid:<id>` during ChatGPT sign-in. The install id is not a UUID, so this is its own get-or-create secret. */
65
- export declare function openAIDeviceId(): string;
66
- export declare function getProviderLabel(providerId: ManagedProviderId): string;
67
- export declare function resolveProviderCandidates(candidates: readonly ProviderCandidate[], now?: number, options?: {
68
- pinnedToFirst?: boolean;
69
- }): {
70
- candidates: ProviderCandidate[];
71
- rejected: Array<{
72
- candidate: ProviderCandidate;
73
- reason: ProviderCandidateRejection;
74
- }>;
75
- };
76
- export declare class DuplicateSubscriptionError extends Error {
77
- constructor(message: string);
31
+ /** Longest a refresh exchange may hold the account's row lock. */
32
+ export declare const REFRESH_EXCHANGE_TIMEOUT_MS = 10000;
33
+ /** Another process held this account's row lock for the whole lock timeout (a slow refresh exchange): the account is cooling for this call, not failed. */
34
+ export declare class RefreshBusyError extends Error {
35
+ constructor(provider: ManagedProviderId, label: string);
78
36
  }
79
- export declare function readSubscriptionPool(provider: ManagedProviderId): SubscriptionCredential[];
37
+ export declare function insertAccount(db: CanvasDb, person: string, provider: ManagedProviderId, row: ProviderRow, entry: StoredCredential): Promise<void>;
38
+ export declare function upsertModelCooldown(db: CanvasDb, person: string, provider: string, accountId: string, family: string, until: number, rateLimitedAt: number): Promise<void>;
39
+ export declare function readSubscriptionPool(provider: ManagedProviderId): Promise<SubscriptionCredential[]>;
80
40
  /** The pool with each account's row id: the stable handle a caller outside the process removes an account by. */
81
- export declare function readSubscriptionPoolRows(provider: ManagedProviderId): Array<{
41
+ export declare function readSubscriptionPoolRows(provider: ManagedProviderId): Promise<Array<{
82
42
  id: string;
83
43
  entry: SubscriptionCredential;
84
- }>;
44
+ }>>;
85
45
  export declare function writeSubscriptionPool(provider: ManagedProviderId, next: SubscriptionCredential[]): Promise<SubscriptionCredential[]>;
86
46
  export declare function addSubscription(provider: ManagedProviderId, entry: SubscriptionCredential): Promise<SubscriptionCredential[]>;
87
47
  export declare function upsertSubscription(provider: ManagedProviderId, entry: SubscriptionCredential): Promise<SubscriptionCredential[]>;
@@ -99,8 +59,8 @@ export declare function markSubscriptionAttempt(provider: ManagedProviderId, ref
99
59
  export declare function markSubscriptionRateLimited(provider: ManagedProviderId, ref: string, retryAfterMs: number, attemptAt?: number, rateLimitedAt?: number, scope?: RateLimitScope): Promise<SubscriptionCredential[]>;
100
60
  /** A call on `modelId` succeeded: clear the account-wide cooldown and auth failure, and that model family's cooldown, unless a newer 429 for the same scope landed after `attemptAt` began. */
101
61
  export declare function markSubscriptionSuccess(provider: ManagedProviderId, ref: string, attemptAt?: number, modelId?: string): Promise<SubscriptionCredential[]>;
62
+ /** Refresh one account's tokens under its row lock. A process that waited on the lock finds the tokens already fresh (or the account already flagged) and does not call the vendor again. */
102
63
  export declare function refreshSubscriptionCredential(provider: ManagedProviderId, label: string, exchange: (refreshToken: string, clientId?: string) => Promise<RefreshedTokens>): Promise<SubscriptionCredential>;
103
- export declare function isInvalidRefreshTokenError(error: unknown): boolean;
104
64
  export declare function modifyElectedSubscription(provider: ManagedProviderId, elect: (pool: SubscriptionCredential[]) => SubscriptionCredential | undefined, apply: (current: SubscriptionCredential) => Promise<RefreshedTokens | undefined>): Promise<SubscriptionCredential | undefined>;
105
65
  export type ManagedLoginIdentity = {
106
66
  accountId?: string;
@@ -121,9 +81,11 @@ export declare function commitManagedLogin(provider: ManagedProviderId, label: s
121
81
  lastRateLimitedAt: number;
122
82
  autoLabel?: boolean;
123
83
  }): Promise<SubscriptionCredential[]>;
124
- export declare function parseRetryAfterHeader(value: string | undefined, now?: number): number | undefined;
125
- export declare function retryAfterMsFromHeaders(headers: Headers | Record<string, string> | undefined): number | undefined;
126
- export declare function retryAfterMsFromError(error: unknown): number | undefined;
127
- export declare function readRotationConfig(): RotationConfig;
128
- export declare function writeRotationConfig(next: RotationConfig): RotationConfig;
129
- export declare function formatStatusLine(provider: ManagedProviderId, now?: number, modelId?: string): string;
84
+ /** The person's rotation settings from their `model_settings` row (defaults when there is no row), also stored for `readRotationConfig`. */
85
+ export declare function loadRotationConfig(): Promise<RotationConfig>;
86
+ /** Rotation settings for route expansion: a broker's launch snapshot (a broker never reads rows), else the person's `model_settings` row. */
87
+ export declare function readRotationConfig(): Promise<RotationConfig>;
88
+ export declare function writeRotationConfig(next: RotationConfig): Promise<RotationConfig>;
89
+ /** Stable per-install UUID sent to OpenAI as `ext_agent_host_id=urn:uuid:<id>` during ChatGPT sign-in: the person's get-or-create secret in `model_settings`. */
90
+ export declare function openAIDeviceId(): Promise<string>;
91
+ export declare function formatStatusLine(provider: ManagedProviderId, now?: number, modelId?: string): Promise<string>;
@@ -1,4 +1 @@
1
- var lt=Object.defineProperty;var r=(t,n)=>lt(t,"name",{value:n,configurable:!0});import{prepared as g}from"./sqlite-statements.js";import{createHash as ut,randomUUID as N}from"node:crypto";import{mkdirSync as dt,readFileSync as mt,writeFileSync as ft}from"node:fs";import{join as G}from"node:path";import{brokerConfigSnapshot as ht}from"./config.js";import{LockedJsonFile as bt}from"./locked-file.js";import{secretsDir as pt}from"./layout.js";import{openScopedStore as At}from"./scoped-state/db.js";import{providerStore as yt}from"./scoped-state/paths.js";import{providerModelCooldowns as P,providerModelCooldownsFor as _t,providerRowByReference as j,providerRows as gt,providerWrite as A}from"./scoped-state/providers.js";import{modelCooldownFamily as W}from"./rate-limit-scope.js";const H="anthropic",z="openai-codex",J="openai",K=600*1e3,Et=720*60*60*1e3,Jt=[H,z,J],Q="strong",X={anthropic:{label:"Claude",credentials:"anthropic-credentials.json"},"openai-codex":{label:"OpenAI Codex",credentials:"openai-codex-credentials.json"},openai:{label:"ChatGPT",credentials:"openai-credentials.json"}};function I(t){return{refresh:t.refresh,access:t.access,expires:t.expires,...t.accountId?{accountId:t.accountId}:{},...t.clientId?{clientId:t.clientId}:{}}}r(I,"stored");function V(t){return typeof t=="string"&&t.trim()?t.trim():void 0}r(V,"optionalString");function f(){return pt()}r(f,"agentDir");function B(t){return G(f(),X[t].credentials)}r(B,"credentialFile");function Y(t){return t==="ultra"||t==="strong"||t==="medium"||t==="light"?t:void 0}r(Y,"normalizeStrength");function h(t){return t.toLowerCase()}r(h,"key");function Lt(t){const n=typeof t.label=="string"?t.label.trim():"",e=typeof t.refresh=="string"?t.refresh.trim():"",a=typeof t.access=="string"?t.access.trim():"",i=Number(t.expires);if(!n||!e||!a||!Number.isFinite(i))return;const o=V(t.accountId),s=V(t.clientId);return{label:n,refresh:e,access:a,expires:i,rateLimitedUntil:Number.isFinite(Number(t.rateLimitedUntil))?Number(t.rateLimitedUntil):0,lastAttemptAt:Number.isFinite(Number(t.lastAttemptAt))?Number(t.lastAttemptAt):0,lastRateLimitedAt:Number.isFinite(Number(t.lastRateLimitedAt))?Number(t.lastRateLimitedAt):0,...t.authFailure==="invalid_grant"?{authFailure:"invalid_grant"}:{},...o?{accountId:o}:{},...s?{clientId:s}:{},...Z(t.modelCooldowns)?{modelCooldowns:Z(t.modelCooldowns)}:{}}}r(Lt,"credential");function Z(t){if(!t||typeof t!="object"||Array.isArray(t))return;const n=Object.entries(t).filter(([e,a])=>e.trim()!==""&&Number.isFinite(Number(a))).map(([e,a])=>[e,Number(a)]);return n.length?Object.fromEntries(n):void 0}r(Z,"modelCooldowns");function U(t,n){if(!n.label?.trim())throw new Error(`${b(t)} subscription label cannot be empty`);const e=Lt(n);if(!e)throw new Error(`Refusing to persist incomplete ${b(t)} subscription "${n.label.trim()}"; pool rows require both OAuth tokens.`);return e}r(U,"assertCredential");function v(t,n){if(!n||n.trim()==="")return{version:1,accounts:{}};let e;try{e=JSON.parse(n)}catch(a){throw new Error(`managed credential file ${t} is corrupt: ${a instanceof Error?a.message:String(a)}`)}if(typeof e!="object"||e===null||Array.isArray(e)||e.version!==1||typeof e.accounts!="object"||e.accounts===null||Array.isArray(e.accounts))throw new Error(`managed credential file ${t} is corrupt`);return e}r(v,"parseCredentials");function xt(t,n,e=[]){const a=n.accounts[t.id];if(!a||typeof a.refresh!="string"||typeof a.access!="string"||!Number.isFinite(a.expires))throw new Error(`managed ${t.providerId} account ${t.id} has no credential record`);return{label:t.label,refresh:a.refresh,access:a.access,expires:a.expires,rateLimitedUntil:t.authFailure||!t.lastRateLimitedAt?t.rateLimitedUntil:Math.min(t.rateLimitedUntil,t.lastRateLimitedAt+K),lastAttemptAt:t.lastAttemptAt,lastRateLimitedAt:t.lastRateLimitedAt,...t.authFailure?{authFailure:t.authFailure}:{},...a.accountId?{accountId:a.accountId}:{},...a.clientId?{clientId:a.clientId}:{},...e.length?{modelCooldowns:Object.fromEntries(e.map(i=>[i.family,i.lastRateLimitedAt?Math.min(i.rateLimitedUntil,i.lastRateLimitedAt+K):i.rateLimitedUntil]))}:{}}}r(xt,"toPublic");function k(t,n){const e=_t(f(),t);return gt(f(),t).map(a=>({row:a,entry:xt(a,n,e.get(a.id))}))}r(k,"rowsWithCredentials");function tt(t){return new bt(B(t),!0,`{"version":1,"accounts":{}}
2
- `)}r(tt,"managedCredentialFile");function et(t){return v(B(t),tt(t).readUnlocked())}r(et,"readFile");function nt(t,n){const e=t.accountId?.trim(),a=n.accountId?.trim();return!!(e&&a&&e!==a)}r(nt,"differentAccount");function _(t){return t.accountId?.trim()||(t.refresh.trim()?`refresh:${ut("sha256").update(t.refresh.trim()).digest("hex")}`:void 0)}r(_,"identity");function at(t,n){const e=V(n);return e?{...t,clientId:e}:t}r(at,"withClientId");function it(t,n,e){const a=e.refresh?.trim()??"",i=e.access?.trim()??"",o=Number(e.expires);if(!a||!i||!Number.isFinite(o))throw new Error(`${b(t)} subscription "${n}" returned an incomplete credential; the stored credential was left unchanged.`);return{refresh:a,access:i,expires:o}}r(it,"assertTokens");function Rt(t,n){const e=W(n);return Math.max(t.rateLimitedUntil||0,e===void 0?0:t.modelCooldowns?.[e]??0)}r(Rt,"coolingUntilFor");function wt(t,n=Date.now()){return[...(t.rateLimitedUntil||0)>n?[{scope:"account",until:t.rateLimitedUntil}]:[],...Object.entries(t.modelCooldowns??{}).filter(([,e])=>e>n).map(([e,a])=>({scope:e,until:a}))]}r(wt,"activeCooldowns");function Qt(t,n=Date.now()){const e=wt(t,n).map(({scope:a,until:i})=>`${a==="account"?"":`${a} `}cooldown ${It(i-n)}`);return e.length?e.join(", "):void 0}r(Qt,"formatCooldowns");function It(t){const n=Math.max(1,Math.ceil(t/6e4));if(n<60)return`${n}m`;const e=Math.floor(n/60);return e<24?`${e}h${n%60?` ${n%60}m`:""}`:`${Math.floor(e/24)}d${e%24?` ${e%24}h`:""}`}r(It,"formatWait");function Ft(t,n){const e=W(n);return e!==void 0&&t.modelCooldowns?.[e]!==void 0}r(Ft,"hasModelCooldownRow");function Xt(t,n){return!!(t.rateLimitedUntil||t.authFailure)||Ft(t,n)}r(Xt,"subscriptionNeedsSuccessWrite");function Yt(t){return t===H||t===z||t===J}r(Yt,"isManagedProvider");function Zt(){const t=G(f(),"openai-device-id");try{const e=mt(t,"utf8").trim();if(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(e))return e.toLowerCase()}catch{}const n=N();return dt(f(),{recursive:!0,mode:448}),ft(t,`${n}
3
- `,{mode:384}),n}r(Zt,"openAIDeviceId");function b(t){return X[t].label}r(b,"getProviderLabel");function vt(t,n=Date.now(),e={}){const a=[],i=[];for(let o=0;o<t.length;o++){const s=t[o],c=s.registered?s.automaticFallback===!1?"non-managed-fallback":s.authenticated?(s.coolingUntil??0)>n?"cooling":void 0:"unauthenticated":"unregistered";if(!c)a.push(s);else if(i.push({candidate:s,reason:c}),e.pinnedToFirst&&o===0&&c!=="cooling")break}return{candidates:a,rejected:i}}r(vt,"resolveProviderCandidates");class w extends Error{static{r(this,"DuplicateSubscriptionError")}constructor(n){super(n),this.name="DuplicateSubscriptionError"}}function D(t){return k(t,et(t)).map(({entry:n})=>n)}r(D,"readSubscriptionPool");function te(t){return k(t,et(t)).map(({row:n,entry:e})=>({id:n.id,entry:e}))}r(te,"readSubscriptionPoolRows");async function E(t,n){const e=B(t);return tt(t).withLockAsync(async(a,i,o)=>{const s=v(e,a);return{result:await n(s,k(t,s),c=>o(`${JSON.stringify(c)}
4
- `))}})}r(E,"locked");function O(t,n,e){g(t,"INSERT INTO managed_accounts(provider_id,id,label,label_key,position,last_attempt_at,last_rate_limited_at,rate_limited_until,auth_failure) VALUES(?,?,?,?,?,?,?,?,?)").run(n,e.id,e.label,e.labelKey,e.position,e.lastAttemptAt,e.lastRateLimitedAt,e.rateLimitedUntil,e.authFailure??null)}r(O,"insert");function L(t,n){g(t,"UPDATE managed_accounts SET label=?,label_key=?,position=?,last_attempt_at=?,last_rate_limited_at=?,rate_limited_until=?,auth_failure=? WHERE provider_id=? AND id=?").run(n.label,n.labelKey,n.position,n.lastAttemptAt,n.lastRateLimitedAt,n.rateLimitedUntil,n.authFailure??null,n.providerId,n.id)}r(L,"update");function rt(t,n,e,a,i,o){g(t,"INSERT INTO managed_account_model_cooldowns(provider_id,account_id,family,rate_limited_until,last_rate_limited_at) VALUES(?,?,?,?,?) ON CONFLICT(provider_id,account_id,family) DO UPDATE SET rate_limited_until=excluded.rate_limited_until,last_rate_limited_at=excluded.last_rate_limited_at").run(n,e,a,i,o)}r(rt,"upsertModelCooldown");function T(t,n,e){e===void 0?g(t,"DELETE FROM managed_account_model_cooldowns WHERE provider_id=?").run(n):g(t,"DELETE FROM managed_account_model_cooldowns WHERE provider_id=? AND account_id=?").run(n,e)}r(T,"deleteModelCooldowns");function x(t,n){return k(t,n).map(({entry:e})=>e)}r(x,"publicPool");function F(t,n){try{n()}catch(e){throw new Error(`managed ${b(t)} credential file was published, but its SQL membership update failed; SQL membership remains authoritative: ${e instanceof Error?e.message:String(e)}`,{cause:e})}}r(F,"writeMembershipAfterCredentialPublish");async function ee(t,n){return E(t,async(e,a,i)=>{const o=n.map(l=>U(t,l)),s=o.map((l,u)=>({entry:l,id:a[u]?.row.id??N()})),c={};for(const{entry:l,id:u}of s)c[u]=I(l);return i({version:1,accounts:c}),F(t,()=>A(f(),l=>{const u=P(l,t);g(l,"DELETE FROM managed_accounts WHERE provider_id=?").run(t),g(l,"DELETE FROM managed_account_model_cooldowns WHERE provider_id=?").run(t),s.forEach(({entry:d,id:m},y)=>{O(l,t,{providerId:t,id:m,label:d.label,labelKey:h(d.label),position:y,lastAttemptAt:d.lastAttemptAt,lastRateLimitedAt:d.lastRateLimitedAt,rateLimitedUntil:d.rateLimitedUntil,...d.authFailure?{authFailure:d.authFailure}:{}});for(const[$,C]of Object.entries(d.modelCooldowns??{}))rt(l,t,m,$,C,u.get(m)?.find(p=>p.family===$)?.lastRateLimitedAt??d.lastRateLimitedAt)})})),o})}r(ee,"writeSubscriptionPool");function M(t,n){const e=n.trim().toLowerCase(),a=Number.parseInt(e,10);return Number.isFinite(a)&&String(a)===e&&a>=1&&a<=t.length?a-1:t.findIndex(({entry:i})=>h(i.label)===e)}r(M,"resolve");async function $t(t,n){const e=U(t,n);return E(t,async(a,i,o)=>{if(i.some(({entry:u})=>h(u.label)===h(e.label)))throw new w(`${b(t)} subscription already exists: ${e.label}`);const s=_(e),c=i.find(({entry:u})=>_(u)===s);if(s&&c)throw new w(`This ${b(t)} account is already added as \u201C${c.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);const l=N();return a.accounts[l]=I(e),o(a),F(t,()=>A(f(),u=>O(u,t,{providerId:t,id:l,label:e.label,labelKey:h(e.label),position:i.length,lastAttemptAt:e.lastAttemptAt,lastRateLimitedAt:e.lastRateLimitedAt,rateLimitedUntil:e.rateLimitedUntil,...e.authFailure?{authFailure:e.authFailure}:{}}))),x(t,a)})}r($t,"addSubscription");async function ne(t,n){const e=U(t,n);return D(t).find(i=>h(i.label)===h(e.label))?St(t,e):$t(t,e)}r(ne,"upsertSubscription");async function St(t,n){const e=U(t,n);return E(t,async(a,i,o)=>{const s=i.findIndex(({entry:m})=>h(m.label)===h(e.label)),c=i[s],l=i.find(({entry:m},y)=>y!==s&&_(m)===_(e));if(_(e)&&l)throw new w(`This ${b(t)} account is already added as \u201C${l.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);if(!c){const m=N();return a.accounts[m]=I(e),o(a),F(t,()=>A(f(),y=>O(y,t,{providerId:t,id:m,label:e.label,labelKey:h(e.label),position:i.length,lastAttemptAt:e.lastAttemptAt,lastRateLimitedAt:e.lastRateLimitedAt,rateLimitedUntil:e.rateLimitedUntil}))),x(t,a)}const u=c.row,d=e.lastRateLimitedAt>=u.lastRateLimitedAt;return a.accounts[u.id]=I(e),o(a),F(t,()=>A(f(),m=>{L(m,{...u,authFailure:void 0,label:e.label,labelKey:h(e.label),lastAttemptAt:Math.max(u.lastAttemptAt,e.lastAttemptAt),lastRateLimitedAt:d?e.lastRateLimitedAt:u.lastRateLimitedAt,rateLimitedUntil:d?e.rateLimitedUntil:u.rateLimitedUntil}),nt(c.entry,e)&&T(m,t,u.id)})),x(t,a)})}r(St,"upsertSubscriptionWithUniqueAccount");async function ae(t,n,e){return E(t,async(a,i)=>{const o=M(i,n);if(o<0)throw new Error(`Unknown ${b(t)} subscription: ${n}`);const s=e.trim();if(!s)throw new Error(`${b(t)} subscription label cannot be empty`);if(i.some((c,l)=>l!==o&&h(c.entry.label)===h(s)))throw new w(`${b(t)} subscription already exists: ${s}`);return A(f(),c=>L(c,{...i[o].row,label:s,labelKey:h(s)})),x(t,a)})}r(ae,"renameSubscription");async function q(t,n,e){return E(t,async(a,i,o)=>{const s=n(i);if(s<0)throw new Error(`Unknown ${b(t)} subscription: ${e}`);const c=i[s].row;return A(f(),l=>{g(l,"DELETE FROM managed_accounts WHERE provider_id=? AND id=?").run(t,c.id),T(l,t,c.id)}),delete a.accounts[c.id],o(a),x(t,a)})}r(q,"remove");async function Nt(t,n){return q(t,e=>M(e,n),n)}r(Nt,"removeManagedAccount");async function ie(t,n){return q(t,e=>e.findIndex(({row:a})=>a.id===n),n)}r(ie,"removeManagedAccountById");async function re(t,n){return q(t,e=>M(e,n),n)}r(re,"removeSubscription");async function Ut(t){return E(t,async(n,e,a)=>{A(f(),i=>{g(i,"DELETE FROM managed_accounts WHERE provider_id=?").run(t),T(i,t)});for(const{row:i}of e)delete n.accounts[i.id];return a(n),e.length})}r(Ut,"removeAllManagedAccounts");async function oe(t,n){return n===void 0?(await Ut(t),[]):Nt(t,n)}r(oe,"removeManagedProvider");async function se(t,n){return E(t,async(e,a)=>{const i=M(a,n);if(i<0)throw new Error(`Unknown ${b(t)} subscription: ${n}`);return A(f(),o=>{[a[i],...a.filter((c,l)=>l!==i)].forEach((c,l)=>L(o,{...c.row,position:l}))}),x(t,e)})}r(se,"promoteSubscription");async function ot(t,n,e){return A(f(),a=>{const i=j(a,t,n);if(!i)throw new Error(`Unknown ${b(t)} subscription: ${n}`);const o=e(i);o&&L(a,o)}),D(t)}r(ot,"mutateHealth");async function ce(t,n,e=Date.now()){return ot(t,n,a=>({...a,lastAttemptAt:Math.max(a.lastAttemptAt,e)}))}r(ce,"markSubscriptionAttempt");async function le(t,n,e,a=Date.now(),i=Date.now(),o={kind:"account"}){return e=Math.min(Math.max(0,e),K),o.kind==="account"?ot(t,n,s=>i<s.lastRateLimitedAt?null:{...s,lastAttemptAt:Math.max(s.lastAttemptAt,a),lastRateLimitedAt:i,rateLimitedUntil:i+e}):(A(f(),s=>{const c=j(s,t,n);if(!c)throw new Error(`Unknown ${b(t)} subscription: ${n}`);L(s,{...c,lastAttemptAt:Math.max(c.lastAttemptAt,a)});const l=P(s,t).get(c.id)?.find(u=>u.family===o.family);l&&i<l.lastRateLimitedAt||rt(s,t,c.id,o.family,i+e,i)}),D(t))}r(le,"markSubscriptionRateLimited");async function ue(t,n,e=Date.now(),a){const i=W(a);return A(f(),o=>{const s=j(o,t,n);if(!s)throw new Error(`Unknown ${b(t)} subscription: ${n}`);if(e>=s.lastRateLimitedAt&&L(o,{...s,authFailure:void 0,lastAttemptAt:Math.max(s.lastAttemptAt,e),rateLimitedUntil:0}),i===void 0)return;const c=P(o,t).get(s.id)?.find(l=>l.family===i);c&&e>=c.lastRateLimitedAt&&g(o,"DELETE FROM managed_account_model_cooldowns WHERE provider_id=? AND account_id=? AND family=?").run(t,s.id,i)}),D(t)}r(ue,"markSubscriptionSuccess");function st(t){const n=Date.now();A(f(),e=>L(e,{...t,authFailure:"invalid_grant",lastAttemptAt:Math.max(t.lastAttemptAt,n),rateLimitedUntil:Math.max(t.rateLimitedUntil,n+Et)}))}r(st,"recordInvalidGrant");async function de(t,n,e){return E(t,async(a,i,o)=>{const s=M(i,n);if(s<0)throw new Error(`${b(t)} subscription "${n}" no longer exists`);const c=i[s];if(c.entry.expires>Date.now())return c.entry;let l;try{l=await e(c.entry.refresh,c.entry.clientId)}catch(d){throw ct(d)&&st(c.row),d}const u=at(it(t,c.entry.label,l),l.clientId??c.entry.clientId);return a.accounts[c.row.id]={...a.accounts[c.row.id],...u},o(a),{...c.entry,...u}})}r(de,"refreshSubscriptionCredential");function ct(t){const n=t,e=[n?.errorMessage,n?.message,t instanceof Error?t.message:""].filter(i=>typeof i=="string").join(" ");return(typeof n?.status=="number"?n.status:Number(e.match(/\bstatus[=:]\s*(\d+)/i)?.[1]))===400&&/invalid_grant|refresh token .*invalid|refresh token .*not found/i.test(e)}r(ct,"isInvalidRefreshTokenError");async function me(t,n,e){return E(t,async(a,i,o)=>{const s=new Set;let c;for(;s.size<i.length;){const l=n(i.filter(({entry:d})=>!s.has(d.label)).map(({entry:d})=>d));if(!l)break;const u=i.find(({entry:d})=>d.label===l.label);s.add(u.entry.label);try{const d=await e(u.entry);if(d){const m=at(it(t,u.entry.label,d),d.clientId??u.entry.clientId);return a.accounts[u.row.id]={...a.accounts[u.row.id],...m},o(a),{...u.entry,...m}}return u.entry}catch(d){if(!ct(d))throw d;c=d,st(u.row)}}if(c)throw c})}r(me,"modifyElectedSubscription");async function Dt(t,n){const e=n.accountId?.trim(),a=n.email?.trim();if(t!==H)return{...e?{accountId:e}:{},...a?{email:a}:{}};try{const i=await fetch("https://api.anthropic.com/api/oauth/profile",{headers:{Authorization:`Bearer ${n.access}`,Accept:"application/json"},signal:AbortSignal.timeout(3e3)});if(!i.ok)return e?{accountId:e}:{};const o=await i.json(),s=typeof o.account?.uuid=="string"&&o.account.uuid.trim()?o.account.uuid.trim():void 0,c=typeof o.account?.email=="string"&&o.account.email.trim()?o.account.email.trim():void 0;return{...s?{accountId:s}:{},...c?{email:c}:{}}}catch{return e?{accountId:e}:{}}}r(Dt,"enrichManagedLoginIdentity");async function fe(t,n,e){const a=await Dt(t,e);return E(t,async(i,o,s)=>{const c=e.autoLabel?void 0:n?.trim(),l=_({refresh:e.refresh,accountId:a.accountId}),u=(c?o.find(({entry:p})=>h(p.label)===h(c)):void 0)??o.find(({entry:p})=>_(p)===l);let d=u?.entry.label??c;if(!d&&e.autoLabel){const p=a.email||n?.trim()||`Account ${o.length+1}`;d=p;for(let S=2;o.some(({entry:R})=>h(R.label)===h(d));S++)d=`${p} (${S})`}if(!d)throw new Error(`${b(t)} subscription label cannot be empty`);const m=U(t,{label:d,refresh:e.refresh,access:e.access,expires:e.expires,...a.accountId?{accountId:a.accountId}:{},...e.clientId?{clientId:e.clientId}:{},rateLimitedUntil:0,lastAttemptAt:Date.now(),lastRateLimitedAt:e.lastRateLimitedAt});if(!u){if(o.some(({entry:R})=>h(R.label)===h(m.label)))throw new w(`${b(t)} subscription already exists: ${m.label}`);const p=o.find(({entry:R})=>_(R)===_(m));if(_(m)&&p)throw new w(`This ${b(t)} account is already added as \u201C${p.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);const S=N();return i.accounts[S]=I(m),s(i),F(t,()=>A(f(),R=>O(R,t,{providerId:t,id:S,label:m.label,labelKey:h(m.label),position:o.length,lastAttemptAt:m.lastAttemptAt,lastRateLimitedAt:m.lastRateLimitedAt,rateLimitedUntil:m.rateLimitedUntil}))),x(t,i)}const y=u.row,$=o.find(({entry:p})=>p.label!==u.entry.label&&_(p)===_(m));if(_(m)&&$)throw new w(`This ${b(t)} account is already added as \u201C${$.entry.label}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`);i.accounts[y.id]=I(m),s(i);const C=m.lastRateLimitedAt>=y.lastRateLimitedAt;return F(t,()=>A(f(),p=>{L(p,{...y,authFailure:void 0,lastAttemptAt:Math.max(y.lastAttemptAt,m.lastAttemptAt),lastRateLimitedAt:C?m.lastRateLimitedAt:y.lastRateLimitedAt,rateLimitedUntil:C?0:y.rateLimitedUntil}),nt(u.entry,m)&&T(p,t,y.id)})),x(t,i)})}r(fe,"commitManagedLogin");function Mt(t,n=Date.now()){if(!t?.trim())return;const e=Number(t.trim());if(Number.isFinite(e))return Math.max(0,Math.round(e*1e3));const a=Date.parse(t);return Number.isFinite(a)?Math.max(0,a-n):void 0}r(Mt,"parseRetryAfterHeader");function Ct(t){if(!t)return;const n=r(a=>t instanceof Headers?t.get(a)??void 0:Object.entries(t).find(([i])=>i.toLowerCase()===a)?.[1],"get"),e=n("retry-after-ms");return e!==void 0&&e.trim()!==""&&Number.isFinite(Number(e))?Math.max(0,Number(e)):Mt(n("retry-after"))}r(Ct,"retryAfterMsFromHeaders");function he(t){if(!t||typeof t!="object"||!("headers"in t))return;const n=t.headers;if(!(!(n instanceof Headers)&&(!n||typeof n!="object"||Array.isArray(n))))return Ct(n)}r(he,"retryAfterMsFromError");function kt(){const t=ht();if(t)return t.rotation;const n=g(At(yt(f())),"SELECT default_fallback_strength,revert_when_available FROM rotation_settings WHERE singleton=1").get();return{defaultFallbackStrength:Y(n?.default_fallback_strength)??Q,revertWhenAvailable:n?.revert_when_available===void 0||n.revert_when_available===null?!0:!!n.revert_when_available}}r(kt,"readRotationConfig");function be(t){const n=kt(),e={defaultFallbackStrength:Y(t.defaultFallbackStrength)??n.defaultFallbackStrength??Q,revertWhenAvailable:t.revertWhenAvailable??n.revertWhenAvailable??!0};return A(f(),a=>g(a,"INSERT INTO rotation_settings(singleton,default_fallback_strength,revert_when_available) VALUES(1,?,?) ON CONFLICT(singleton) DO UPDATE SET default_fallback_strength=excluded.default_fallback_strength,revert_when_available=excluded.revert_when_available").run(e.defaultFallbackStrength,e.revertWhenAvailable?1:0)),e}r(be,"writeRotationConfig");function pe(t,n=Date.now(),e){const a=D(t),i=a.filter(s=>s.authFailure===void 0&&Rt(s,e)<=n),o=b(t);return i.length===0?`${o} pool exhausted`:`${o} ${i[0]?.label??t} (${i.length}/${a.length} ready)`}r(pe,"formatStatusLine");export{H as ANTHROPIC_PROVIDER_ID,w as DuplicateSubscriptionError,Et as INVALID_REFRESH_TOKEN_BACKOFF_MS,Jt as MANAGED_PROVIDER_IDS,K as MAX_RATE_LIMIT_COOLDOWN_MS,J as OPENAI_CHATGPT_PROVIDER_ID,z as OPENAI_CODEX_PROVIDER_ID,wt as activeCooldowns,$t as addSubscription,fe as commitManagedLogin,Rt as coolingUntilFor,Dt as enrichManagedLoginIdentity,Qt as formatCooldowns,pe as formatStatusLine,b as getProviderLabel,ct as isInvalidRefreshTokenError,Yt as isManagedProvider,ce as markSubscriptionAttempt,le as markSubscriptionRateLimited,ue as markSubscriptionSuccess,me as modifyElectedSubscription,Zt as openAIDeviceId,Mt as parseRetryAfterHeader,se as promoteSubscription,kt as readRotationConfig,D as readSubscriptionPool,te as readSubscriptionPoolRows,de as refreshSubscriptionCredential,Ut as removeAllManagedAccounts,Nt as removeManagedAccount,ie as removeManagedAccountById,oe as removeManagedProvider,re as removeSubscription,ae as renameSubscription,vt as resolveProviderCandidates,he as retryAfterMsFromError,Ct as retryAfterMsFromHeaders,Xt as subscriptionNeedsSuccessWrite,ne as upsertSubscription,St as upsertSubscriptionWithUniqueAccount,be as writeRotationConfig,ee as writeSubscriptionPool};
1
+ var _t=Object.defineProperty;var l=(t,a)=>_t(t,"name",{value:a,configurable:!0});import{createHash as ht,randomUUID as x}from"node:crypto";import{brokerConfigSnapshot as pt}from"./config.js";import{openDb as P,withCanvasWrite as N}from"./canvas/db.js";import{personIdAsync as y}from"./canvas/person.js";import{loadSecretsKey as K,open as X,seal as $}from"./secrets/index.js";import{modelCooldownFamily as wt}from"./rate-limit-scope.js";import{registerRotationReader as At}from"./model-routes.js";import{coolingUntilFor as bt,getProviderLabel as h,isInvalidRefreshTokenError as Y,DuplicateSubscriptionError as O,ANTHROPIC_PROVIDER_ID as yt,MAX_RATE_LIMIT_COOLDOWN_MS as B,INVALID_REFRESH_TOKEN_BACKOFF_MS as Et}from"./managed-providers.js";export*from"./managed-providers.js";const J="strong";function Q(t){return t==="ultra"||t==="strong"||t==="medium"||t==="light"?t:void 0}l(Q,"normalizeStrength");function _(t){return t.toLowerCase()}l(_,"key");function j(t){return typeof t=="string"&&t.trim()?t.trim():void 0}l(j,"optionalString");function Lt(t){const a=typeof t.label=="string"?t.label.trim():"",e=typeof t.refresh=="string"?t.refresh.trim():"",n=typeof t.access=="string"?t.access.trim():"",i=Number(t.expires);if(!a||!e||!n||!Number.isFinite(i))return;const r=j(t.accountId),s=j(t.clientId);return{label:a,refresh:e,access:n,expires:i,rateLimitedUntil:Number.isFinite(Number(t.rateLimitedUntil))?Number(t.rateLimitedUntil):0,lastAttemptAt:Number.isFinite(Number(t.lastAttemptAt))?Number(t.lastAttemptAt):0,lastRateLimitedAt:Number.isFinite(Number(t.lastRateLimitedAt))?Number(t.lastRateLimitedAt):0,...t.authFailure==="invalid_grant"?{authFailure:"invalid_grant"}:{},...r?{accountId:r}:{},...s?{clientId:s}:{},...Z(t.modelCooldowns)?{modelCooldowns:Z(t.modelCooldowns)}:{}}}l(Lt,"credential");function Z(t){if(!t||typeof t!="object"||Array.isArray(t))return;const a=Object.entries(t).filter(([e,n])=>e.trim()!==""&&Number.isFinite(Number(n))).map(([e,n])=>[e,Number(n)]);return a.length?Object.fromEntries(a):void 0}l(Z,"modelCooldowns");function F(t,a){if(!a.label?.trim())throw new Error(`${h(t)} subscription label cannot be empty`);const e=Lt(a);if(!e)throw new Error(`Refusing to persist incomplete ${h(t)} subscription "${a.label.trim()}"; pool rows require both OAuth tokens.`);return e}l(F,"assertCredential");function v(t,a){const e=t.accountId?.trim(),n=a.accountId?.trim();return!!(e&&n&&e!==n)}l(v,"differentAccount");function p(t){return t.accountId?.trim()||(t.refresh.trim()?`refresh:${ht("sha256").update(t.refresh.trim()).digest("hex")}`:void 0)}l(p,"identity");function tt(t,a){const e=j(a);return e?{...t,clientId:e}:t}l(tt,"withClientId");function et(t,a,e){const n=e.refresh?.trim()??"",i=e.access?.trim()??"",r=Number(e.expires);if(!n||!i||!Number.isFinite(r))throw new Error(`${h(t)} subscription "${a}" returned an incomplete credential; the stored credential was left unchanged.`);return{refresh:n,access:i,expires:r}}l(et,"assertTokens");const at=1e4,Rt="15s",gt="55P03";class nt extends Error{static{l(this,"RefreshBusyError")}constructor(a,e){super(`${h(a)} subscription "${e}" is being refreshed by another process; treating it as cooling for this call`),this.name="RefreshBusyError"}}const it="id,label,label_key,position,last_attempt_at,last_rate_limited_at,rate_limited_until,auth_failure,refresh_sealed,access_sealed,expires,vendor_account_id,client_id,key_id",I=l((t,a,e,n)=>`model_accounts|${t}|${a}|${e}|${n}`,"aadOf");function E(t){return{refresh:t.refresh,access:t.access,expires:t.expires,...t.accountId?{accountId:t.accountId}:{},...t.clientId?{clientId:t.clientId}:{}}}l(E,"stored");function It(t,a){return{providerId:t,id:a.id,label:a.label,labelKey:a.label_key,position:a.position,lastAttemptAt:a.last_attempt_at,lastRateLimitedAt:a.last_rate_limited_at,rateLimitedUntil:a.rate_limited_until,...a.auth_failure?{authFailure:a.auth_failure}:{}}}l(It,"rowOf");function rt(t,a,e,n=[]){if(!e.refresh_sealed||!e.access_sealed||e.expires===null||!e.key_id)throw new Error(`managed ${a} account ${e.id} has no credential record`);const i=K(),r=X(e.refresh_sealed,e.key_id,i,I(t,a,e.id,"refresh")),s=X(e.access_sealed,e.key_id,i,I(t,a,e.id,"access")),o=It(a,e),c={label:o.label,refresh:r,access:s,expires:e.expires,rateLimitedUntil:o.authFailure||!o.lastRateLimitedAt?o.rateLimitedUntil:Math.min(o.rateLimitedUntil,o.lastRateLimitedAt+B),lastAttemptAt:o.lastAttemptAt,lastRateLimitedAt:o.lastRateLimitedAt,...o.authFailure?{authFailure:o.authFailure}:{},...e.vendor_account_id?{accountId:e.vendor_account_id}:{},...e.client_id?{clientId:e.client_id}:{},...n.length?{modelCooldowns:Object.fromEntries(n.map(d=>[d.family,d.lastRateLimitedAt?Math.min(d.rateLimitedUntil,d.lastRateLimitedAt+B):d.rateLimitedUntil]))}:{}};return{row:o,entry:c}}l(rt,"toPublic");async function S(t,a,e){const n=new Map,i=await t.prepare("SELECT account_id,family,rate_limited_until,last_rate_limited_at FROM model_account_cooldowns WHERE person=? AND provider_id=? ORDER BY family").all(a,e);for(const r of i){const s=n.get(r.account_id)??[];s.push({family:r.family,rateLimitedUntil:r.rate_limited_until,lastRateLimitedAt:r.last_rate_limited_at}),n.set(r.account_id,s)}return n}l(S,"loadCooldowns");async function k(t,a,e,n=!1){const i=await t.prepare(`SELECT ${it} FROM model_accounts WHERE person=? AND provider_id=? ORDER BY position${n?" FOR UPDATE":""}`).all(a,e),r=await S(t,a,e);return i.map(s=>rt(a,e,s,r.get(s.id)))}l(k,"loadPool");async function st(t,a,e,n){const i=n.trim().toLowerCase(),r=Number.parseInt(i,10),s=Number.isFinite(r)&&String(r)===i&&r>=1,o=await t.prepare(`SELECT ${it} FROM model_accounts WHERE person=? AND provider_id=? AND ${s?"position=?":"label_key=?"} FOR UPDATE`).get(a,e,s?r-1:i);if(o)return rt(a,e,o,(await S(t,a,e)).get(o.id))}l(st,"lockedAccount");async function T(t){await t.exec(`SET LOCAL lock_timeout = '${Rt}'`)}l(T,"setLockTimeout");async function L(t,a){const e=await y();return N(async n=>(await T(n),await n.prepare("SELECT pg_advisory_xact_lock(hashtext(?))").get(`model_accounts|${e}|${t}`),a({db:n,person:e,rows:await k(n,e,t,!0)})))}l(L,"locked");async function C(t,a,e,n,i){const r=K(),s=$(i.refresh,r,I(a,e,n.id,"refresh")),o=$(i.access,r,I(a,e,n.id,"access"));await t.prepare("INSERT INTO model_accounts(person,provider_id,id,label,label_key,position,last_attempt_at,last_rate_limited_at,rate_limited_until,auth_failure,refresh_sealed,access_sealed,expires,vendor_account_id,client_id,key_id) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)").run(a,e,n.id,n.label,n.labelKey,n.position,n.lastAttemptAt,n.lastRateLimitedAt,n.rateLimitedUntil,n.authFailure??null,s.sealed,o.sealed,i.expires,i.accountId??null,i.clientId??null,s.keyId)}l(C,"insertAccount");async function M(t,a,e,n,i){const r=K(),s=$(i.refresh,r,I(a,e,n,"refresh")),o=$(i.access,r,I(a,e,n,"access"));await t.prepare("UPDATE model_accounts SET refresh_sealed=?,access_sealed=?,expires=?,vendor_account_id=?,client_id=?,key_id=? WHERE person=? AND provider_id=? AND id=?").run(s.sealed,o.sealed,i.expires,i.accountId??null,i.clientId??null,s.keyId,a,e,n)}l(M,"setCredential");async function b(t,a,e){await t.prepare("UPDATE model_accounts SET label=?,label_key=?,position=?,last_attempt_at=?,last_rate_limited_at=?,rate_limited_until=?,auth_failure=? WHERE person=? AND provider_id=? AND id=?").run(e.label,e.labelKey,e.position,e.lastAttemptAt,e.lastRateLimitedAt,e.rateLimitedUntil,e.authFailure??null,a,e.providerId,e.id)}l(b,"updateHealth");async function ot(t,a,e,n,i,r,s){await t.prepare("INSERT INTO model_account_cooldowns(person,provider_id,account_id,family,rate_limited_until,last_rate_limited_at) VALUES(?,?,?,?,?,?) ON CONFLICT(person,provider_id,account_id,family) DO UPDATE SET rate_limited_until=excluded.rate_limited_until,last_rate_limited_at=excluded.last_rate_limited_at").run(a,e,n,i,r,s)}l(ot,"upsertModelCooldown");async function D(t,a,e,n){n===void 0?await t.prepare("DELETE FROM model_account_cooldowns WHERE person=? AND provider_id=?").run(a,e):await t.prepare("DELETE FROM model_account_cooldowns WHERE person=? AND provider_id=? AND account_id=?").run(a,e,n)}l(D,"deleteModelCooldowns");const R=l(async({db:t,person:a},e)=>(await k(t,a,e)).map(({entry:n})=>n),"publicPool"),lt=l(t=>t?.code===gt,"lockWaitExpired");function ct(t,a){let e;return Promise.race([a,new Promise((n,i)=>{e=setTimeout(()=>i(new Error(`${t} did not answer within ${at} ms`)),at)})]).finally(()=>clearTimeout(e))}l(ct,"withExchangeTimeout");async function V(t){return(await xt(t)).map(({entry:a})=>a)}l(V,"readSubscriptionPool");async function xt(t){const a=await y();return(await k(await P(),a,t)).map(({row:e,entry:n})=>({id:e.id,entry:n}))}l(xt,"readSubscriptionPoolRows");async function jt(t,a){return L(t,async e=>{const{db:n,person:i,rows:r}=e,s=a.map(d=>F(t,d)),o=s.map((d,u)=>({entry:d,id:r[u]?.row.id??x()})),c=await S(n,i,t);await n.prepare("DELETE FROM model_accounts WHERE person=? AND provider_id=?").run(i,t),await D(n,i,t);for(const[d,{entry:u,id:m}]of o.entries()){await C(n,i,t,{providerId:t,id:m,label:u.label,labelKey:_(u.label),position:d,lastAttemptAt:u.lastAttemptAt,lastRateLimitedAt:u.lastRateLimitedAt,rateLimitedUntil:u.rateLimitedUntil,...u.authFailure?{authFailure:u.authFailure}:{}},E(u));for(const[f,A]of Object.entries(u.modelCooldowns??{}))await ot(n,i,t,m,f,A,c.get(m)?.find(U=>U.family===f)?.lastRateLimitedAt??u.lastRateLimitedAt)}return s})}l(jt,"writeSubscriptionPool");function W(t,a){const e=a.trim().toLowerCase(),n=Number.parseInt(e,10);return Number.isFinite(n)&&String(n)===e&&n>=1&&n<=t.length?n-1:t.findIndex(({entry:i})=>_(i.label)===e)}l(W,"resolve");const H=l((t,a)=>new O(`This ${h(t)} account is already added as \u201C${a}\u201D \u2014 it shares the same quota, so a second slot adds no redundancy.`),"duplicateAccount");async function Nt(t,a){const e=F(t,a);return L(t,async n=>{const{db:i,person:r,rows:s}=n;if(s.some(({entry:d})=>_(d.label)===_(e.label)))throw new O(`${h(t)} subscription already exists: ${e.label}`);const o=p(e),c=s.find(({entry:d})=>p(d)===o);if(o&&c)throw H(t,c.entry.label);return await C(i,r,t,{providerId:t,id:x(),label:e.label,labelKey:_(e.label),position:s.length,lastAttemptAt:e.lastAttemptAt,lastRateLimitedAt:e.lastRateLimitedAt,rateLimitedUntil:e.rateLimitedUntil,...e.authFailure?{authFailure:e.authFailure}:{}},E(e)),R(n,t)})}l(Nt,"addSubscription");async function Vt(t,a){const e=F(t,a);return(await V(t)).find(i=>_(i.label)===_(e.label))?Ft(t,e):Nt(t,e)}l(Vt,"upsertSubscription");async function Ft(t,a){const e=F(t,a);return L(t,async n=>{const{db:i,person:r,rows:s}=n,o=s.findIndex(({entry:f})=>_(f.label)===_(e.label)),c=s[o],d=s.find(({entry:f},A)=>A!==o&&p(f)===p(e));if(p(e)&&d)throw H(t,d.entry.label);if(!c)return await C(i,r,t,{providerId:t,id:x(),label:e.label,labelKey:_(e.label),position:s.length,lastAttemptAt:e.lastAttemptAt,lastRateLimitedAt:e.lastRateLimitedAt,rateLimitedUntil:e.rateLimitedUntil},E(e)),R(n,t);const u=c.row,m=e.lastRateLimitedAt>=u.lastRateLimitedAt;return await M(i,r,t,u.id,E(e)),await b(i,r,{...u,authFailure:void 0,label:e.label,labelKey:_(e.label),lastAttemptAt:Math.max(u.lastAttemptAt,e.lastAttemptAt),lastRateLimitedAt:m?e.lastRateLimitedAt:u.lastRateLimitedAt,rateLimitedUntil:m?e.rateLimitedUntil:u.rateLimitedUntil}),v(c.entry,e)&&await D(i,r,t,u.id),R(n,t)})}l(Ft,"upsertSubscriptionWithUniqueAccount");async function Gt(t,a,e){return L(t,async n=>{const{db:i,person:r,rows:s}=n,o=W(s,a);if(o<0)throw new Error(`Unknown ${h(t)} subscription: ${a}`);const c=e.trim();if(!c)throw new Error(`${h(t)} subscription label cannot be empty`);if(s.some((d,u)=>u!==o&&_(d.entry.label)===_(c)))throw new O(`${h(t)} subscription already exists: ${c}`);return await b(i,r,{...s[o].row,label:c,labelKey:_(c)}),R(n,t)})}l(Gt,"renameSubscription");async function G(t,a,e){return L(t,async n=>{const{db:i,person:r,rows:s}=n,o=a(s);if(o<0)throw new Error(`Unknown ${h(t)} subscription: ${e}`);const c=s[o].row;return await i.prepare("DELETE FROM model_accounts WHERE person=? AND provider_id=? AND id=?").run(r,t,c.id),await D(i,r,t,c.id),R(n,t)})}l(G,"remove");async function St(t,a){return G(t,e=>W(e,a),a)}l(St,"removeManagedAccount");async function zt(t,a){return G(t,e=>e.findIndex(({row:n})=>n.id===a),a)}l(zt,"removeManagedAccountById");async function qt(t,a){return G(t,e=>W(e,a),a)}l(qt,"removeSubscription");async function Tt(t){return L(t,async({db:a,person:e,rows:n})=>(await a.prepare("DELETE FROM model_accounts WHERE person=? AND provider_id=?").run(e,t),await D(a,e,t),n.length))}l(Tt,"removeAllManagedAccounts");async function Xt(t,a){return a===void 0?(await Tt(t),[]):St(t,a)}l(Xt,"removeManagedProvider");async function Yt(t,a){return L(t,async e=>{const{db:n,person:i,rows:r}=e,s=W(r,a);if(s<0)throw new Error(`Unknown ${h(t)} subscription: ${a}`);const o=[r[s],...r.filter((c,d)=>d!==s)];for(const[c,d]of o.entries())await b(n,i,{...d.row,position:c});return R(e,t)})}l(Yt,"promoteSubscription");async function z(t,a,e){const n=await y();return await N(async i=>{await T(i);const r=await st(i,n,t,a);if(!r)throw new Error(`Unknown ${h(t)} subscription: ${a}`);await e(i,n,r.row)}),V(t)}l(z,"healthWrite");async function Jt(t,a,e=Date.now()){return z(t,a,(n,i,r)=>b(n,i,{...r,lastAttemptAt:Math.max(r.lastAttemptAt,e)}))}l(Jt,"markSubscriptionAttempt");async function Qt(t,a,e,n=Date.now(),i=Date.now(),r={kind:"account"}){return e=Math.min(Math.max(0,e),B),z(t,a,async(s,o,c)=>{if(r.kind==="account"){if(i<c.lastRateLimitedAt)return;await b(s,o,{...c,lastAttemptAt:Math.max(c.lastAttemptAt,n),lastRateLimitedAt:i,rateLimitedUntil:i+e});return}await b(s,o,{...c,lastAttemptAt:Math.max(c.lastAttemptAt,n)});const d=(await S(s,o,t)).get(c.id)?.find(u=>u.family===r.family);d&&i<d.lastRateLimitedAt||await ot(s,o,t,c.id,r.family,i+e,i)})}l(Qt,"markSubscriptionRateLimited");async function Zt(t,a,e=Date.now(),n){const i=wt(n);return z(t,a,async(r,s,o)=>{if(e>=o.lastRateLimitedAt&&await b(r,s,{...o,authFailure:void 0,lastAttemptAt:Math.max(o.lastAttemptAt,e),rateLimitedUntil:0}),i===void 0)return;const c=(await S(r,s,t)).get(o.id)?.find(d=>d.family===i);c&&e>=c.lastRateLimitedAt&&await r.prepare("DELETE FROM model_account_cooldowns WHERE person=? AND provider_id=? AND account_id=? AND family=?").run(s,t,o.id,i)})}l(Zt,"markSubscriptionSuccess");async function ut(t,a,e){const n=Date.now();await b(t,a,{...e,authFailure:"invalid_grant",lastAttemptAt:Math.max(e.lastAttemptAt,n),rateLimitedUntil:Math.max(e.rateLimitedUntil,n+Et)})}l(ut,"recordInvalidGrant");async function vt(t,a,e){const n=await y();let i;try{i=await N(async r=>{await T(r);const s=await st(r,n,t,a);if(!s)throw new Error(`${h(t)} subscription "${a}" no longer exists`);if(s.entry.expires>Date.now())return{entry:s.entry};if(s.row.authFailure)throw new Error(`${h(t)} subscription "${s.entry.label}" refresh refused with status=400 invalid_grant; it needs a new sign-in`);let o;try{o=await ct(`${h(t)} refresh`,e(s.entry.refresh,s.entry.clientId))}catch(d){if(!Y(d))throw d;return await ut(r,n,s.row),{refused:d}}const c=tt(et(t,s.entry.label,o),o.clientId??s.entry.clientId);return await M(r,n,t,s.row.id,E({...s.entry,...c})),{entry:{...s.entry,...c}}})}catch(r){throw lt(r)?new nt(t,a):r}if("refused"in i)throw i.refused;return i.entry}l(vt,"refreshSubscriptionCredential");async function te(t,a,e){const n=await y();let i;try{i=await N(async r=>{await T(r);const s=await k(r,n,t,!0),o=new Set;let c;for(;o.size<s.length;){const d=a(s.filter(({entry:m})=>!o.has(m.label)).map(({entry:m})=>m));if(!d)break;const u=s.find(({entry:m})=>m.label===d.label);o.add(u.entry.label);try{const m=await ct(`${h(t)} refresh`,e(u.entry));if(!m)return{entry:u.entry};const f=tt(et(t,u.entry.label,m),m.clientId??u.entry.clientId);return await M(r,n,t,u.row.id,E({...u.entry,...f})),{entry:{...u.entry,...f}}}catch(m){if(!Y(m))throw m;c=m,await ut(r,n,u.row)}}return c?{refused:c}:{}})}catch(r){throw lt(r)?new nt(t,a([])?.label??t):r}if("refused"in i)throw i.refused;return i.entry}l(te,"modifyElectedSubscription");async function Dt(t,a){const e=a.accountId?.trim(),n=a.email?.trim();if(t!==yt)return{...e?{accountId:e}:{},...n?{email:n}:{}};try{const i=await fetch("https://api.anthropic.com/api/oauth/profile",{headers:{Authorization:`Bearer ${a.access}`,Accept:"application/json"},signal:AbortSignal.timeout(3e3)});if(!i.ok)return e?{accountId:e}:{};const r=await i.json(),s=typeof r.account?.uuid=="string"&&r.account.uuid.trim()?r.account.uuid.trim():void 0,o=typeof r.account?.email=="string"&&r.account.email.trim()?r.account.email.trim():void 0;return{...s?{accountId:s}:{},...o?{email:o}:{}}}catch{return e?{accountId:e}:{}}}l(Dt,"enrichManagedLoginIdentity");async function ee(t,a,e){const n=await Dt(t,e);return L(t,async i=>{const{db:r,person:s,rows:o}=i,c=e.autoLabel?void 0:a?.trim(),d=p({refresh:e.refresh,accountId:n.accountId}),u=(c?o.find(({entry:w})=>_(w.label)===_(c)):void 0)??o.find(({entry:w})=>p(w)===d);let m=u?.entry.label??c;if(!m&&e.autoLabel){const w=n.email||a?.trim()||`Account ${o.length+1}`;m=w;for(let g=2;o.some(({entry:ft})=>_(ft.label)===_(m));g++)m=`${w} (${g})`}if(!m)throw new Error(`${h(t)} subscription label cannot be empty`);const f=F(t,{label:m,refresh:e.refresh,access:e.access,expires:e.expires,...n.accountId?{accountId:n.accountId}:{},...e.clientId?{clientId:e.clientId}:{},rateLimitedUntil:0,lastAttemptAt:Date.now(),lastRateLimitedAt:e.lastRateLimitedAt});if(!u){if(o.some(({entry:g})=>_(g.label)===_(f.label)))throw new O(`${h(t)} subscription already exists: ${f.label}`);const w=o.find(({entry:g})=>p(g)===p(f));if(p(f)&&w)throw H(t,w.entry.label);return await C(r,s,t,{providerId:t,id:x(),label:f.label,labelKey:_(f.label),position:o.length,lastAttemptAt:f.lastAttemptAt,lastRateLimitedAt:f.lastRateLimitedAt,rateLimitedUntil:f.rateLimitedUntil},E(f)),R(i,t)}const A=u.row,U=o.find(({entry:w})=>w.label!==u.entry.label&&p(w)===p(f));if(p(f)&&U)throw H(t,U.entry.label);const q=f.lastRateLimitedAt>=A.lastRateLimitedAt;return await M(r,s,t,A.id,E(f)),await b(r,s,{...A,authFailure:void 0,lastAttemptAt:Math.max(A.lastAttemptAt,f.lastAttemptAt),lastRateLimitedAt:q?f.lastRateLimitedAt:A.lastRateLimitedAt,rateLimitedUntil:q?0:A.rateLimitedUntil}),v(u.entry,f)&&await D(r,s,t,A.id),R(i,t)})}l(ee,"commitManagedLogin");const dt=new Map;function mt(t){return{defaultFallbackStrength:Q(t?.default_fallback_strength)??J,revertWhenAvailable:t?.revert_when_available===void 0||t.revert_when_available===null?!0:!!t.revert_when_available}}l(mt,"rotationFromRow");async function Ut(){const t=await y(),a=mt(await(await P()).prepare("SELECT default_fallback_strength,revert_when_available FROM model_settings WHERE person=?").get(t));return dt.set(t,a),a}l(Ut,"loadRotationConfig"),At(()=>$t());async function $t(){const t=pt();return t?t.rotation:Ut()}l($t,"readRotationConfig");async function ae(t){const a=await y(),e=await N(async n=>{await T(n),await n.prepare("INSERT INTO model_settings(person) VALUES(?) ON CONFLICT(person) DO NOTHING").run(a);const i=mt(await n.prepare("SELECT default_fallback_strength,revert_when_available FROM model_settings WHERE person=? FOR UPDATE").get(a)),r={defaultFallbackStrength:Q(t.defaultFallbackStrength)??i.defaultFallbackStrength??J,revertWhenAvailable:t.revertWhenAvailable??i.revertWhenAvailable??!0};return await n.prepare("UPDATE model_settings SET default_fallback_strength=?,revert_when_available=?,revision=revision+1 WHERE person=?").run(r.defaultFallbackStrength,r.revertWhenAvailable?1:0,a),r});return dt.set(a,e),e}l(ae,"writeRotationConfig");async function ne(){const t=await y();return(await(await P()).prepare("INSERT INTO model_settings(person,openai_device_id) VALUES(?,?) ON CONFLICT(person) DO UPDATE SET openai_device_id=COALESCE(model_settings.openai_device_id,EXCLUDED.openai_device_id) RETURNING openai_device_id").get(t,x())).openai_device_id}l(ne,"openAIDeviceId");async function ie(t,a=Date.now(),e){const n=await V(t),i=n.filter(s=>s.authFailure===void 0&&bt(s,e)<=a),r=h(t);return i.length===0?`${r} pool exhausted`:`${r} ${i[0]?.label??t} (${i.length}/${n.length} ready)`}l(ie,"formatStatusLine");export{at as REFRESH_EXCHANGE_TIMEOUT_MS,nt as RefreshBusyError,Nt as addSubscription,ee as commitManagedLogin,Dt as enrichManagedLoginIdentity,ie as formatStatusLine,C as insertAccount,Ut as loadRotationConfig,Jt as markSubscriptionAttempt,Qt as markSubscriptionRateLimited,Zt as markSubscriptionSuccess,te as modifyElectedSubscription,ne as openAIDeviceId,Yt as promoteSubscription,$t as readRotationConfig,V as readSubscriptionPool,xt as readSubscriptionPoolRows,vt as refreshSubscriptionCredential,Tt as removeAllManagedAccounts,St as removeManagedAccount,zt as removeManagedAccountById,Xt as removeManagedProvider,qt as removeSubscription,Gt as renameSubscription,ot as upsertModelCooldown,Vt as upsertSubscription,Ft as upsertSubscriptionWithUniqueAccount,ae as writeRotationConfig,jt as writeSubscriptionPool};
@@ -1,3 +1,3 @@
1
1
  var p=Object.defineProperty;var d=(e,i)=>p(e,"name",{value:i,configurable:!0});import{dirname as h}from"node:path";import{parseFrontmatterGeneric as w}from"../../frontmatter.js";import{readText as g,realpathOrSelf as R}from"../../fs-utils.js";import{checkoutChain as b}from"../../graph/repo-sync/identity.js";import{minRung as k,rungAtLeast as N,rungRank as m}from"../schema.js";import{ruleConstraintsFit as O,ruleGate as F}from"./match.js";function E(e){return e.doc.ownNode?"knowledge":e.doc.kind}d(E,"bootPartitionOf");async function L(e,i,t,a,n,o={}){const r=await v(e,n,o),c=(n==="document-read"&&o.doc!==void 0?a.filter(u=>u.object.id!==o.doc.object.id):a).map(u=>j(u,i,n,r));return W(n,c),{on:n,subject:i,reader:t,docs:c}}d(L,"planDelivery");async function v(e,i,t){if(i!=="file-read"||t.absoluteFile===void 0)return t;const a=R(t.absoluteFile);return{...t,absoluteFile:a,readFrontmatter:t.readFrontmatter??x(a),repoRoots:await y(e,a)}}d(v,"resolvePayload");function x(e){if(!/\.(md|mdx|markdown)$/i.test(e))return{};try{return w(g(e)).data??{}}catch{return{}}}d(x,"readFileFrontmatter");async function y(e,i){const t=new Map,a=e.prepare(`SELECT o.id FROM identities i JOIN objects o ON o.id = i.object
2
- WHERE o.type = 'repo' AND i.scheme = ? AND i.external_id = ?`);for(const o of b(h(i))){if(!o.ok)break;const r=await a.get(o.scheme,o.externalId);r!==void 0&&!t.has(r.id)&&t.set(r.id,o.root)}const n=await e.prepare(`SELECT o.id, i.external_id AS path FROM identities i JOIN objects o ON o.id = i.object
2
+ WHERE o.type = 'repo' AND i.scheme = ? AND i.external_id = ?`);for(const o of await b(h(i))){if(!o.ok)break;const r=await a.get(o.scheme,o.externalId);r!==void 0&&!t.has(r.id)&&t.set(r.id,o.root)}const n=await e.prepare(`SELECT o.id, i.external_id AS path FROM identities i JOIN objects o ON o.id = i.object
3
3
  WHERE o.type = 'repo' AND i.scheme = 'local-path'`).all();for(const{id:o,path:r}of n)!t.has(o)&&i.startsWith(r.endsWith("/")?r:`${r}/`)&&t.set(o,r);return t}d(y,"repoRootsOf");function j(e,i,t,a){const n=e.rules.filter(s=>s.on===t).map(s=>{const f=F(s,i);return{rule:s,gate:f,matched:f.pass&&O(s,e,t,a)}});let o=null;for(const{rule:s,matched:f}of n)f&&(o===null||m(s.deliver)>m(o.deliver))&&(o=s);const r=o?.deliver??"none",l=(t==="boot"||t==="workspace-open")&&!e.ownNode?e.limit:null,c=l===null?r:k(r,l),u=t==="boot"&&e.ownNode&&n.length===0?"name":c;return{doc:e,authoredRules:n,matchedRule:o,authoredRung:r,limit:l,cappedRung:c,ruleGate:n.length===0?null:o!==null?{pass:!0}:n.find(s=>!s.gate.pass)?.gate??{pass:!0},finalRung:u,winner:!1,shadowedBy:null}}d(j,"decide");function A(e,i){if(i==="boot"){const t=e.authoredRules.length>0&&e.authoredRules.every(a=>!a.gate.pass);return e.limit!=="none"&&!t}return N(e.finalRung,"name")}d(A,"holdsName");function W(e,i){const t=e==="boot"||e==="workspace-open",a=new Map;for(const n of i){if(!A(n,e))continue;if(!t){n.winner=!0;continue}const o=e==="boot"?`${E(n)}:${n.doc.name}`:n.doc.name,r=a.get(o);r===void 0?(a.set(o,n),n.winner=!0):n.shadowedBy=r.doc}}d(W,"resolveWinners");export{E as bootPartitionOf,L as planDelivery};
@@ -1 +1 @@
1
- var v=Object.defineProperty;var n=(e,t)=>v(e,"name",{value:t,configurable:!0});import{CONDENSED_HISTORY_MODES as u,MOUSE_MODE_DEFAULTS as p,WHIP_MESSAGE_MODES as d}from"../types.js";function i(e,t,o){const s=e.indexOf(t),a=s>=0?s:0;return e[(a+o+e.length)%e.length]}n(i,"cycleChoice");function c(e,t,o){const s=e.indexOf(t);return s>=0?e[(s+o+e.length)%e.length]:o>=0?e.find(a=>a>t)??e[0]:[...e].reverse().find(a=>a<t)??e[e.length-1]}n(c,"cycleNumber");function r(e,t){return e==="true"?!0:e==="false"?!1:`${t} must be true or false`}n(r,"booleanFromString");function f(e,t){if(!/^\d+$/.test(e))return`${t} must be an integer >= 0`;const o=Number(e);return Number.isSafeInteger(o)?o:`${t} must be an integer >= 0`}n(f,"nonNegativeIntegerFromString");function _(e,t){const o=f(e,t);return typeof o=="string"||o<1?`${t} must be an integer >= 1`:o}n(_,"positiveIntegerFromString");const m=[!1,"notify","apply"],w=[0,1,6,12,24,48,72,168],b=[1,2,3,4,5,6,8,10,12],l="openai-codex";function g(e,t){const o=e.providerOptions[t];if(o!==void 0)return o;const s={};return e.providerOptions[t]=s,s}n(g,"providerOptionsFor");const x=[1,2,3,4,5,6],h={schema_version:{surface:"specialized",owner:"internal schema migration"},marketplaces:{surface:"specialized",owner:"Plugins and pkg market commands"},plugins:{surface:"specialized",owner:"Plugins tab and pkg plugin commands"},auto_update:{surface:"settings",settings:{crtr:{key:"auto_update.crtr",label:"crouter updates",description:"Notify about crouter releases, install them automatically, or turn checks off.",valueConstraint:"true|false|notify|apply; true stores notify",applies:"Applies on the next crouter command.",group:"updates",order:70,format:n(e=>e.auto_update.crtr===!1?"off":e.auto_update.crtr,"format"),cycle:n((e,t)=>{e.auto_update.crtr=i(m,e.auto_update.crtr,t)},"cycle"),setFromString:n((e,t)=>{const o=t==="true"?"notify":t;return o!=="notify"&&o!=="apply"&&o!=="false"?"auto_update.crtr must be notify, apply, or false":(e.auto_update.crtr=o==="false"?!1:o,null)},"setFromString")},content:{key:"auto_update.content",label:"Content updates",description:"Notify about plugin and marketplace updates, apply them automatically, or turn checks off.",valueConstraint:"false|notify|apply",applies:"Applies on the next crouter command.",group:"updates",order:80,format:n(e=>e.auto_update.content===!1?"off":e.auto_update.content,"format"),cycle:n((e,t)=>{e.auto_update.content=i(m,e.auto_update.content,t)},"cycle"),setFromString:n((e,t)=>t!=="notify"&&t!=="apply"&&t!=="false"?"auto_update.content must be notify, apply, or false":(e.auto_update.content=t==="false"?!1:t,null),"setFromString")},interval_hours:{key:"auto_update.interval_hours",label:"Update interval",description:"Minimum hours between automatic update checks. Zero checks on every command.",valueConstraint:"integer >= 0",applies:"Applies on the next crouter command.",group:"updates",order:90,format:n(e=>`${e.auto_update.interval_hours}h`,"format"),cycle:n((e,t)=>{e.auto_update.interval_hours=c(w,e.auto_update.interval_hours,t)},"cycle"),setFromString:n((e,t)=>{const o=f(t,"auto_update.interval_hours");return typeof o=="string"?o:(e.auto_update.interval_hours=o,null)},"setFromString")}}},max_panes_per_window:{surface:"settings",setting:{key:"max_panes_per_window",label:"Maximum panes",description:"Maximum human-review panes placed in one tmux window before using another window.",valueConstraint:"integer >= 1",applies:"Applies to the next pane placement.",group:"panes",order:20,format:n(e=>String(e.max_panes_per_window),"format"),cycle:n((e,t)=>{e.max_panes_per_window=c(b,e.max_panes_per_window,t)},"cycle"),setFromString:n((e,t)=>{const o=_(t,"max_panes_per_window");return typeof o=="string"?o:(e.max_panes_per_window=o,null)},"setFromString")}},auto_open_child_viewers:{surface:"settings",setting:{key:"auto_open_child_viewers",label:"Open child panes",description:"Open each managed child beside its watched parent. Off keeps new children running in the background.",valueConstraint:"true|false",applies:"Applies to the next managed child created; independent roots still open their attended viewer.",group:"panes",order:10,format:n(e=>e.auto_open_child_viewers?"on":"off","format"),cycle:n(e=>{e.auto_open_child_viewers=!e.auto_open_child_viewers},"cycle"),setFromString:n((e,t)=>{const o=r(t,"auto_open_child_viewers");return typeof o=="string"?o:(e.auto_open_child_viewers=o,null)},"setFromString")}},brokerThresholds:{surface:"specialized",owner:"Broker limits tab"},lifecycle:{surface:"specialized",owner:"sys config set lifecycle.unattendedParkMs"},completion_bell:{surface:"settings",setting:{key:"completion_bell",label:"Completion bell",description:"Ring a short sound when the node in an attached viewer finishes a turn.",valueConstraint:"true|false",applies:"Applies on the next turn end.",group:"display",order:41,format:n(e=>e.completion_bell?"on":"off","format"),cycle:n(e=>{e.completion_bell=!e.completion_bell},"cycle"),setFromString:n((e,t)=>{const o=r(t,"completion_bell");return typeof o=="string"?o:(e.completion_bell=o,null)},"setFromString")}},working_gerunds:{surface:"specialized",owner:"sys config set working_gerunds"},whip_mode:{surface:"settings",setting:{key:"whip_mode",label:"Whip mode",description:"Play the whip animation when you send a prompt and when a viewer opens for an agent with an initial prompt.",valueConstraint:"true|false",applies:"Applies on the next attach.",group:"display",order:39,format:n(e=>e.whip_mode?"on":"off","format"),cycle:n(e=>{e.whip_mode=!e.whip_mode},"cycle"),setFromString:n((e,t)=>{const o=r(t,"whip_mode");return typeof o=="string"?o:(e.whip_mode=o,null)},"setFromString")}},page_surface:{surface:"settings",setting:{key:"page_surface",label:"Page human surface",description:"This host reads pages in a browser rather than a terminal. Agents are told the page module runs as written, HTML pages are offered, and document review and live display leave the command tree.",valueConstraint:"true|false",applies:"Applies when human command help is assembled.",group:"display",order:37,format:n(e=>e.page_surface?"on":"off","format"),cycle:n(e=>{e.page_surface=!e.page_surface},"cycle"),setFromString:n((e,t)=>{const o=r(t,"page_surface");return typeof o=="string"?o:(e.page_surface=o,null)},"setFromString")}},whip_messages:{surface:"specialized",owner:"sys config set whip_messages"},whip_message_mode:{surface:"settings",setting:{key:"whip_message_mode",label:"Whip message",description:"What the whip action sends: a random line from whip_messages, or the fixed \u201CGo faster\u201D.",valueConstraint:d.join("|"),applies:"Applies on the next attach.",group:"display",order:40,format:n(e=>e.whip_message_mode==="go-faster"?"go faster":"random","format"),cycle:n((e,t)=>{e.whip_message_mode=i(d,e.whip_message_mode,t)},"cycle"),setFromString:n((e,t)=>d.includes(t)?(e.whip_message_mode=t,null):`whip_message_mode must be one of: ${d.join("|")}`,"setFromString")}},mouse_mode_default:{surface:"settings",setting:{key:"mouse_mode_default",label:"Default mouse mode",description:"Initial mouse wheel scrolling for each attached viewer.",valueConstraint:p.join("|"),applies:"Applies on the next attach.",group:"display",order:38,format:n(e=>e.mouse_mode_default==="tmux"?"follow tmux":e.mouse_mode_default,"format"),cycle:n((e,t)=>{e.mouse_mode_default=i(p,e.mouse_mode_default,t)},"cycle"),setFromString:n((e,t)=>p.includes(t)?(e.mouse_mode_default=t,null):`mouse_mode_default must be one of: ${p.join("|")}`,"setFromString")}},live_cycles:{surface:"settings",setting:{key:"live_cycles",label:"Live cycles",description:"Trailing refresh cycles rendered at full fidelity; older cycles condense to text.",valueConstraint:"integer >= 1",applies:"Applies on the next attach.",group:"display",order:50,format:n(e=>String(e.live_cycles),"format"),cycle:n((e,t)=>{e.live_cycles=c(x,e.live_cycles,t)},"cycle"),setFromString:n((e,t)=>{const o=_(t,"live_cycles");return typeof o=="string"?o:(e.live_cycles=o,null)},"setFromString")}},condensed_history:{surface:"settings",setting:{key:"condensed_history",label:"Condensed history",description:"Messages retained from condensed cycles. Tool calls are never kept.",valueConstraint:u.join("|"),applies:"Applies on the next attach.",group:"display",order:60,format:n(e=>e.condensed_history,"format"),cycle:n((e,t)=>{e.condensed_history=i(u,e.condensed_history,t)},"cycle"),setFromString:n((e,t)=>u.includes(t)?(e.condensed_history=t,null):`condensed_history must be one of: ${u.join("|")}`,"setFromString")}},bash_tool_purpose:{surface:"settings",setting:{key:"bash_tool_purpose",label:"Bash purpose field",description:"Expose the optional purpose label to agents in the bash tool schema. Off removes the field without disabling the bash valve.",valueConstraint:"true|false",applies:"Applies on the next node session.",group:"display",order:25,format:n(e=>e.bash_tool_purpose?"on":"off","format"),cycle:n(e=>{e.bash_tool_purpose=!e.bash_tool_purpose},"cycle"),setFromString:n((e,t)=>{const o=r(t,"bash_tool_purpose");return typeof o=="string"?o:(e.bash_tool_purpose=o,null)},"setFromString")}},fold_finished_tools:{surface:"settings",setting:{key:"fold_finished_tools",label:"Fold finished tools",description:"Fold every settled tool call to its call line. Toggle live with /fold-tools.",valueConstraint:"true|false",applies:"Applies on the next attach.",group:"display",order:30,format:n(e=>e.fold_finished_tools?"on":"off","format"),cycle:n(e=>{e.fold_finished_tools=!e.fold_finished_tools},"cycle"),setFromString:n((e,t)=>{const o=r(t,"fold_finished_tools");return typeof o=="string"?o:(e.fold_finished_tools=o,null)},"setFromString")}},summarize_tool_calls:{surface:"settings",setting:{key:"summarize_tool_calls",label:"Summarize tool calls",description:"Replace completed folded tool-call groups with a one-line broker summary when available.",valueConstraint:"true|false",applies:"Applies when the next visible message closes a tool-call group.",group:"display",order:35,format:n(e=>e.summarize_tool_calls?"on":"off","format"),cycle:n(e=>{e.summarize_tool_calls=!e.summarize_tool_calls},"cycle"),setFromString:n((e,t)=>{const o=r(t,"summarize_tool_calls");return typeof o=="string"?o:(e.summarize_tool_calls=o,null)},"setFromString")}},document_port:{surface:"settings",setting:{key:"document_port",label:"Document browser port",description:"Fixed localhost port where crtrd serves editable document pages.",valueConstraint:"1-65535",applies:"Applies after daemon handover and on the next attach.",group:"display",order:37.5,format:n(e=>String(e.document_port),"format"),cycle:n((e,t)=>{e.document_port=Math.max(1,Math.min(65535,e.document_port+t))},"cycle"),setFromString:n((e,t)=>{const o=Number(t);return!/^\d+$/.test(t)||!Number.isInteger(o)||o<1||o>65535?"document_port must be an integer from 1 to 65535":(e.document_port=o,null)},"setFromString")}},detailed_tool_recaps:{surface:"settings",setting:{key:"detailed_tool_recaps",label:"Detailed tool recaps",description:"Show each document read and edited file in folded tool recaps. Off shows a compact count line instead.",valueConstraint:"true|false",applies:"Applies on the next attach.",group:"display",order:36,format:n(e=>e.detailed_tool_recaps?"on":"off","format"),cycle:n(e=>{e.detailed_tool_recaps=!e.detailed_tool_recaps},"cycle"),setFromString:n((e,t)=>{const o=r(t,"detailed_tool_recaps");return typeof o=="string"?o:(e.detailed_tool_recaps=o,null)},"setFromString")}},consolidate_replies:{surface:"settings",setting:{key:"consolidate_replies",label:"Consolidate replies",description:"Experiment: show one rewritten agent reply per message you type, with the working folded behind it (/show-working).",valueConstraint:"true|false",applies:"Applies on the next attach; the broker reads it per turn.",group:"display",order:36.5,format:n(e=>e.consolidate_replies?"on":"off","format"),cycle:n(e=>{e.consolidate_replies=!e.consolidate_replies},"cycle"),setFromString:n((e,t)=>{const o=r(t,"consolidate_replies");return typeof o=="string"?o:(e.consolidate_replies=o,null)},"setFromString")}},keybindings:{surface:"specialized",owner:"Keybindings tab"},tmux_passthrough:{surface:"specialized",owner:"Keybindings tab"},providerOptions:{surface:"settings",settings:{"openai-codex.fastMode":{key:`providerOptions.${l}.fastMode`,label:"Codex fast mode",description:"Serve Codex turns at the ChatGPT priority tier: about 1.5x faster generation for about 2x the credits. Displayed cost does not reflect the premium.",valueConstraint:"true|false",applies:"Applies on each node's next turn.",group:"providers",order:40,format:n(e=>e.providerOptions[l]?.fastMode?"on":"off","format"),cycle:n(e=>{const t=g(e,l);t.fastMode=!t.fastMode},"cycle"),setFromString:n((e,t)=>{const o=r(t,`providerOptions.${l}.fastMode`);return typeof o=="string"?o:(g(e,l).fastMode=o,null)},"setFromString")}}},modelLadders:{surface:"specialized",owner:"Models tab"},modelRoutes:{surface:"specialized",owner:"Models route declarations"},modelRouting:{surface:"specialized",owner:"Models route declarations"},kinds:{surface:"specialized",owner:"Models tab and kind registry"},page_components:{surface:"specialized",owner:"Page component catalog: product-registered page component kinds"},remoteCanvas:{surface:"specialized",owner:"canvas config commands"},api:{surface:"specialized",owner:"sys connect"},paths:{surface:"specialized",owner:"runtime layout"},ledger:{surface:"specialized",owner:"money ledger (take_pct)"},store:{surface:"specialized",owner:"daemon conversation store"},runtime:{surface:"specialized",owner:"app-facing listener"},spawnEnv:{surface:"specialized",owner:"spawn environment allow-list"},bin:{surface:"specialized",owner:"bare-binary contributions on every node PATH"},humanActions:{surface:"specialized",owner:"scope-configured human completion actions"}},O=Object.keys(h);function S(){const e=[];for(const t of Object.values(h))t.surface==="settings"&&("setting"in t?e.push(t.setting):e.push(...Object.values(t.settings)));return e.sort((t,o)=>t.order-o.order)}n(S,"collectUserSettings");const y=S();function A(e){return y.find(t=>t.key===e)}n(A,"findUserSetting");function z(e){return y.filter(t=>t.group===e)}n(z,"userSettingsInGroup");export{h as CONFIG_SETTINGS_COVERAGE,O as SCOPE_CONFIG_KEYS,y as USER_SETTINGS,A as findUserSetting,z as userSettingsInGroup};
1
+ var v=Object.defineProperty;var n=(e,t)=>v(e,"name",{value:t,configurable:!0});import{CONDENSED_HISTORY_MODES as u,MOUSE_MODE_DEFAULTS as p,WHIP_MESSAGE_MODES as d}from"../types.js";function i(e,t,o){const s=e.indexOf(t),a=s>=0?s:0;return e[(a+o+e.length)%e.length]}n(i,"cycleChoice");function c(e,t,o){const s=e.indexOf(t);return s>=0?e[(s+o+e.length)%e.length]:o>=0?e.find(a=>a>t)??e[0]:[...e].reverse().find(a=>a<t)??e[e.length-1]}n(c,"cycleNumber");function r(e,t){return e==="true"?!0:e==="false"?!1:`${t} must be true or false`}n(r,"booleanFromString");function f(e,t){if(!/^\d+$/.test(e))return`${t} must be an integer >= 0`;const o=Number(e);return Number.isSafeInteger(o)?o:`${t} must be an integer >= 0`}n(f,"nonNegativeIntegerFromString");function _(e,t){const o=f(e,t);return typeof o=="string"||o<1?`${t} must be an integer >= 1`:o}n(_,"positiveIntegerFromString");const m=[!1,"notify","apply"],w=[0,1,6,12,24,48,72,168],b=[1,2,3,4,5,6,8,10,12],l="openai-codex";function g(e,t){const o=e.providerOptions[t];if(o!==void 0)return o;const s={};return e.providerOptions[t]=s,s}n(g,"providerOptionsFor");const x=[1,2,3,4,5,6],h={schema_version:{surface:"specialized",owner:"internal schema migration"},marketplaces:{surface:"specialized",owner:"Plugins and pkg market commands"},plugins:{surface:"specialized",owner:"Plugins tab and pkg plugin commands"},auto_update:{surface:"settings",settings:{crtr:{key:"auto_update.crtr",label:"crouter updates",description:"Notify about crouter releases, install them automatically, or turn checks off.",valueConstraint:"true|false|notify|apply; true stores notify",applies:"Applies on the next crouter command.",group:"updates",order:70,format:n(e=>e.auto_update.crtr===!1?"off":e.auto_update.crtr,"format"),cycle:n((e,t)=>{e.auto_update.crtr=i(m,e.auto_update.crtr,t)},"cycle"),setFromString:n((e,t)=>{const o=t==="true"?"notify":t;return o!=="notify"&&o!=="apply"&&o!=="false"?"auto_update.crtr must be notify, apply, or false":(e.auto_update.crtr=o==="false"?!1:o,null)},"setFromString")},content:{key:"auto_update.content",label:"Content updates",description:"Notify about plugin and marketplace updates, apply them automatically, or turn checks off.",valueConstraint:"false|notify|apply",applies:"Applies on the next crouter command.",group:"updates",order:80,format:n(e=>e.auto_update.content===!1?"off":e.auto_update.content,"format"),cycle:n((e,t)=>{e.auto_update.content=i(m,e.auto_update.content,t)},"cycle"),setFromString:n((e,t)=>t!=="notify"&&t!=="apply"&&t!=="false"?"auto_update.content must be notify, apply, or false":(e.auto_update.content=t==="false"?!1:t,null),"setFromString")},interval_hours:{key:"auto_update.interval_hours",label:"Update interval",description:"Minimum hours between automatic update checks. Zero checks on every command.",valueConstraint:"integer >= 0",applies:"Applies on the next crouter command.",group:"updates",order:90,format:n(e=>`${e.auto_update.interval_hours}h`,"format"),cycle:n((e,t)=>{e.auto_update.interval_hours=c(w,e.auto_update.interval_hours,t)},"cycle"),setFromString:n((e,t)=>{const o=f(t,"auto_update.interval_hours");return typeof o=="string"?o:(e.auto_update.interval_hours=o,null)},"setFromString")}}},max_panes_per_window:{surface:"settings",setting:{key:"max_panes_per_window",label:"Maximum panes",description:"Maximum human-review panes placed in one tmux window before using another window.",valueConstraint:"integer >= 1",applies:"Applies to the next pane placement.",group:"panes",order:20,format:n(e=>String(e.max_panes_per_window),"format"),cycle:n((e,t)=>{e.max_panes_per_window=c(b,e.max_panes_per_window,t)},"cycle"),setFromString:n((e,t)=>{const o=_(t,"max_panes_per_window");return typeof o=="string"?o:(e.max_panes_per_window=o,null)},"setFromString")}},auto_open_child_viewers:{surface:"settings",setting:{key:"auto_open_child_viewers",label:"Open child panes",description:"Open each managed child beside its watched parent. Off keeps new children running in the background.",valueConstraint:"true|false",applies:"Applies to the next managed child created; independent roots still open their attended viewer.",group:"panes",order:10,format:n(e=>e.auto_open_child_viewers?"on":"off","format"),cycle:n(e=>{e.auto_open_child_viewers=!e.auto_open_child_viewers},"cycle"),setFromString:n((e,t)=>{const o=r(t,"auto_open_child_viewers");return typeof o=="string"?o:(e.auto_open_child_viewers=o,null)},"setFromString")}},brokerThresholds:{surface:"specialized",owner:"Broker limits tab"},lifecycle:{surface:"specialized",owner:"sys config set lifecycle.unattendedParkMs"},completion_bell:{surface:"settings",setting:{key:"completion_bell",label:"Completion bell",description:"Ring a short sound when the node in an attached viewer finishes a turn.",valueConstraint:"true|false",applies:"Applies on the next turn end.",group:"display",order:41,format:n(e=>e.completion_bell?"on":"off","format"),cycle:n(e=>{e.completion_bell=!e.completion_bell},"cycle"),setFromString:n((e,t)=>{const o=r(t,"completion_bell");return typeof o=="string"?o:(e.completion_bell=o,null)},"setFromString")}},working_gerunds:{surface:"specialized",owner:"sys config set working_gerunds"},whip_mode:{surface:"settings",setting:{key:"whip_mode",label:"Whip mode",description:"Play the whip animation when you send a prompt and when a viewer opens for an agent with an initial prompt.",valueConstraint:"true|false",applies:"Applies on the next attach.",group:"display",order:39,format:n(e=>e.whip_mode?"on":"off","format"),cycle:n(e=>{e.whip_mode=!e.whip_mode},"cycle"),setFromString:n((e,t)=>{const o=r(t,"whip_mode");return typeof o=="string"?o:(e.whip_mode=o,null)},"setFromString")}},page_surface:{surface:"settings",setting:{key:"page_surface",label:"Page human surface",description:"This host reads pages in a browser rather than a terminal. Agents are told the page module runs as written, HTML pages are offered, and document review and live display leave the command tree.",valueConstraint:"true|false",applies:"Applies when human command help is assembled.",group:"display",order:37,format:n(e=>e.page_surface?"on":"off","format"),cycle:n(e=>{e.page_surface=!e.page_surface},"cycle"),setFromString:n((e,t)=>{const o=r(t,"page_surface");return typeof o=="string"?o:(e.page_surface=o,null)},"setFromString")}},whip_messages:{surface:"specialized",owner:"sys config set whip_messages"},whip_message_mode:{surface:"settings",setting:{key:"whip_message_mode",label:"Whip message",description:"What the whip action sends: a random line from whip_messages, or the fixed \u201CGo faster\u201D.",valueConstraint:d.join("|"),applies:"Applies on the next attach.",group:"display",order:40,format:n(e=>e.whip_message_mode==="go-faster"?"go faster":"random","format"),cycle:n((e,t)=>{e.whip_message_mode=i(d,e.whip_message_mode,t)},"cycle"),setFromString:n((e,t)=>d.includes(t)?(e.whip_message_mode=t,null):`whip_message_mode must be one of: ${d.join("|")}`,"setFromString")}},mouse_mode_default:{surface:"settings",setting:{key:"mouse_mode_default",label:"Default mouse mode",description:"Initial mouse wheel scrolling for each attached viewer.",valueConstraint:p.join("|"),applies:"Applies on the next attach.",group:"display",order:38,format:n(e=>e.mouse_mode_default==="tmux"?"follow tmux":e.mouse_mode_default,"format"),cycle:n((e,t)=>{e.mouse_mode_default=i(p,e.mouse_mode_default,t)},"cycle"),setFromString:n((e,t)=>p.includes(t)?(e.mouse_mode_default=t,null):`mouse_mode_default must be one of: ${p.join("|")}`,"setFromString")}},live_cycles:{surface:"settings",setting:{key:"live_cycles",label:"Live cycles",description:"Trailing refresh cycles rendered at full fidelity; older cycles condense to text.",valueConstraint:"integer >= 1",applies:"Applies on the next attach.",group:"display",order:50,format:n(e=>String(e.live_cycles),"format"),cycle:n((e,t)=>{e.live_cycles=c(x,e.live_cycles,t)},"cycle"),setFromString:n((e,t)=>{const o=_(t,"live_cycles");return typeof o=="string"?o:(e.live_cycles=o,null)},"setFromString")}},condensed_history:{surface:"settings",setting:{key:"condensed_history",label:"Condensed history",description:"Messages retained from condensed cycles. Tool calls are never kept.",valueConstraint:u.join("|"),applies:"Applies on the next attach.",group:"display",order:60,format:n(e=>e.condensed_history,"format"),cycle:n((e,t)=>{e.condensed_history=i(u,e.condensed_history,t)},"cycle"),setFromString:n((e,t)=>u.includes(t)?(e.condensed_history=t,null):`condensed_history must be one of: ${u.join("|")}`,"setFromString")}},bash_tool_purpose:{surface:"settings",setting:{key:"bash_tool_purpose",label:"Bash purpose field",description:"Expose the optional purpose label to agents in the bash tool schema. Off removes the field without disabling the bash valve.",valueConstraint:"true|false",applies:"Applies on the next node session.",group:"display",order:25,format:n(e=>e.bash_tool_purpose?"on":"off","format"),cycle:n(e=>{e.bash_tool_purpose=!e.bash_tool_purpose},"cycle"),setFromString:n((e,t)=>{const o=r(t,"bash_tool_purpose");return typeof o=="string"?o:(e.bash_tool_purpose=o,null)},"setFromString")}},fold_finished_tools:{surface:"settings",setting:{key:"fold_finished_tools",label:"Fold finished tools",description:"Fold every settled tool call to its call line. Toggle live with /fold-tools.",valueConstraint:"true|false",applies:"Applies on the next attach.",group:"display",order:30,format:n(e=>e.fold_finished_tools?"on":"off","format"),cycle:n(e=>{e.fold_finished_tools=!e.fold_finished_tools},"cycle"),setFromString:n((e,t)=>{const o=r(t,"fold_finished_tools");return typeof o=="string"?o:(e.fold_finished_tools=o,null)},"setFromString")}},summarize_tool_calls:{surface:"settings",setting:{key:"summarize_tool_calls",label:"Summarize tool calls",description:"Replace completed folded tool-call groups with a one-line broker summary when available.",valueConstraint:"true|false",applies:"Applies when the next visible message closes a tool-call group.",group:"display",order:35,format:n(e=>e.summarize_tool_calls?"on":"off","format"),cycle:n(e=>{e.summarize_tool_calls=!e.summarize_tool_calls},"cycle"),setFromString:n((e,t)=>{const o=r(t,"summarize_tool_calls");return typeof o=="string"?o:(e.summarize_tool_calls=o,null)},"setFromString")}},document_port:{surface:"settings",setting:{key:"document_port",label:"Document browser port",description:"Fixed localhost port where crtrd serves editable document pages.",valueConstraint:"1-65535",applies:"Applies after daemon handover and on the next attach.",group:"display",order:37.5,format:n(e=>String(e.document_port),"format"),cycle:n((e,t)=>{e.document_port=Math.max(1,Math.min(65535,e.document_port+t))},"cycle"),setFromString:n((e,t)=>{const o=Number(t);return!/^\d+$/.test(t)||!Number.isInteger(o)||o<1||o>65535?"document_port must be an integer from 1 to 65535":(e.document_port=o,null)},"setFromString")}},detailed_tool_recaps:{surface:"settings",setting:{key:"detailed_tool_recaps",label:"Detailed tool recaps",description:"Show each document read and edited file in folded tool recaps. Off shows a compact count line instead.",valueConstraint:"true|false",applies:"Applies on the next attach.",group:"display",order:36,format:n(e=>e.detailed_tool_recaps?"on":"off","format"),cycle:n(e=>{e.detailed_tool_recaps=!e.detailed_tool_recaps},"cycle"),setFromString:n((e,t)=>{const o=r(t,"detailed_tool_recaps");return typeof o=="string"?o:(e.detailed_tool_recaps=o,null)},"setFromString")}},consolidate_replies:{surface:"settings",setting:{key:"consolidate_replies",label:"Consolidate replies",description:"Experiment: show one rewritten agent reply per message you type, with the working folded behind it (/show-working).",valueConstraint:"true|false",applies:"Applies on the next attach; the broker reads it per turn.",group:"display",order:36.5,format:n(e=>e.consolidate_replies?"on":"off","format"),cycle:n(e=>{e.consolidate_replies=!e.consolidate_replies},"cycle"),setFromString:n((e,t)=>{const o=r(t,"consolidate_replies");return typeof o=="string"?o:(e.consolidate_replies=o,null)},"setFromString")}},keybindings:{surface:"specialized",owner:"Keybindings tab"},tmux_passthrough:{surface:"specialized",owner:"Keybindings tab"},providerOptions:{surface:"settings",settings:{"openai-codex.fastMode":{key:`providerOptions.${l}.fastMode`,label:"Codex fast mode",description:"Serve Codex turns at the ChatGPT priority tier: about 1.5x faster generation for about 2x the credits. Displayed cost does not reflect the premium.",valueConstraint:"true|false",applies:"Applies on each node's next turn.",group:"providers",order:40,format:n(e=>e.providerOptions[l]?.fastMode?"on":"off","format"),cycle:n(e=>{const t=g(e,l);t.fastMode=!t.fastMode},"cycle"),setFromString:n((e,t)=>{const o=r(t,`providerOptions.${l}.fastMode`);return typeof o=="string"?o:(g(e,l).fastMode=o,null)},"setFromString")}}},modelLadders:{surface:"specialized",owner:"Models tab"},modelRoutes:{surface:"specialized",owner:"Models route declarations"},modelRouting:{surface:"specialized",owner:"Models route declarations"},kinds:{surface:"specialized",owner:"Models tab and kind registry"},page_components:{surface:"specialized",owner:"Page component catalog: product-registered page component kinds"},remoteCanvas:{surface:"specialized",owner:"canvas config commands"},api:{surface:"specialized",owner:"sys connect"},paths:{surface:"specialized",owner:"runtime layout"},ledger:{surface:"specialized",owner:"money ledger (take_pct, sandbox rates)"},store:{surface:"specialized",owner:"daemon conversation store"},runtime:{surface:"specialized",owner:"app-facing listener"},spawnEnv:{surface:"specialized",owner:"spawn environment allow-list"},bin:{surface:"specialized",owner:"bare-binary contributions on every node PATH"},humanActions:{surface:"specialized",owner:"scope-configured human completion actions"}},O=Object.keys(h);function S(){const e=[];for(const t of Object.values(h))t.surface==="settings"&&("setting"in t?e.push(t.setting):e.push(...Object.values(t.settings)));return e.sort((t,o)=>t.order-o.order)}n(S,"collectUserSettings");const y=S();function A(e){return y.find(t=>t.key===e)}n(A,"findUserSetting");function z(e){return y.filter(t=>t.group===e)}n(z,"userSettingsInGroup");export{h as CONFIG_SETTINGS_COVERAGE,O as SCOPE_CONFIG_KEYS,y as USER_SETTINGS,A as findUserSetting,z as userSettingsInGroup};