@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.
- package/README.md +5 -0
- package/docs/guides/tools-administration.md +6 -1
- package/docs/guides/tools-coordination.md +1 -0
- package/docs/guides/tools-tasks.md +8 -0
- package/docs/guides/workflows.md +58 -0
- package/docs/openapi.json +414 -1
- package/package.json +8 -17
- package/public/assets/{MessageBubble-BWpoBjl4.js → MessageBubble-BWLqMGKG.js} +2 -2
- package/public/assets/{PlanGraphPanel-C0auck3n.js → PlanGraphPanel-3TlIQpjk.js} +2 -2
- package/public/assets/{TraceView-DBnOx7AQ.js → TraceView-Wimm0Czv.js} +2 -2
- package/public/assets/{VaultDecisionCard-0kp2oNBC.js → VaultDecisionCard-Bau_vNax.js} +2 -2
- package/public/assets/{activity-yLztXEBt.js → activity-CrSurn-r.js} +2 -2
- package/public/assets/{agent-lineage-BbJDR1dW.js → agent-lineage-CknAJKcb.js} +2 -2
- package/public/assets/agent-liveness-CaNwK0Zf.js +2 -0
- package/public/assets/{agent-name-CTZBrSD9.js → agent-name-DKbiIhBo.js} +2 -2
- package/public/assets/agent-profiles-DwF6EQ4R.js +2 -0
- package/public/assets/{agent-project-badge-9TqzUUYE.js → agent-project-badge-Diz4Sjjb.js} +2 -2
- package/public/assets/{agent-tools-D4n40pn8.js → agent-tools-CRhpRQza.js} +2 -2
- package/public/assets/{agent-type-icon-D6FUarOf.js → agent-type-icon-LrUEU3lB.js} +2 -2
- package/public/assets/{agents-BYDslkJc.js → agents-B-v66C-4.js} +2 -2
- package/public/assets/{analytics-B-XSbEMC.js → analytics-BqCb-LKi.js} +2 -2
- package/public/assets/{artifact-viewer-DdWIfV6M.js → artifact-viewer-kf4pGuAR.js} +2 -2
- package/public/assets/{artifacts-oBOqks-2.js → artifacts-D32zyw4Y.js} +2 -2
- package/public/assets/{artifacts-DPN1q4pA.js → artifacts-DJevXcvg.js} +2 -2
- package/public/assets/{automation-4KcRFXEu.js → automation-BpRajx_5.js} +2 -2
- package/public/assets/{backlog-Cw9xPTCF.js → backlog-D_UjTdzX.js} +2 -2
- package/public/assets/{busy-health-CTu32MZt.js → busy-health-N3VEh4ao.js} +2 -2
- package/public/assets/{channels-BiXzg1G9.js → channels-9jopungq.js} +2 -2
- package/public/assets/{chat-Ctk2qwvA.js → chat-CSXrTB3C.js} +2 -2
- package/public/assets/{code-preview-Bm1wWqAp.js → code-preview-p1NnmqK0.js} +2 -2
- package/public/assets/{connectors-C36ax4Vw.js → connectors-CZdqlDNL.js} +2 -2
- package/public/assets/{cosmos-Jj9LK2Wf.js → cosmos-BgdsE4dG.js} +2 -2
- package/public/assets/{crate-agent-H6S7-B9T.js → crate-agent-30aXTKva.js} +2 -2
- package/public/assets/{dependency-graph-Cd1E23wa.js → dependency-graph-B_jvw2GE.js} +2 -2
- package/public/assets/{deploys-Bwfq146q.js → deploys-DoN6bAb5.js} +2 -2
- package/public/assets/{display-CfO7rTuF.js → display-BrKWFGJz.js} +2 -2
- package/public/assets/{files-DhhZI2Ih.js → files-D6XEu8Ng.js} +2 -2
- package/public/assets/fleet-C7jQWbw2.js +2 -0
- package/public/assets/{fleet-metrics-BkHP-P3R.js → fleet-metrics-BiDs3MLx.js} +2 -2
- package/public/assets/{formatted-body-Cm1RrzmN.js → formatted-body-CQBwc1fb.js} +3 -3
- package/public/assets/{formatted-body-impl-DQiOuOD1.js → formatted-body-impl-DyG0WsQS.js} +2 -2
- package/public/assets/{host-providers-dialog-CHqwXTov.js → host-providers-dialog-DKaW31dN.js} +2 -2
- package/public/assets/index-CRHep3M9.js +15 -0
- package/public/assets/index-MYAIwYTX.css +2 -0
- package/public/assets/{insights-fH1EL-qZ.js → insights-CzrtkpOE.js} +2 -2
- package/public/assets/{integrations-C5Oix6ng.js → integrations-aVsUmigt.js} +2 -2
- package/public/assets/{issue-ref-chip-ODyondIa.js → issue-ref-chip-BnSlM_95.js} +2 -2
- package/public/assets/{log-viewer-Cvfy6PW7.js → log-viewer-qe7Ee47Z.js} +2 -2
- package/public/assets/{maintenance-D8CDH5wZ.js → maintenance-NfjqFZAk.js} +2 -2
- package/public/assets/{managed-agents-egllbpij.js → managed-agents-BiH0HgaE.js} +2 -2
- package/public/assets/{markdown-preview-CYSFgVgK.js → markdown-preview-BixaT3Xr.js} +3 -3
- package/public/assets/{markdown-preview-impl-BteVIjX4.js → markdown-preview-impl-DNGlWIS3.js} +2 -2
- package/public/assets/{memory-C8WmXsyr.js → memory-DNsCl6UX.js} +2 -2
- package/public/assets/{messages-BzQdqlO6.js → messages-DopU1L1X.js} +2 -2
- package/public/assets/{orchestrators-B1HxzkNw.js → orchestrators-Cq7ckLQT.js} +2 -2
- package/public/assets/{overview-DCUnI4Z7.js → overview-C1DCTzzV.js} +2 -2
- package/public/assets/{pairs-ftCd2XcD.js → pairs-DE_m6rrE.js} +2 -2
- package/public/assets/{profile-icon-B2zH8DMH.js → profile-icon-B3rfh77F.js} +2 -2
- package/public/assets/project-profile-instructions-DQWZE0kA.js +2 -0
- package/public/assets/projects-Br2Bd2h1.js +2 -0
- package/public/assets/{prompt-settings-Bftq9czX.js → prompt-settings-BKvFsvGD.js} +2 -2
- package/public/assets/provider-display-DsuWMvVL.js +2 -0
- package/public/assets/{registry-DWCU5c8q.js → registry-Cw38_6gW.js} +2 -2
- package/public/assets/run-card-CpKqRbhg.js +2 -0
- package/public/assets/scroll-text-BKXS1ahr.js +2 -0
- package/public/assets/{secrets-C7GIUATz.js → secrets-DxI-vzSq.js} +2 -2
- package/public/assets/{service-access-settings-C3aujqTG.js → service-access-settings-D9s_VJbd.js} +2 -2
- package/public/assets/{settings-C6sZ9eBa.js → settings-CKZTVGIx.js} +2 -2
- package/public/assets/{shipping-Bu8p1gEw.js → shipping-B9YtnwtG.js} +2 -2
- package/public/assets/{stats-V9Z-GxUd.js → stats-DyUBKyWI.js} +2 -2
- package/public/assets/{status-dot-CVv0iGHL.js → status-dot-DYqlEUhJ.js} +2 -2
- package/public/assets/{store-BvtFdlGX.js → store-DcClXLH0.js} +3 -3
- package/public/assets/{tasks-CXU75HRF.js → tasks-Bh7Chr4-.js} +2 -2
- package/public/assets/{teams-BPf976zb.js → teams-bowI7qVY.js} +2 -2
- package/public/assets/{terminal-viewer-DE9BOe-8.js → terminal-viewer-BgVLHdsU.js} +3 -3
- package/public/assets/{terminal-viewer-impl-D04pjmGh.js → terminal-viewer-impl-Dz22UPy6.js} +2 -2
- package/public/assets/token-estimate-Co-Jdphh.js +2 -0
- package/public/assets/{use-agent-terminal-B1yR456R.js → use-agent-terminal-DSQQ-iY7.js} +2 -2
- package/public/assets/{use-decisions-D-S4tpDf.js → use-decisions-samMQhQf.js} +2 -2
- package/public/assets/use-project-list-59LheVP7.js +2 -0
- package/public/assets/{use-selectors-CnrAk2YP.js → use-selectors-BZa25xEB.js} +2 -2
- package/public/assets/{user-avatar-nBFIcR4M.js → user-avatar-DHxlvUvV.js} +2 -2
- package/public/assets/versioned-prompt-editor-BmED5DnJ.js +8 -0
- package/public/assets/{work-queue-TCjJxeT-.js → work-queue-CSvpuNLz.js} +2 -2
- package/public/assets/{workflow-marker-Cf3Hf2fN.js → workflow-marker-DAZcj5qt.js} +2 -2
- package/public/assets/{workflows-CXQX20Qj.js → workflows-DdmxRSyg.js} +2 -2
- package/public/assets/{workspaces-qSYzKYMA.js → workspaces-BxeKfVuF.js} +2 -2
- package/public/cosmos.html +4 -4
- package/public/index.html +31 -29
- package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/runner/plugins/claude/hooks/gate-run-guard.provisioned.mjs +31 -1
- package/runner/src/adapters/claude-effort.ts +2 -4
- package/runner/src/adapters/codex-launch-config.ts +4 -5
- package/runner/src/adapters/codex.ts +1 -1
- package/runner/src/adapters/opencode-provider.ts +7 -22
- package/runner/src/adapters/opencode.ts +4 -4
- package/runner/src/agent-runner/credentials.ts +42 -12
- package/runner/src/agent-runner/inbox.ts +27 -0
- package/runner/src/agent-runner/lifecycle-milestones.ts +0 -2
- package/runner/src/bin/claude.ts +4 -0
- package/runner/src/bin/codex.ts +4 -0
- package/runner/src/bin/opencode.ts +4 -0
- package/runner/src/index.ts +5 -2
- package/runner/src/provider-setup.ts +34 -0
- package/runner/src/providers.ts +2 -1
- package/runner/src/runner-core.ts +18 -23
- package/runner/src/spawn-input.ts +2 -1
- package/runner/src/test-run-dedup.ts +59 -56
- package/scripts/cd-config.ts +24 -3
- package/scripts/gate-run-lease.ts +10 -7
- package/scripts/gate.sh +5 -1
- package/src/branch-land-notice.ts +93 -0
- package/src/branch-landed.ts +7 -76
- package/src/bus.ts +19 -20
- package/src/cd-deploy-notify.ts +30 -2
- package/src/cd-host-lag-notify.ts +7 -6
- package/src/ci/incidents.ts +2 -2
- package/src/ci/monitor.ts +5 -8
- package/src/ci/root-missing.ts +2 -7
- package/src/ci/transition-notify.ts +7 -39
- package/src/cli/cd-pauses.ts +19 -0
- package/src/cli/orchestrator.ts +1 -1
- package/src/cli/state.ts +2 -19
- package/src/cli/task-edit.ts +22 -0
- package/src/cli/task.ts +3 -1
- package/src/cli/workflow.ts +8 -2
- package/src/commands-db.ts +10 -6
- package/src/commands-write.ts +8 -0
- package/src/config-store-write.ts +13 -1
- package/src/config-store.ts +25 -14
- package/src/db/activity-async.ts +6 -1
- package/src/db/activity-write-ops.ts +13 -1
- package/src/db/activity.ts +1 -14
- package/src/db/agent-lifecycle-write-ops.ts +1 -0
- package/src/db/agent-meta.ts +2 -2
- package/src/db/agent-ownership-write-ops.ts +4 -0
- package/src/db/agent-search.ts +1 -15
- package/src/db/agents-async.ts +58 -7
- package/src/db/agents-write-ops.ts +28 -5
- package/src/db/agents.ts +81 -54
- package/src/db/artifacts-write-ops.ts +8 -3
- package/src/db/bus-outbox-write-ops.ts +1 -0
- package/src/db/channels-write-ops.ts +4 -0
- package/src/db/ci-incidents-async.ts +5 -5
- package/src/db/ci-incidents-write-ops.ts +2 -2
- package/src/db/claude-api-budget-ops.ts +5 -5
- package/src/db/claude-app-oauth.ts +4 -4
- package/src/db/codex-reset-consumes-write-ops.ts +3 -0
- package/src/db/command-row.ts +10 -2
- package/src/db/commands-list-query.ts +2 -0
- package/src/db/commands-spawn-read.ts +1 -1
- package/src/db/commands-write-ops.ts +21 -0
- package/src/db/config-store-write-ops.ts +9 -2
- package/src/db/continuation-archives-async.ts +5 -0
- package/src/db/continuation-archives-write-ops.ts +9 -0
- package/src/db/continuations.ts +2 -2
- package/src/db/db-writer-worker.ts +8 -1
- package/src/db/delivery-write-ops.ts +5 -0
- package/src/db/deploy-events-async.ts +1 -0
- package/src/db/deploy-events-read-ops.ts +2 -1
- package/src/db/deploy-events.ts +35 -4
- package/src/db/hosts-registration.ts +10 -2
- package/src/db/hosts-write-ops.ts +14 -0
- package/src/db/hosts.ts +9 -6
- package/src/db/inbox-write-ops.ts +4 -0
- package/src/db/inbox.ts +0 -14
- package/src/db/index.ts +2 -0
- package/src/db/insights-write-ops.ts +1 -0
- package/src/db/integrations-async.ts +8 -0
- package/src/db/integrations-ops.ts +15 -0
- package/src/db/integrations.ts +15 -21
- package/src/db/issue-lifecycle.ts +3 -4
- package/src/db/issue-refs-async.ts +20 -0
- package/src/db/issue-refs-write-ops.ts +29 -0
- package/src/db/issue-refs.ts +7 -8
- package/src/db/long-running-work.ts +2 -2
- package/src/db/macos-privacy.ts +1 -1
- package/src/db/main-connection-writes.ts +23 -2
- package/src/db/maintenance-job-reconcile.ts +61 -0
- package/src/db/maintenance-job-runs-async.ts +14 -0
- package/src/db/maintenance-job-write-ops.ts +9 -0
- package/src/db/mappers.ts +2 -14
- package/src/db/merge-lease.ts +1 -15
- package/src/db/message-reads-write-ops.ts +3 -0
- package/src/db/message-write-ops.ts +4 -0
- package/src/db/migrations.ts +7 -15
- package/src/db/orchestrators-async.ts +7 -3
- package/src/db/orchestrators-write-ops.ts +7 -1
- package/src/db/orchestrators.ts +21 -77
- package/src/db/pairs.ts +1 -13
- package/src/db/project-profile-instructions.ts +50 -0
- package/src/db/prompt-versions-ops.ts +1 -0
- package/src/db/prompt-versions.ts +9 -2
- package/src/db/provider-catalog-write-ops.ts +4 -4
- package/src/db/provider-config-history.ts +8 -3
- package/src/db/provider-flap-messages-write-ops.ts +1 -0
- package/src/db/provider-model-seed.ts +34 -0
- package/src/db/provider-quota-lease-write-ops.ts +1 -0
- package/src/db/provider-quota-write-ops.ts +2 -0
- package/src/db/provisioning-write-ops.ts +9 -8
- package/src/db/read-op-marshaling-fixtures.ts +5 -0
- package/src/db/read-ops.ts +4 -0
- package/src/db/routing-policy.ts +3 -4
- package/src/db/schema.ts +9 -3
- package/src/db/secrets-broker-write-ops.ts +5 -5
- package/src/db/secrets-vault-write.ts +7 -7
- package/src/db/services-write-ops.ts +3 -0
- package/src/db/spawn-delegation-state.ts +11 -4
- package/src/db/spawn-tree-cleanup.ts +3 -0
- package/src/db/stats-async.ts +4 -3
- package/src/db/task-entity.ts +23 -2
- package/src/db/task-list.ts +3 -1
- package/src/db/tasks.ts +1 -17
- package/src/db/team-spawn-delegation.ts +1 -1
- package/src/db/teams-write-ops.ts +4 -4
- package/src/db/timer-cutover-write-ops.ts +7 -0
- package/src/db/timer-ingress-write-ops.ts +9 -0
- package/src/db/timer-ingress.ts +7 -1
- package/src/db/timer-operator-write-ops.ts +14 -0
- package/src/db/timer-transfer-write-ops.ts +4 -0
- package/src/db/timer-write-ops.ts +14 -0
- package/src/db/token-db-write-ops.ts +21 -0
- package/src/db/token-standing.ts +15 -11
- package/src/db/user-settings-write-ops.ts +1 -0
- package/src/db/vault-backup.ts +2 -2
- package/src/db/vault-expiry.ts +2 -2
- package/src/db/vault-health.ts +3 -3
- package/src/db/vault-token-ops.ts +8 -8
- package/src/db/web-tool-call-ops.ts +3 -3
- package/src/db/workflow-contact.ts +1 -1
- package/src/db/workflow-task-read-ops.ts +30 -0
- package/src/db/write-ops.ts +15 -1
- package/src/db/writer-fault-seam.ts +5 -2
- package/src/db/writer-gateway.ts +21 -12
- package/src/dispatch-availability.ts +10 -6
- package/src/ephemeral/{landing-ingress-proof-worker.ts → landing-ingress-proof.test-fixture.ts} +4 -2
- package/src/host/disk-alert.ts +121 -0
- package/src/lifecycle-manager.ts +3 -2
- package/src/maintenance/cd-watcher.ts +3 -3
- package/src/maintenance/critical-job-watchdog.ts +16 -17
- package/src/maintenance/index.ts +1 -0
- package/src/maintenance/jobs.ts +3 -2
- package/src/maintenance/scheduler.ts +84 -128
- package/src/maintenance/stalled-delivery-pager.ts +6 -5
- package/src/maintenance/workspaces.ts +1 -1
- package/src/managed-policy.ts +31 -27
- package/src/mcp/index.ts +5 -2
- package/src/mcp/output-descriptions.ts +8 -8
- package/src/mcp/presentation.ts +4 -4
- package/src/mcp/project-profile-instructions.ts +32 -0
- package/src/mcp/tool-groups.ts +1 -1
- package/src/mcp/tools-spawn.ts +4 -3
- package/src/mcp-agent-action.ts +1 -2
- package/src/mcp-host-tools.ts +1 -1
- package/src/mcp-issue-ref-tools.ts +12 -11
- package/src/mcp-maintenance-tools.ts +2 -2
- package/src/mcp-profile-trust-tools.ts +4 -4
- package/src/mcp-provider-catalog.ts +2 -2
- package/src/mcp-task-tools.ts +11 -5
- package/src/mcp-team-template-tools.ts +3 -3
- package/src/mcp-workflow-tools.ts +10 -3
- package/src/notification-types.ts +2 -1
- package/src/operator-alarm.ts +26 -11
- package/src/package-pack.ts +42 -39
- package/src/project-instructions.ts +4 -0
- package/src/project-profile-instruction-text.ts +6 -0
- package/src/project-profile-instructions.ts +20 -0
- package/src/provider-catalog-store.ts +3 -49
- package/src/provider-config-store.ts +12 -11
- package/src/provider-model-seed.ts +1 -18
- package/src/provider-quota-config-store.ts +5 -4
- package/src/provider-secrets.test-fixture.ts +3 -3
- package/src/recipe-runner.ts +2 -2
- package/src/repo-config-source.ts +10 -4
- package/src/routes/agent-profiles.ts +6 -6
- package/src/routes/agents-spawn.ts +1 -1
- package/src/routes/agents.ts +6 -6
- package/src/routes/commands-listing.ts +4 -0
- package/src/routes/commands.ts +2 -2
- package/src/routes/continuation-archives.ts +2 -2
- package/src/routes/host-resource-control.ts +4 -1
- package/src/routes/index.ts +10 -1
- package/src/routes/integrations.ts +24 -50
- package/src/routes/issue-refs.ts +7 -7
- package/src/routes/orchestrator-bootstrap.ts +3 -3
- package/src/routes/orchestrator-host-registration.ts +2 -2
- package/src/routes/orchestrator-host-utilization.ts +3 -1
- package/src/routes/orchestrator-proxy.ts +2 -2
- package/src/routes/orchestrator.ts +4 -4
- package/src/routes/project-profile-instructions.ts +27 -0
- package/src/routes/project-trust.ts +2 -2
- package/src/routes/provider-config.ts +9 -8
- package/src/routes/provider-quota-config.ts +2 -2
- package/src/routes/settings.ts +5 -5
- package/src/routes/stats.ts +6 -6
- package/src/routes/task-http.ts +15 -1
- package/src/routes/task-issues.ts +11 -26
- package/src/routes/tasks.ts +22 -53
- package/src/routes/team-templates.ts +5 -5
- package/src/routes/teams-config.ts +2 -2
- package/src/routes/tokens.ts +12 -5
- package/src/routes/workflows.ts +20 -5
- package/src/routing-policy-normalize.ts +1 -2
- package/src/routing-step-defaults.ts +0 -2
- package/src/runner-token-mint-limit.ts +63 -0
- package/src/runtime-grant.ts +4 -2
- package/src/runtime-tokens.ts +50 -9
- package/src/security.ts +13 -20
- package/src/server-background.ts +1 -1
- package/src/server.ts +5 -2
- package/src/services/host-utilization.ts +23 -0
- package/src/services/idle-epics.ts +5 -6
- package/src/services/issue-lifecycle.ts +57 -61
- package/src/services/issue-refs.ts +9 -13
- package/src/services/kernel-timers-cutover-chaos-worker.ts +12 -7
- package/src/services/kernel-timers-ingress.ts +1 -1
- package/src/services/kernel-timers-request-key-migration-chaos-worker.ts +4 -1
- package/src/services/landed-ancestry.ts +19 -3
- package/src/services/memory-areas.ts +15 -51
- package/src/services/plan-ci-notice.ts +57 -0
- package/src/services/plan-ci-recovery.ts +2 -55
- package/src/services/plan-wake-digest.ts +1 -0
- package/src/services/profile-trust.ts +4 -3
- package/src/services/project-config-read.ts +70 -0
- package/src/services/project-profile-instructions.ts +54 -0
- package/src/services/provisioning-harness/budget-baseline.json +4 -4
- package/src/services/restart-spawn.ts +61 -49
- package/src/services/self-care-state.ts +2 -1
- package/src/services/self-effort.ts +5 -5
- package/src/services/send-message.ts +5 -1
- package/src/services/spawn-agent.ts +13 -2
- package/src/services/spawn-chain-inspector.ts +6 -13
- package/src/services/spawn-policy.ts +68 -0
- package/src/services/spawn-tree-cleanup.ts +2 -2
- package/src/services/task-dispatch.ts +3 -0
- package/src/services/task-state-reconcile.ts +6 -14
- package/src/services/team-templates.ts +9 -8
- package/src/services/transient-agent-reaper.ts +3 -1
- package/src/services/workflow-auto-start.ts +25 -13
- package/src/services/workflow-ci-main.ts +1 -1
- package/src/services/workflow-deferred-cleanup.ts +4 -2
- package/src/services/workflow-effect-driver.ts +2 -0
- package/src/services/workflow-effects.ts +18 -11
- package/src/services/workflow-land-notice.ts +10 -0
- package/src/services/workflow-pause-text.ts +43 -0
- package/src/services/workflow-reconcile.ts +13 -11
- package/src/services/workflow-run-view.ts +2 -0
- package/src/services/workflow-runs.ts +29 -13
- package/src/services/workflow-supervisor-state.ts +5 -0
- package/src/services/workflow-supervisor.ts +7 -3
- package/src/settings/registry.ts +1 -2
- package/src/spawn-command.ts +4 -4
- package/src/spawn-models.ts +10 -12
- package/src/spawn-placement.ts +1 -1
- package/src/spawn-targets.ts +1 -1
- package/src/startup-context-injection.ts +1 -0
- package/src/state-adoption/cd-cutover.ts +53 -7
- package/src/state-adoption/fleet-cutover.ts +35 -0
- package/src/state-adoption/fleet-plan.ts +36 -0
- package/src/state-adoption/index.ts +2 -2
- package/src/state-adoption/linux-plan.ts +2 -2
- package/src/state-adoption/linux.ts +2 -2
- package/src/state-adoption/macos-plan.ts +2 -2
- package/src/state-adoption/macos.ts +1 -1
- package/src/steward.ts +7 -6
- package/src/task-dispatch-live.ts +1 -1
- package/src/task-dispatcher.ts +1 -1
- package/src/teams-config-store.ts +4 -3
- package/src/token-db-write.ts +10 -0
- package/src/workspace-command-settle.ts +2 -1
- package/src/workspace-steward-wake.ts +2 -2
- package/node_modules/@humwick/kernel/LICENSE +0 -167
- package/node_modules/@humwick/kernel/async-entity-persistence.ts +0 -344
- package/node_modules/@humwick/kernel/async-event-log.ts +0 -107
- package/node_modules/@humwick/kernel/automations.pilot.ts +0 -147
- package/node_modules/@humwick/kernel/config.ts +0 -111
- package/node_modules/@humwick/kernel/contract.ts +0 -572
- package/node_modules/@humwick/kernel/control-authz.ts +0 -281
- package/node_modules/@humwick/kernel/control.ts +0 -645
- package/node_modules/@humwick/kernel/effects.ts +0 -493
- package/node_modules/@humwick/kernel/event-log.ts +0 -319
- package/node_modules/@humwick/kernel/http-transport.ts +0 -384
- package/node_modules/@humwick/kernel/identity-grammar.ts +0 -20
- package/node_modules/@humwick/kernel/identity.ts +0 -248
- package/node_modules/@humwick/kernel/index.ts +0 -28
- package/node_modules/@humwick/kernel/kernel-persistence.ts +0 -181
- package/node_modules/@humwick/kernel/landing-incident.ts +0 -306
- package/node_modules/@humwick/kernel/landing.ts +0 -953
- package/node_modules/@humwick/kernel/manifest-validation.ts +0 -460
- package/node_modules/@humwick/kernel/mcp-transport.ts +0 -109
- package/node_modules/@humwick/kernel/message-authority-chaos-worker.ts +0 -200
- package/node_modules/@humwick/kernel/message-authority.ts +0 -281
- package/node_modules/@humwick/kernel/module-host.ts +0 -722
- package/node_modules/@humwick/kernel/openapi-init.ts +0 -14
- package/node_modules/@humwick/kernel/package.json +0 -47
- package/node_modules/@humwick/kernel/persistence-schema.ts +0 -248
- package/node_modules/@humwick/kernel/persistence.ts +0 -510
- package/node_modules/@humwick/kernel/registry.ts +0 -60
- package/node_modules/@humwick/kernel/sqlite-kernel-persistence.ts +0 -499
- package/node_modules/@humwick/kernel/sqlite-module-host.ts +0 -87
- package/node_modules/@humwick/kernel/timers-chaos-worker.ts +0 -186
- package/node_modules/@humwick/kernel/timers.pilot.ts +0 -1411
- package/node_modules/@humwick/kernel/workflow-activation.ts +0 -25
- package/node_modules/@humwick/kernel/workflow-backstop.ts +0 -40
- package/node_modules/@humwick/kernel/workflow-cancel-tree.ts +0 -20
- package/node_modules/@humwick/kernel/workflow-chaos-worker.ts +0 -213
- package/node_modules/@humwick/kernel/workflow-composition.ts +0 -231
- package/node_modules/@humwick/kernel/workflow-container.ts +0 -72
- package/node_modules/@humwick/kernel/workflow-control.test-fixture.ts +0 -42
- package/node_modules/@humwick/kernel/workflow-control.ts +0 -178
- package/node_modules/@humwick/kernel/workflow-definition.ts +0 -507
- package/node_modules/@humwick/kernel/workflow-dispatch.ts +0 -60
- package/node_modules/@humwick/kernel/workflow-foreach.ts +0 -167
- package/node_modules/@humwick/kernel/workflow-journal-codec.ts +0 -339
- package/node_modules/@humwick/kernel/workflow-journal.ts +0 -922
- package/node_modules/@humwick/kernel/workflow-lane.ts +0 -48
- package/node_modules/@humwick/kernel/workflow-lease-owner.ts +0 -60
- package/node_modules/@humwick/kernel/workflow-recovery.test-fixture.ts +0 -19
- package/node_modules/@humwick/kernel/workflow-runtime.ts +0 -938
- package/node_modules/@humwick/kernel/workflow-script.ts +0 -11
- package/node_modules/@humwick/kernel/workflow-state.ts +0 -299
- package/node_modules/@humwick/kernel/workflow-step-commit.ts +0 -195
- package/node_modules/@humwick/kernel/workflow-transitions.ts +0 -317
- package/node_modules/@humwick/kernel/workflow-values.ts +0 -153
- package/node_modules/@humwick/kernel/workflow.pilot.ts +0 -376
- package/node_modules/@humwick/offload/LICENSE +0 -167
- package/node_modules/@humwick/offload/README.md +0 -57
- package/node_modules/@humwick/offload/dist/async-runner.d.ts +0 -18
- package/node_modules/@humwick/offload/dist/async-runner.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/async-runner.js +0 -158
- package/node_modules/@humwick/offload/dist/async-runner.js.map +0 -1
- package/node_modules/@humwick/offload/dist/envelope.d.ts +0 -25
- package/node_modules/@humwick/offload/dist/envelope.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/envelope.js +0 -80
- package/node_modules/@humwick/offload/dist/envelope.js.map +0 -1
- package/node_modules/@humwick/offload/dist/fixtures/worker-task.d.ts +0 -10
- package/node_modules/@humwick/offload/dist/fixtures/worker-task.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/fixtures/worker-task.js +0 -59
- package/node_modules/@humwick/offload/dist/fixtures/worker-task.js.map +0 -1
- package/node_modules/@humwick/offload/dist/index.d.ts +0 -5
- package/node_modules/@humwick/offload/dist/index.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/index.js +0 -5
- package/node_modules/@humwick/offload/dist/index.js.map +0 -1
- package/node_modules/@humwick/offload/dist/subprocess-runner.d.ts +0 -62
- package/node_modules/@humwick/offload/dist/subprocess-runner.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/subprocess-runner.js +0 -295
- package/node_modules/@humwick/offload/dist/subprocess-runner.js.map +0 -1
- package/node_modules/@humwick/offload/dist/types.d.ts +0 -111
- package/node_modules/@humwick/offload/dist/types.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/types.js +0 -176
- package/node_modules/@humwick/offload/dist/types.js.map +0 -1
- package/node_modules/@humwick/offload/dist/worker-pool.d.ts +0 -23
- package/node_modules/@humwick/offload/dist/worker-pool.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/worker-pool.js +0 -435
- package/node_modules/@humwick/offload/dist/worker-pool.js.map +0 -1
- package/node_modules/@humwick/offload/dist/worker-runtime.d.ts +0 -2
- package/node_modules/@humwick/offload/dist/worker-runtime.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/worker-runtime.js +0 -27
- package/node_modules/@humwick/offload/dist/worker-runtime.js.map +0 -1
- package/node_modules/@humwick/offload/dist/worker.d.ts +0 -5
- package/node_modules/@humwick/offload/dist/worker.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/worker.js +0 -1
- package/node_modules/@humwick/offload/dist/worker.js.map +0 -1
- package/node_modules/@humwick/offload/package.json +0 -46
- package/node_modules/@humwick/offload/src/async-runner.ts +0 -170
- package/node_modules/@humwick/offload/src/envelope.ts +0 -92
- package/node_modules/@humwick/offload/src/fixtures/worker-task.ts +0 -49
- package/node_modules/@humwick/offload/src/index.ts +0 -4
- package/node_modules/@humwick/offload/src/subprocess-runner.ts +0 -334
- package/node_modules/@humwick/offload/src/types.ts +0 -235
- package/node_modules/@humwick/offload/src/worker-pool.ts +0 -463
- package/node_modules/@humwick/offload/src/worker-runtime.ts +0 -32
- package/node_modules/@humwick/offload/src/worker.ts +0 -5
- package/public/assets/agent-liveness-DyqxfB-t.js +0 -2
- package/public/assets/agent-profiles-BpI_jTxq.js +0 -2
- package/public/assets/fleet-BL4UXS_k.js +0 -2
- package/public/assets/index-B7ZerHLY.css +0 -2
- package/public/assets/index-BK0WV6hF.js +0 -15
- package/public/assets/projects-B_lmcqfO.js +0 -2
- package/public/assets/provider-display-DbLWpP3j.js +0 -2
- package/public/assets/run-card-CO_bguQf.js +0 -2
- package/public/assets/use-project-list-B-T2Bnnn.js +0 -2
- package/public/assets/versioned-prompt-editor-Diu8BIaM.js +0 -8
- package/runner/src/gate-run-lease-probe.ts +0 -38
- package/src/db/claude-catalog-reconcile.ts +0 -27
- package/src/routing-model-aliases.ts +0 -21
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { listIntegrationTaskStats, reconcileIntegrationRegistry, type IntegrationRegistryInput } from "./integrations.ts";
|
|
2
|
+
import type { WriterOpSpec } from "./write-ops.ts";
|
|
3
|
+
|
|
4
|
+
export const INTEGRATIONS_READ_OPS = {
|
|
5
|
+
"integrations.taskStats": () => listIntegrationTaskStats(),
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const INTEGRATIONS_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = {
|
|
9
|
+
"integrations.reconcileRegistry": {
|
|
10
|
+
offLoop: true,
|
|
11
|
+
idempotent: true,
|
|
12
|
+
fn: (args: { configured: IntegrationRegistryInput[]; observedSources: string[] }, ctx) =>
|
|
13
|
+
reconcileIntegrationRegistry(args.configured, args.observedSources, ctx.now),
|
|
14
|
+
},
|
|
15
|
+
};
|
package/src/db/integrations.ts
CHANGED
|
@@ -1,18 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { isRecord, stringValue, isMechanicalMessageKind } from "@humwick/sdk";
|
|
3
|
-
import { ORCHESTRATOR_PROTOCOL_VERSION, VERSION } from "../config.ts";
|
|
1
|
+
import { stringValue } from "@humwick/sdk";
|
|
4
2
|
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
3
|
import { normalizeTags, parseStringArray } from "./agents.ts";
|
|
17
4
|
import { linkAttachmentRefs, validateAttachmentRefs } from "./artifacts.ts";
|
|
18
5
|
import { ValidationError, getDb, writeTransaction } from "./connection.ts";
|
|
@@ -143,9 +130,7 @@ function integrationTaskMetadata(
|
|
|
143
130
|
};
|
|
144
131
|
}
|
|
145
132
|
|
|
146
|
-
|
|
147
|
-
export function listIntegrationTaskStats(): IntegrationTaskStats[] {
|
|
148
|
-
const rows = getDb().query(`
|
|
133
|
+
export const INTEGRATION_TASK_STATS_SQL = `
|
|
149
134
|
SELECT
|
|
150
135
|
coalesce(json_extract(metadata, '$.relayIntegrationName'), source) AS source,
|
|
151
136
|
COUNT(*) AS tasks,
|
|
@@ -157,7 +142,10 @@ export function listIntegrationTaskStats(): IntegrationTaskStats[] {
|
|
|
157
142
|
FROM tasks
|
|
158
143
|
GROUP BY coalesce(json_extract(metadata, '$.relayIntegrationName'), source)
|
|
159
144
|
ORDER BY last_seen_at DESC, source ASC
|
|
160
|
-
|
|
145
|
+
`;
|
|
146
|
+
|
|
147
|
+
export function listIntegrationTaskStats(): IntegrationTaskStats[] {
|
|
148
|
+
const rows = getDb().query(INTEGRATION_TASK_STATS_SQL).all() as any[];
|
|
161
149
|
|
|
162
150
|
return rows.map((row) => ({
|
|
163
151
|
source: row.source,
|
|
@@ -170,7 +158,7 @@ export function listIntegrationTaskStats(): IntegrationTaskStats[] {
|
|
|
170
158
|
}));
|
|
171
159
|
}
|
|
172
160
|
|
|
173
|
-
type IntegrationRegistryInput = {
|
|
161
|
+
export type IntegrationRegistryInput = {
|
|
174
162
|
name: string;
|
|
175
163
|
displayName?: string;
|
|
176
164
|
description?: string;
|
|
@@ -220,10 +208,9 @@ function rowToIntegrationRegistry(row: any): Omit<IntegrationSummary, "configure
|
|
|
220
208
|
};
|
|
221
209
|
}
|
|
222
210
|
|
|
223
|
-
export function upsertIntegrationRegistry(input: IntegrationRegistryInput): ReturnType<typeof rowToIntegrationRegistry> {
|
|
211
|
+
export function upsertIntegrationRegistry(input: IntegrationRegistryInput, now = Date.now()): ReturnType<typeof rowToIntegrationRegistry> {
|
|
224
212
|
const name = stringValue(input.name);
|
|
225
213
|
if (!name) throw new ValidationError("integration name required");
|
|
226
|
-
const now = Date.now();
|
|
227
214
|
getDb().query(`
|
|
228
215
|
INSERT INTO integration_registry (
|
|
229
216
|
name, display_name, description, enabled, scopes, targets, channels, type, icon, accent_color,
|
|
@@ -295,3 +282,10 @@ export function isIntegrationRegistryEnabled(name: string): boolean {
|
|
|
295
282
|
const registry = getIntegrationRegistry(name);
|
|
296
283
|
return registry?.enabled !== false;
|
|
297
284
|
}
|
|
285
|
+
|
|
286
|
+
// GET reconciliation preserves registry precedence, but runs entirely on the writer (#3740).
|
|
287
|
+
export function reconcileIntegrationRegistry(configured: IntegrationRegistryInput[], observedSources: string[], now: number): ReturnType<typeof listIntegrationRegistry> {
|
|
288
|
+
for (const input of configured) upsertIntegrationRegistry({ ...input, source: "env" }, now);
|
|
289
|
+
for (const name of observedSources) upsertIntegrationRegistry({ name, enabled: true, source: "observed" }, now);
|
|
290
|
+
return listIntegrationRegistry();
|
|
291
|
+
}
|
|
@@ -5,7 +5,7 @@ const ISSUE_LIFECYCLE_STATUSES = ["succeeded", "failed"] as const;
|
|
|
5
5
|
type IssueLifecycleAction = (typeof ISSUE_LIFECYCLE_ACTIONS)[number];
|
|
6
6
|
type IssueLifecycleStatus = (typeof ISSUE_LIFECYCLE_STATUSES)[number];
|
|
7
7
|
|
|
8
|
-
interface IssueLifecycleActionRecord {
|
|
8
|
+
export interface IssueLifecycleActionRecord {
|
|
9
9
|
id: number;
|
|
10
10
|
issueRefId: string;
|
|
11
11
|
action: IssueLifecycleAction;
|
|
@@ -24,7 +24,7 @@ interface IssueLifecycleActionRecord {
|
|
|
24
24
|
updatedAt: number;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
interface UpsertIssueLifecycleActionInput {
|
|
27
|
+
export interface UpsertIssueLifecycleActionInput {
|
|
28
28
|
issueRefId: string;
|
|
29
29
|
action: IssueLifecycleAction;
|
|
30
30
|
landKey: string;
|
|
@@ -150,8 +150,7 @@ export function listSuccessfulCloseActionsForCommit(input: { commitSha?: string;
|
|
|
150
150
|
return rows.map(rowToIssueLifecycleAction);
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
export function upsertIssueLifecycleAction(input: UpsertIssueLifecycleActionInput): IssueLifecycleActionRecord {
|
|
154
|
-
const now = Date.now();
|
|
153
|
+
export function upsertIssueLifecycleAction(input: UpsertIssueLifecycleActionInput, now: number = Date.now()): IssueLifecycleActionRecord {
|
|
155
154
|
const issueRefId = cleanRequiredText(input.issueRefId, "issueRefId", 120);
|
|
156
155
|
const action = cleanAction(input.action);
|
|
157
156
|
const landKey = cleanRequiredText(input.landKey, "landKey", 240);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// #1870 S7 — async facades for the issue-ref writer ops (`issue-refs-write-ops.ts`).
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { getGateway } from "./gateway.ts";
|
|
4
|
+
import type { IssueRef } from "../types";
|
|
5
|
+
import type { IssueTrackerIssueSummary } from "../issue-tracker/adapter";
|
|
6
|
+
import type { ResolveIssueRefByExternalKeyInput, ResolveIssueRefFromStringInput, UpdateIssueRefInput, UpsertIssueRefInput } from "./issue-refs.ts";
|
|
7
|
+
import type { IssueLifecycleActionRecord, UpsertIssueLifecycleActionInput } from "./issue-lifecycle.ts";
|
|
8
|
+
|
|
9
|
+
const write = <T>(op: string, args: unknown, opts?: { idempotencyKey?: string }) => getGateway().write(op, args as never, opts) as unknown as Promise<T>;
|
|
10
|
+
|
|
11
|
+
export const upsertIssueRefAsync = (input: UpsertIssueRefInput) => write<IssueRef>("issueRefs.upsert", input);
|
|
12
|
+
export const resolveIssueRefByExternalKeyAsync = (input: ResolveIssueRefByExternalKeyInput) => write<IssueRef>("issueRefs.resolveExternalKey", input);
|
|
13
|
+
export const resolveIssueRefFromStringAsync = (input: ResolveIssueRefFromStringInput) => write<IssueRef>("issueRefs.resolveFromString", input);
|
|
14
|
+
export const updateIssueRefAsync = (id: string, input: UpdateIssueRefInput) => write<IssueRef | null>("issueRefs.update", { id, input });
|
|
15
|
+
export const deleteIssueRefAsync = (id: string) => write<boolean>("issueRefs.delete", { id });
|
|
16
|
+
/** The tracker answered: clear the not-found streak and cache its status, in one write. */
|
|
17
|
+
export const recordIssueRefLiveStatusAsync = (id: string, input: UpdateIssueRefInput) => write<IssueRef | null>("issueRefs.recordLiveStatus", { id, input });
|
|
18
|
+
export const putIssueHierarchyAsync = (id: string, children: IssueTrackerIssueSummary[]) => write<void>("issueRefs.putHierarchy", { id, children });
|
|
19
|
+
export const recordIssueTrackerNotFoundAsync = (id: string) => write<number>("issueRefs.recordNotFound", { id }, { idempotencyKey: randomUUID() });
|
|
20
|
+
export const upsertIssueLifecycleActionAsync = (input: UpsertIssueLifecycleActionInput) => write<IssueLifecycleActionRecord>("issueLifecycle.upsertAction", input);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// #1870 S7 — issue-ref cache, tracker refresh state and lifecycle ledger writes run on the writer thread.
|
|
2
|
+
// Every body is plain SQL against its own tables. All but `recordNotFound` converge on replay: upserts are
|
|
3
|
+
// keyed by (provider, repo, externalKey) or (issue, action, landKey), updates re-set the same columns.
|
|
4
|
+
import { resolveIssueRefByExternalKey, resolveIssueRefFromString, updateIssueRef, upsertIssueRef, deleteIssueRef, type ResolveIssueRefByExternalKeyInput, type ResolveIssueRefFromStringInput, type UpdateIssueRefInput, type UpsertIssueRefInput } from "./issue-refs.ts";
|
|
5
|
+
import { clearIssueTrackerNotFound, putIssueHierarchy, recordIssueTrackerNotFound } from "./long-running-work.ts";
|
|
6
|
+
import { upsertIssueLifecycleAction, type UpsertIssueLifecycleActionInput } from "./issue-lifecycle.ts";
|
|
7
|
+
import type { IssueTrackerIssueSummary } from "../issue-tracker/adapter";
|
|
8
|
+
import type { WriterOpContext, WriterOpSpec } from "./write-ops.ts";
|
|
9
|
+
|
|
10
|
+
export const ISSUE_REFS_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze({
|
|
11
|
+
"issueRefs.upsert": { offLoop: true, idempotent: true, fn: (args: UpsertIssueRefInput, ctx: WriterOpContext) => upsertIssueRef(args, ctx.now) },
|
|
12
|
+
"issueRefs.resolveExternalKey": { offLoop: true, idempotent: true, fn: (args: ResolveIssueRefByExternalKeyInput, ctx: WriterOpContext) => resolveIssueRefByExternalKey(args, ctx.now) },
|
|
13
|
+
"issueRefs.resolveFromString": { offLoop: true, idempotent: true, fn: (args: ResolveIssueRefFromStringInput, ctx: WriterOpContext) => resolveIssueRefFromString(args, ctx.now) },
|
|
14
|
+
"issueRefs.update": { offLoop: true, idempotent: true, fn: (args: { id: string; input: UpdateIssueRefInput }, ctx: WriterOpContext) => updateIssueRef(args.id, args.input, ctx.now) },
|
|
15
|
+
"issueRefs.delete": { offLoop: true, idempotent: true, fn: (args: { id: string }) => deleteIssueRef(args.id) },
|
|
16
|
+
// A tracker answered for this ref: the not-found streak ends and the cached status follows the tracker.
|
|
17
|
+
"issueRefs.recordLiveStatus": {
|
|
18
|
+
offLoop: true,
|
|
19
|
+
idempotent: true,
|
|
20
|
+
fn: (args: { id: string; input: UpdateIssueRefInput }, ctx: WriterOpContext) => {
|
|
21
|
+
clearIssueTrackerNotFound(args.id);
|
|
22
|
+
return updateIssueRef(args.id, args.input, ctx.now);
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
"issueRefs.putHierarchy": { offLoop: true, idempotent: true, fn: (args: { id: string; children: IssueTrackerIssueSummary[] }, ctx: WriterOpContext) => putIssueHierarchy(args.id, args.children, ctx.now) },
|
|
26
|
+
// Increments a counter, so a replay would double-count: keyed.
|
|
27
|
+
"issueRefs.recordNotFound": { offLoop: true, idempotent: false, fn: (args: { id: string }) => recordIssueTrackerNotFound(args.id) },
|
|
28
|
+
"issueLifecycle.upsertAction": { offLoop: true, idempotent: true, fn: (args: UpsertIssueLifecycleActionInput, ctx: WriterOpContext) => upsertIssueLifecycleAction(args, ctx.now) },
|
|
29
|
+
});
|
package/src/db/issue-refs.ts
CHANGED
|
@@ -151,7 +151,7 @@ export function getIssueRefByExternalKey(provider: IssueProvider, repo: string,
|
|
|
151
151
|
return row ? rowToIssueRef(row) : null;
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
export function upsertIssueRef(input: UpsertIssueRefInput): IssueRef {
|
|
154
|
+
export function upsertIssueRef(input: UpsertIssueRefInput, now: number = Date.now()): IssueRef {
|
|
155
155
|
const provider = cleanProvider(input.provider);
|
|
156
156
|
const repo = requiredText(input.repo, "repo", 500);
|
|
157
157
|
const externalKey = requiredText(input.externalKey, "externalKey", 240);
|
|
@@ -162,7 +162,6 @@ export function upsertIssueRef(input: UpsertIssueRefInput): IssueRef {
|
|
|
162
162
|
// An open/unknown upsert clears a stale closeReason; an explicit closeReason requires closed.
|
|
163
163
|
const closeReason = input.closeReason === undefined && status !== "closed" ? undefined : cleanCloseReason(input.closeReason, status) ?? (status === "closed" ? existing?.closeReason : undefined);
|
|
164
164
|
const statusSyncedAt = cleanSyncedAt(input.statusSyncedAt);
|
|
165
|
-
const now = Date.now();
|
|
166
165
|
if (existing) {
|
|
167
166
|
getDb().query(`
|
|
168
167
|
UPDATE issue_refs
|
|
@@ -179,7 +178,7 @@ export function upsertIssueRef(input: UpsertIssueRefInput): IssueRef {
|
|
|
179
178
|
return getIssueRef(id)!;
|
|
180
179
|
}
|
|
181
180
|
|
|
182
|
-
export function resolveIssueRefByExternalKey(input: ResolveIssueRefByExternalKeyInput): IssueRef {
|
|
181
|
+
export function resolveIssueRefByExternalKey(input: ResolveIssueRefByExternalKeyInput, now: number = Date.now()): IssueRef {
|
|
183
182
|
const provider = cleanProvider(input.provider);
|
|
184
183
|
const repo = requiredText(input.repo, "repo", 500);
|
|
185
184
|
const externalKey = requiredText(input.externalKey, "externalKey", 240);
|
|
@@ -196,7 +195,7 @@ export function resolveIssueRefByExternalKey(input: ResolveIssueRefByExternalKey
|
|
|
196
195
|
status: input.status ?? existing?.status,
|
|
197
196
|
closeReason: input.closeReason ?? (input.status !== undefined ? undefined : existing?.closeReason),
|
|
198
197
|
statusSyncedAt: input.statusSyncedAt ?? existing?.statusSyncedAt,
|
|
199
|
-
});
|
|
198
|
+
}, now);
|
|
200
199
|
}
|
|
201
200
|
|
|
202
201
|
// A BARE `#123` ref carries no repo, so it only resolves when the caller supplies a
|
|
@@ -231,7 +230,7 @@ export function issueRefForRefString(ref: string, defaultRepo?: string): IssueRe
|
|
|
231
230
|
return getIssueRefByExternalKey("github", parsed.repo, parsed.externalKey);
|
|
232
231
|
}
|
|
233
232
|
|
|
234
|
-
export function resolveIssueRefFromString(input: ResolveIssueRefFromStringInput): IssueRef {
|
|
233
|
+
export function resolveIssueRefFromString(input: ResolveIssueRefFromStringInput, now: number = Date.now()): IssueRef {
|
|
235
234
|
const ref = requiredText(input.ref, "ref", 1000);
|
|
236
235
|
if (ref.startsWith("issue_")) {
|
|
237
236
|
const existing = getIssueRef(ref);
|
|
@@ -250,7 +249,7 @@ export function resolveIssueRefFromString(input: ResolveIssueRefFromStringInput)
|
|
|
250
249
|
status: input.status,
|
|
251
250
|
closeReason: input.closeReason,
|
|
252
251
|
statusSyncedAt: input.statusSyncedAt,
|
|
253
|
-
});
|
|
252
|
+
}, now);
|
|
254
253
|
}
|
|
255
254
|
|
|
256
255
|
export function listIssueRefs(opts: ListIssueRefsOptions = {}): IssueRef[] {
|
|
@@ -281,7 +280,7 @@ export function listIssueRefs(opts: ListIssueRefsOptions = {}): IssueRef[] {
|
|
|
281
280
|
return rows.map(rowToIssueRef);
|
|
282
281
|
}
|
|
283
282
|
|
|
284
|
-
export function updateIssueRef(id: string, input: UpdateIssueRefInput): IssueRef | null {
|
|
283
|
+
export function updateIssueRef(id: string, input: UpdateIssueRefInput, now: number = Date.now()): IssueRef | null {
|
|
285
284
|
const existing = getIssueRef(requiredText(id, "id", 120));
|
|
286
285
|
if (!existing) return null;
|
|
287
286
|
const url = optionalText(input.url, "url", 2000) ?? existing.url;
|
|
@@ -295,7 +294,7 @@ export function updateIssueRef(id: string, input: UpdateIssueRefInput): IssueRef
|
|
|
295
294
|
UPDATE issue_refs
|
|
296
295
|
SET url = ?, title = ?, status = ?, close_reason = ?, status_synced_at = ?, updated_at = ?
|
|
297
296
|
WHERE id = ?
|
|
298
|
-
`).run(url, title ?? null, status, closeReason ?? null, statusSyncedAt ?? null,
|
|
297
|
+
`).run(url, title ?? null, status, closeReason ?? null, statusSyncedAt ?? null, now, existing.id);
|
|
299
298
|
return getIssueRef(existing.id);
|
|
300
299
|
}
|
|
301
300
|
|
|
@@ -49,10 +49,10 @@ export function settlePendingIssueClose(args: PendingIssueCloseKey & { finished:
|
|
|
49
49
|
else getDb().query("UPDATE pending_issue_closes SET updated_at = ? WHERE issue_ref_id = ? AND land_key = ?").run(now, args.issueRefId, args.landKey);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
export function putIssueHierarchy(issueRefId: string, children: IssueTrackerIssueSummary[]): void {
|
|
52
|
+
export function putIssueHierarchy(issueRefId: string, children: IssueTrackerIssueSummary[], now: number = Date.now()): void {
|
|
53
53
|
getDb().query(`INSERT INTO issue_hierarchy_snapshots(issue_ref_id, children, updated_at) VALUES (?, ?, ?)
|
|
54
54
|
ON CONFLICT(issue_ref_id) DO UPDATE SET children = excluded.children, updated_at = excluded.updated_at`)
|
|
55
|
-
.run(issueRefId, JSON.stringify(children),
|
|
55
|
+
.run(issueRefId, JSON.stringify(children), now);
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
export function getIssueHierarchy(issueRefId: string): IssueTrackerIssueSummary[] | undefined {
|
package/src/db/macos-privacy.ts
CHANGED
|
@@ -68,5 +68,5 @@ export function reconcileMacosPrivacy(args: { orchestratorId: string; prompts: M
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
export const MACOS_PRIVACY_WRITE_OPS = {
|
|
71
|
-
"macosPrivacy.reconcile": { idempotent: true, fn: reconcileMacosPrivacy },
|
|
71
|
+
"macosPrivacy.reconcile": { offLoop: true, idempotent: true, fn: reconcileMacosPrivacy },
|
|
72
72
|
};
|
|
@@ -34,10 +34,31 @@ export class MainConnectionWriteCounter {
|
|
|
34
34
|
}
|
|
35
35
|
let counter = new MainConnectionWriteCounter();
|
|
36
36
|
export function resetMainConnectionWrites(): void {
|
|
37
|
-
if (Bun.isMainThread) counter = new MainConnectionWriteCounter();
|
|
37
|
+
if (Bun.isMainThread) { counter = new MainConnectionWriteCounter(); bootWrites = 0; loggedLabels.clear(); }
|
|
38
|
+
}
|
|
39
|
+
// #1870 S7: schema, migrations, seeds and pre-bind reconciles write on the main connection before the
|
|
40
|
+
// server binds. That is the documented boot exception (S8 flips query_only after it), so it is tallied
|
|
41
|
+
// apart and never enters the 1 h label window. Only `startServer` opens the phase; tests count from zero.
|
|
42
|
+
let booting = false;
|
|
43
|
+
let bootWrites = 0;
|
|
44
|
+
export function beginMainWriteBootPhase(): void { booting = true; }
|
|
45
|
+
export function endMainWriteBootPhase(): void { booting = false; }
|
|
46
|
+
export function mainConnectionBootWrites(): number { return bootWrites; }
|
|
47
|
+
// The first sighting of each label per process logs its caller once, so the remaining raw writers are named
|
|
48
|
+
// from hub logs without a debugger. Bounded by the counter's own label cap.
|
|
49
|
+
const loggedLabels = new Set<string>();
|
|
50
|
+
function logFirstSighting(label: string): void {
|
|
51
|
+
if (process.env.NODE_ENV === "test" || loggedLabels.has(label) || loggedLabels.size >= MAX_LABELS) return;
|
|
52
|
+
loggedLabels.add(label);
|
|
53
|
+
const site = (new Error().stack ?? "").split("\n").filter((line) => line.includes(" at ") && !/connection|main-connection-writes/.test(line)).slice(0, 4)
|
|
54
|
+
.map((line) => line.trim().replace(/^at /, "").replace(/\(?\/[^)]*\/(src\/[^):]+:\d+)[^)]*\)?/, "$1")).join(" < ");
|
|
55
|
+
console.log(`[relay] #1870 main-connection write: ${label} :: ${site}`);
|
|
38
56
|
}
|
|
39
57
|
export function recordMainConnectionWrite(label: string | undefined): void {
|
|
40
|
-
if (Bun.isMainThread
|
|
58
|
+
if (!Bun.isMainThread || !label) return;
|
|
59
|
+
if (booting) { bootWrites++; return; }
|
|
60
|
+
counter.record(label, Date.now());
|
|
61
|
+
logFirstSighting(label);
|
|
41
62
|
}
|
|
42
63
|
export function mainConnectionWrites(now: number = Date.now()): Record<string, number> {
|
|
43
64
|
return counter.snapshot(now);
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { getDb } from "./connection.ts";
|
|
2
|
+
|
|
3
|
+
// #1870 S7 — the maintenance job-table reconcile, run-on-start rescheduling and dead-lease reclaim.
|
|
4
|
+
// The scheduler runs the reconcile on every tick, so these were its largest raw main-connection writes.
|
|
5
|
+
|
|
6
|
+
export interface MaintenanceJobSpec {
|
|
7
|
+
id: string;
|
|
8
|
+
title: string;
|
|
9
|
+
description: string;
|
|
10
|
+
intervalMs: number;
|
|
11
|
+
deadlineMs: number;
|
|
12
|
+
enabled: boolean;
|
|
13
|
+
critical: boolean;
|
|
14
|
+
runOnStart: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** #1645: `enabled` is code-owned and re-asserted on every reconcile; operator stops live in `maintenance_job_pauses`.
|
|
18
|
+
* PRECONDITION: convertManualJobDisablesToPauses() (migrations.ts) ran first. Runs inside the caller's write transaction. */
|
|
19
|
+
export function upsertMaintenanceJobs(jobs: readonly MaintenanceJobSpec[], now: number): void {
|
|
20
|
+
const stmt = getDb().query(`
|
|
21
|
+
INSERT INTO maintenance_jobs (id, title, description, interval_ms, timeout_ms, enabled, critical, run_on_start, next_run_at, updated_at)
|
|
22
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
23
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
24
|
+
title = excluded.title,
|
|
25
|
+
description = excluded.description,
|
|
26
|
+
interval_ms = excluded.interval_ms,
|
|
27
|
+
timeout_ms = excluded.timeout_ms,
|
|
28
|
+
run_on_start = excluded.run_on_start,
|
|
29
|
+
enabled = excluded.enabled, -- #1645: code-owned, see the note above this query
|
|
30
|
+
critical = excluded.critical,
|
|
31
|
+
next_run_at = COALESCE(maintenance_jobs.next_run_at, excluded.next_run_at),
|
|
32
|
+
updated_at = excluded.updated_at
|
|
33
|
+
`);
|
|
34
|
+
// The DB column stays `timeout_ms` (#1728): a rename is a migration. Code-side it is `deadlineMs`.
|
|
35
|
+
for (const job of jobs) {
|
|
36
|
+
stmt.run(job.id, job.title, job.description, job.intervalMs, job.deadlineMs, job.enabled ? 1 : 0, job.critical ? 1 : 0, job.runOnStart ? 1 : 0,
|
|
37
|
+
job.runOnStart ? now : now + job.intervalMs, now);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** `runOnStart` means every process start; this is boot-only, never part of the periodic reconcile. */
|
|
42
|
+
export function rescheduleRunOnStartJobs(now: number): void {
|
|
43
|
+
getDb().query(`
|
|
44
|
+
UPDATE maintenance_jobs
|
|
45
|
+
SET next_run_at = ?, updated_at = ?
|
|
46
|
+
WHERE enabled = 1 AND run_on_start = 1
|
|
47
|
+
`).run(now, now);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface DeadLease { id: string; owner: string; pid: number }
|
|
51
|
+
|
|
52
|
+
/** #1493/#3645: a confirmed-dead owner is a crash, not a job failure — no failure streak bump, no last_error overwrite. */
|
|
53
|
+
export function reclaimDeadLeases(leases: readonly DeadLease[], now: number): void {
|
|
54
|
+
const stmt = getDb().query(`
|
|
55
|
+
UPDATE maintenance_jobs
|
|
56
|
+
SET lease_owner = NULL, lease_until = NULL, last_status = 'failed',
|
|
57
|
+
last_error = COALESCE(last_error, ?), run_token = NULL, last_progress_at = ?, updated_at = ?
|
|
58
|
+
WHERE id = ? AND lease_owner = ?
|
|
59
|
+
`);
|
|
60
|
+
for (const lease of leases) stmt.run(`reclaimed stale lease at startup: owner pid ${lease.pid} is confirmed dead`, now, now, lease.id, lease.owner);
|
|
61
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { getGateway } from "./gateway.ts";
|
|
2
|
+
import type { ResetCriticalMaintenanceRun } from "./maintenance-health.ts";
|
|
3
|
+
import type { DeadLease, MaintenanceJobSpec } from "./maintenance-job-reconcile.ts";
|
|
2
4
|
import type { MaintenanceRunClaim, MaintenanceRunOutcome } from "./maintenance-job-runs.ts";
|
|
3
5
|
|
|
4
6
|
// #1870 S3 — lease and outcome writes are critical: a shed claim skips a due job and a shed outcome
|
|
@@ -13,3 +15,15 @@ export async function heartbeatMaintenanceRunAsync(args: { id: string; runToken:
|
|
|
13
15
|
|
|
14
16
|
export const finishMaintenanceRunAsync = (args: MaintenanceRunOutcome) =>
|
|
15
17
|
getGateway().write("maintenanceJobs.finish", args, { critical: true }) as unknown as Promise<boolean>;
|
|
18
|
+
|
|
19
|
+
export async function ensureMaintenanceJobsAsync(jobs: MaintenanceJobSpec[]): Promise<void> {
|
|
20
|
+
await getGateway().write("maintenanceJobs.ensure", { jobs }, { critical: true });
|
|
21
|
+
}
|
|
22
|
+
export async function rescheduleRunOnStartJobsAsync(): Promise<void> {
|
|
23
|
+
await getGateway().write("maintenanceJobs.rescheduleRunOnStart", {}, { critical: true });
|
|
24
|
+
}
|
|
25
|
+
export async function reclaimDeadLeasesAsync(leases: DeadLease[]): Promise<void> {
|
|
26
|
+
if (leases.length) await getGateway().write("maintenanceJobs.reclaimDeadLeases", { leases }, { critical: true });
|
|
27
|
+
}
|
|
28
|
+
export const resetExpiredCriticalRunsAsync = (now: number) =>
|
|
29
|
+
getGateway().write("maintenanceJobs.resetExpiredCritical", { now }, { critical: true }) as unknown as Promise<ResetCriticalMaintenanceRun[]>;
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import { claimMaintenanceRun, finishMaintenanceRun, heartbeatMaintenanceRun } from "./maintenance-job-runs.ts";
|
|
2
2
|
import type { MaintenanceRunClaim, MaintenanceRunOutcome } from "./maintenance-job-runs.ts";
|
|
3
|
+
import { resetExpiredCriticalMaintenanceRuns } from "./maintenance-health.ts";
|
|
4
|
+
import { reclaimDeadLeases, rescheduleRunOnStartJobs, upsertMaintenanceJobs } from "./maintenance-job-reconcile.ts";
|
|
5
|
+
import type { DeadLease, MaintenanceJobSpec } from "./maintenance-job-reconcile.ts";
|
|
3
6
|
import type { WriterOpSpec } from "./write-ops.ts";
|
|
4
7
|
|
|
5
8
|
// #1870 S3 — each op is a compare-and-set on the run's own owner/token, so a replay changes nothing.
|
|
6
9
|
export const MAINTENANCE_JOB_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze({
|
|
7
10
|
"maintenanceJobs.claim": { offLoop: true, idempotent: true, fn: (args: MaintenanceRunClaim) => claimMaintenanceRun(args) },
|
|
8
11
|
"maintenanceJobs.heartbeat": { offLoop: true, idempotent: true, fn: (args: { id: string; runToken: string }, ctx) => heartbeatMaintenanceRun(args, ctx.now) },
|
|
12
|
+
// Reconcile is a re-assertion of code-owned columns, so a replay changes nothing.
|
|
13
|
+
"maintenanceJobs.ensure": { offLoop: true, idempotent: true, fn: (args: { jobs: MaintenanceJobSpec[] }, ctx) => upsertMaintenanceJobs(args.jobs, ctx.now) },
|
|
14
|
+
"maintenanceJobs.rescheduleRunOnStart": { offLoop: true, idempotent: true, fn: (_args: Record<string, never>, ctx) => rescheduleRunOnStartJobs(ctx.now) },
|
|
15
|
+
"maintenanceJobs.reclaimDeadLeases": { offLoop: true, idempotent: true, fn: (args: { leases: DeadLease[] }, ctx) => reclaimDeadLeases(args.leases, ctx.now) },
|
|
16
|
+
// A compare-and-set on the observed lease, so a replay finds nothing left to reset.
|
|
17
|
+
"maintenanceJobs.resetExpiredCritical": { offLoop: true, idempotent: true, fn: (args: { now: number }) => resetExpiredCriticalMaintenanceRuns(args.now) },
|
|
9
18
|
"maintenanceJobs.finish": { offLoop: true, idempotent: true, fn: (args: MaintenanceRunOutcome) => finishMaintenanceRun(args) },
|
|
10
19
|
});
|
package/src/db/mappers.ts
CHANGED
|
@@ -1,17 +1,4 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { isRecord, stringValue, isMechanicalMessageKind } from "@humwick/sdk";
|
|
3
|
-
import { ORCHESTRATOR_PROTOCOL_VERSION, VERSION } from "../config.ts";
|
|
4
1
|
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
2
|
import { parseStringArray } from "./shared.ts";
|
|
16
3
|
import type {
|
|
17
4
|
AgentCard,
|
|
@@ -291,12 +278,14 @@ export function rowToTask(row: any): Task {
|
|
|
291
278
|
...(row.dispatch != null ? { dispatch: parseJson(row.dispatch, {}) } : {}),
|
|
292
279
|
...(row.reaped_at != null ? { reapedAt: row.reaped_at } : {}),
|
|
293
280
|
...(row.tags != null ? { tags: parseStringArray(row.tags) } : {}),
|
|
281
|
+
...(row.paths != null ? { paths: parseStringArray(row.paths) } : {}),
|
|
294
282
|
...(row.priority != null ? { priority: row.priority as Task["priority"] } : {}),
|
|
295
283
|
...(row.parent_task_id != null ? { parentTaskId: row.parent_task_id } : {}),
|
|
296
284
|
...(row.backlog_state != null ? { backlogState: row.backlog_state as Task["backlogState"] } : {}),
|
|
297
285
|
// #2518 — omit false on the wire so legacy payload shapes stay byte-identical for unheld Tasks.
|
|
298
286
|
...(row.validation_items != null ? { validationItems: parseJson(row.validation_items, []) } : {}),
|
|
299
287
|
...(row.completed_at != null ? { completedAt: row.completed_at } : {}),
|
|
288
|
+
...(row.on_deploy ? { onDeploy: row.on_deploy } : {}),
|
|
300
289
|
...(row.terminal_stage != null ? { terminalStage: row.terminal_stage as Task["terminalStage"] } : {}),
|
|
301
290
|
...(row.stale_owner_gone === 1 ? { staleOwnerGone: true } : {}),
|
|
302
291
|
...(row.review_hold === 1 ? { reviewHold: true } : {}),
|
|
@@ -406,7 +395,6 @@ export function rowToActivityEvent(row: any): ActivityEvent {
|
|
|
406
395
|
};
|
|
407
396
|
}
|
|
408
397
|
|
|
409
|
-
|
|
410
398
|
export const MSG_SELECT = `SELECT m.*, (
|
|
411
399
|
SELECT json_group_array(agent_id) FROM message_reads WHERE message_id = m.id
|
|
412
400
|
) AS read_by_agents, (
|
package/src/db/merge-lease.ts
CHANGED
|
@@ -1,19 +1,5 @@
|
|
|
1
|
-
import { Database } from "bun:sqlite";
|
|
2
1
|
import { randomUUID } from "node:crypto";
|
|
3
|
-
import {
|
|
4
|
-
import { ORCHESTRATOR_PROTOCOL_VERSION, VERSION } from "../config.ts";
|
|
5
|
-
import { parseJson } from "../utils";
|
|
6
|
-
import { isLiveIsolatedWorkspace } from "../workspace-phase";
|
|
7
|
-
import {
|
|
8
|
-
CONTRACT_REQUIREMENTS,
|
|
9
|
-
contractCompatibility,
|
|
10
|
-
parseRuntimeCapabilities,
|
|
11
|
-
parseRuntimeContracts,
|
|
12
|
-
parseRuntimePackage,
|
|
13
|
-
type RuntimeContracts,
|
|
14
|
-
} from "../contracts";
|
|
15
|
-
import { STALE_TTL_MS, DAY_MS, CLAIM_LEASE_MS, POOL_CLAIM_LEASE_MS, WORKSPACE_MERGE_LEASE_MS, MAIN_MERGE_PAUSE_TTL_MS } from "../config";
|
|
16
|
-
import { matchAgents } from "../agent-ref";
|
|
2
|
+
import { WORKSPACE_MERGE_LEASE_MS, MAIN_MERGE_PAUSE_TTL_MS } from "../config";
|
|
17
3
|
import { incidentEligibleSql } from "./land-incident-eligibility";
|
|
18
4
|
import { getDb, writeTransaction } from "./connection.ts";
|
|
19
5
|
|
|
@@ -6,15 +6,18 @@ export const MESSAGE_READS_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = O
|
|
|
6
6
|
// Both writes converge on retry: INSERT OR IGNORE/read-state updates and deleting an absent row
|
|
7
7
|
// are no-ops, so a phantom-failure retry is safe without a caller key.
|
|
8
8
|
"messageReads.markRead": {
|
|
9
|
+
offLoop: true,
|
|
9
10
|
idempotent: true,
|
|
10
11
|
fn: (args: { messageId: number; agentId: string }, ctx: WriterOpContext) => markRead(args.messageId, args.agentId, ctx.now),
|
|
11
12
|
},
|
|
12
13
|
"messageReads.delete": {
|
|
14
|
+
offLoop: true,
|
|
13
15
|
idempotent: true,
|
|
14
16
|
fn: (args: { id: number }) => deleteMessage(args.id, false),
|
|
15
17
|
},
|
|
16
18
|
// #3054 — re-derives each drifted pair from the source tables at write time, so a stale snapshot is harmless.
|
|
17
19
|
"messageReads.reconcileReplyObligations": {
|
|
20
|
+
offLoop: true,
|
|
18
21
|
idempotent: true,
|
|
19
22
|
fn: (args: { pairs: Array<{ messageId: number; agentId: string }> }) => {
|
|
20
23
|
for (const { messageId, agentId } of args.pairs) reconcileReplyObligation(messageId, agentId);
|
|
@@ -52,6 +52,7 @@ export interface MessageClaimOpArgs {
|
|
|
52
52
|
|
|
53
53
|
export const MESSAGE_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze({
|
|
54
54
|
"messages.flushCoordinatorDigests": {
|
|
55
|
+
offLoop: true,
|
|
55
56
|
idempotent: true,
|
|
56
57
|
fn: (_args: unknown, ctx: WriterOpContext) => flushCoordinatorDigestsBody(ctx.now),
|
|
57
58
|
},
|
|
@@ -77,6 +78,7 @@ export const MESSAGE_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object.
|
|
|
77
78
|
// A keyed reaction row is an upsert/delete, so replaying the same request converges on the
|
|
78
79
|
// requested state. The lookup and mutation execute in the same writer transaction.
|
|
79
80
|
"messages.reaction": {
|
|
81
|
+
offLoop: true,
|
|
80
82
|
idempotent: true,
|
|
81
83
|
fn: (args: { input: Parameters<typeof setMessageReaction>[0] }, ctx: WriterOpContext) =>
|
|
82
84
|
setMessageReaction(args.input, ctx.now),
|
|
@@ -84,11 +86,13 @@ export const MESSAGE_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object.
|
|
|
84
86
|
// The expiry sweep, eligibility reads, message CAS, linked-task transition, and renewal all run
|
|
85
87
|
// in this single writer transaction. A separate release/read hop would race a concurrent claimer.
|
|
86
88
|
"messages.claim": {
|
|
89
|
+
offLoop: true,
|
|
87
90
|
idempotent: true,
|
|
88
91
|
fn: (args: MessageClaimOpArgs, ctx: WriterOpContext) =>
|
|
89
92
|
claimMessageBody(args.messageId, args.agentId, args.guard, ctx.now),
|
|
90
93
|
},
|
|
91
94
|
"messages.renewClaim": {
|
|
95
|
+
offLoop: true,
|
|
92
96
|
idempotent: true,
|
|
93
97
|
fn: (args: MessageClaimOpArgs, ctx: WriterOpContext) =>
|
|
94
98
|
renewMessageClaimBody(args.messageId, args.agentId, args.guard, ctx.now),
|
package/src/db/migrations.ts
CHANGED
|
@@ -21,7 +21,6 @@ import { backfillLegacyMessages } from "./legacy-message-backfill";
|
|
|
21
21
|
import { installHistoryDeliverySettlement } from "./history-delivery-settle";
|
|
22
22
|
import { installReplyObligations } from "./reply-obligations";
|
|
23
23
|
import { beginStartupPhase, timeStartupPhase } from "../startup-timing";
|
|
24
|
-
import { Database } from "bun:sqlite";
|
|
25
24
|
import { AGENT_EXIT_REPORT_SCHEMA } from "./agent-exit-report";
|
|
26
25
|
import { CONTEXT_RESET_SCHEMA } from "./context-reset-carry";
|
|
27
26
|
import { TASK_TOKEN_TOTALS_SCHEMA } from "./token-usage";
|
|
@@ -30,20 +29,8 @@ import { migrateOperatorActionsSchema } from "./operator-actions-schema";
|
|
|
30
29
|
import { SELF_EFFORT_PERMISSIONS_SCHEMA } from "./self-effort-permissions";
|
|
31
30
|
import { AGENT_EFFORT_STATE_SCHEMA, importLegacyEffortCommands } from "./agent-effort-state";
|
|
32
31
|
import { randomUUID } from "node:crypto";
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
35
|
-
import { parseJson } from "../utils";
|
|
36
|
-
import { isLiveIsolatedWorkspace } from "../workspace-phase";
|
|
37
|
-
import {
|
|
38
|
-
CONTRACT_REQUIREMENTS,
|
|
39
|
-
contractCompatibility,
|
|
40
|
-
parseRuntimeCapabilities,
|
|
41
|
-
parseRuntimeContracts,
|
|
42
|
-
parseRuntimePackage,
|
|
43
|
-
type RuntimeContracts,
|
|
44
|
-
} from "../contracts";
|
|
45
|
-
import { STALE_TTL_MS, DAY_MS, CLAIM_LEASE_MS, POOL_CLAIM_LEASE_MS, WORKSPACE_MERGE_LEASE_MS } from "../config";
|
|
46
|
-
import { matchAgents } from "../agent-ref";
|
|
32
|
+
import { PROJECT_PROFILE_INSTRUCTIONS_SCHEMA_SQL } from "./project-profile-instructions";
|
|
33
|
+
import { CLAIM_LEASE_MS } from "../config";
|
|
47
34
|
import { COORDINATOR_PROFILE_NAMES } from "../agent-system-tags";
|
|
48
35
|
import { getDb } from "./connection.ts";
|
|
49
36
|
import { normalizeExistingAgentSpawnRequestIds } from "./spawn-request-id-migration.ts";
|
|
@@ -154,6 +141,7 @@ function backfillProjectRootRemoteUrls(): void {
|
|
|
154
141
|
// shape, so they must run in historical order. Built-in agent seeding and the
|
|
155
142
|
// read_by backfill live here too — both run unconditionally and idempotently.
|
|
156
143
|
export function applyMigrations(): void {
|
|
144
|
+
getDb().run(PROJECT_PROFILE_INSTRUCTIONS_SCHEMA_SQL);
|
|
157
145
|
getDb().run(PLAN_IMPORT_SCHEMA_SQL);
|
|
158
146
|
ensureVaultTokensSchema();
|
|
159
147
|
ensureVaultHealthSchema();
|
|
@@ -1281,6 +1269,7 @@ export function applyMigrations(): void {
|
|
|
1281
1269
|
// addTaskCol guard as the rest of the #834 batch: existing rows read back as absent.
|
|
1282
1270
|
addTaskCol("tags", "tags TEXT");
|
|
1283
1271
|
addTaskCol("priority", "priority TEXT");
|
|
1272
|
+
addTaskCol("paths", "paths TEXT"); // #3736
|
|
1284
1273
|
// #1535 WS1 — parent/epic tree edge. Self-referential FK via ALTER TABLE ADD COLUMN is
|
|
1285
1274
|
// proven safe in this codebase already (messages.reply_to above); cycle-guarding lives
|
|
1286
1275
|
// in application code (task-entity.ts), not a DB constraint.
|
|
@@ -1294,10 +1283,13 @@ export function applyMigrations(): void {
|
|
|
1294
1283
|
addTaskCol("review_hold", "review_hold INTEGER NOT NULL DEFAULT 0 CHECK (review_hold IN (0, 1))");
|
|
1295
1284
|
addTaskCol("validation_items", "validation_items TEXT");
|
|
1296
1285
|
addTaskCol("terminal_stage", "terminal_stage TEXT");
|
|
1286
|
+
addTaskCol("on_deploy", "on_deploy TEXT");
|
|
1297
1287
|
addTaskCol("completed_at", "completed_at INTEGER");
|
|
1298
1288
|
addTaskCol("owner", "owner TEXT"); // #2862 — backfilled by backfillTaskOwners below
|
|
1299
1289
|
migratePlanBlurbsOnStartup(); // #2082 S5 — fold legacy bound-node blurbs before stripping them
|
|
1300
1290
|
getDb().run("CREATE INDEX IF NOT EXISTS idx_tasks_message_status ON tasks(message_id, status) WHERE message_id IS NOT NULL");
|
|
1291
|
+
// #3740: existing databases need the same covering expression index as fresh schemas.
|
|
1292
|
+
getDb().run("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)");
|
|
1301
1293
|
getDb().run("CREATE INDEX IF NOT EXISTS idx_tasks_kind ON tasks(kind, status, updated_at)");
|
|
1302
1294
|
getDb().run("CREATE INDEX IF NOT EXISTS idx_tasks_branch ON tasks(branch) WHERE branch IS NOT NULL");
|
|
1303
1295
|
getDb().run("CREATE INDEX IF NOT EXISTS idx_tasks_team ON tasks(team_id) WHERE team_id IS NOT NULL");
|
|
@@ -4,13 +4,17 @@ import { getGateway } from "./gateway.ts";
|
|
|
4
4
|
import { coalesceLifecycleWrite } from "./lifecycle-write-window.ts";
|
|
5
5
|
import { nextLifecycleSeq } from "./lifecycle-seq.ts";
|
|
6
6
|
import { evictPendingPresence } from "./presence-buffer.ts";
|
|
7
|
-
import type { getOrchestrator, listOrchestrators, OrchestratorRosterResult, upsertOrchestrator } from "./orchestrators.ts";
|
|
7
|
+
import type { getOrchestrator, listOrchestrators, OrchestratorRosterResult, registerOrchestratorOp, upsertOrchestrator } from "./orchestrators.ts";
|
|
8
|
+
import { finishAgentUpsertAsync } from "./agents-async.ts";
|
|
8
9
|
import { emitWorkspaceUpsert } from "./workspaces.ts";
|
|
9
10
|
import type { ManagedAgent, OrchestratorRuntimeInput, OrchestratorUpgradeState, QueuedSpawn } from "../types.ts";
|
|
10
11
|
import { STALE_TTL_MS } from "../config.ts";
|
|
11
12
|
|
|
12
|
-
export function upsertOrchestratorAsync(input: Parameters<typeof upsertOrchestrator>[0]): Promise<ReturnType<typeof upsertOrchestrator>> {
|
|
13
|
-
|
|
13
|
+
export async function upsertOrchestratorAsync(input: Parameters<typeof upsertOrchestrator>[0]): Promise<ReturnType<typeof upsertOrchestrator>> {
|
|
14
|
+
const seq = nextLifecycleSeq(); // before the await, per the ordinal-timing rule (lifecycle-seq.ts)
|
|
15
|
+
const { orchestrator, agent, contextState } = await getGateway().write("orchestrators.register", { input, seq }, { critical: true }) as unknown as ReturnType<typeof registerOrchestratorOp>;
|
|
16
|
+
await finishAgentUpsertAsync(agent.id, { agent, contextState });
|
|
17
|
+
return orchestrator;
|
|
14
18
|
}
|
|
15
19
|
|
|
16
20
|
export function getOrchestratorAsync(id: string): Promise<ReturnType<typeof getOrchestrator>> {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import {
|
|
4
4
|
orchestratorHeartbeatInternal,
|
|
5
5
|
reapStaleOrchestratorsInternal,
|
|
6
|
+
registerOrchestratorOp,
|
|
6
7
|
reportOrchestratorRosterRow,
|
|
7
8
|
setOrchestratorUpgradeState,
|
|
8
9
|
upsertOrchestrator,
|
|
@@ -14,10 +15,13 @@ import type { WriterOpContext, WriterOpSpec } from "./write-ops.ts";
|
|
|
14
15
|
|
|
15
16
|
export const ORCHESTRATORS_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze({
|
|
16
17
|
"orchestrators.register": {
|
|
18
|
+
offLoop: true,
|
|
17
19
|
idempotent: true,
|
|
18
|
-
|
|
20
|
+
// The agent row's follow-ups (presence eviction, pool binding, steward election) run in the facade.
|
|
21
|
+
fn: (a: { input: Parameters<typeof upsertOrchestrator>[0]; seq: number }, ctx) => registerOrchestratorOp(a.input, ctx.now, a.seq),
|
|
19
22
|
},
|
|
20
23
|
"orchestrators.heartbeat": {
|
|
24
|
+
offLoop: true,
|
|
21
25
|
idempotent: true,
|
|
22
26
|
fn: (a: { id: string; runtime: OrchestratorRuntimeInput; seq?: number }, ctx) => {
|
|
23
27
|
const orchestrator = orchestratorHeartbeatInternal(a.id, a.runtime, ctx.now);
|
|
@@ -42,10 +46,12 @@ export const ORCHESTRATORS_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = O
|
|
|
42
46
|
fn: (a: { id: string; agents: ManagedAgent[]; queuedSpawns?: QueuedSpawn[] }, ctx) => reportOrchestratorRosterRow(a.id, a.agents, a.queuedSpawns, ctx.now),
|
|
43
47
|
},
|
|
44
48
|
"orchestrators.upgrade.set": {
|
|
49
|
+
offLoop: true,
|
|
45
50
|
idempotent: true,
|
|
46
51
|
fn: (a: { id: string; state: OrchestratorUpgradeState | null }) => setOrchestratorUpgradeState(a.id, a.state),
|
|
47
52
|
},
|
|
48
53
|
"orchestrators.reap": {
|
|
54
|
+
offLoop: true,
|
|
49
55
|
idempotent: true,
|
|
50
56
|
fn: (a: { cutoff: number; seq: number }, ctx) => reapStaleOrchestratorsInternal(ctx.now - STALE_TTL_MS, a.seq),
|
|
51
57
|
},
|