@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
@@ -100,11 +100,13 @@ function markLandingReplay(result: unknown): unknown {
100
100
 
101
101
  export const COMMANDS_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze({
102
102
  "commands.claimPending": {
103
+ offLoop: true,
103
104
  idempotent: true,
104
105
  fn: (args: { id: string; expectedType?: string; ownerToken?: string }, ctx) =>
105
106
  claimPendingCommand(args.id, args.expectedType, args.ownerToken, ctx.now),
106
107
  },
107
108
  "commands.createAgentCleanupIfAbsent": {
109
+ offLoop: true,
108
110
  idempotent: false,
109
111
  fn: (args: { target: string; id: string }, ctx: WriterOpContext) => hasActiveAgentCleanup(args.target) ? null
110
112
  : createCommandInternal({ id: args.id, input: { type: "agent.cleanup", source: "system", target: args.target,
@@ -117,10 +119,12 @@ export const COMMANDS_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object
117
119
  fn: (args: { command: Command }, ctx: WriterOpContext) => settleAgentCleanupWorkspaces(args.command, ctx.now),
118
120
  },
119
121
  "commands.reconcileScheduler": {
122
+ offLoop: true,
120
123
  idempotent: true,
121
124
  fn: (args: Parameters<typeof reconcileSchedulerCommandsBody>[0], ctx: WriterOpContext) => reconcileSchedulerCommandsBody(args, ctx.now),
122
125
  },
123
126
  "commands.createLegacyLanding": {
127
+ offLoop: true,
124
128
  idempotent: false,
125
129
  fn: (args: CreateLegacyLandingArgs, ctx: WriterOpContext) => createLegacyLandingBody(args, ctx.now),
126
130
  serializeIdempotencyResult: redactCreateResult,
@@ -136,61 +140,74 @@ export const COMMANDS_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object
136
140
  recordIdempotencyResult: (result) => (result as { claimed?: boolean } | null)?.claimed === true,
137
141
  },
138
142
  "commands.acceptLandingProgress": {
143
+ offLoop: true,
139
144
  idempotent: false,
140
145
  fn: (args: AcceptLandingProgressArgs, ctx: WriterOpContext) => acceptLandingProgressBody(args, ctx.now),
141
146
  },
142
147
  "commands.acceptLandingResult": {
148
+ offLoop: true,
143
149
  idempotent: false,
144
150
  fn: (args: AcceptLandingResultArgs, ctx: WriterOpContext) => acceptLandingResultBody(args, ctx.now),
145
151
  replayIdempotencyResult: markLandingReplay,
146
152
  },
147
153
  "commands.resolveLegacyPublication": {
154
+ offLoop: true,
148
155
  idempotent: true,
149
156
  fn: (args: ResolveLegacyPublicationArgs, ctx: WriterOpContext) => resolveLegacyPublicationBody(args, ctx.now),
150
157
  },
151
158
  "commands.acquireLegacyPublishFence": {
159
+ offLoop: true,
152
160
  idempotent: false,
153
161
  fn: (args: AcquireLegacyPublishFenceArgs, ctx: WriterOpContext) => acquireLegacyPublishFenceBody(args, ctx.now),
154
162
  },
155
163
  "commands.acquireKernelPublishFence": {
164
+ offLoop: true,
156
165
  idempotent: false,
157
166
  fn: (args: AcquireKernelPublishFenceArgs, ctx: WriterOpContext) => acquireKernelPublishFenceBody(args, ctx.now),
158
167
  },
159
168
  "commands.requestLandingRetirement": {
169
+ offLoop: true,
160
170
  idempotent: false,
161
171
  fn: (args: RequestLandingRetirementArgs, ctx: WriterOpContext) =>
162
172
  requestLandingRetirementBody(args, ctx.now),
163
173
  },
164
174
  "commands.grantKernelDryRunOwnership": {
175
+ offLoop: true,
165
176
  idempotent: false,
166
177
  fn: (args: GrantKernelDryRunOwnershipArgs, ctx: WriterOpContext) =>
167
178
  grantKernelDryRunOwnershipBody(args, ctx.now),
168
179
  },
169
180
  "commands.consumeLegacyPublishPermit": {
181
+ offLoop: true,
170
182
  idempotent: false,
171
183
  fn: (args: ConsumeLegacyPublishPermitArgs, ctx: WriterOpContext) => consumeLegacyPublishPermitBody(args, ctx.now),
172
184
  },
173
185
  "commands.recordExecutionProof": {
186
+ offLoop: true,
174
187
  idempotent: false,
175
188
  fn: (args: RecordExecutionProofArgs, ctx: WriterOpContext) => recordExecutionProofBody(args, ctx.now),
176
189
  replayIdempotencyResult: markLandingReplay,
177
190
  },
178
191
  "commands.terminalizeLegacyLanding": {
192
+ offLoop: true,
179
193
  idempotent: false,
180
194
  fn: (args: TerminalizeLegacyLandingArgs, ctx: WriterOpContext) => terminalizeLegacyLandingBody(args, ctx.now),
181
195
  replayIdempotencyResult: markLandingReplay,
182
196
  },
183
197
  "commands.cancelPendingLegacyLandings": {
198
+ offLoop: true,
184
199
  idempotent: true,
185
200
  fn: (args: CancelPendingLegacyLandingsArgs, ctx: WriterOpContext) => cancelPendingLegacyLandingsBody(args, ctx.now),
186
201
  },
187
202
  // #2609 — the withdraw's legacy-lane mid-flight cancel (pending delegates to the never-started
188
203
  // path; accepted/running is a fenced status CAS + publish-permit retirement).
189
204
  "commands.cancelLegacyLandingExecution": {
205
+ offLoop: true,
190
206
  idempotent: false,
191
207
  fn: (args: CancelLegacyLandingExecutionArgs, ctx: WriterOpContext) => cancelLegacyLandingExecutionBody(args, ctx.now),
192
208
  },
193
209
  "commands.backfillLegacyLandingPage": {
210
+ offLoop: true,
194
211
  idempotent: true,
195
212
  fn: (args: { rows: BackfillLegacyLandingRow[] }, ctx: WriterOpContext) => backfillLegacyLandingPageBody(args, ctx.now),
196
213
  },
@@ -221,6 +238,7 @@ export const COMMANDS_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object
221
238
  // only when `commandUpdateHasPostCommitEffects` says there is something to do. Idempotent: the
222
239
  // terminal-event record is guarded by an existence check and the pill re-sets the same meta keys.
223
240
  "commands.applyUpdatePostCommit": {
241
+ offLoop: true,
224
242
  idempotent: true,
225
243
  fn: (args: ApplyCommandUpdatePostCommitOpArgs): null => {
226
244
  applyCommandUpdatePostCommit(args.previousStatus, args.command);
@@ -230,6 +248,7 @@ export const COMMANDS_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object
230
248
  // Spawn reaping must not overwrite a concurrent async success settlement. The status predicate
231
249
  // lives inside this writer transaction, making the rows-changed result a real CAS winner signal.
232
250
  "commands.cancelIfStatus": {
251
+ offLoop: true,
233
252
  idempotent: false,
234
253
  fn: (args: CancelCommandIfStatusWriteArgs, ctx: WriterOpContext) => cancelCommandIfStatusInternal(args, ctx.now),
235
254
  serializeIdempotencyResult: redactCancelResult,
@@ -243,6 +262,7 @@ export const COMMANDS_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object
243
262
  // submission recovers the SAME {command, created} from the ledger (so the emit survives an
244
263
  // indeterminate ack), while a genuine later re-drive presents a fresh id and finds `created:false`.
245
264
  "commands.createKernelIfAbsent": {
265
+ offLoop: true,
246
266
  idempotent: false,
247
267
  fn: (args: CreateKernelCommandIfAbsentArgs, ctx: WriterOpContext) => {
248
268
  let input = args.input;
@@ -278,6 +298,7 @@ export const COMMANDS_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object
278
298
  // One caller's quota admission is all-or-nothing: the live + in-flight reads and INSERT share this
279
299
  // operation's transaction, rather than independent-caller coalescing/savepoints.
280
300
  "commands.createSpawnWithinQuota": {
301
+ offLoop: true,
281
302
  idempotent: false,
282
303
  fn: (args: CreateSpawnCommandWithinQuotaWriteArgs, ctx: WriterOpContext) =>
283
304
  createSpawnCommandWithinQuotaInternal(args, ctx.now),
@@ -13,14 +13,15 @@
13
13
  // `_writer_idempotency`. That result is a `ConfigEntry` whose `value` is the SAME operator data already
14
14
  // persisted verbatim in the `config` table of the SAME database file — so this introduces NO new
15
15
  // at-rest exposure class (unlike a signed token, which is never otherwise stored). We therefore keep
16
- // the real result in the ledger (redacting it would corrupt a keyed phantom-retry's return value) and
17
- // deliberately omit `serializeIdempotencyResult`.
16
+ // the real result in the ledger, except provider credentials: the ledger is a copy outside the live row,
17
+ // so it masks them like config_history does (#3540, #1870 S7).
18
18
 
19
19
  import {
20
20
  deleteConfigInternal,
21
21
  setConfigInternal,
22
22
  } from "../config-store.ts";
23
23
  import type { ConfigEntry } from "../types.ts";
24
+ import { credentialFreeConfigValue } from "./provider-config-history.ts";
24
25
  import type { WriterOpContext, WriterOpSpec } from "./write-ops.ts";
25
26
 
26
27
  /** Structured-clone-safe args for `config.set` (H6: plain data only). `value` is arbitrary JSON. */
@@ -43,14 +44,20 @@ export const CONFIG_STORE_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Ob
43
44
  // retry would double-bump/duplicate. The gateway requires an idempotencyKey (the facade supplies a
44
45
  // per-submission one) and dedups on it in the SAME transaction as the write.
45
46
  "config.set": {
47
+ offLoop: true,
46
48
  idempotent: false,
47
49
  idempotencySnapshotKey: (args: SetConfigOpArgs) => JSON.stringify([args.namespace, args.key]),
50
+ serializeIdempotencyResult: (result: unknown) => {
51
+ const entry = result as ConfigEntry;
52
+ return { ...entry, value: credentialFreeConfigValue(entry) };
53
+ },
48
54
  fn: (args: SetConfigOpArgs, ctx: WriterOpContext): ConfigEntry =>
49
55
  setConfigInternal(args.namespace, args.key, args.value, args.updatedBy, ctx.now),
50
56
  },
51
57
  // Idempotent by construction: deleting again finds no row (`existing` null → early false) and writes
52
58
  // nothing, so re-applying converges. No key required.
53
59
  "config.delete": {
60
+ offLoop: true,
54
61
  idempotent: true,
55
62
  fn: (args: DeleteConfigOpArgs, ctx: WriterOpContext): boolean =>
56
63
  deleteConfigInternal(args.namespace, args.key, args.updatedBy, ctx.now),
@@ -3,6 +3,7 @@ import { getDbPath, runDbMaintenanceWorker } from "./connection.ts";
3
3
  import { purgeContinuationArchives, type ContinuationArchivePurgeInput, type ContinuationArchivePurgeResult } from "./continuation-archive-purge.ts";
4
4
  import { syncInMemoryContinuationArchiveSearchIndex, type ContinuationArchiveSearchSyncInput, type ContinuationArchiveSearchSyncResult } from "./continuation-archive-search.ts";
5
5
  import { getGateway } from "./gateway.ts";
6
+ import type { appendContinuationArchiveRefs } from "./continuations.ts";
6
7
  import type { archiveAndAdvanceContinuation, archiveContinuationSegment, getContinuationArchiveByRef, searchContinuationArchives } from "./continuation-archives.ts";
7
8
 
8
9
  // Anything left after the budget is still removed, by the agent delete's cascade.
@@ -24,6 +25,10 @@ export function archiveAndAdvanceContinuationAsync(input: Parameters<typeof arch
24
25
  return getGateway().write("continuationArchives.compact", { input }, { idempotencyKey: requestId }) as unknown as Promise<ReturnType<typeof archiveAndAdvanceContinuation>>;
25
26
  }
26
27
 
28
+ export function appendContinuationArchiveRefsAsync(agentId: string, refs: string[]): Promise<ReturnType<typeof appendContinuationArchiveRefs>> {
29
+ return getGateway().write("continuationArchives.appendRefs", { agentId, refs }) as unknown as Promise<ReturnType<typeof appendContinuationArchiveRefs>>;
30
+ }
31
+
27
32
  /**
28
33
  * #3012 — catch the side FTS index up. A lock-free reader probe gates the run; the work runs in the
29
34
  * maintenance subprocess against the side file's own write lock, never on the hub's loop. An
@@ -1,15 +1,24 @@
1
1
  import { archiveAndAdvanceContinuation, archiveContinuationSegment } from "./continuation-archives.ts";
2
+ import { appendContinuationArchiveRefs } from "./continuations.ts";
2
3
  import type { WriterOpContext, WriterOpSpec } from "./write-ops.ts";
3
4
 
4
5
  export const CONTINUATION_ARCHIVES_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze({
5
6
  "continuationArchives.archive": {
7
+ offLoop: true,
6
8
  idempotent: false,
7
9
  fn: (args: { input: Parameters<typeof archiveContinuationSegment>[0] }, ctx: WriterOpContext) =>
8
10
  archiveContinuationSegment({ ...args.input, now: args.input.now ?? ctx.now }),
9
11
  },
10
12
  "continuationArchives.compact": {
13
+ offLoop: true,
11
14
  idempotent: false,
12
15
  fn: (args: { input: Parameters<typeof archiveAndAdvanceContinuation>[0] }, ctx: WriterOpContext) =>
13
16
  archiveAndAdvanceContinuation({ ...args.input, now: args.input.now ?? ctx.now }),
14
17
  },
18
+ // #1870 S7: the envelope's ref list is a set, so a replay converges.
19
+ "continuationArchives.appendRefs": {
20
+ offLoop: true,
21
+ idempotent: true,
22
+ fn: (args: { agentId: string; refs: string[] }, ctx: WriterOpContext) => appendContinuationArchiveRefs(args.agentId, args.refs, ctx.now),
23
+ },
15
24
  });
@@ -143,7 +143,7 @@ function mergeLedger(
143
143
  return { ledger: merged.slice(start), dropped: merged.slice(0, start) };
144
144
  }
145
145
 
146
- export function appendContinuationArchiveRefs(agentId: string, archiveRefs: string[]): ContinuationEnvelope | null {
146
+ export function appendContinuationArchiveRefs(agentId: string, archiveRefs: string[], now: number = Date.now()): ContinuationEnvelope | null {
147
147
  if (archiveRefs.length === 0) return getContinuationEnvelope(agentId);
148
148
  const existing = getContinuationEnvelope(agentId);
149
149
  if (!existing) return null;
@@ -155,7 +155,7 @@ export function appendContinuationArchiveRefs(agentId: string, archiveRefs: stri
155
155
  UPDATE agent_continuations
156
156
  SET archive_refs = ?, updated_at = ?
157
157
  WHERE agent_id = ?
158
- `).run(JSON.stringify(refs), Date.now(), agentId);
158
+ `).run(JSON.stringify(refs), now, agentId);
159
159
  return getContinuationEnvelope(agentId);
160
160
  }
161
161
 
@@ -14,12 +14,19 @@
14
14
  // It default-exports the offload task function; `createWorkerPool` invokes it via the offload
15
15
  // worker-runtime bootstrap.
16
16
 
17
+ import { relayEnv } from "@humwick/sdk/env";
17
18
  import type { OffloadWorkerTask } from "@humwick/offload/worker";
18
19
  import { Database } from "bun:sqlite";
19
20
  import { applyConnectionPragmas, closeDb, getDbPath, setDb } from "./connection.ts";
20
21
  import { serializeDbError } from "./db-error-registry.ts";
21
22
  import { runWriterBatch, runWriterOp, type SerializedBatchEntry, type WriterJobInput, type WriterJobResult } from "./write-ops.ts";
22
23
 
24
+ // A lock-contention test may shorten the wait; production keeps 4 s.
25
+ function writerBusyTimeoutMs(): number {
26
+ const override = Number(relayEnv(process.env, "AGENT_RELAY_WRITER_BUSY_TIMEOUT_MS")?.trim() || Number.NaN);
27
+ return Number.isSafeInteger(override) && override >= 0 ? override : 4000;
28
+ }
29
+
23
30
  /** Ensure this worker's thread-local RW connection points at `dbPath`, opening it once and reusing
24
31
  * it across jobs. A path change (never expected in production) reopens — cheap and defensive. */
25
32
  function ensureConnection(dbPath: string): void {
@@ -33,7 +40,7 @@ function ensureConnection(dbPath: string): void {
33
40
  // per-connection pragmas before it begins executing serialized write operations.
34
41
  applyConnectionPragmas(conn, { ensureWal: false });
35
42
  // #1870: external 1.2 s holds wait here, below the gateway's 5 s deadline, never on the loop.
36
- conn.run("PRAGMA busy_timeout = 4000");
43
+ conn.run(`PRAGMA busy_timeout = ${writerBusyTimeoutMs()}`);
37
44
  setDb(conn, dbPath);
38
45
  }
39
46
 
@@ -40,23 +40,27 @@ export interface ApplyDeliveryActionOpArgs {
40
40
 
41
41
  export const DELIVERY_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze({
42
42
  "delivery.recordChannelFanoutSuppression": {
43
+ offLoop: true,
43
44
  idempotent: true,
44
45
  fn: (args: { messageId: number; channelAgentId: string; reason: string }, ctx: WriterOpContext) =>
45
46
  recordChannelFanoutSuppression(args.messageId, args.channelAgentId, args.reason, ctx.now),
46
47
  },
47
48
  "delivery.recordAttempt": {
49
+ offLoop: true,
48
50
  idempotent: false,
49
51
  idempotencySnapshotKey: (args: RecordDeliveryAttemptOpArgs) => String(args.messageId),
50
52
  fn: (args: RecordDeliveryAttemptOpArgs, ctx: WriterOpContext) =>
51
53
  recordMessageDeliveryAttempt(args.messageId, args.input, ctx.now),
52
54
  },
53
55
  "delivery.applyAction": {
56
+ offLoop: true,
54
57
  idempotent: false,
55
58
  idempotencySnapshotKey: (args: ApplyDeliveryActionOpArgs) => String(args.messageId),
56
59
  fn: (args: ApplyDeliveryActionOpArgs, ctx: WriterOpContext) =>
57
60
  applyMessageDeliveryAction(args.messageId, args.input, ctx.now),
58
61
  },
59
62
  "delivery.reevaluate": {
63
+ offLoop: true,
60
64
  idempotent: true,
61
65
  fn: (args: ApplyDeliveryActionOpArgs, ctx: WriterOpContext) =>
62
66
  applyMessageDeliveryAction(args.messageId, args.input, ctx.now),
@@ -66,6 +70,7 @@ export const DELIVERY_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object
66
70
  // elapsed time (chiefly tests) needs to drive it directly rather than only via real wall-clock
67
71
  // submission time. Production callers omit it and get ctx.now, per H6.
68
72
  "delivery.expireQueued": {
73
+ offLoop: true,
69
74
  idempotent: true,
70
75
  fn: (args: { now?: number }, ctx: WriterOpContext) => expireQueuedMessagesWithNotices(args?.now ?? ctx.now),
71
76
  },
@@ -25,6 +25,7 @@ export function tryBeginDeployEventAsync(input: TryBeginDeployEventInput): Promi
25
25
  export function backfillDeployEventVersionAsync(id: string, version: string): Promise<DeployEvent | null> {
26
26
  return write("deployEvents.backfillVersion", { id, version });
27
27
  }
28
+ export function getDeployEventForCommitAsync(commitSha: string): Promise<DeployEvent | null> { return read("deployEvents.forCommit", { commitSha }); }
28
29
  export function getDeployEventAsync(id: string): Promise<DeployEvent | null> { return read("deployEvents.get", { id }); }
29
30
  export function listDeployEventsAsync(limit: number, offset: number): Promise<DeployEvent[]> {
30
31
  return read("deployEvents.list", { limit, offset });
@@ -3,7 +3,7 @@
3
3
  import {
4
4
  currentPendingDeployEvent,
5
5
  deployChangesForCommits,
6
- getDeployEvent,
6
+ getDeployEvent, getDeployEventForCommit,
7
7
  latestFailedDeployForCommit,
8
8
  latestSuccessfulDeploy,
9
9
  listDeployEvents,
@@ -14,6 +14,7 @@ import {
14
14
  } from "./deploy-events.ts";
15
15
 
16
16
  export const DEPLOY_EVENTS_READ_OPS = {
17
+ "deployEvents.forCommit": (args: { commitSha: string }): DeployEvent | null => getDeployEventForCommit(args.commitSha),
17
18
  "deployEvents.get": (args: { id: string }): DeployEvent | null => getDeployEvent(args.id),
18
19
  "deployEvents.list": (args: { limit: number; offset: number }): DeployEvent[] => listDeployEvents(args.limit, args.offset),
19
20
  "deployEvents.needingChanges": (args: { limit: number }): DeployEvent[] => listDeployEventsNeedingChanges(args.limit),
@@ -1,14 +1,16 @@
1
+ import { defaultTaskIssueRelation } from "./task-issue-relation";
2
+ import { parseJson, type TaskDispatch } from "@humwick/sdk";
1
3
  import { randomUUID } from "node:crypto";
2
4
  import { getDb, writeTransaction } from "./connection.ts";
3
5
  import { setTaskStageBody } from "./task-entity.ts";
4
6
  import { insertTaskEvent } from "./task-core.ts";
5
7
  import { listIssuesForEntity } from "./issue-associations.ts";
6
- import type { IssueRef } from "../types";
8
+ import type { IssueRef, IssueRefRelation } from "../types";
7
9
 
8
10
  export type DeployStatus = "pending" | "succeeded" | "failed" | "rolled-back";
9
11
  export type DeployHost = { id: string; fromVersion?: string; toVersion?: string; status: string; reason?: string };
10
12
  export type DeployCommit = { sha: string; subject: string };
11
- export type DeployTaskChange = { id: number; label: string; issues: IssueRef[] };
13
+ export type DeployTaskChange = { id: number; label: string; issues: Array<IssueRef & { relation?: IssueRefRelation }>; onDeploy?: string };
12
14
  export type DeployChangesSummary = {
13
15
  range: { previousSha?: string; commitSha: string };
14
16
  tasks: DeployTaskChange[];
@@ -219,6 +221,24 @@ export function getDeployEvent(id: string): DeployEvent | null {
219
221
  return row ? rowToDeployEvent(row) : null;
220
222
  }
221
223
 
224
+ /** The running artifact may boot while its deploy ledger is still pending (#3761). */
225
+ export function getDeployEventForCommit(commitSha: string): DeployEvent | null {
226
+ const row = getDb().query("SELECT * FROM deploy_events WHERE commit_sha = ? ORDER BY started_at DESC LIMIT 1").get(commitSha);
227
+ const event = row ? rowToDeployEvent(row) : null;
228
+ if (event?.changes?.state === "ready" && event.changes.tasks.length) {
229
+ const notes = getDb().query("SELECT id, on_deploy, dispatch, owner_agent_id FROM tasks WHERE id IN (SELECT value FROM json_each(?))")
230
+ .all(JSON.stringify(event.changes.tasks.map(task => task.id))) as Array<{ id: number; on_deploy: string | null; dispatch: string | null; owner_agent_id: string | null }>;
231
+ const byId = new Map(notes.map(task => [task.id, task]));
232
+ event.changes.tasks = event.changes.tasks.map(task => {
233
+ const { onDeploy, ...snapshot } = task;
234
+ const current = byId.get(task.id);
235
+ const note = current ? current.on_deploy : onDeploy;
236
+ return { ...snapshot, ...(current ? { issues: deployTaskIssues(task.id, current.dispatch, current.owner_agent_id) } : {}), ...(note ? { onDeploy: note } : {}) };
237
+ });
238
+ }
239
+ return event;
240
+ }
241
+
222
242
  /** Newest-first page of the deploy ledger for the dashboard timeline (#1327). */
223
243
  export function listDeployEvents(limit: number, offset: number): DeployEvent[] {
224
244
  const rows = getDb()
@@ -250,6 +270,11 @@ export function listDeployEventsNeedingChanges(limit: number): DeployEvent[] {
250
270
  return rows.map(rowToDeployEvent);
251
271
  }
252
272
 
273
+ function deployTaskIssues(id: number, dispatch: string | null, ownerAgentId: string | null): DeployTaskChange["issues"] {
274
+ const relation = defaultTaskIssueRelation(parseJson<TaskDispatch | undefined>(dispatch, undefined), ownerAgentId ?? undefined);
275
+ return listIssuesForEntity("task", String(id)).map(item => ({ ...item.issueRef, relation: item.association.relation ?? relation }));
276
+ }
277
+
253
278
  /**
254
279
  * Match a deploy's ordered commit list to every durable Task with an exact stored
255
280
  * commit SHA. This CD-only query loads the task commit payload once; the HTTP
@@ -258,7 +283,7 @@ export function listDeployEventsNeedingChanges(limit: number): DeployEvent[] {
258
283
  export function deployChangesForCommits(commits: DeployCommit[]): Pick<DeployChangesSummary, "tasks" | "unmatchedCommits"> {
259
284
  const rangeShas = new Set(commits.map((commit) => commit.sha));
260
285
  const matchedShas = new Set<string>();
261
- const rows = getDb().query("SELECT id, title, commits FROM tasks WHERE kind = 'deliverable' AND commits IS NOT NULL ORDER BY id ASC").all() as any[];
286
+ const rows = getDb().query("SELECT id, title, commits, on_deploy, dispatch, owner_agent_id FROM tasks WHERE kind = 'deliverable' AND commits IS NOT NULL ORDER BY id ASC").all() as any[];
262
287
  const tasks: DeployTaskChange[] = [];
263
288
  for (const row of rows) {
264
289
  let taskCommits: Array<{ sha?: unknown }>;
@@ -268,7 +293,8 @@ export function deployChangesForCommits(commits: DeployCommit[]): Pick<DeployCha
268
293
  tasks.push({
269
294
  id: Number(row.id),
270
295
  label: String(row.title),
271
- issues: listIssuesForEntity("task", String(row.id)).map((item) => item.issueRef),
296
+ issues: deployTaskIssues(Number(row.id), row.dispatch, row.owner_agent_id),
297
+ ...(row.on_deploy ? { onDeploy: String(row.on_deploy) } : {}),
272
298
  });
273
299
  }
274
300
  return { tasks, unmatchedCommits: commits.filter((commit) => !matchedShas.has(commit.sha)) };
@@ -316,6 +342,11 @@ export function renewDeployEventLease(input: { id: string; now?: number }): bool
316
342
  return writeTransaction(() => renewDeployEventLeaseBody(input, input.now ?? Date.now()));
317
343
  }
318
344
 
345
+ /** Migration progress shares the existing deploy row and dashboard surface (#3470). */
346
+ export function recordDeployEventHosts(id: string, hosts: DeployHost[]): boolean {
347
+ return getDb().query("UPDATE deploy_events SET hosts = ? WHERE id = ? AND status = 'pending'").run(JSON.stringify(hosts), id).changes > 0;
348
+ }
349
+
319
350
  /**
320
351
  * Emergency operator recovery for a genuinely stranded deploy owner:
321
352
  * `bun run scripts/release.ts --clear-deploy-lease <event-id>` (→ `failed`), or
@@ -139,8 +139,16 @@ export type HardwareReconciliation =
139
139
 
140
140
  /** Capacity is an observed, runtime-varying property, not host identity. WSL2 in particular can
141
141
  * report less RAM after a reboot, and swap may appear/disappear with zram configuration. Keep the
142
- * latest value for placement without making a legitimate capacity change an anti-spoof conflict. */
143
- const TOLERANT_CAPACITY_HARDWARE_FIELDS = new Set(["totalMemMb", "totalStorageMb", "totalSwapMb"]);
142
+ * latest value for placement without making a legitimate capacity change an anti-spoof conflict.
143
+ * `osVersion` (the kernel string) changes on every OS patch; it is not hardware identity (#3770). */
144
+ const TOLERANT_CAPACITY_HARDWARE_FIELDS = new Set(["totalMemMb", "totalStorageMb", "totalSwapMb", "osVersion"]);
145
+
146
+ /** The identity-defining keys present on both sides with different values: what a conflict notice names. */
147
+ export function conflictingHardwareFields(stored: HostHardware, reported: HostHardware): string[] {
148
+ const a = stored as unknown as Record<string, unknown>;
149
+ const b = reported as unknown as Record<string, unknown>;
150
+ return Object.keys(a).filter((key) => !TOLERANT_CAPACITY_HARDWARE_FIELDS.has(key) && a[key] !== undefined && b[key] !== undefined && a[key] !== b[key]).sort();
151
+ }
144
152
 
145
153
  export function reconcileHardwareFields(stored: HostHardware, reported: HostHardware): HardwareReconciliation {
146
154
  const storedRec = stored as unknown as Record<string, unknown>;
@@ -27,6 +27,7 @@ export const HOSTS_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object.fr
27
27
  // Idempotent by construction (re-registration converges; conflict/join tags flip only on the
28
28
  // transition), so no idempotencyKey is required.
29
29
  "hosts.upsertFromRegistration": {
30
+ offLoop: true,
30
31
  idempotent: true,
31
32
  fn: (a: HostRegistrationInput, ctx) => upsertHostFromRegistrationInternal(a, ctx.now),
32
33
  },
@@ -36,42 +37,52 @@ export const HOSTS_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object.fr
36
37
  // the gateway's bounded-queue shedding policy, dropping a utilization update is the correct
37
38
  // outcome, because the next beat supersedes it 30 s later (§8).
38
39
  "hosts.recordUtilization": {
40
+ offLoop: true,
39
41
  idempotent: true,
40
42
  fn: (a: Parameters<typeof recordHostUtilizationInternal>[0]) => recordHostUtilizationInternal(a),
41
43
  },
42
44
  "hosts.setLabel": {
45
+ offLoop: true,
43
46
  idempotent: true,
44
47
  fn: (a: { id: string; label: string }) => setHostLabelInternal(a.id, a.label),
45
48
  },
46
49
  "hosts.setTags": {
50
+ offLoop: true,
47
51
  idempotent: true,
48
52
  fn: (a: { id: string; tags: string[] }) => setHostTagsInternal(a.id, a.tags),
49
53
  },
50
54
  "hosts.acceptHardware": {
55
+ offLoop: true,
51
56
  idempotent: true,
52
57
  fn: (a: { id: string }) => acceptHostHardwareInternal(a.id),
53
58
  },
54
59
  "hosts.setEnabled": {
60
+ offLoop: true,
55
61
  idempotent: true,
56
62
  fn: (a: { id: string; enabled: boolean }) => setHostEnabledInternal(a.id, a.enabled),
57
63
  },
58
64
  "hosts.setLandingPlacement": {
65
+ offLoop: true,
59
66
  idempotent: true,
60
67
  fn: (a: { id: string; noLandingWorkspaces: boolean | null }) => setHostLandingPlacementInternal(a.id, a.noLandingWorkspaces),
61
68
  },
62
69
  "hosts.setAgentCap": {
70
+ offLoop: true,
63
71
  idempotent: true,
64
72
  fn: (a: { id: string; maxAgents: number | null }) => setHostPlacementPolicyInternal(a.id, "maxAgents", a.maxAgents),
65
73
  },
66
74
  "hosts.setLandingCap": {
75
+ offLoop: true,
67
76
  idempotent: true,
68
77
  fn: (a: { id: string; maxLandingSlots: number | null }) => setHostPlacementPolicyInternal(a.id, "maxLandingSlots", a.maxLandingSlots),
69
78
  },
70
79
  "hosts.setGateBudget": {
80
+ offLoop: true,
71
81
  idempotent: true,
72
82
  fn: (a: { id: string } & HostGatePolicyPatch) => { const { id, ...patch } = a; return setHostGatePolicyInternal(id, patch); },
73
83
  },
74
84
  "hosts.setAdmission": {
85
+ offLoop: true,
75
86
  idempotent: true,
76
87
  fn: (a: { id: string; flags: Pick<HostPolicy, "spawnEnabled" | "gatesEnabled"> }, ctx) => {
77
88
  const host = setHostAdmissionInternal(a.id, a.flags);
@@ -80,14 +91,17 @@ export const HOSTS_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object.fr
80
91
  },
81
92
  },
82
93
  "hosts.setPolicy": {
94
+ offLoop: true,
83
95
  idempotent: true,
84
96
  fn: (a: { id: string; policy: HostPolicy }) => setHostPolicyInternal(a.id, a.policy),
85
97
  },
86
98
  "hosts.claimAlias": {
99
+ offLoop: true,
87
100
  idempotent: true,
88
101
  fn: (a: { hostId: string; alias: string; source: string }, ctx) => claimHostAliasInternal(a.hostId, a.alias, a.source, ctx.now),
89
102
  },
90
103
  "hosts.releaseAlias": {
104
+ offLoop: true,
91
105
  idempotent: true,
92
106
  fn: (a: { alias: string }) => releaseHostAliasInternal(a.alias),
93
107
  },
package/src/db/hosts.ts CHANGED
@@ -9,6 +9,7 @@ import type { Host, HostDaemonSample, HostHardware, HostMachineSample, HostPolic
9
9
  import { normalizeHostToken, parseJson } from "../utils";
10
10
  import { getDb } from "./connection.ts";
11
11
  import { primeHostAliases, type HostAliasRecord } from "../host/identity.ts";
12
+ import { settleDiskWatch, type DiskAlert } from "../host/disk-alert.ts";
12
13
  import { settleGateArming, type GateCapacityAlert } from "../host/gate-capacity-alert.ts";
13
14
 
14
15
  function rowToHost(row: any): Host {
@@ -99,18 +100,20 @@ function storedSample<T extends { at: number }>(table: "hosts" | "orchestrators"
99
100
  */
100
101
  export function recordHostUtilizationInternal(
101
102
  input: { hostId: string; orchestratorId: string; machine: HostMachineSample; daemon: HostDaemonSample },
102
- ): { hostUpdated: boolean; generationUpdated: boolean; gateAlert?: GateCapacityAlert; hostLabel?: string } {
103
+ ): { hostUpdated: boolean; generationUpdated: boolean; gateAlert?: GateCapacityAlert; diskAlert?: DiskAlert; hostLabel?: string } {
103
104
  // `last_seen_at` is deliberately NOT touched here: it is the Host's freshness clock and the §3.5
104
105
  // seam already bumps it on every beat with the RELAY's clock. `machine.at` is the PRODUCER's wall
105
106
  // clock, so writing it here would let a skewed daemon move a Host's liveness backwards.
106
107
  const prevHost = storedSample<HostMachineSample>("hosts", input.hostId);
107
108
  let hostUpdated = false;
108
109
  let gateAlert: GateCapacityAlert | undefined;
110
+ let diskAlert: DiskAlert | undefined;
109
111
  if (!prevHost || input.machine.at > prevHost.at) {
110
- // #2822 — settled inside this single-writer read-modify-write, so each transition alerts once.
111
- const settled = settleGateArming(prevHost ?? undefined, input.machine);
112
+ // #2822 / #3762 — settled inside this single-writer read-modify-write, so each transition alerts once.
113
+ const gate = settleGateArming(prevHost ?? undefined, input.machine);
114
+ const settled = settleDiskWatch(prevHost ?? undefined, gate.machine);
112
115
  hostUpdated = getDb().query("UPDATE hosts SET utilization = ? WHERE id = ?").run(JSON.stringify(settled.machine), input.hostId).changes > 0;
113
- if (hostUpdated) gateAlert = settled.alert;
116
+ if (hostUpdated) { gateAlert = gate.alert; diskAlert = settled.alert; }
114
117
  }
115
118
 
116
119
  const generationAt = storedSample<HostDaemonSample>("orchestrators", input.orchestratorId)?.at ?? null;
@@ -118,9 +121,9 @@ export function recordHostUtilizationInternal(
118
121
  ? false
119
122
  : getDb().query("UPDATE orchestrators SET utilization = ? WHERE id = ?").run(JSON.stringify(input.daemon), input.orchestratorId).changes > 0;
120
123
 
121
- if (!gateAlert) return { hostUpdated, generationUpdated };
124
+ if (!gateAlert && !diskAlert) return { hostUpdated, generationUpdated };
122
125
  const label = (getDb().query("SELECT label FROM hosts WHERE id = ?").get(input.hostId) as { label: string } | null)?.label;
123
- return { hostUpdated, generationUpdated, gateAlert, hostLabel: label ?? input.hostId };
126
+ return { hostUpdated, generationUpdated, ...(gateAlert ? { gateAlert } : {}), ...(diskAlert ? { diskAlert } : {}), hostLabel: label ?? input.hostId };
124
127
  }
125
128
 
126
129
  export function setHostLabelInternal(id: string, label: string): Host | null {
@@ -4,20 +4,24 @@ import type { WriterOpContext, WriterOpSpec } from "./write-ops.ts";
4
4
 
5
5
  export const INBOX_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze({
6
6
  "inbox.setThreadState": {
7
+ offLoop: true,
7
8
  idempotent: true,
8
9
  fn: (args: { input: Parameters<typeof setInboxThreadState>[0] }, ctx: WriterOpContext) =>
9
10
  setInboxThreadState(args.input, ctx.now),
10
11
  },
11
12
  "inbox.setDraft": {
13
+ offLoop: true,
12
14
  idempotent: true,
13
15
  fn: (args: { input: Parameters<typeof setInboxDraft>[0] }, ctx: WriterOpContext) =>
14
16
  setInboxDraft(args.input, ctx.now, false),
15
17
  },
16
18
  "inbox.deleteDraft": {
19
+ offLoop: true,
17
20
  idempotent: true,
18
21
  fn: (args: { operatorId: string; peerId: string }) => deleteInboxDraft(args.operatorId, args.peerId),
19
22
  },
20
23
  "inbox.createHistoryImport": {
24
+ offLoop: true,
21
25
  idempotent: true,
22
26
  fn: (args: { input: Parameters<typeof createChatHistoryImport>[0] }, ctx: WriterOpContext) =>
23
27
  createChatHistoryImport(args.input, ctx.now, false),
package/src/db/inbox.ts CHANGED
@@ -1,18 +1,4 @@
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";
15
- import { matchAgents } from "../agent-ref";
16
2
  import { ValidationError, getDb, writeTransaction } from "./connection.ts";
17
3
  import { rowToChatHistoryImport, rowToChatHistoryImportEntry, rowToInboxDraft, rowToInboxThreadState } from "./mappers.ts";
18
4
  import { getMessage } from "./messages.ts";
package/src/db/index.ts CHANGED
@@ -44,6 +44,7 @@ export * from "./task-comment-through-async.ts";
44
44
  export * from "./deploy-events.ts";
45
45
  export * from "./deploy-events-async.ts";
46
46
  export * from "./integrations.ts";
47
+ export * from "./integrations-async.ts";
47
48
  export * from "./pairs.ts";
48
49
  export * from "./artifacts.ts";
49
50
  export * from "./artifact-browse.ts";
@@ -75,6 +76,7 @@ export * from "./orchestrators-async.ts";
75
76
  export * from "./hosts.ts";
76
77
  export * from "./hosts-async.ts";
77
78
  export type { HostRegistrationEvent, HostRegistrationInput, HostRegistrationResult } from "./hosts-registration.ts";
79
+ export { conflictingHardwareFields } from "./hosts-registration.ts";
78
80
  export * from "./workspaces.ts";
79
81
  export * from "./workspace-writes.ts";
80
82
  export * from "./workspace-writes-async.ts";
@@ -7,6 +7,7 @@ import type { WriterOpContext, WriterOpSpec } from "./write-ops.ts";
7
7
 
8
8
  export const INSIGHTS_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze({
9
9
  "insights.observations.record": {
10
+ offLoop: true,
10
11
  // An observation is append-only; callers must provide the per-request key carried by the facade.
11
12
  idempotent: false,
12
13
  fn: (args: { input: Parameters<typeof recordObservation>[1] }, ctx: WriterOpContext) =>
@@ -0,0 +1,8 @@
1
+ import { getGateway } from "./gateway.ts";
2
+ import type { IntegrationRegistryInput, reconcileIntegrationRegistry } from "./integrations.ts";
3
+
4
+ export const listIntegrationTaskStatsAsync = () => getGateway().read("integrations.taskStats", undefined);
5
+
6
+ export async function reconcileIntegrationRegistryAsync(configured: IntegrationRegistryInput[], observedSources: string[]): Promise<ReturnType<typeof reconcileIntegrationRegistry>> {
7
+ return await getGateway().write("integrations.reconcileRegistry", { configured, observedSources }) as unknown as ReturnType<typeof reconcileIntegrationRegistry>;
8
+ }