@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,9 +1,7 @@
1
1
  import { isDeepStrictEqual } from "node:util";
2
- import { randomUUID } from "node:crypto";
3
- import { isRecord, stringValue, isMechanicalMessageKind, orchestratorAgentId } from "@humwick/sdk";
4
- import { ORCHESTRATOR_PROTOCOL_VERSION, VERSION } from "../config.ts";
2
+ import { stringValue, orchestratorAgentId } from "@humwick/sdk";
3
+ import { VERSION } from "../config.ts";
5
4
  import { parseJson } from "../utils";
6
- import { isLiveIsolatedWorkspace } from "../workspace-phase";
7
5
  import {
8
6
  CONTRACT_REQUIREMENTS,
9
7
  contractCompatibility,
@@ -12,77 +10,13 @@ import {
12
10
  parseRuntimePackage,
13
11
  type RuntimeContracts,
14
12
  } from "../contracts";
15
- import { STALE_TTL_MS, DAY_MS, CLAIM_LEASE_MS, POOL_CLAIM_LEASE_MS, WORKSPACE_MERGE_LEASE_MS } from "../config";
16
- import { matchAgents } from "../agent-ref";
17
- import { deleteAgent, heartbeat, upsertAgent } from "./agents.ts";
13
+ import { STALE_TTL_MS } from "../config";
14
+ import { deleteAgent, heartbeat, upsertAgent, upsertAgentInternal, type UpsertAgentTxResult } from "./agents.ts";
18
15
  import { evictPendingPresence } from "./presence-buffer.ts";
19
16
  import { getDb, writeTransaction } from "./connection.ts";
20
17
  import { fencedColumnSql, nextLifecycleSeq } from "./lifecycle-seq.ts";
21
18
  import { electWorkspaceStewards, emitWorkspaceUpsert, getWorkspace, upsertWorkspaceFromManagedAgentRow, type WorkspaceUpsertResult } from "./workspaces.ts";
22
- import type {
23
- AgentCard,
24
- ActivityEvent,
25
- ActivityEventInput,
26
- AgentKind,
27
- AgentSessionGuard,
28
- Artifact,
29
- ArtifactBlob,
30
- ArtifactKind,
31
- ArtifactLink,
32
- ArtifactSensitivity,
33
- ArtifactVisibility,
34
- AttachmentRef,
35
- ChannelBinding,
36
- ChannelBindingMode,
37
- ChannelRouteTarget,
38
- ChatHistoryImport,
39
- ChatHistoryImportEntry,
40
- ChannelSummary,
41
- ChannelTargetHealth,
42
- CreatePairInput,
43
- HealthCheck,
44
- HealthReport,
45
- ManagedAgent,
46
- ManagedSessionExitDiagnostics,
47
- Message,
48
- MessageDeliveryAttempt,
49
- MessageDeliveryStatus,
50
- HostDaemonSample,
51
- Orchestrator,
52
- OrchestratorHealth,
53
- OrchestratorRuntimeInput,
54
- OrchestratorStatus,
55
- OrchestratorUpgradeState,
56
- PairActionInput,
57
- PairMessageInput,
58
- PairSession,
59
- PairStatus,
60
- RegisterAgentInput,
61
- ReplyObligation,
62
- RegisterOrchestratorInput,
63
- SendMessageInput,
64
- PollQuery,
65
- SpawnApprovalMode,
66
- SpawnProvider,
67
- Task,
68
- TaskEvent,
69
- TaskSeverity,
70
- TaskStatus,
71
- IntegrationEventInput,
72
- IntegrationSummary,
73
- IntegrationTaskStats,
74
- InboxDraft,
75
- InboxState,
76
- InboxThreadState,
77
- ContextSnapshot,
78
- ContextState,
79
- ProviderCapabilities,
80
- QueuedSpawn,
81
- TaskStatusInput,
82
- WorkspaceMetadata,
83
- WorkspaceRecord,
84
- WorkspaceStatus,
85
- } from "../types";
19
+ import type { ManagedAgent, HostDaemonSample, Orchestrator, OrchestratorHealth, OrchestratorRuntimeInput, OrchestratorStatus, OrchestratorUpgradeState, RegisterAgentInput, RegisterOrchestratorInput, SpawnProvider, QueuedSpawn } from "../types";
86
20
 
87
21
  // --- Orchestrators ---
88
22
 
@@ -185,7 +119,6 @@ export function setOrchestratorUpgradeState(id: string, state: OrchestratorUpgra
185
119
  return getOrchestrator(id);
186
120
  }
187
121
 
188
-
189
122
  function orchestratorHealth(version: string | undefined, compatibility: ReturnType<typeof contractCompatibility>): OrchestratorHealth {
190
123
  const issues: OrchestratorHealth["issues"] = [];
191
124
  if (!version) {
@@ -259,8 +192,8 @@ function mergeOrchestratorRuntimeMeta(meta: Record<string, unknown>, input: Orch
259
192
  };
260
193
  }
261
194
 
262
- export function upsertOrchestrator(input: RegisterOrchestratorInput): Orchestrator {
263
- const now = Date.now();
195
+ /** The orchestrator row write alone (no agent registration); returns the agent registration to follow. */
196
+ function writeOrchestratorRow(input: RegisterOrchestratorInput, now: number): RegisterAgentInput {
264
197
  // #1554 — the command-delivery key. Built from the SAME helper the orchestrator's command poller
265
198
  // binds with, so a registered host is always addressed under the exact id it polls under.
266
199
  const agentId = orchestratorAgentId(input.id);
@@ -311,7 +244,7 @@ export function upsertOrchestrator(input: RegisterOrchestratorInput): Orchestrat
311
244
  });
312
245
 
313
246
  // Also register as an agent so the orchestrator can receive messages
314
- upsertAgent({
247
+ return {
315
248
  id: agentId,
316
249
  name: `Orchestrator (${input.hostname})`,
317
250
  tags: ["orchestrator", input.hostname],
@@ -329,11 +262,23 @@ export function upsertOrchestrator(input: RegisterOrchestratorInput): Orchestrat
329
262
  ...(input.protocolVersion !== undefined ? { protocolVersion: input.protocolVersion } : {}),
330
263
  ...(input.gitSha ? { gitSha: input.gitSha } : {}),
331
264
  },
332
- });
265
+ };
266
+ }
333
267
 
268
+ export function upsertOrchestrator(input: RegisterOrchestratorInput): Orchestrator {
269
+ upsertAgent(writeOrchestratorRow(input, Date.now()));
334
270
  return getOrchestrator(input.id)!;
335
271
  }
336
272
 
273
+ /** #1870 S6c — the whole registration as one off-loop op: the orchestrator row and its agent row
274
+ * commit together. The agent's main-thread follow-ups (presence eviction, pool binding, steward
275
+ * election) run in the facade from the returned `agent`/`contextState`. */
276
+ export function registerOrchestratorOp(input: RegisterOrchestratorInput, now: number, seq: number): { orchestrator: Orchestrator } & UpsertAgentTxResult {
277
+ const agentInput = writeOrchestratorRow(input, now);
278
+ const tx = upsertAgentInternal(agentInput, undefined, now, seq);
279
+ return { orchestrator: getOrchestrator(input.id)!, ...tx };
280
+ }
281
+
337
282
  export function getOrchestrator(id: string): Orchestrator | null {
338
283
  const row = getDb().query("SELECT * FROM orchestrators WHERE id = ?").get(id) as any;
339
284
  return row ? rowToOrchestrator(row) : null;
@@ -440,7 +385,6 @@ export function reportOrchestratorRosterRow(id: string, agents: ManagedAgent[],
440
385
  return { orchestrator: getOrchestrator(id), rosterChanged: true, workspaces };
441
386
  }
442
387
 
443
-
444
388
  export function deleteOrchestrator(id: string): boolean {
445
389
  const orch = getOrchestrator(id);
446
390
  if (!orch) return false;
package/src/db/pairs.ts CHANGED
@@ -1,17 +1,5 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import { isRecord, stringValue, isMechanicalMessageKind } from "@humwick/sdk";
3
- import { ORCHESTRATOR_PROTOCOL_VERSION, VERSION } from "../config.ts";
4
- import { parseJson } from "../utils";
5
- import { isLiveIsolatedWorkspace } from "../workspace-phase";
6
- import {
7
- CONTRACT_REQUIREMENTS,
8
- contractCompatibility,
9
- parseRuntimeCapabilities,
10
- parseRuntimeContracts,
11
- parseRuntimePackage,
12
- type RuntimeContracts,
13
- } from "../contracts";
14
- import { STALE_TTL_MS, DAY_MS, CLAIM_LEASE_MS, POOL_CLAIM_LEASE_MS, WORKSPACE_MERGE_LEASE_MS } from "../config";
2
+ import { DAY_MS } from "../config";
15
3
  import { matchAgents } from "../agent-ref";
16
4
  import { getAgent, listAgents } from "./agents.ts";
17
5
  import { getDb } from "./connection.ts";
@@ -0,0 +1,50 @@
1
+ import type { ProjectProfileInstructions } from "../types";
2
+ import { getDb, ValidationError } from "./connection";
3
+ import { promptHistory, writePromptVersion } from "./prompt-versions";
4
+ import type { WriterOpContext, WriterOpSpec } from "./write-ops";
5
+
6
+ export const PROJECT_PROFILE_INSTRUCTIONS_SCHEMA_SQL = `
7
+ CREATE TABLE IF NOT EXISTS project_profile_instructions (
8
+ project_id TEXT NOT NULL REFERENCES projects(id), profile TEXT NOT NULL,
9
+ slug TEXT NOT NULL UNIQUE, PRIMARY KEY (project_id, profile)
10
+ );
11
+ CREATE INDEX IF NOT EXISTS project_profile_instructions_profile ON project_profile_instructions(profile);
12
+ `;
13
+
14
+ export interface ProjectProfileKey { projectId: string; profile: string }
15
+ const columns = `p.project_id AS projectId, p.profile, coalesce(h.template, '') AS template,
16
+ coalesce(h.version, 0) AS version, coalesce(h.updated_at, 0) AS updatedAt`;
17
+
18
+ export function getProjectProfileInstructions(key: ProjectProfileKey): ProjectProfileInstructions {
19
+ return getDb().query(`SELECT ${columns} FROM project_profile_instructions p
20
+ LEFT JOIN prompt_heads h ON h.slug = p.slug WHERE p.project_id = ? AND p.profile = ?`)
21
+ .get(key.projectId, key.profile) as ProjectProfileInstructions | null
22
+ ?? { ...key, template: "", version: 0, updatedAt: 0 };
23
+ }
24
+
25
+ function instructionSlug(key: ProjectProfileKey): string {
26
+ return `project-profile:${JSON.stringify([key.projectId, key.profile])}`;
27
+ }
28
+
29
+ export function writeProjectProfileInstructions(args: ProjectProfileKey & {
30
+ template?: string; expectedVersion: number; actor: string; reason?: string; restoreVersion?: number;
31
+ }, now: number): ProjectProfileInstructions {
32
+ if (!getDb().query("SELECT 1 FROM projects WHERE id = ?").get(args.projectId)) throw new ValidationError("project not found");
33
+ getDb().query("INSERT OR IGNORE INTO project_profile_instructions VALUES (?, ?, ?)").run(args.projectId, args.profile, instructionSlug(args));
34
+ writePromptVersion({ ...args, slug: instructionSlug(args), action: args.restoreVersion === undefined ? undefined : "restore" }, now);
35
+ return getProjectProfileInstructions(args);
36
+ }
37
+
38
+ export const PROJECT_PROFILE_INSTRUCTION_READ_OPS = {
39
+ "projectProfileInstructions.get": getProjectProfileInstructions,
40
+ "projectProfileInstructions.history": (key: ProjectProfileKey) => promptHistory(instructionSlug(key)),
41
+ "projectProfileInstructions.list": (key: { projectId?: string; profile?: string }) => getDb().query(`SELECT ${columns}
42
+ FROM project_profile_instructions p LEFT JOIN prompt_heads h ON h.slug = p.slug
43
+ WHERE (?1 IS NULL OR p.project_id = ?1) AND (?2 IS NULL OR p.profile = ?2)`)
44
+ .all(key.projectId ?? null, key.profile ?? null) as ProjectProfileInstructions[],
45
+ };
46
+
47
+ export const PROJECT_PROFILE_INSTRUCTION_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = {
48
+ "projectProfileInstructions.write": { offLoop: true, idempotent: false,
49
+ fn: (args: Parameters<typeof writeProjectProfileInstructions>[0], ctx: WriterOpContext) => writeProjectProfileInstructions(args, ctx.now) },
50
+ };
@@ -10,6 +10,7 @@ export const PROMPT_VERSION_READ_OPS = {
10
10
 
11
11
  export const PROMPT_VERSION_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = {
12
12
  "prompts.write": {
13
+ offLoop: true,
13
14
  idempotent: false,
14
15
  fn: (args: PromptWrite, ctx: WriterOpContext) => writePromptVersion(args, ctx.now),
15
16
  },
@@ -5,6 +5,7 @@ import { PROFILE_PROMPT_SCHEMA_SQL } from "./prompt-version-migration";
5
5
  export { migratePromptVersions, promptLegacyHistory } from "./prompt-version-migration";
6
6
  import { bundledPrompt, hasBundledPrompt, BUNDLED_PROMPT_MANIFEST, profilePromptSlug, versionedPromptsEnabled } from "../versioned-prompts";
7
7
  import { getDb, ValidationError } from "./connection";
8
+ import { validateProjectProfileInstructionText } from "../project-profile-instruction-text";
8
9
 
9
10
  export const PROMPT_VERSIONS_SCHEMA_SQL = PROFILE_PROMPT_SCHEMA_SQL + `
10
11
  CREATE TABLE IF NOT EXISTS prompt_heads (
@@ -32,7 +33,10 @@ export function promptVersionMetadata(slug: string): Partial<PromptTemplate> | u
32
33
  if (!versionedPromptsEnabled()) return;
33
34
  const current = head(slug);
34
35
  const bundle = hasBundledPrompt(slug) ? bundledPrompt(slug) : undefined;
35
- if (!current && !bundle) return;
36
+ if (!current && !bundle) {
37
+ if (getDb().query("SELECT 1 FROM project_profile_instructions WHERE slug = ?").get(slug)) return { template: "", version: 0, updatedAt: 0, source: "canonical" };
38
+ return;
39
+ }
36
40
  return {
37
41
  template: current?.template ?? bundle!.template,
38
42
  version: current?.version ?? 0, seedHash: current?.seed_hash ?? null,
@@ -50,7 +54,8 @@ export function promptHistory(slug: string): PromptRevision[] {
50
54
  function checkedPromptHead(args: Pick<PromptWrite, "slug" | "expectedVersion">, current = head(args.slug)): Head | null {
51
55
  if (!versionedPromptsEnabled()) throw new ValidationError("versioned prompt not found");
52
56
  const mapped = !current && !hasBundledPrompt(args.slug) ? getDb().query(`SELECT 1 FROM prompt_profile_slots
53
- WHERE system_slug = ?1 OR EXISTS (SELECT 1 FROM json_each(append_slugs) WHERE value = ?1) LIMIT 1`).get(args.slug) : true;
57
+ WHERE system_slug = ?1 OR EXISTS (SELECT 1 FROM json_each(append_slugs) WHERE value = ?1)
58
+ UNION ALL SELECT 1 FROM project_profile_instructions WHERE slug = ?1 LIMIT 1`).get(args.slug) : true;
54
59
  if (!mapped) throw new ValidationError("versioned prompt not found");
55
60
  if (!Number.isSafeInteger(args.expectedVersion) || args.expectedVersion! < 0) throw new ValidationError(`expectedVersion for ${args.slug} must be a non-negative integer`);
56
61
  if (args.expectedVersion !== (current?.version ?? 0)) throw new PromptVersionConflictError(JSON.stringify({ slug: args.slug, ...promptVersionMetadata(args.slug), version: current?.version ?? 0 }));
@@ -70,6 +75,8 @@ function appendPromptVersion(args: PromptWrite, current: Head | null, now: numbe
70
75
  if (args.action === "restore" && !restored) throw new ValidationError("prompt revision not found");
71
76
  const template = args.action === "adopt" ? bundle!.template : restored?.template ?? args.template;
72
77
  if (template === undefined) throw new ValidationError("template required");
78
+ const projectProfile = getDb().query("SELECT project_id, profile FROM project_profile_instructions WHERE slug = ?").get(args.slug) as { project_id: string; profile: string } | null;
79
+ if (projectProfile) validateProjectProfileInstructionText(template, `project ${projectProfile.project_id} profile ${projectProfile.profile} instructions`);
73
80
  const seedHash = args.action === "adopt" ? bundle!.hash : restored ? restored.seedHash ?? null : current?.seed_hash ?? null;
74
81
  const version = (current?.version ?? 0) + 1;
75
82
  getDb().query("INSERT INTO prompt_revisions VALUES (?, ?, ?, ?, ?, ?, ?)")
@@ -9,8 +9,8 @@ import type { SpawnProvider } from "../types.ts";
9
9
  import type { WriterOpContext, WriterOpSpec } from "./write-ops.ts";
10
10
 
11
11
  export const PROVIDER_CATALOG_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze({
12
- "providerCatalog.upsert": { idempotent: false, fn: (args: { input: ProviderModelOverrideInput }, ctx: WriterOpContext) => upsertProviderModelOverride(args.input, ctx.now) },
13
- "providerCatalog.setDisabled": { idempotent: false, fn: (args: { provider: SpawnProvider; alias: string; disabled: boolean; updatedBy?: string }, ctx: WriterOpContext) => setProviderModelDisabled(args.provider, args.alias, args.disabled, args.updatedBy, ctx.now) },
14
- "providerCatalog.remove": { idempotent: false, fn: (args: { provider: SpawnProvider; alias: string; updatedBy?: string }, ctx: WriterOpContext) => removeProviderModelOverride(args.provider, args.alias, args.updatedBy, ctx.now) },
15
- "providerCatalog.setDefault": { idempotent: false, fn: (args: { provider: SpawnProvider; alias: string; updatedBy?: string }, ctx: WriterOpContext) => setProviderDefaultModel(args.provider, args.alias, args.updatedBy, ctx.now) },
12
+ "providerCatalog.upsert": { offLoop: true, idempotent: false, fn: (args: { input: ProviderModelOverrideInput }, ctx: WriterOpContext) => upsertProviderModelOverride(args.input, ctx.now) },
13
+ "providerCatalog.setDisabled": { offLoop: true, idempotent: false, fn: (args: { provider: SpawnProvider; alias: string; disabled: boolean; updatedBy?: string }, ctx: WriterOpContext) => setProviderModelDisabled(args.provider, args.alias, args.disabled, args.updatedBy, ctx.now) },
14
+ "providerCatalog.remove": { offLoop: true, idempotent: false, fn: (args: { provider: SpawnProvider; alias: string; updatedBy?: string }, ctx: WriterOpContext) => removeProviderModelOverride(args.provider, args.alias, args.updatedBy, ctx.now) },
15
+ "providerCatalog.setDefault": { offLoop: true, idempotent: false, fn: (args: { provider: SpawnProvider; alias: string; updatedBy?: string }, ctx: WriterOpContext) => setProviderDefaultModel(args.provider, args.alias, args.updatedBy, ctx.now) },
16
16
  });
@@ -13,11 +13,16 @@ function redactionPatch(env: unknown, nextEnv?: Record<string, unknown>): Record
13
13
  ).map(([key]) => [key, SECRET_VALUE_MASK]));
14
14
  }
15
15
 
16
- /** Update and delete archive through the same seam so neither persists credentials (#3540). */
17
- export function recordConfigHistory(entry: ConfigEntry, changedAt: string, changedBy?: string, nextValue?: unknown): void {
18
- const value = entry.namespace === "provider-config" && isRecord(entry.value) && isRecord(entry.value.env)
16
+ /** A config value with its provider credentials masked: what any copy outside the live row stores (#3540). */
17
+ export function credentialFreeConfigValue(entry: ConfigEntry, nextValue?: unknown): unknown {
18
+ return entry.namespace === "provider-config" && isRecord(entry.value) && isRecord(entry.value.env)
19
19
  ? { ...entry.value, env: { ...entry.value.env, ...redactionPatch(entry.value.env, isRecord(nextValue) && isRecord(nextValue.env) ? nextValue.env : undefined) } }
20
20
  : entry.value;
21
+ }
22
+
23
+ /** Update and delete archive through the same seam so neither persists credentials (#3540). */
24
+ export function recordConfigHistory(entry: ConfigEntry, changedAt: string, changedBy?: string, nextValue?: unknown): void {
25
+ const value = credentialFreeConfigValue(entry, nextValue);
21
26
  getDb().query(`
22
27
  INSERT INTO config_history (namespace, key, value, version, changed_at, changed_by)
23
28
  VALUES (?, ?, ?, ?, ?, ?)
@@ -3,6 +3,7 @@ import type { WriterOpSpec } from "./write-ops.ts";
3
3
 
4
4
  export const PROVIDER_FLAP_MESSAGES_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze({
5
5
  "providerFlapMessages.ensureIndex": {
6
+ offLoop: true,
6
7
  idempotent: true,
7
8
  fn: () => {
8
9
  ensureProviderFlapMessagesIndex();
@@ -0,0 +1,34 @@
1
+ import { providerCatalogList } from "@humwick/sdk/provider-catalog";
2
+ import { getDb } from "./connection";
3
+
4
+ const SEED_KEY = "provider-model-db-catalog-v1";
5
+
6
+ /** One migration backfills missing rows; later boots and releases leave the database alone. */
7
+ export function reconcileProviderModelSeed(): void {
8
+ const db = getDb();
9
+ if (db.query("SELECT 1 FROM config WHERE namespace = 'system' AND key = ?").get(SEED_KEY)) return;
10
+ db.transaction(() => {
11
+ seedProviderModelOverrides("seed:db-catalog-v1");
12
+ db.query("INSERT INTO config (namespace, key, value, version, updated_at, updated_by) VALUES ('system', ?, 'true', 1, ?, 'server-startup')")
13
+ .run(SEED_KEY, Date.now());
14
+ })();
15
+ }
16
+
17
+ /** Insert-only migration: operator edits, disabled entries and tombstones always win. */
18
+ export function seedProviderModelOverrides(stamp: string, now = Date.now()): { inserted: number } {
19
+ const db = getDb();
20
+ const insert = db.query(`INSERT INTO provider_model_overrides (provider, alias, entry, deleted, disabled, updated_at, updated_by)
21
+ VALUES (?, ?, ?, 0, ?, ?, ?) ON CONFLICT(provider, alias) DO NOTHING`);
22
+ const defaultModel = db.query(`INSERT INTO provider_model_defaults (provider, alias, updated_at, updated_by)
23
+ VALUES (?, ?, ?, ?) ON CONFLICT(provider) DO NOTHING`);
24
+ return db.transaction(() => {
25
+ let inserted = 0;
26
+ for (const provider of providerCatalogList()) {
27
+ for (const model of provider.models) {
28
+ inserted += insert.run(provider.provider, model.alias, JSON.stringify(model), model.disabled ? 1 : 0, now, stamp).changes;
29
+ }
30
+ if (provider.defaultModel) defaultModel.run(provider.provider, provider.defaultModel, now, stamp);
31
+ }
32
+ return { inserted };
33
+ })();
34
+ }
@@ -17,6 +17,7 @@ export const PROVIDER_QUOTA_LEASE_WRITE_OPS: Readonly<Record<string, WriterOpSpe
17
17
  acquireProviderQuotaLeaseInTx(normalizeProviderQuotaLeaseAcquireInput({ ...args.input, now: args.input.now ?? ctx.now })),
18
18
  },
19
19
  "providerQuotaLeases.release": {
20
+ offLoop: true,
20
21
  idempotent: true,
21
22
  fn: (args: { input: Parameters<typeof releaseProviderQuotaLeaseInTx>[0] }) => releaseProviderQuotaLeaseInTx(args.input),
22
23
  },
@@ -54,6 +54,7 @@ export const PROVIDER_QUOTA_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> =
54
54
  // It returns concrete messages for main-thread SSE fanout; never call notifySystemMessage in a
55
55
  // worker, whose listener set is intentionally not shared with the server.
56
56
  "providerQuotas.routeNotification": {
57
+ offLoop: true,
57
58
  // Routing inserts message rows. The effect facade supplies a stable per-effect key so a
58
59
  // post-commit acknowledgement loss returns these exact messages instead of routing twice.
59
60
  idempotent: false,
@@ -63,6 +64,7 @@ export const PROVIDER_QUOTA_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> =
63
64
  fn: (args: { input: RouteNotificationInput; now?: number }, ctx: WriterOpContext) => routeProviderQuotaNotificationInTx(args.input, args.now ?? ctx.now),
64
65
  },
65
66
  "providerQuotas.pruneHistory": {
67
+ offLoop: true,
66
68
  idempotent: true,
67
69
  fn: (args: { olderThan: number }) => ({ changes: pruneProviderQuotaHistory(args.olderThan) }),
68
70
  },
@@ -26,18 +26,19 @@ import type { WriterOpContext, WriterOpSpec } from "./write-ops.ts";
26
26
 
27
27
  export const PROVISIONING_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze({
28
28
  // Creating a capability mints a random id, so the generic durable ledger owns phantom-ACK retry.
29
- "provisioning.capabilities.create": { idempotent: false, fn: (args: CreateProvisioningCapabilityInput, ctx: WriterOpContext) => createProvisioningCapability(args, ctx.now) },
30
- "provisioning.capabilities.update": { idempotent: true, fn: (args: { id: string; patch: UpdateProvisioningCapabilityInput }, ctx: WriterOpContext) => updateProvisioningCapability(args.id, args.patch, ctx.now) },
31
- "provisioning.capabilities.delete": { idempotent: true, fn: (args: { id: string }) => deleteProvisioningCapability(args.id) },
29
+ "provisioning.capabilities.create": { offLoop: true, idempotent: false, fn: (args: CreateProvisioningCapabilityInput, ctx: WriterOpContext) => createProvisioningCapability(args, ctx.now) },
30
+ "provisioning.capabilities.update": { offLoop: true, idempotent: true, fn: (args: { id: string; patch: UpdateProvisioningCapabilityInput }, ctx: WriterOpContext) => updateProvisioningCapability(args.id, args.patch, ctx.now) },
31
+ "provisioning.capabilities.delete": { offLoop: true, idempotent: true, fn: (args: { id: string }) => deleteProvisioningCapability(args.id) },
32
32
  // The capability lookup and its variant insert/update execute under one writer lock.
33
- "provisioning.variants.createMcp": { idempotent: false, fn: (args: { capabilityId: string; input: CreateMcpProvisioningVariantInput }, ctx: WriterOpContext) => createMcpProvisioningVariant(args.capabilityId, args.input, ctx.now) },
34
- "provisioning.variants.patch": { idempotent: true, fn: (args: { id: string; patch: PatchProvisioningAssetVariantInput }, ctx: WriterOpContext) => patchProvisioningAssetVariant(args.id, args.patch, ctx.now) },
35
- "provisioning.variants.validate": { idempotent: true, fn: (args: { id: string }, ctx: WriterOpContext) => validateProvisioningAssetVariant(args.id, ctx.now) },
36
- "provisioning.assets.upsert": { idempotent: false, fn: (args: UpsertProvisioningAssetInput, ctx: WriterOpContext) => upsertProvisioningAsset(args, ctx.now) },
33
+ "provisioning.variants.createMcp": { offLoop: true, idempotent: false, fn: (args: { capabilityId: string; input: CreateMcpProvisioningVariantInput }, ctx: WriterOpContext) => createMcpProvisioningVariant(args.capabilityId, args.input, ctx.now) },
34
+ "provisioning.variants.patch": { offLoop: true, idempotent: true, fn: (args: { id: string; patch: PatchProvisioningAssetVariantInput }, ctx: WriterOpContext) => patchProvisioningAssetVariant(args.id, args.patch, ctx.now) },
35
+ "provisioning.variants.validate": { offLoop: true, idempotent: true, fn: (args: { id: string }, ctx: WriterOpContext) => validateProvisioningAssetVariant(args.id, ctx.now) },
36
+ "provisioning.assets.upsert": { offLoop: true, idempotent: false, fn: (args: UpsertProvisioningAssetInput, ctx: WriterOpContext) => upsertProvisioningAsset(args, ctx.now) },
37
37
  "provisioning.assets.importGithub": {
38
+ offLoop: true,
38
39
  idempotent: false,
39
40
  fn: (args: { input: ImportProvisioningAssetFromGitHubInput; source: GitHubVendoredSource; definition: ResolvedAssetDefinition; now: number; target?: ProvisioningGitHubImportTarget }) =>
40
41
  upsertImportedAssetBody(args.input, args.source, args.definition, args.now, args.target),
41
42
  },
42
- "provisioning.assets.delete": { idempotent: true, fn: (args: { kind: ProvisioningAssetKind; name: string; provider?: AgentProfileProvider }) => deleteProvisioningAsset(args.kind, args.name, args.provider) },
43
+ "provisioning.assets.delete": { offLoop: true, idempotent: true, fn: (args: { kind: ProvisioningAssetKind; name: string; provider?: AgentProfileProvider }) => deleteProvisioningAsset(args.kind, args.name, args.provider) },
43
44
  });
@@ -101,6 +101,9 @@ export const KERNEL_ENTITY_CATALOG = {
101
101
  } as const;
102
102
 
103
103
  export const READ_OP_MARSHALING_FIXTURES: ReadOpFixtures = {
104
+ "projectProfileInstructions.get": { args: { projectId: "missing", profile: "worker" } },
105
+ "projectProfileInstructions.history": { args: { projectId: "missing", profile: "worker" } },
106
+ "projectProfileInstructions.list": { args: { projectId: "missing" } },
104
107
  "planGraph.import.preview": { args: { scope: "import-fixture", actor: "system", expectedVersion: 0, requestKey: "preview", documentHash: "h", requestHash: "h", newPlanId: "plan-fixture", executions: { a: { workflow: "build-review" } }, document: { version: 1, key: "fixture", revision: "r1", title: "Fixture", acceptance: [{ id: "works", text: "Works" }], tasks: [{ key: "a", title: "A", intent: "Build A" }] } } },
105
108
  "guides.search": { args: { query: "violet", guides: [{ id: "workflows", title: "Workflows", summary: "Build and review", tags: ["review"], body: "A violet checksum", revision: "fixture-revision" }] } },
106
109
  "commands.latestByCorrelation": { args: { type: "scheduler.cancel", correlationId: "fixture" } },
@@ -164,6 +167,7 @@ export const READ_OP_MARSHALING_FIXTURES: ReadOpFixtures = {
164
167
 
165
168
  // --- channels / projects / teams / hosts --------------------------------------------------------
166
169
  "channels.bindingOwner": { args: { id: "binding-t9" } },
170
+ "integrations.taskStats": { args: undefined },
167
171
  "channels.snapshot": { args: undefined },
168
172
  "channels.bindings": { args: {} },
169
173
  "channels.routes": { args: { channelId: "channel-t9" } },
@@ -216,6 +220,7 @@ export const READ_OP_MARSHALING_FIXTURES: ReadOpFixtures = {
216
220
  "ciIncidents.sha.hasExpired": { args: { now: FIXTURE_NOW } },
217
221
  "ciIncidents.lease.hasExpired": { args: { now: FIXTURE_NOW } },
218
222
  "deployEvents.get": { args: { id: "dep-t9" } },
223
+ "deployEvents.forCommit": { args: { commitSha: "a".repeat(40) } },
219
224
  // #2189 P7 — restart-epoch reads. Every one is a point/keyset read against ids that do not exist in
220
225
  // the fixture DB, which is the point: marshaling has to survive the empty answer too.
221
226
  "restartEpoch.openAll": { args: undefined },
@@ -1,4 +1,6 @@
1
+ import { INTEGRATIONS_READ_OPS } from "./integrations-ops.ts";
1
2
  import { PROMPT_VERSION_READ_OPS } from "./prompt-versions-ops";
3
+ import { PROJECT_PROFILE_INSTRUCTION_READ_OPS } from "./project-profile-instructions";
2
4
  import { VAULT_READ_OPS } from "./secrets-vault";
3
5
  import { GUIDE_READ_OPS } from "./guides-search";
4
6
  import { ROUTING_POLICY_READ_OPS } from "./routing-policy";
@@ -129,6 +131,7 @@ const SPIN_MAX_MS = 2_000;
129
131
  export const readOps = {
130
132
  ...GUIDE_READ_OPS,
131
133
  ...PROMPT_VERSION_READ_OPS,
134
+ ...PROJECT_PROFILE_INSTRUCTION_READ_OPS,
132
135
  ...SERVICE_ACCESS_READ_OPS,
133
136
  ...VAULT_TOKEN_READ_OPS,
134
137
  ...VAULT_HEALTH_READ_OPS,
@@ -254,6 +257,7 @@ export const readOps = {
254
257
  ...PROJECTS_READ_OPS,
255
258
  ...PROJECT_ENTRIES_READ_OPS,
256
259
  ...CHANNELS_READ_OPS,
260
+ ...INTEGRATIONS_READ_OPS,
257
261
  } satisfies Record<string, ReadOpFn>;
258
262
 
259
263
  /** The registered read-op names. */
@@ -3,7 +3,6 @@ import { isRecord, ValidationError, type RoutingPolicy, type RoutingPolicyEnvelo
3
3
  import { getConfig, setConfigInternal } from "../config-store";
4
4
  import { getDb } from "./connection";
5
5
  import { DEFAULT_ROUTING_POLICY, normalizeRoutingPolicy, ROUTING_POLICY_NAMESPACE as NS, ROUTING_POLICY_KEY as KEY } from "../routing-policy-normalize";
6
- import { currentRoutingModels } from "../routing-model-aliases";
7
6
 
8
7
  export function readRoutingPolicy(): RoutingPolicyEnvelope {
9
8
  const entry = getConfig(NS, KEY);
@@ -19,7 +18,7 @@ export function readRoutingPolicy(): RoutingPolicyEnvelope {
19
18
  policy.migration = { sourceSchemaVersion: 1, notes: stored.migration.notes.filter((note): note is string => typeof note === "string") };
20
19
  }
21
20
  }
22
- return { policy: currentRoutingModels(policy), revision: entry?.version ?? 0, diagnostics: policy.migration ?? null };
21
+ return { policy, revision: entry?.version ?? 0, diagnostics: policy.migration ?? null };
23
22
  }
24
23
 
25
24
  export interface RoutingPolicyWrite {
@@ -53,12 +52,12 @@ export function writeRoutingPolicy(args: RoutingPolicyWrite, now: number) {
53
52
  }
54
53
  }
55
54
  const policy: RoutingPolicy = { ...parsed, ...(migration ? { migration } : {}), ...(sourceRevision ? { migrationSourceRevision: sourceRevision } : {}) };
56
- const changed = (isRecord(stored?.value) && stored.value.schemaVersion === 1) || JSON.stringify(currentRoutingModels(policy)) !== JSON.stringify(current.policy);
55
+ const changed = (isRecord(stored?.value) && stored.value.schemaVersion === 1) || JSON.stringify(policy) !== JSON.stringify(current.policy);
57
56
  if (changed) setConfigInternal(NS, KEY, policy, args.updatedBy, now);
58
57
  return { envelope: readRoutingPolicy(), previous: current.policy, changed };
59
58
  }
60
59
 
61
60
  export const ROUTING_POLICY_READ_OPS = { "routingPolicy.get": () => readRoutingPolicy() };
62
61
  export const ROUTING_POLICY_WRITE_OPS = {
63
- "routingPolicy.set": { idempotent: false, fn: (args: RoutingPolicyWrite, ctx: { now: number }) => writeRoutingPolicy(args, ctx.now) },
62
+ "routingPolicy.set": { offLoop: true, idempotent: false, fn: (args: RoutingPolicyWrite, ctx: { now: number }) => writeRoutingPolicy(args, ctx.now) },
64
63
  };
package/src/db/schema.ts CHANGED
@@ -25,7 +25,8 @@ import { resetLifecycleSeqSeed } from "./lifecycle-seq.ts";
25
25
  import { resetFannedSeq } from "../sse-outbox-cursor";
26
26
  import { dropLegacyContinuationArchiveIndex } from "./continuation-archive-search.ts";
27
27
  import { applyMigrations } from "./migrations.ts";
28
- import { PROMPT_TEMPLATES_SCHEMA_SQL, seedPromptTemplatesIfNeeded } from "./prompt-templates-schema";
28
+ import { reconcileProviderModelSeed } from "./provider-model-seed"; import { PROMPT_TEMPLATES_SCHEMA_SQL, seedPromptTemplatesIfNeeded } from "./prompt-templates-schema";
29
+ import { PROJECT_PROFILE_INSTRUCTIONS_SCHEMA_SQL } from "./project-profile-instructions";
29
30
  import { ensureLandingPhaseOneSchema } from "./landing-schema.ts";
30
31
  import { beginStartupPhase, timeStartupPhase } from "../startup-timing";
31
32
  import { resetMainConnectionWrites } from "./main-connection-writes";
@@ -552,6 +553,7 @@ export function initDb(path: string = "agent-relay.db"): Database {
552
553
  CREATE INDEX IF NOT EXISTS idx_config_history_key ON config_history(namespace, key, version);
553
554
 
554
555
  ${PROMPT_TEMPLATES_SCHEMA_SQL}
556
+ ${PROJECT_PROFILE_INSTRUCTIONS_SCHEMA_SQL}
555
557
 
556
558
  -- Per-user settings (#391, epic #388). The user dimension the global \`config\` table lacks:
557
559
  -- the same \`(namespace, key)\` descriptor address resolves to a per-user value, keyed by the
@@ -844,6 +846,8 @@ export function initDb(path: string = "agent-relay.db"): Database {
844
846
  -- distinct from severity). Both optional; absent on claim-kind rows.
845
847
  tags TEXT,
846
848
  priority TEXT,
849
+ -- #3736 — paths[] (JSON array of repo-relative paths) a project spawn policy matches.
850
+ paths TEXT,
847
851
  -- #1535 WS1 — parent/epic tree edge: a task has at most one parent. Cycle-guarded
848
852
  -- in application code (src/db/task-entity.ts setTaskParent), the same way as the
849
853
  -- depends-on DAG below (#834 finding 7) — SQLite CHECK can't express acyclicity.
@@ -858,7 +862,7 @@ export function initDb(path: string = "agent-relay.db"): Database {
858
862
  -- gate-validated but does NOT arm a land (no ingress); the coordinator's clear (CAS on
859
863
  -- the held delivery's host-probed HEAD) promotes that exact delivery. Distinct from the
860
864
  -- permanent workspace landBlocked. Default 0 keeps every pre-#2518 Task unheld.
861
- validation_items TEXT, terminal_stage TEXT, completed_at INTEGER,
865
+ validation_items TEXT, terminal_stage TEXT, completed_at INTEGER, on_deploy TEXT,
862
866
  review_hold INTEGER NOT NULL DEFAULT 0 CHECK (review_hold IN (0, 1)),
863
867
  -- #2862 durable owner: a coordinator role (coordinator:TEAM) or a human id, never an agent id.
864
868
  owner TEXT
@@ -867,6 +871,8 @@ export function initDb(path: string = "agent-relay.db"): Database {
867
871
  CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
868
872
  CREATE INDEX IF NOT EXISTS idx_tasks_target ON tasks(target);
869
873
  CREATE INDEX IF NOT EXISTS idx_tasks_updated ON tasks(updated_at);
874
+ -- #3740: cover the integration aggregation without fetching task bodies/metadata.
875
+ CREATE INDEX IF NOT EXISTS idx_tasks_integration_stats ON tasks(coalesce(json_extract(metadata, '$.relayIntegrationName'), source), status, claimed_by, last_seen_at, updated_at, source);
870
876
  CREATE INDEX IF NOT EXISTS idx_tasks_message_status
871
877
  ON tasks(message_id, status)
872
878
  WHERE message_id IS NOT NULL;
@@ -1456,7 +1462,7 @@ export function initDb(path: string = "agent-relay.db"): Database {
1456
1462
  ensureLandingPhaseOneSchema();
1457
1463
  // Vault catalog, environments and metadata upgrades share applyMigrations (#3561, #3562).
1458
1464
  timeStartupPhase("db.migrations", applyMigrations);
1459
- timeStartupPhase("db.prompt-seed", seedPromptTemplatesIfNeeded);
1465
+ timeStartupPhase("db.prompt-seed", seedPromptTemplatesIfNeeded); timeStartupPhase("db.model-seed", reconcileProviderModelSeed);
1460
1466
  timeStartupPhase("db.token-profile-seed", seedBuiltInTokenProfiles);
1461
1467
 
1462
1468
  timeStartupPhase("db.planner-statistics", () => refreshPlannerStats(getDb()));
@@ -14,10 +14,10 @@ import type { WriterOpContext, WriterOpSpec } from "./write-ops.ts";
14
14
  export const SECRETS_BROKER_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze({
15
15
  // Caller-generated ids + INSERT OR IGNORE make both inserts safe to replay.
16
16
  "secretsBroker.audit.record": { idempotent: true, offLoop: true, fn: (a: SecretAuditInput, ctx: WriterOpContext) => recordSecretAudit(a, ctx.now) },
17
- "secretsBroker.link.create": { idempotent: true, fn: (a: SecretUploadLinkInput, ctx: WriterOpContext) => createSecretUploadLink(a, ctx.now) },
17
+ "secretsBroker.link.create": { offLoop: true, idempotent: true, fn: (a: SecretUploadLinkInput, ctx: WriterOpContext) => createSecretUploadLink(a, ctx.now) },
18
18
  // A replayed claim must not hand the link out twice: the ledger returns the first claim's result.
19
- "secretsBroker.link.claim": { idempotent: false, fn: (a: { tokenHash: string }, ctx: WriterOpContext) => claimSecretUploadLink(a.tokenHash, ctx.now) },
20
- "secretsBroker.loginLatch.set": { idempotent: true, fn: (a: { fingerprint: string; state: SecretBrokerLoginLatchState }, ctx: WriterOpContext) => setSecretBrokerLoginLatch(a, ctx.now) },
21
- "secretsBroker.loginLatch.clear": { idempotent: true, fn: (a: { fingerprint: string }) => clearSecretBrokerLoginLatch(a.fingerprint) },
22
- "secretsBroker.link.finish": { idempotent: true, fn: (a: Parameters<typeof finishSecretUploadLink>[0]) => finishSecretUploadLink(a) },
19
+ "secretsBroker.link.claim": { offLoop: true, idempotent: false, fn: (a: { tokenHash: string }, ctx: WriterOpContext) => claimSecretUploadLink(a.tokenHash, ctx.now) },
20
+ "secretsBroker.loginLatch.set": { offLoop: true, idempotent: true, fn: (a: { fingerprint: string; state: SecretBrokerLoginLatchState }, ctx: WriterOpContext) => setSecretBrokerLoginLatch(a, ctx.now) },
21
+ "secretsBroker.loginLatch.clear": { offLoop: true, idempotent: true, fn: (a: { fingerprint: string }) => clearSecretBrokerLoginLatch(a.fingerprint) },
22
+ "secretsBroker.link.finish": { offLoop: true, idempotent: true, fn: (a: Parameters<typeof finishSecretUploadLink>[0]) => finishSecretUploadLink(a) },
23
23
  });
@@ -87,11 +87,11 @@ export function editVaultMetadata(args: { name: string; patch: VaultMetadataPatc
87
87
  });
88
88
  }
89
89
  export const VAULT_WRITE_OPS = {
90
- "secretsVault.metadata": { idempotent: true, fn: editVaultMetadata },
91
- "secretsVault.delete": { idempotent: false, fn: deleteVaultSecret },
92
- "secretsVault.set": { idempotent: true, fn: setVaultRow },
93
- "secretsVault.request": { idempotent: false, fn: (args: Parameters<typeof fileVaultDecision>[0]) => fileVaultDecision(args) },
94
- "secretsVault.withdraw": { idempotent: true, fn: withdrawVaultDecisions },
95
- "secretsVault.expire": { idempotent: true, fn: expireVaultDecisions },
96
- "secretsVault.answer": { idempotent: true, fn: (args: { id: number; now: number }) => answerOpenOperatorDecision(args.id, { answers: {}, answeredBy: "vault-form", now: args.now }) },
90
+ "secretsVault.metadata": { offLoop: true, idempotent: true, fn: editVaultMetadata },
91
+ "secretsVault.delete": { offLoop: true, idempotent: false, fn: deleteVaultSecret },
92
+ "secretsVault.set": { offLoop: true, idempotent: true, fn: setVaultRow },
93
+ "secretsVault.request": { offLoop: true, idempotent: false, fn: (args: Parameters<typeof fileVaultDecision>[0]) => fileVaultDecision(args) },
94
+ "secretsVault.withdraw": { offLoop: true, idempotent: true, fn: withdrawVaultDecisions },
95
+ "secretsVault.expire": { offLoop: true, idempotent: true, fn: expireVaultDecisions },
96
+ "secretsVault.answer": { offLoop: true, idempotent: true, fn: (args: { id: number; now: number }) => answerOpenOperatorDecision(args.id, { answers: {}, answeredBy: "vault-form", now: args.now }) },
97
97
  };
@@ -16,6 +16,7 @@ function parsedParams(raw: string): Record<string, unknown> {
16
16
 
17
17
  export const SERVICES_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze({
18
18
  "services.spawnWatchdog.markUnconfirmed": {
19
+ offLoop: true,
19
20
  idempotent: true,
20
21
  fn: (args: { rows: SpawnCommandPatchRow[]; now: number; phase: "dispatch_unacknowledged" | "registration_unconfirmed" }) => {
21
22
  const changed: string[] = [];
@@ -31,6 +32,7 @@ export const SERVICES_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object
31
32
  },
32
33
  },
33
34
  "services.spawnWatchdog.markConfirmed": {
35
+ offLoop: true,
34
36
  idempotent: true,
35
37
  fn: (args: { rows: SpawnCommandPatchRow[]; now: number }) => {
36
38
  const changed: string[] = [];
@@ -46,6 +48,7 @@ export const SERVICES_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object
46
48
  },
47
49
  },
48
50
  "services.providerFlap.persist": {
51
+ offLoop: true,
49
52
  idempotent: true,
50
53
  fn: (args: { agentId: string; kind: string; episodeCount: number; lastEpisodeAt: number; latestSuppressedSeq: number }) => {
51
54
  const row = getDb().query(`