@humwick/server 0.130.0 → 0.130.2

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 (486) hide show
  1. package/README.md +5 -0
  2. package/docs/guides/tools-administration.md +6 -1
  3. package/docs/guides/tools-coordination.md +1 -0
  4. package/docs/guides/tools-tasks.md +8 -0
  5. package/docs/guides/workflows.md +58 -0
  6. package/docs/openapi.json +414 -1
  7. package/package.json +8 -17
  8. package/public/assets/{MessageBubble-BWpoBjl4.js → MessageBubble-BWLqMGKG.js} +2 -2
  9. package/public/assets/{PlanGraphPanel-C0auck3n.js → PlanGraphPanel-3TlIQpjk.js} +2 -2
  10. package/public/assets/{TraceView-DBnOx7AQ.js → TraceView-Wimm0Czv.js} +2 -2
  11. package/public/assets/{VaultDecisionCard-0kp2oNBC.js → VaultDecisionCard-Bau_vNax.js} +2 -2
  12. package/public/assets/{activity-yLztXEBt.js → activity-CrSurn-r.js} +2 -2
  13. package/public/assets/{agent-lineage-BbJDR1dW.js → agent-lineage-CknAJKcb.js} +2 -2
  14. package/public/assets/agent-liveness-CaNwK0Zf.js +2 -0
  15. package/public/assets/{agent-name-CTZBrSD9.js → agent-name-DKbiIhBo.js} +2 -2
  16. package/public/assets/agent-profiles-DwF6EQ4R.js +2 -0
  17. package/public/assets/{agent-project-badge-9TqzUUYE.js → agent-project-badge-Diz4Sjjb.js} +2 -2
  18. package/public/assets/{agent-tools-D4n40pn8.js → agent-tools-CRhpRQza.js} +2 -2
  19. package/public/assets/{agent-type-icon-D6FUarOf.js → agent-type-icon-LrUEU3lB.js} +2 -2
  20. package/public/assets/{agents-BYDslkJc.js → agents-B-v66C-4.js} +2 -2
  21. package/public/assets/{analytics-B-XSbEMC.js → analytics-BqCb-LKi.js} +2 -2
  22. package/public/assets/{artifact-viewer-DdWIfV6M.js → artifact-viewer-kf4pGuAR.js} +2 -2
  23. package/public/assets/{artifacts-oBOqks-2.js → artifacts-D32zyw4Y.js} +2 -2
  24. package/public/assets/{artifacts-DPN1q4pA.js → artifacts-DJevXcvg.js} +2 -2
  25. package/public/assets/{automation-4KcRFXEu.js → automation-BpRajx_5.js} +2 -2
  26. package/public/assets/{backlog-Cw9xPTCF.js → backlog-D_UjTdzX.js} +2 -2
  27. package/public/assets/{busy-health-CTu32MZt.js → busy-health-N3VEh4ao.js} +2 -2
  28. package/public/assets/{channels-BiXzg1G9.js → channels-9jopungq.js} +2 -2
  29. package/public/assets/{chat-Ctk2qwvA.js → chat-CSXrTB3C.js} +2 -2
  30. package/public/assets/{code-preview-Bm1wWqAp.js → code-preview-p1NnmqK0.js} +2 -2
  31. package/public/assets/{connectors-C36ax4Vw.js → connectors-CZdqlDNL.js} +2 -2
  32. package/public/assets/{cosmos-Jj9LK2Wf.js → cosmos-BgdsE4dG.js} +2 -2
  33. package/public/assets/{crate-agent-H6S7-B9T.js → crate-agent-30aXTKva.js} +2 -2
  34. package/public/assets/{dependency-graph-Cd1E23wa.js → dependency-graph-B_jvw2GE.js} +2 -2
  35. package/public/assets/{deploys-Bwfq146q.js → deploys-DoN6bAb5.js} +2 -2
  36. package/public/assets/{display-CfO7rTuF.js → display-BrKWFGJz.js} +2 -2
  37. package/public/assets/{files-DhhZI2Ih.js → files-D6XEu8Ng.js} +2 -2
  38. package/public/assets/fleet-C7jQWbw2.js +2 -0
  39. package/public/assets/{fleet-metrics-BkHP-P3R.js → fleet-metrics-BiDs3MLx.js} +2 -2
  40. package/public/assets/{formatted-body-Cm1RrzmN.js → formatted-body-CQBwc1fb.js} +3 -3
  41. package/public/assets/{formatted-body-impl-DQiOuOD1.js → formatted-body-impl-DyG0WsQS.js} +2 -2
  42. package/public/assets/{host-providers-dialog-CHqwXTov.js → host-providers-dialog-DKaW31dN.js} +2 -2
  43. package/public/assets/index-CRHep3M9.js +15 -0
  44. package/public/assets/index-MYAIwYTX.css +2 -0
  45. package/public/assets/{insights-fH1EL-qZ.js → insights-CzrtkpOE.js} +2 -2
  46. package/public/assets/{integrations-C5Oix6ng.js → integrations-aVsUmigt.js} +2 -2
  47. package/public/assets/{issue-ref-chip-ODyondIa.js → issue-ref-chip-BnSlM_95.js} +2 -2
  48. package/public/assets/{log-viewer-Cvfy6PW7.js → log-viewer-qe7Ee47Z.js} +2 -2
  49. package/public/assets/{maintenance-D8CDH5wZ.js → maintenance-NfjqFZAk.js} +2 -2
  50. package/public/assets/{managed-agents-egllbpij.js → managed-agents-BiH0HgaE.js} +2 -2
  51. package/public/assets/{markdown-preview-CYSFgVgK.js → markdown-preview-BixaT3Xr.js} +3 -3
  52. package/public/assets/{markdown-preview-impl-BteVIjX4.js → markdown-preview-impl-DNGlWIS3.js} +2 -2
  53. package/public/assets/{memory-C8WmXsyr.js → memory-DNsCl6UX.js} +2 -2
  54. package/public/assets/{messages-BzQdqlO6.js → messages-DopU1L1X.js} +2 -2
  55. package/public/assets/{orchestrators-B1HxzkNw.js → orchestrators-Cq7ckLQT.js} +2 -2
  56. package/public/assets/{overview-DCUnI4Z7.js → overview-C1DCTzzV.js} +2 -2
  57. package/public/assets/{pairs-ftCd2XcD.js → pairs-DE_m6rrE.js} +2 -2
  58. package/public/assets/{profile-icon-B2zH8DMH.js → profile-icon-B3rfh77F.js} +2 -2
  59. package/public/assets/project-profile-instructions-DQWZE0kA.js +2 -0
  60. package/public/assets/projects-Br2Bd2h1.js +2 -0
  61. package/public/assets/{prompt-settings-Bftq9czX.js → prompt-settings-BKvFsvGD.js} +2 -2
  62. package/public/assets/provider-display-DsuWMvVL.js +2 -0
  63. package/public/assets/{registry-DWCU5c8q.js → registry-Cw38_6gW.js} +2 -2
  64. package/public/assets/run-card-CpKqRbhg.js +2 -0
  65. package/public/assets/scroll-text-BKXS1ahr.js +2 -0
  66. package/public/assets/{secrets-C7GIUATz.js → secrets-DxI-vzSq.js} +2 -2
  67. package/public/assets/{service-access-settings-C3aujqTG.js → service-access-settings-D9s_VJbd.js} +2 -2
  68. package/public/assets/{settings-C6sZ9eBa.js → settings-CKZTVGIx.js} +2 -2
  69. package/public/assets/{shipping-Bu8p1gEw.js → shipping-B9YtnwtG.js} +2 -2
  70. package/public/assets/{stats-V9Z-GxUd.js → stats-DyUBKyWI.js} +2 -2
  71. package/public/assets/{status-dot-CVv0iGHL.js → status-dot-DYqlEUhJ.js} +2 -2
  72. package/public/assets/{store-BvtFdlGX.js → store-DcClXLH0.js} +3 -3
  73. package/public/assets/{tasks-CXU75HRF.js → tasks-Bh7Chr4-.js} +2 -2
  74. package/public/assets/{teams-BPf976zb.js → teams-bowI7qVY.js} +2 -2
  75. package/public/assets/{terminal-viewer-DE9BOe-8.js → terminal-viewer-BgVLHdsU.js} +3 -3
  76. package/public/assets/{terminal-viewer-impl-D04pjmGh.js → terminal-viewer-impl-Dz22UPy6.js} +2 -2
  77. package/public/assets/token-estimate-Co-Jdphh.js +2 -0
  78. package/public/assets/{use-agent-terminal-B1yR456R.js → use-agent-terminal-DSQQ-iY7.js} +2 -2
  79. package/public/assets/{use-decisions-D-S4tpDf.js → use-decisions-samMQhQf.js} +2 -2
  80. package/public/assets/use-project-list-59LheVP7.js +2 -0
  81. package/public/assets/{use-selectors-CnrAk2YP.js → use-selectors-BZa25xEB.js} +2 -2
  82. package/public/assets/{user-avatar-nBFIcR4M.js → user-avatar-DHxlvUvV.js} +2 -2
  83. package/public/assets/versioned-prompt-editor-BmED5DnJ.js +8 -0
  84. package/public/assets/{work-queue-TCjJxeT-.js → work-queue-CSvpuNLz.js} +2 -2
  85. package/public/assets/{workflow-marker-Cf3Hf2fN.js → workflow-marker-DAZcj5qt.js} +2 -2
  86. package/public/assets/{workflows-CXQX20Qj.js → workflows-DdmxRSyg.js} +2 -2
  87. package/public/assets/{workspaces-qSYzKYMA.js → workspaces-BxeKfVuF.js} +2 -2
  88. package/public/cosmos.html +4 -4
  89. package/public/index.html +31 -29
  90. package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
  91. package/runner/plugins/claude/hooks/gate-run-guard.provisioned.mjs +31 -1
  92. package/runner/src/adapters/claude-effort.ts +2 -4
  93. package/runner/src/adapters/codex-launch-config.ts +4 -5
  94. package/runner/src/adapters/codex.ts +1 -1
  95. package/runner/src/adapters/opencode-provider.ts +7 -22
  96. package/runner/src/adapters/opencode.ts +4 -4
  97. package/runner/src/agent-runner/credentials.ts +42 -12
  98. package/runner/src/agent-runner/inbox.ts +27 -0
  99. package/runner/src/agent-runner/lifecycle-milestones.ts +0 -2
  100. package/runner/src/bin/claude.ts +4 -0
  101. package/runner/src/bin/codex.ts +4 -0
  102. package/runner/src/bin/opencode.ts +4 -0
  103. package/runner/src/index.ts +5 -2
  104. package/runner/src/provider-setup.ts +34 -0
  105. package/runner/src/providers.ts +2 -1
  106. package/runner/src/runner-core.ts +18 -23
  107. package/runner/src/spawn-input.ts +2 -1
  108. package/runner/src/test-run-dedup.ts +59 -56
  109. package/scripts/cd-config.ts +24 -3
  110. package/scripts/gate-run-lease.ts +10 -7
  111. package/scripts/gate.sh +5 -1
  112. package/src/branch-land-notice.ts +93 -0
  113. package/src/branch-landed.ts +7 -76
  114. package/src/bus.ts +19 -20
  115. package/src/cd-deploy-notify.ts +30 -2
  116. package/src/cd-host-lag-notify.ts +7 -6
  117. package/src/ci/incidents.ts +2 -2
  118. package/src/ci/monitor.ts +5 -8
  119. package/src/ci/root-missing.ts +2 -7
  120. package/src/ci/transition-notify.ts +7 -39
  121. package/src/cli/cd-pauses.ts +19 -0
  122. package/src/cli/orchestrator.ts +1 -1
  123. package/src/cli/state.ts +2 -19
  124. package/src/cli/task-edit.ts +22 -0
  125. package/src/cli/task.ts +3 -1
  126. package/src/cli/workflow.ts +8 -2
  127. package/src/commands-db.ts +10 -6
  128. package/src/commands-write.ts +8 -0
  129. package/src/config-store-write.ts +13 -1
  130. package/src/config-store.ts +25 -14
  131. package/src/db/activity-async.ts +6 -1
  132. package/src/db/activity-write-ops.ts +13 -1
  133. package/src/db/activity.ts +1 -14
  134. package/src/db/agent-lifecycle-write-ops.ts +1 -0
  135. package/src/db/agent-meta.ts +2 -2
  136. package/src/db/agent-ownership-write-ops.ts +4 -0
  137. package/src/db/agent-search.ts +1 -15
  138. package/src/db/agents-async.ts +58 -7
  139. package/src/db/agents-write-ops.ts +28 -5
  140. package/src/db/agents.ts +81 -54
  141. package/src/db/artifacts-write-ops.ts +8 -3
  142. package/src/db/bus-outbox-write-ops.ts +1 -0
  143. package/src/db/channels-write-ops.ts +4 -0
  144. package/src/db/ci-incidents-async.ts +5 -5
  145. package/src/db/ci-incidents-write-ops.ts +2 -2
  146. package/src/db/claude-api-budget-ops.ts +5 -5
  147. package/src/db/claude-app-oauth.ts +4 -4
  148. package/src/db/codex-reset-consumes-write-ops.ts +3 -0
  149. package/src/db/command-row.ts +10 -2
  150. package/src/db/commands-list-query.ts +2 -0
  151. package/src/db/commands-spawn-read.ts +1 -1
  152. package/src/db/commands-write-ops.ts +21 -0
  153. package/src/db/config-store-write-ops.ts +9 -2
  154. package/src/db/continuation-archives-async.ts +5 -0
  155. package/src/db/continuation-archives-write-ops.ts +9 -0
  156. package/src/db/continuations.ts +2 -2
  157. package/src/db/db-writer-worker.ts +8 -1
  158. package/src/db/delivery-write-ops.ts +5 -0
  159. package/src/db/deploy-events-async.ts +1 -0
  160. package/src/db/deploy-events-read-ops.ts +2 -1
  161. package/src/db/deploy-events.ts +35 -4
  162. package/src/db/hosts-registration.ts +10 -2
  163. package/src/db/hosts-write-ops.ts +14 -0
  164. package/src/db/hosts.ts +9 -6
  165. package/src/db/inbox-write-ops.ts +4 -0
  166. package/src/db/inbox.ts +0 -14
  167. package/src/db/index.ts +2 -0
  168. package/src/db/insights-write-ops.ts +1 -0
  169. package/src/db/integrations-async.ts +8 -0
  170. package/src/db/integrations-ops.ts +15 -0
  171. package/src/db/integrations.ts +15 -21
  172. package/src/db/issue-lifecycle.ts +3 -4
  173. package/src/db/issue-refs-async.ts +20 -0
  174. package/src/db/issue-refs-write-ops.ts +29 -0
  175. package/src/db/issue-refs.ts +7 -8
  176. package/src/db/long-running-work.ts +2 -2
  177. package/src/db/macos-privacy.ts +1 -1
  178. package/src/db/main-connection-writes.ts +23 -2
  179. package/src/db/maintenance-job-reconcile.ts +61 -0
  180. package/src/db/maintenance-job-runs-async.ts +14 -0
  181. package/src/db/maintenance-job-write-ops.ts +9 -0
  182. package/src/db/mappers.ts +2 -14
  183. package/src/db/merge-lease.ts +1 -15
  184. package/src/db/message-reads-write-ops.ts +3 -0
  185. package/src/db/message-write-ops.ts +4 -0
  186. package/src/db/migrations.ts +7 -15
  187. package/src/db/orchestrators-async.ts +7 -3
  188. package/src/db/orchestrators-write-ops.ts +7 -1
  189. package/src/db/orchestrators.ts +21 -77
  190. package/src/db/pairs.ts +1 -13
  191. package/src/db/project-profile-instructions.ts +50 -0
  192. package/src/db/prompt-versions-ops.ts +1 -0
  193. package/src/db/prompt-versions.ts +9 -2
  194. package/src/db/provider-catalog-write-ops.ts +4 -4
  195. package/src/db/provider-config-history.ts +8 -3
  196. package/src/db/provider-flap-messages-write-ops.ts +1 -0
  197. package/src/db/provider-model-seed.ts +34 -0
  198. package/src/db/provider-quota-lease-write-ops.ts +1 -0
  199. package/src/db/provider-quota-write-ops.ts +2 -0
  200. package/src/db/provisioning-write-ops.ts +9 -8
  201. package/src/db/read-op-marshaling-fixtures.ts +5 -0
  202. package/src/db/read-ops.ts +4 -0
  203. package/src/db/routing-policy.ts +3 -4
  204. package/src/db/schema.ts +9 -3
  205. package/src/db/secrets-broker-write-ops.ts +5 -5
  206. package/src/db/secrets-vault-write.ts +7 -7
  207. package/src/db/services-write-ops.ts +3 -0
  208. package/src/db/spawn-delegation-state.ts +11 -4
  209. package/src/db/spawn-tree-cleanup.ts +3 -0
  210. package/src/db/stats-async.ts +4 -3
  211. package/src/db/task-entity.ts +23 -2
  212. package/src/db/task-list.ts +3 -1
  213. package/src/db/tasks.ts +1 -17
  214. package/src/db/team-spawn-delegation.ts +1 -1
  215. package/src/db/teams-write-ops.ts +4 -4
  216. package/src/db/timer-cutover-write-ops.ts +7 -0
  217. package/src/db/timer-ingress-write-ops.ts +9 -0
  218. package/src/db/timer-ingress.ts +7 -1
  219. package/src/db/timer-operator-write-ops.ts +14 -0
  220. package/src/db/timer-transfer-write-ops.ts +4 -0
  221. package/src/db/timer-write-ops.ts +14 -0
  222. package/src/db/token-db-write-ops.ts +21 -0
  223. package/src/db/token-standing.ts +15 -11
  224. package/src/db/user-settings-write-ops.ts +1 -0
  225. package/src/db/vault-backup.ts +2 -2
  226. package/src/db/vault-expiry.ts +2 -2
  227. package/src/db/vault-health.ts +3 -3
  228. package/src/db/vault-token-ops.ts +8 -8
  229. package/src/db/web-tool-call-ops.ts +3 -3
  230. package/src/db/workflow-contact.ts +1 -1
  231. package/src/db/workflow-task-read-ops.ts +30 -0
  232. package/src/db/write-ops.ts +15 -1
  233. package/src/db/writer-fault-seam.ts +5 -2
  234. package/src/db/writer-gateway.ts +21 -12
  235. package/src/dispatch-availability.ts +10 -6
  236. package/src/ephemeral/{landing-ingress-proof-worker.ts → landing-ingress-proof.test-fixture.ts} +4 -2
  237. package/src/host/disk-alert.ts +121 -0
  238. package/src/lifecycle-manager.ts +3 -2
  239. package/src/maintenance/cd-watcher.ts +3 -3
  240. package/src/maintenance/critical-job-watchdog.ts +16 -17
  241. package/src/maintenance/index.ts +1 -0
  242. package/src/maintenance/jobs.ts +3 -2
  243. package/src/maintenance/scheduler.ts +84 -128
  244. package/src/maintenance/stalled-delivery-pager.ts +6 -5
  245. package/src/maintenance/workspaces.ts +1 -1
  246. package/src/managed-policy.ts +31 -27
  247. package/src/mcp/index.ts +5 -2
  248. package/src/mcp/output-descriptions.ts +8 -8
  249. package/src/mcp/presentation.ts +4 -4
  250. package/src/mcp/project-profile-instructions.ts +32 -0
  251. package/src/mcp/tool-groups.ts +1 -1
  252. package/src/mcp/tools-spawn.ts +4 -3
  253. package/src/mcp-agent-action.ts +1 -2
  254. package/src/mcp-host-tools.ts +1 -1
  255. package/src/mcp-issue-ref-tools.ts +12 -11
  256. package/src/mcp-maintenance-tools.ts +2 -2
  257. package/src/mcp-profile-trust-tools.ts +4 -4
  258. package/src/mcp-provider-catalog.ts +2 -2
  259. package/src/mcp-task-tools.ts +11 -5
  260. package/src/mcp-team-template-tools.ts +3 -3
  261. package/src/mcp-workflow-tools.ts +10 -3
  262. package/src/notification-types.ts +2 -1
  263. package/src/operator-alarm.ts +26 -11
  264. package/src/package-pack.ts +42 -39
  265. package/src/project-instructions.ts +4 -0
  266. package/src/project-profile-instruction-text.ts +6 -0
  267. package/src/project-profile-instructions.ts +20 -0
  268. package/src/provider-catalog-store.ts +3 -49
  269. package/src/provider-config-store.ts +12 -11
  270. package/src/provider-model-seed.ts +1 -18
  271. package/src/provider-quota-config-store.ts +5 -4
  272. package/src/provider-secrets.test-fixture.ts +3 -3
  273. package/src/recipe-runner.ts +2 -2
  274. package/src/repo-config-source.ts +10 -4
  275. package/src/routes/agent-profiles.ts +6 -6
  276. package/src/routes/agents-spawn.ts +1 -1
  277. package/src/routes/agents.ts +6 -6
  278. package/src/routes/commands-listing.ts +4 -0
  279. package/src/routes/commands.ts +2 -2
  280. package/src/routes/continuation-archives.ts +2 -2
  281. package/src/routes/host-resource-control.ts +4 -1
  282. package/src/routes/index.ts +10 -1
  283. package/src/routes/integrations.ts +24 -50
  284. package/src/routes/issue-refs.ts +7 -7
  285. package/src/routes/orchestrator-bootstrap.ts +3 -3
  286. package/src/routes/orchestrator-host-registration.ts +2 -2
  287. package/src/routes/orchestrator-host-utilization.ts +3 -1
  288. package/src/routes/orchestrator-proxy.ts +2 -2
  289. package/src/routes/orchestrator.ts +4 -4
  290. package/src/routes/project-profile-instructions.ts +27 -0
  291. package/src/routes/project-trust.ts +2 -2
  292. package/src/routes/provider-config.ts +9 -8
  293. package/src/routes/provider-quota-config.ts +2 -2
  294. package/src/routes/settings.ts +5 -5
  295. package/src/routes/stats.ts +6 -6
  296. package/src/routes/task-http.ts +15 -1
  297. package/src/routes/task-issues.ts +11 -26
  298. package/src/routes/tasks.ts +22 -53
  299. package/src/routes/team-templates.ts +5 -5
  300. package/src/routes/teams-config.ts +2 -2
  301. package/src/routes/tokens.ts +12 -5
  302. package/src/routes/workflows.ts +20 -5
  303. package/src/routing-policy-normalize.ts +1 -2
  304. package/src/routing-step-defaults.ts +0 -2
  305. package/src/runner-token-mint-limit.ts +63 -0
  306. package/src/runtime-grant.ts +4 -2
  307. package/src/runtime-tokens.ts +50 -9
  308. package/src/security.ts +13 -20
  309. package/src/server-background.ts +1 -1
  310. package/src/server.ts +5 -2
  311. package/src/services/host-utilization.ts +23 -0
  312. package/src/services/idle-epics.ts +5 -6
  313. package/src/services/issue-lifecycle.ts +57 -61
  314. package/src/services/issue-refs.ts +9 -13
  315. package/src/services/kernel-timers-cutover-chaos-worker.ts +12 -7
  316. package/src/services/kernel-timers-ingress.ts +1 -1
  317. package/src/services/kernel-timers-request-key-migration-chaos-worker.ts +4 -1
  318. package/src/services/landed-ancestry.ts +19 -3
  319. package/src/services/memory-areas.ts +15 -51
  320. package/src/services/plan-ci-notice.ts +57 -0
  321. package/src/services/plan-ci-recovery.ts +2 -55
  322. package/src/services/plan-wake-digest.ts +1 -0
  323. package/src/services/profile-trust.ts +4 -3
  324. package/src/services/project-config-read.ts +70 -0
  325. package/src/services/project-profile-instructions.ts +54 -0
  326. package/src/services/provisioning-harness/budget-baseline.json +4 -4
  327. package/src/services/restart-spawn.ts +61 -49
  328. package/src/services/self-care-state.ts +2 -1
  329. package/src/services/self-effort.ts +5 -5
  330. package/src/services/send-message.ts +5 -1
  331. package/src/services/spawn-agent.ts +13 -2
  332. package/src/services/spawn-chain-inspector.ts +6 -13
  333. package/src/services/spawn-policy.ts +68 -0
  334. package/src/services/spawn-tree-cleanup.ts +2 -2
  335. package/src/services/task-dispatch.ts +3 -0
  336. package/src/services/task-state-reconcile.ts +6 -14
  337. package/src/services/team-templates.ts +9 -8
  338. package/src/services/transient-agent-reaper.ts +3 -1
  339. package/src/services/workflow-auto-start.ts +25 -13
  340. package/src/services/workflow-ci-main.ts +1 -1
  341. package/src/services/workflow-deferred-cleanup.ts +4 -2
  342. package/src/services/workflow-effect-driver.ts +2 -0
  343. package/src/services/workflow-effects.ts +18 -11
  344. package/src/services/workflow-land-notice.ts +10 -0
  345. package/src/services/workflow-pause-text.ts +43 -0
  346. package/src/services/workflow-reconcile.ts +13 -11
  347. package/src/services/workflow-run-view.ts +2 -0
  348. package/src/services/workflow-runs.ts +29 -13
  349. package/src/services/workflow-supervisor-state.ts +5 -0
  350. package/src/services/workflow-supervisor.ts +7 -3
  351. package/src/settings/registry.ts +1 -2
  352. package/src/spawn-command.ts +4 -4
  353. package/src/spawn-models.ts +10 -12
  354. package/src/spawn-placement.ts +1 -1
  355. package/src/spawn-targets.ts +1 -1
  356. package/src/startup-context-injection.ts +1 -0
  357. package/src/state-adoption/cd-cutover.ts +53 -7
  358. package/src/state-adoption/fleet-cutover.ts +35 -0
  359. package/src/state-adoption/fleet-plan.ts +36 -0
  360. package/src/state-adoption/index.ts +2 -2
  361. package/src/state-adoption/linux-plan.ts +2 -2
  362. package/src/state-adoption/linux.ts +2 -2
  363. package/src/state-adoption/macos-plan.ts +2 -2
  364. package/src/state-adoption/macos.ts +1 -1
  365. package/src/steward.ts +7 -6
  366. package/src/task-dispatch-live.ts +1 -1
  367. package/src/task-dispatcher.ts +1 -1
  368. package/src/teams-config-store.ts +4 -3
  369. package/src/token-db-write.ts +10 -0
  370. package/src/workspace-command-settle.ts +2 -1
  371. package/src/workspace-steward-wake.ts +2 -2
  372. package/node_modules/@humwick/kernel/LICENSE +0 -167
  373. package/node_modules/@humwick/kernel/async-entity-persistence.ts +0 -344
  374. package/node_modules/@humwick/kernel/async-event-log.ts +0 -107
  375. package/node_modules/@humwick/kernel/automations.pilot.ts +0 -147
  376. package/node_modules/@humwick/kernel/config.ts +0 -111
  377. package/node_modules/@humwick/kernel/contract.ts +0 -572
  378. package/node_modules/@humwick/kernel/control-authz.ts +0 -281
  379. package/node_modules/@humwick/kernel/control.ts +0 -645
  380. package/node_modules/@humwick/kernel/effects.ts +0 -493
  381. package/node_modules/@humwick/kernel/event-log.ts +0 -319
  382. package/node_modules/@humwick/kernel/http-transport.ts +0 -384
  383. package/node_modules/@humwick/kernel/identity-grammar.ts +0 -20
  384. package/node_modules/@humwick/kernel/identity.ts +0 -248
  385. package/node_modules/@humwick/kernel/index.ts +0 -28
  386. package/node_modules/@humwick/kernel/kernel-persistence.ts +0 -181
  387. package/node_modules/@humwick/kernel/landing-incident.ts +0 -306
  388. package/node_modules/@humwick/kernel/landing.ts +0 -953
  389. package/node_modules/@humwick/kernel/manifest-validation.ts +0 -460
  390. package/node_modules/@humwick/kernel/mcp-transport.ts +0 -109
  391. package/node_modules/@humwick/kernel/message-authority-chaos-worker.ts +0 -200
  392. package/node_modules/@humwick/kernel/message-authority.ts +0 -281
  393. package/node_modules/@humwick/kernel/module-host.ts +0 -722
  394. package/node_modules/@humwick/kernel/openapi-init.ts +0 -14
  395. package/node_modules/@humwick/kernel/package.json +0 -47
  396. package/node_modules/@humwick/kernel/persistence-schema.ts +0 -248
  397. package/node_modules/@humwick/kernel/persistence.ts +0 -510
  398. package/node_modules/@humwick/kernel/registry.ts +0 -60
  399. package/node_modules/@humwick/kernel/sqlite-kernel-persistence.ts +0 -499
  400. package/node_modules/@humwick/kernel/sqlite-module-host.ts +0 -87
  401. package/node_modules/@humwick/kernel/timers-chaos-worker.ts +0 -186
  402. package/node_modules/@humwick/kernel/timers.pilot.ts +0 -1411
  403. package/node_modules/@humwick/kernel/workflow-activation.ts +0 -25
  404. package/node_modules/@humwick/kernel/workflow-backstop.ts +0 -40
  405. package/node_modules/@humwick/kernel/workflow-cancel-tree.ts +0 -20
  406. package/node_modules/@humwick/kernel/workflow-chaos-worker.ts +0 -213
  407. package/node_modules/@humwick/kernel/workflow-composition.ts +0 -231
  408. package/node_modules/@humwick/kernel/workflow-container.ts +0 -72
  409. package/node_modules/@humwick/kernel/workflow-control.test-fixture.ts +0 -42
  410. package/node_modules/@humwick/kernel/workflow-control.ts +0 -178
  411. package/node_modules/@humwick/kernel/workflow-definition.ts +0 -507
  412. package/node_modules/@humwick/kernel/workflow-dispatch.ts +0 -60
  413. package/node_modules/@humwick/kernel/workflow-foreach.ts +0 -167
  414. package/node_modules/@humwick/kernel/workflow-journal-codec.ts +0 -339
  415. package/node_modules/@humwick/kernel/workflow-journal.ts +0 -922
  416. package/node_modules/@humwick/kernel/workflow-lane.ts +0 -48
  417. package/node_modules/@humwick/kernel/workflow-lease-owner.ts +0 -60
  418. package/node_modules/@humwick/kernel/workflow-recovery.test-fixture.ts +0 -19
  419. package/node_modules/@humwick/kernel/workflow-runtime.ts +0 -938
  420. package/node_modules/@humwick/kernel/workflow-script.ts +0 -11
  421. package/node_modules/@humwick/kernel/workflow-state.ts +0 -299
  422. package/node_modules/@humwick/kernel/workflow-step-commit.ts +0 -195
  423. package/node_modules/@humwick/kernel/workflow-transitions.ts +0 -317
  424. package/node_modules/@humwick/kernel/workflow-values.ts +0 -153
  425. package/node_modules/@humwick/kernel/workflow.pilot.ts +0 -376
  426. package/node_modules/@humwick/offload/LICENSE +0 -167
  427. package/node_modules/@humwick/offload/README.md +0 -57
  428. package/node_modules/@humwick/offload/dist/async-runner.d.ts +0 -18
  429. package/node_modules/@humwick/offload/dist/async-runner.d.ts.map +0 -1
  430. package/node_modules/@humwick/offload/dist/async-runner.js +0 -158
  431. package/node_modules/@humwick/offload/dist/async-runner.js.map +0 -1
  432. package/node_modules/@humwick/offload/dist/envelope.d.ts +0 -25
  433. package/node_modules/@humwick/offload/dist/envelope.d.ts.map +0 -1
  434. package/node_modules/@humwick/offload/dist/envelope.js +0 -80
  435. package/node_modules/@humwick/offload/dist/envelope.js.map +0 -1
  436. package/node_modules/@humwick/offload/dist/fixtures/worker-task.d.ts +0 -10
  437. package/node_modules/@humwick/offload/dist/fixtures/worker-task.d.ts.map +0 -1
  438. package/node_modules/@humwick/offload/dist/fixtures/worker-task.js +0 -59
  439. package/node_modules/@humwick/offload/dist/fixtures/worker-task.js.map +0 -1
  440. package/node_modules/@humwick/offload/dist/index.d.ts +0 -5
  441. package/node_modules/@humwick/offload/dist/index.d.ts.map +0 -1
  442. package/node_modules/@humwick/offload/dist/index.js +0 -5
  443. package/node_modules/@humwick/offload/dist/index.js.map +0 -1
  444. package/node_modules/@humwick/offload/dist/subprocess-runner.d.ts +0 -62
  445. package/node_modules/@humwick/offload/dist/subprocess-runner.d.ts.map +0 -1
  446. package/node_modules/@humwick/offload/dist/subprocess-runner.js +0 -295
  447. package/node_modules/@humwick/offload/dist/subprocess-runner.js.map +0 -1
  448. package/node_modules/@humwick/offload/dist/types.d.ts +0 -111
  449. package/node_modules/@humwick/offload/dist/types.d.ts.map +0 -1
  450. package/node_modules/@humwick/offload/dist/types.js +0 -176
  451. package/node_modules/@humwick/offload/dist/types.js.map +0 -1
  452. package/node_modules/@humwick/offload/dist/worker-pool.d.ts +0 -23
  453. package/node_modules/@humwick/offload/dist/worker-pool.d.ts.map +0 -1
  454. package/node_modules/@humwick/offload/dist/worker-pool.js +0 -435
  455. package/node_modules/@humwick/offload/dist/worker-pool.js.map +0 -1
  456. package/node_modules/@humwick/offload/dist/worker-runtime.d.ts +0 -2
  457. package/node_modules/@humwick/offload/dist/worker-runtime.d.ts.map +0 -1
  458. package/node_modules/@humwick/offload/dist/worker-runtime.js +0 -27
  459. package/node_modules/@humwick/offload/dist/worker-runtime.js.map +0 -1
  460. package/node_modules/@humwick/offload/dist/worker.d.ts +0 -5
  461. package/node_modules/@humwick/offload/dist/worker.d.ts.map +0 -1
  462. package/node_modules/@humwick/offload/dist/worker.js +0 -1
  463. package/node_modules/@humwick/offload/dist/worker.js.map +0 -1
  464. package/node_modules/@humwick/offload/package.json +0 -46
  465. package/node_modules/@humwick/offload/src/async-runner.ts +0 -170
  466. package/node_modules/@humwick/offload/src/envelope.ts +0 -92
  467. package/node_modules/@humwick/offload/src/fixtures/worker-task.ts +0 -49
  468. package/node_modules/@humwick/offload/src/index.ts +0 -4
  469. package/node_modules/@humwick/offload/src/subprocess-runner.ts +0 -334
  470. package/node_modules/@humwick/offload/src/types.ts +0 -235
  471. package/node_modules/@humwick/offload/src/worker-pool.ts +0 -463
  472. package/node_modules/@humwick/offload/src/worker-runtime.ts +0 -32
  473. package/node_modules/@humwick/offload/src/worker.ts +0 -5
  474. package/public/assets/agent-liveness-DyqxfB-t.js +0 -2
  475. package/public/assets/agent-profiles-BpI_jTxq.js +0 -2
  476. package/public/assets/fleet-BL4UXS_k.js +0 -2
  477. package/public/assets/index-B7ZerHLY.css +0 -2
  478. package/public/assets/index-BK0WV6hF.js +0 -15
  479. package/public/assets/projects-B_lmcqfO.js +0 -2
  480. package/public/assets/provider-display-DbLWpP3j.js +0 -2
  481. package/public/assets/run-card-CO_bguQf.js +0 -2
  482. package/public/assets/use-project-list-B-T2Bnnn.js +0 -2
  483. package/public/assets/versioned-prompt-editor-Diu8BIaM.js +0 -8
  484. package/runner/src/gate-run-lease-probe.ts +0 -38
  485. package/src/db/claude-catalog-reconcile.ts +0 -27
  486. package/src/routing-model-aliases.ts +0 -21
@@ -1,510 +0,0 @@
1
- import type { Database } from "bun:sqlite";
2
- import { Buffer } from "node:buffer";
3
- import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
4
- import type {
5
- Clock,
6
- EntityDecl,
7
- EntityListOptions,
8
- EntityPage,
9
- EntityStore,
10
- EntityStores,
11
- ModuleDefinition,
12
- ModuleManifest,
13
- Scope,
14
- } from "./contract";
15
- import { assertValidManifest } from "./manifest-validation";
16
- import {
17
- decodeField,
18
- encodeField,
19
- installModuleEntities,
20
- type EntityField,
21
- type EntityTable,
22
- } from "./persistence-schema";
23
-
24
- export const DEFAULT_ENTITY_PAGE_SIZE = 50;
25
- export const MAX_ENTITY_PAGE_SIZE = 200;
26
-
27
- export class EntityNotFoundError extends Error {
28
- constructor(readonly moduleId: string, readonly entityName: string, readonly key: unknown) {
29
- super(`entity '${moduleId}.${entityName}' with key '${String(key)}' was not found`);
30
- this.name = "EntityNotFoundError";
31
- }
32
- }
33
-
34
- export class EntityConflictError extends Error {
35
- constructor(readonly moduleId: string, readonly entityName: string, readonly key: unknown) {
36
- super(`stale entity version for '${moduleId}.${entityName}' key '${String(key)}'`);
37
- this.name = "EntityConflictError";
38
- }
39
- }
40
-
41
- export class EntitySessionClosedError extends Error {
42
- constructor() {
43
- super("entity session is closed");
44
- this.name = "EntitySessionClosedError";
45
- }
46
- }
47
-
48
- type EntityValue = Record<string, unknown>;
49
- type StoredRow = Record<string, unknown> & { version: number };
50
- type Mutation =
51
- | { readonly kind: "put"; readonly table: EntityTable; readonly key: unknown; readonly expectedVersion: number | null; value: EntityValue }
52
- | { readonly kind: "delete"; readonly table: EntityTable; readonly key: unknown; readonly expectedVersion: number };
53
-
54
- interface RowState {
55
- readonly table: EntityTable;
56
- readonly key: unknown;
57
- readonly expectedVersion: number | null;
58
- value?: EntityValue;
59
- deleted?: boolean;
60
- mutation?: Mutation;
61
- }
62
-
63
- interface CursorPayload {
64
- readonly v: 2;
65
- readonly module: string;
66
- readonly entity: string;
67
- readonly scope: string;
68
- readonly type: string;
69
- readonly key: string | number;
70
- }
71
-
72
- function signCursorBody(secret: Buffer, body: string): string {
73
- return createHmac("sha256", secret).update(body).digest("base64url");
74
- }
75
-
76
- /** Length-prefixed (not delimited) so no field value, however constructed, can forge a collision. */
77
- function keyIdentity(table: EntityTable, key: unknown): string {
78
- const encoded = encodeField(table.key, key);
79
- const parts = [table.name, typeof encoded, String(encoded)];
80
- return parts.map((part) => `${part.length}:${part}`).join("");
81
- }
82
-
83
- function rowValue(table: EntityTable, row: StoredRow): EntityValue {
84
- const raw: EntityValue = {};
85
- for (const field of table.fields) {
86
- const value = decodeField(field, row[field.name]);
87
- if (value !== undefined) raw[field.name] = value;
88
- }
89
- return table.decl.schema.parse(raw) as EntityValue;
90
- }
91
-
92
- function encodeCursor(secret: Buffer, table: EntityTable, scope: Scope, key: unknown): string {
93
- const encoded = encodeField(table.key, key);
94
- const payload: CursorPayload = {
95
- v: 2,
96
- module: table.moduleId,
97
- entity: table.entityName,
98
- scope,
99
- type: typeof encoded,
100
- key: typeof encoded === "bigint" ? encoded.toString() : encoded as string | number,
101
- };
102
- const body = Buffer.from(JSON.stringify(payload)).toString("base64url");
103
- return `${body}.${signCursorBody(secret, body)}`;
104
- }
105
-
106
- function decodeCursor(secret: Buffer, table: EntityTable, scope: Scope, cursor: string): unknown {
107
- try {
108
- const separator = cursor.lastIndexOf(".");
109
- if (separator < 0) throw new Error();
110
- const body = cursor.slice(0, separator);
111
- const signature = Buffer.from(cursor.slice(separator + 1));
112
- const expected = Buffer.from(signCursorBody(secret, body));
113
- if (signature.length !== expected.length || !timingSafeEqual(signature, expected)) throw new Error();
114
- const payload = JSON.parse(Buffer.from(body, "base64url").toString("utf8")) as CursorPayload;
115
- if (payload.v !== 2 || payload.module !== table.moduleId || payload.entity !== table.entityName || payload.scope !== scope) {
116
- throw new Error();
117
- }
118
- if (payload.type === "bigint") return BigInt(payload.key);
119
- if (payload.type !== typeof payload.key) throw new Error();
120
- return payload.key;
121
- } catch {
122
- throw new TypeError(`invalid cursor for entity '${table.moduleId}.${table.entityName}'`);
123
- }
124
- }
125
-
126
- function compareKeys(table: EntityTable, left: unknown, right: unknown): number {
127
- const a = encodeField(table.key, left);
128
- const b = encodeField(table.key, right);
129
- if (a === b) return 0;
130
- return a! < b! ? -1 : 1;
131
- }
132
-
133
- function parsedWhere(table: EntityTable, where: Readonly<Record<string, unknown>> | undefined): ReadonlyMap<EntityField, unknown> {
134
- const result = new Map<EntityField, unknown>();
135
- for (const [name, value] of Object.entries(where ?? {})) {
136
- const field = table.fieldByName.get(name);
137
- if (!field) throw new TypeError(`unknown where field '${name}' on '${table.moduleId}.${table.entityName}'`);
138
- result.set(field, field.schema.parse(value));
139
- }
140
- return result;
141
- }
142
-
143
- function matchesWhere(value: EntityValue, where: ReadonlyMap<EntityField, unknown>): boolean {
144
- for (const [field, expected] of where) {
145
- if (encodeField(field, value[field.name]) !== encodeField(field, expected)) return false;
146
- }
147
- return true;
148
- }
149
-
150
- function pageSize(limit: number | undefined): number {
151
- const resolved = limit ?? DEFAULT_ENTITY_PAGE_SIZE;
152
- if (!Number.isInteger(resolved) || resolved < 1 || resolved > MAX_ENTITY_PAGE_SIZE) {
153
- throw new RangeError(`entity page limit must be an integer from 1 to ${MAX_ENTITY_PAGE_SIZE}`);
154
- }
155
- return resolved;
156
- }
157
-
158
- export interface EntitySession<E> {
159
- /** Scope-bound stores installed directly as ModuleCtx.entities. */
160
- readonly entities: EntityStores<E>;
161
- readonly pendingMutations: number;
162
- /** Close the async attempt before entering its synchronous commit transaction. */
163
- seal(): void;
164
- /** Flush into a caller-owned synchronous transaction (for workflow checkpoint composition). */
165
- flush(): void;
166
- /** Seal and flush in a standalone synchronous transaction. */
167
- commit(): void;
168
- /** Close the session and forget every buffered mutation. */
169
- discard(): void;
170
- }
171
-
172
- class EntitySessionRuntime<E> implements EntitySession<E> {
173
- readonly entities: EntityStores<E>;
174
- readonly #states = new Map<string, RowState>();
175
- #closed = false;
176
- #finished = false;
177
-
178
- constructor(
179
- readonly db: Database,
180
- readonly clock: Clock,
181
- readonly scope: Scope,
182
- readonly cursorSecret: Buffer,
183
- tables: ReadonlyMap<string, EntityTable>,
184
- ) {
185
- const stores: Record<string, EntityStore> = {};
186
- for (const [name, table] of tables) stores[name] = this.#store(table);
187
- this.entities = Object.freeze(stores) as EntityStores<E>;
188
- }
189
-
190
- get pendingMutations(): number {
191
- let count = 0;
192
- for (const state of this.#states.values()) if (state.mutation) count++;
193
- return count;
194
- }
195
-
196
- #assertOpen(): void {
197
- if (this.#closed) throw new EntitySessionClosedError();
198
- }
199
-
200
- #read(table: EntityTable, key: unknown): RowState {
201
- const identity = keyIdentity(table, key);
202
- const known = this.#states.get(identity);
203
- if (known) return known;
204
- const columns = [...table.fields.map((field) => field.quoted), "version"].join(", ");
205
- const row = this.db.query(`SELECT ${columns} FROM ${table.quoted} WHERE scope = ? AND ${table.key.quoted} = ?`)
206
- .get(this.scope, encodeField(table.key, key)) as StoredRow | null;
207
- const state: RowState = {
208
- table,
209
- key,
210
- expectedVersion: row?.version ?? null,
211
- value: row ? rowValue(table, row) : undefined,
212
- };
213
- this.#states.set(identity, state);
214
- return state;
215
- }
216
-
217
- #remember(table: EntityTable, row: StoredRow): RowState {
218
- const value = rowValue(table, row);
219
- const key = value[table.key.name];
220
- const identity = keyIdentity(table, key);
221
- const known = this.#states.get(identity);
222
- if (known) return known;
223
- const state: RowState = { table, key, expectedVersion: row.version, value };
224
- this.#states.set(identity, state);
225
- return state;
226
- }
227
-
228
- #store(table: EntityTable): EntityStore {
229
- return {
230
- get: async (key) => {
231
- this.#assertOpen();
232
- const state = this.#read(table, key);
233
- return state.deleted ? undefined : state.value;
234
- },
235
- list: async (options) => this.#list(table, options as EntityListOptions<EntityValue> | undefined),
236
- create: async (input) => {
237
- this.#assertOpen();
238
- const value = table.decl.schema.parse(input) as EntityValue;
239
- const key = value[table.key.name];
240
- const state = this.#read(table, key);
241
- if (!state.deleted && state.value !== undefined) throw new EntityConflictError(table.moduleId, table.entityName, key);
242
- state.value = value;
243
- state.deleted = false;
244
- state.mutation = { kind: "put", table, key, expectedVersion: state.expectedVersion, value };
245
- return value;
246
- },
247
- update: async (key, patch) => {
248
- this.#assertOpen();
249
- const state = this.#read(table, key);
250
- if (state.deleted || !state.value) throw new EntityNotFoundError(table.moduleId, table.entityName, key);
251
- const value = table.decl.schema.parse({ ...state.value, ...patch }) as EntityValue;
252
- if (keyIdentity(table, value[table.key.name]) !== keyIdentity(table, key)) {
253
- throw new TypeError(`entity key '${table.key.name}' cannot be changed`);
254
- }
255
- state.value = value;
256
- state.mutation = { kind: "put", table, key, expectedVersion: state.expectedVersion, value };
257
- return value;
258
- },
259
- delete: async (key) => {
260
- this.#assertOpen();
261
- const state = this.#read(table, key);
262
- if (state.deleted || !state.value) return false;
263
- state.deleted = true;
264
- state.value = undefined;
265
- if (state.expectedVersion === null) state.mutation = undefined;
266
- else state.mutation = { kind: "delete", table, key, expectedVersion: state.expectedVersion };
267
- return true;
268
- },
269
- };
270
- }
271
-
272
- async #list(table: EntityTable, options: EntityListOptions<EntityValue> | undefined): Promise<EntityPage<EntityValue>> {
273
- this.#assertOpen();
274
- const limit = pageSize(options?.limit);
275
- const where = parsedWhere(table, options?.where);
276
- const cursorKey = options?.cursor ? decodeCursor(this.cursorSecret, table, this.scope, options.cursor) : undefined;
277
- const clauses = ["scope = ?"];
278
- const bindings: Array<string | number | bigint | null> = [this.scope];
279
- for (const [field, value] of where) {
280
- const encoded = encodeField(field, value);
281
- clauses.push(encoded === null ? `${field.quoted} IS NULL` : `${field.quoted} = ?`);
282
- if (encoded !== null) bindings.push(encoded);
283
- }
284
- if (cursorKey !== undefined) {
285
- clauses.push(`${table.key.quoted} > ?`);
286
- bindings.push(encodeField(table.key, cursorKey));
287
- }
288
- const stagedCount = [...this.#states.values()].filter((state) => state.table === table && state.mutation).length;
289
- bindings.push(limit + stagedCount + 1);
290
- const columns = [...table.fields.map((field) => field.quoted), "version"].join(", ");
291
- const rows = this.db.query(`SELECT ${columns} FROM ${table.quoted}
292
- WHERE ${clauses.join(" AND ")} ORDER BY ${table.key.quoted} ASC LIMIT ?`).all(...bindings) as StoredRow[];
293
-
294
- const candidates = new Map<string, EntityValue>();
295
- for (const row of rows) {
296
- const state = this.#remember(table, row);
297
- if (!state.deleted && state.value && matchesWhere(state.value, where)) {
298
- candidates.set(keyIdentity(table, state.key), state.value);
299
- }
300
- }
301
- for (const state of this.#states.values()) {
302
- if (state.table !== table || state.deleted || !state.value || !matchesWhere(state.value, where)) continue;
303
- if (cursorKey !== undefined && compareKeys(table, state.key, cursorKey) <= 0) continue;
304
- candidates.set(keyIdentity(table, state.key), state.value);
305
- }
306
- const sorted = [...candidates.values()].sort((a, b) => compareKeys(table, a[table.key.name], b[table.key.name]));
307
- const hasMore = sorted.length > limit;
308
- const items = sorted.slice(0, limit);
309
- return {
310
- items,
311
- ...(hasMore ? { cursor: encodeCursor(this.cursorSecret, table, this.scope, items[items.length - 1]![table.key.name]) } : {}),
312
- };
313
- }
314
-
315
- seal(): void {
316
- if (this.#finished) throw new EntitySessionClosedError();
317
- this.#closed = true;
318
- }
319
-
320
- #flushInsideTransaction(): void {
321
- if (!this.#closed || this.#finished) throw new EntitySessionClosedError();
322
- if (!this.db.inTransaction) throw new Error("entity mutations must flush inside a synchronous SQLite transaction");
323
- try {
324
- for (const state of this.#states.values()) {
325
- const mutation = state.mutation;
326
- if (!mutation) continue;
327
- const now = this.clock.now();
328
- let changes: number;
329
- if (mutation.kind === "delete") {
330
- changes = this.db.query(`DELETE FROM ${mutation.table.quoted}
331
- WHERE scope = ? AND ${mutation.table.key.quoted} = ? AND version = ?`)
332
- .run(this.scope, encodeField(mutation.table.key, mutation.key), mutation.expectedVersion).changes;
333
- } else if (mutation.expectedVersion === null) {
334
- const fields = mutation.table.fields;
335
- const columns = ["scope", ...fields.map((field) => field.quoted), "version", "created_at", "updated_at"];
336
- const placeholders = columns.map(() => "?").join(", ");
337
- changes = this.db.query(`INSERT INTO ${mutation.table.quoted} (${columns.join(", ")})
338
- VALUES (${placeholders}) ON CONFLICT(scope, ${mutation.table.key.quoted}) DO NOTHING`)
339
- .run(this.scope, ...fields.map((field) => encodeField(field, mutation.value[field.name])), 0, now, now).changes;
340
- } else {
341
- const fields = mutation.table.fields.filter((field) => field !== mutation.table.key);
342
- const assignments = [
343
- ...fields.map((field) => `${field.quoted} = ?`),
344
- "version = version + 1",
345
- "updated_at = ?",
346
- ];
347
- changes = this.db.query(`UPDATE ${mutation.table.quoted}
348
- SET ${assignments.join(", ")}
349
- WHERE scope = ? AND ${mutation.table.key.quoted} = ? AND version = ?`)
350
- .run(...fields.map((field) => encodeField(field, mutation.value[field.name])), now, this.scope,
351
- encodeField(mutation.table.key, mutation.key), mutation.expectedVersion).changes;
352
- }
353
- if (changes !== 1) throw new EntityConflictError(mutation.table.moduleId, mutation.table.entityName, mutation.key);
354
- }
355
- this.#finished = true;
356
- } catch (error) {
357
- this.#finished = true;
358
- throw error;
359
- }
360
- }
361
-
362
- flush(): void {
363
- this.#flushInsideTransaction();
364
- }
365
-
366
- commit(): void {
367
- if (!this.#closed) this.seal();
368
- this.db.transaction(() => this.#flushInsideTransaction())();
369
- }
370
-
371
- discard(): void {
372
- if (this.#finished) throw new EntitySessionClosedError();
373
- this.#closed = true;
374
- this.#states.clear();
375
- this.#finished = true;
376
- }
377
- }
378
-
379
- export interface EntityPersistenceOptions {
380
- readonly db: Database;
381
- readonly clock: Clock;
382
- readonly modules?: Iterable<ModuleDefinition<ModuleManifest>>;
383
- }
384
-
385
- type RowMeta = { readonly created_at: number; readonly updated_at: number };
386
-
387
- /** One decoded `kernel_entity__<module>__<entity>` row, module-agnostic (KAPI-1 / G-C). `fields`
388
- * carries every declared column decoded through the entity's own codec (including the key) —
389
- * raw, ungated: a control-plane caller applies redaction/allowlisting on top (§1.3, K-C3). */
390
- export interface EntityRawRow {
391
- readonly module: string;
392
- readonly entity: string;
393
- readonly scope: Scope;
394
- readonly key: unknown;
395
- readonly version: number;
396
- readonly createdAt: number;
397
- readonly updatedAt: number;
398
- readonly fields: Readonly<Record<string, unknown>>;
399
- }
400
-
401
- export interface EntityRawListOptions {
402
- readonly where?: Readonly<Record<string, unknown>>;
403
- readonly cursor?: string;
404
- readonly limit?: number;
405
- }
406
-
407
- export interface EntityRawPage {
408
- readonly items: readonly EntityRawRow[];
409
- readonly cursor?: string;
410
- }
411
-
412
- function toRawRow(table: EntityTable, scope: Scope, row: StoredRow & RowMeta): EntityRawRow {
413
- const value = rowValue(table, row);
414
- return {
415
- module: table.moduleId,
416
- entity: table.entityName,
417
- scope,
418
- key: value[table.key.name],
419
- version: row.version,
420
- createdAt: row.created_at,
421
- updatedAt: row.updated_at,
422
- fields: value,
423
- };
424
- }
425
-
426
- export class EntityPersistence {
427
- readonly #tables = new Map<string, ReadonlyMap<string, EntityTable>>();
428
- readonly #cursorSecret = randomBytes(32);
429
-
430
- constructor(readonly options: EntityPersistenceOptions) {
431
- options.db.run("PRAGMA foreign_keys = ON");
432
- const { foreign_keys: enforced } = options.db.query("PRAGMA foreign_keys").get() as { foreign_keys: number };
433
- if (enforced !== 1) {
434
- throw new Error("entity persistence requires PRAGMA foreign_keys = ON to be honored by the injected SQLite handle");
435
- }
436
- for (const module of options.modules ?? []) this.register(module);
437
- }
438
-
439
- register<M extends ModuleManifest>(module: ModuleDefinition<M>): void {
440
- assertValidManifest(module);
441
- if (this.#tables.has(module.id)) throw new Error(`module '${module.id}' entity persistence is already installed`);
442
- this.#tables.set(module.id, installModuleEntities(this.options.db, module));
443
- }
444
-
445
- begin<M extends ModuleManifest>(module: ModuleDefinition<M>, scope: Scope): EntitySession<M["entities"]> {
446
- const tables = this.#tables.get(module.id);
447
- if (!tables) throw new Error(`module '${module.id}' entity persistence is not installed`);
448
- return new EntitySessionRuntime<M["entities"]>(this.options.db, this.options.clock, scope, this.#cursorSecret, tables);
449
- }
450
-
451
- #lookupTable(moduleId: string, entityName: string): EntityTable | undefined {
452
- return this.#tables.get(moduleId)?.get(entityName);
453
- }
454
-
455
- /**
456
- * Generic `(module, entity, key)` point read (KAPI-1 / G-C) — the module-agnostic sibling of
457
- * `EntityStore.get`, usable without a module's typed `ModuleDefinition` on hand. Returns
458
- * `undefined` for an unregistered module/entity (not an error: a caller enumerating module ids
459
- * dynamically should see "no such row," not a throw) or an absent key, exactly like `getInstance`
460
- * on the workflow journal. No owner-scoping or redaction — that's the control module's job.
461
- */
462
- readEntity(moduleId: string, entityName: string, scope: Scope, key: unknown): EntityRawRow | undefined {
463
- const table = this.#lookupTable(moduleId, entityName);
464
- if (!table) return undefined;
465
- const columns = [...table.fields.map((field) => field.quoted), "version", "created_at", "updated_at"].join(", ");
466
- const row = this.options.db.query(`SELECT ${columns} FROM ${table.quoted} WHERE scope = ? AND ${table.key.quoted} = ?`)
467
- .get(scope, encodeField(table.key, key)) as (StoredRow & RowMeta) | null;
468
- return row ? toRawRow(table, scope, row) : undefined;
469
- }
470
-
471
- /**
472
- * Generic `(module, entity)` cursor-paginated list (KAPI-1 / G-C), mirroring `EntityStore.list`'s
473
- * cursor/where semantics but keyed by string module/entity names instead of a typed
474
- * `ModuleDefinition`. Returns an empty page for an unregistered module/entity. No owner-scoping
475
- * or redaction — the control module filters/gates the raw rows this returns.
476
- */
477
- listEntity(moduleId: string, entityName: string, scope: Scope, options?: EntityRawListOptions): EntityRawPage {
478
- const table = this.#lookupTable(moduleId, entityName);
479
- if (!table) return { items: [] };
480
- const limit = pageSize(options?.limit);
481
- const where = parsedWhere(table, options?.where);
482
- const cursorKey = options?.cursor ? decodeCursor(this.#cursorSecret, table, scope, options.cursor) : undefined;
483
- const clauses = ["scope = ?"];
484
- const bindings: Array<string | number | bigint | null> = [scope];
485
- for (const [field, value] of where) {
486
- const encoded = encodeField(field, value);
487
- clauses.push(encoded === null ? `${field.quoted} IS NULL` : `${field.quoted} = ?`);
488
- if (encoded !== null) bindings.push(encoded);
489
- }
490
- if (cursorKey !== undefined) {
491
- clauses.push(`${table.key.quoted} > ?`);
492
- bindings.push(encodeField(table.key, cursorKey));
493
- }
494
- bindings.push(limit + 1);
495
- const columns = [...table.fields.map((field) => field.quoted), "version", "created_at", "updated_at"].join(", ");
496
- const rows = this.options.db.query(`SELECT ${columns} FROM ${table.quoted}
497
- WHERE ${clauses.join(" AND ")} ORDER BY ${table.key.quoted} ASC LIMIT ?`)
498
- .all(...bindings) as Array<StoredRow & RowMeta>;
499
- const hasMore = rows.length > limit;
500
- const items = rows.slice(0, limit).map((row) => toRawRow(table, scope, row));
501
- return {
502
- items,
503
- ...(hasMore ? { cursor: encodeCursor(this.#cursorSecret, table, scope, items[items.length - 1]!.key) } : {}),
504
- };
505
- }
506
- }
507
-
508
- export function createEntityPersistence(options: EntityPersistenceOptions): EntityPersistence {
509
- return new EntityPersistence(options);
510
- }
@@ -1,60 +0,0 @@
1
- import type { ModuleDefinition, ModuleManifest } from "./contract";
2
- import { assertValidManifest } from "./manifest-validation";
3
-
4
- export class DuplicateModuleIdError extends Error {
5
- constructor(readonly moduleId: string) {
6
- super(`module '${moduleId}' is already registered`);
7
- this.name = "DuplicateModuleIdError";
8
- }
9
- }
10
-
11
- export class UnknownModuleError extends Error {
12
- constructor(readonly moduleId: string) {
13
- super(`module '${moduleId}' is not registered`);
14
- this.name = "UnknownModuleError";
15
- }
16
- }
17
-
18
- type AnyModuleDefinition = ModuleDefinition<ModuleManifest>;
19
-
20
- /** In-process catalog of validated, compile-time module definitions. */
21
- export class ModuleRegistry {
22
- readonly #modules = new Map<string, AnyModuleDefinition>();
23
-
24
- constructor(definitions: Iterable<AnyModuleDefinition> = []) {
25
- for (const definition of definitions) this.register(definition);
26
- }
27
-
28
- register<const M extends ModuleManifest>(definition: ModuleDefinition<M>): ModuleDefinition<M> {
29
- assertValidManifest(definition);
30
- if (this.#modules.has(definition.id)) throw new DuplicateModuleIdError(definition.id);
31
- this.#modules.set(definition.id, definition as AnyModuleDefinition);
32
- return definition;
33
- }
34
-
35
- has(moduleId: string): boolean {
36
- return this.#modules.has(moduleId);
37
- }
38
-
39
- get(moduleId: string): AnyModuleDefinition | undefined {
40
- return this.#modules.get(moduleId);
41
- }
42
-
43
- require(moduleId: string): AnyModuleDefinition {
44
- const definition = this.get(moduleId);
45
- if (!definition) throw new UnknownModuleError(moduleId);
46
- return definition;
47
- }
48
-
49
- list(): readonly AnyModuleDefinition[] {
50
- return [...this.#modules.values()];
51
- }
52
-
53
- get size(): number {
54
- return this.#modules.size;
55
- }
56
- }
57
-
58
- export function createModuleRegistry(definitions: Iterable<AnyModuleDefinition> = []): ModuleRegistry {
59
- return new ModuleRegistry(definitions);
60
- }