@humwick/server 0.130.1 → 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 +4 -0
- 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-B_rFEbnL.js → fleet-C7jQWbw2.js} +2 -2
- 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 +8 -0
- 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 +3 -3
- 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 +12 -1
- package/src/db/activity.ts +1 -14
- package/src/db/agent-search.ts +1 -15
- package/src/db/agents-async.ts +52 -5
- package/src/db/agents-write-ops.ts +12 -3
- package/src/db/agents.ts +81 -54
- package/src/db/ci-incidents-async.ts +5 -5
- package/src/db/ci-incidents-write-ops.ts +2 -2
- 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/config-store-write-ops.ts +7 -2
- package/src/db/continuation-archives-async.ts +5 -0
- package/src/db/continuation-archives-write-ops.ts +7 -0
- package/src/db/continuations.ts +2 -2
- package/src/db/db-writer-worker.ts +8 -1
- 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.ts +9 -6
- package/src/db/inbox.ts +0 -14
- package/src/db/index.ts +2 -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/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/migrations.ts +7 -15
- package/src/db/orchestrators.ts +4 -72
- package/src/db/pairs.ts +1 -13
- package/src/db/project-profile-instructions.ts +50 -0
- package/src/db/prompt-versions.ts +9 -2
- package/src/db/provider-config-history.ts +8 -3
- package/src/db/provider-model-seed.ts +34 -0
- package/src/db/read-op-marshaling-fixtures.ts +5 -0
- package/src/db/read-ops.ts +4 -0
- package/src/db/routing-policy.ts +2 -3
- package/src/db/schema.ts +9 -3
- package/src/db/spawn-delegation-state.ts +11 -4
- 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/timer-ingress.ts +7 -1
- package/src/db/token-db-write-ops.ts +17 -0
- package/src/db/token-standing.ts +15 -11
- package/src/db/workflow-contact.ts +1 -1
- package/src/db/workflow-task-read-ops.ts +30 -0
- package/src/db/write-ops.ts +6 -0
- package/src/db/writer-gateway.ts +17 -11
- 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-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/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/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 +1 -1
- 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 +2 -2
- package/src/routes/project-profile-instructions.ts +27 -0
- package/src/routes/project-trust.ts +2 -2
- package/src/routes/provider-config.ts +6 -6
- 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 +39 -7
- 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 +10 -7
- package/src/services/kernel-timers-ingress.ts +1 -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 -39
- 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 -42
- 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/index-B7ZerHLY.css +0 -2
- package/public/assets/index-BU3uWo2t.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
package/src/db/agents.ts
CHANGED
|
@@ -479,28 +479,51 @@ export function applyAgentLifecycleUpdate(
|
|
|
479
479
|
guard?: AgentSessionGuard,
|
|
480
480
|
now: number = Date.now(),
|
|
481
481
|
): AgentLifecycleUpdateResult {
|
|
482
|
-
const
|
|
483
|
-
const before =
|
|
484
|
-
if (!before) return
|
|
485
|
-
|
|
482
|
+
const seq = nextLifecycleSeq();
|
|
483
|
+
const before = prepareAgentLifecycleUpdate(id, update, guard);
|
|
484
|
+
if (!before) return { ok: false, changed: false, metaWriteBytes: 0, before: null, after: null };
|
|
485
|
+
return { ok: true, before, ...inWriteTransaction(() => applyAgentLifecycleUpdateInternal(agentLifecycleWriteArgs(before, update, seq, now))) };
|
|
486
|
+
}
|
|
486
487
|
|
|
488
|
+
/** The writer-op args for one lifecycle delta (#1870 S7). `seq` is taken at the caller's entry (lifecycle-seq.ts
|
|
489
|
+
* rule 1); `expectedEpoch` fences the write to the instance `before` was read from across the writer hop. */
|
|
490
|
+
export interface AgentLifecycleWriteArgs {
|
|
491
|
+
id: string;
|
|
492
|
+
update: AgentLifecycleUpdate;
|
|
493
|
+
seq: number;
|
|
494
|
+
now: number;
|
|
495
|
+
expectedEpoch: number;
|
|
496
|
+
before: { instanceId?: string; spawnRequestId: string; terminalProviderExit?: unknown };
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
export function agentLifecycleWriteArgs(before: AgentCard, update: AgentLifecycleUpdate, seq: number, now: number): AgentLifecycleWriteArgs {
|
|
500
|
+
return { id: before.id, update, seq, now, expectedEpoch: before.epoch, before: { instanceId: before.instanceId, spawnRequestId: String(before.meta?.spawnRequestId ?? ""), terminalProviderExit: before.meta?.terminalProviderExit } };
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/** The main-thread half of a lifecycle delta: guard, `before` snapshot, transition handlers and presence
|
|
504
|
+
* eviction (main-thread state), all before the write is submitted. Null when the guard rejects. */
|
|
505
|
+
export function prepareAgentLifecycleUpdate(id: string, update: AgentLifecycleUpdate, guard?: AgentSessionGuard): AgentCard | null {
|
|
506
|
+
const before = getPersistedAgentRuntimeCard(id);
|
|
507
|
+
if (!before || !agentSessionGuardResult(before, guard).ok) return null;
|
|
487
508
|
const nextStatus = update.status;
|
|
488
|
-
// Pre-transition handlers
|
|
489
|
-
// and must run BEFORE the write lands, exactly as `setStatus` does.
|
|
509
|
+
// Pre-transition handlers must run BEFORE the write lands, exactly as `setStatus` does.
|
|
490
510
|
if (nextStatus !== undefined && nextStatus !== before.status) runBeforeAgentStatusTransitionHandlers(before, nextStatus);
|
|
491
511
|
// #1468 — a terminal transition evicts any buffered beat so a coalesced advance cannot later
|
|
492
512
|
// resurrect a gone agent at flush.
|
|
493
513
|
if (nextStatus === "offline" || nextStatus === "stale") evictPendingPresence(id);
|
|
514
|
+
return before;
|
|
515
|
+
}
|
|
494
516
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
const seq =
|
|
517
|
+
/** Op body (`agents.applyLifecycleUpdate`): the fenced UPDATE, its effects, and the offline post-commit, in the
|
|
518
|
+
* caller's transaction. #2111 review BLOCKER 1 — each written column's ordinal is raised, so a lifecycle write
|
|
519
|
+
* the coalescing window deferred cannot commit afterwards and undo it. */
|
|
520
|
+
export function applyAgentLifecycleUpdateInternal(args: AgentLifecycleWriteArgs): Omit<AgentLifecycleUpdateResult, "ok" | "before"> {
|
|
521
|
+
const { id, update, seq, now, before } = args;
|
|
522
|
+
const nextStatus = update.status;
|
|
500
523
|
// Named params throughout: the fenced-column fragments repeat `$seq`, which positional `?` could
|
|
501
524
|
// only express by counting placeholders across a dynamically-built SET list.
|
|
502
525
|
const sets: string[] = ["last_seen = $now"];
|
|
503
|
-
const params: Record<string, unknown> = { $now: now, $seq: seq, $id: id };
|
|
526
|
+
const params: Record<string, unknown> = { $now: now, $seq: seq, $id: id, $epoch: args.expectedEpoch };
|
|
504
527
|
let metaWriteBytes = 0;
|
|
505
528
|
if (nextStatus !== undefined) {
|
|
506
529
|
sets.push(fencedColumnSql("status", "$status", "$seq"));
|
|
@@ -558,16 +581,10 @@ export function applyAgentLifecycleUpdate(
|
|
|
558
581
|
params.$qs = runtime?.quota ? JSON.stringify(runtime.quota) : null;
|
|
559
582
|
}
|
|
560
583
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
const terminalLaunch = terminalProviderLaunchId(update.meta?.terminalProviderExit ?? before.meta?.terminalProviderExit);
|
|
566
|
-
if (applied && nextStatus === "offline" && terminalLaunch) revokeVaultTokens(id, now, "provider_terminal_exit", before.instanceId, String(before.meta?.spawnRequestId ?? ""), terminalLaunch);
|
|
567
|
-
if (applied && contextState) recordContextSnapshot(id, contextState, now);
|
|
568
|
-
return applied;
|
|
569
|
-
});
|
|
570
|
-
|
|
584
|
+
const changed = getDb().query(`UPDATE agents SET ${sets.join(", ")} WHERE id = $id AND epoch = $epoch`).run(params as never).changes > 0;
|
|
585
|
+
const terminalLaunch = terminalProviderLaunchId(update.meta?.terminalProviderExit ?? before.terminalProviderExit);
|
|
586
|
+
if (changed && nextStatus === "offline" && terminalLaunch) revokeVaultTokens(id, now, "provider_terminal_exit", before.instanceId, before.spawnRequestId, terminalLaunch);
|
|
587
|
+
if (changed && contextState) recordContextSnapshot(id, contextState, now);
|
|
571
588
|
// NOTE (from setStatus): `offline` is a LIVENESS observation, not a confirmed death — it re-elects
|
|
572
589
|
// stewards and closes pairs but must NOT release a steward's workspace claim (#1789 part 2, F2).
|
|
573
590
|
// Gated on the requested status, not on a status CHANGE, matching setStatus's own condition.
|
|
@@ -575,7 +592,7 @@ export function applyAgentLifecycleUpdate(
|
|
|
575
592
|
closeOpenPairsForAgent(id, now);
|
|
576
593
|
electWorkspaceStewards();
|
|
577
594
|
}
|
|
578
|
-
return {
|
|
595
|
+
return { changed, metaWriteBytes, after: getPersistedAgentRuntimeCard(id) };
|
|
579
596
|
}
|
|
580
597
|
|
|
581
598
|
export function mergeAgentMeta(id: string, meta: Record<string, unknown>, guard?: AgentSessionGuard, now: number = Date.now()): boolean {
|
|
@@ -828,21 +845,27 @@ export function heartbeat(
|
|
|
828
845
|
// withBufferedPresence / bufferedLiveAgentIds) live in the leaf module ./presence-buffer.ts so every
|
|
829
846
|
// liveness reader can fold in the buffer without an import cycle (agents.ts imports channels.ts).
|
|
830
847
|
|
|
848
|
+
/** Heartbeat: buffers the steady-state liveness advance (flushed by `flushPresence`), writes presence-critical
|
|
849
|
+
* transitions at once. False if the session guard rejects or the agent is gone. Sync twin for tests; the
|
|
850
|
+
* transports use `recordHeartbeatAsync`, which writes the bypass on the writer thread (#1870 S7). */
|
|
851
|
+
export function recordHeartbeat(id: string, opts: HeartbeatInput = {}): boolean {
|
|
852
|
+
const beat = bufferOrBypassHeartbeat(id, opts);
|
|
853
|
+
return typeof beat === "boolean" ? beat : applyAgentLifecycleUpdate(id, beat, opts.guard, opts.now ?? Date.now()).changed;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
export interface HeartbeatInput {
|
|
857
|
+
guard?: AgentSessionGuard;
|
|
858
|
+
/** Semantic status reported by the beat (bus path); undefined for the runtime-less API path. */
|
|
859
|
+
status?: AgentCard["status"];
|
|
860
|
+
runtime?: { providerCapabilities?: ProviderCapabilities; context?: ContextState; quota?: QuotaState };
|
|
861
|
+
now?: number;
|
|
862
|
+
}
|
|
863
|
+
|
|
831
864
|
/**
|
|
832
|
-
*
|
|
833
|
-
*
|
|
834
|
-
* presence-critical transitions. Returns false if the session guard rejects or the agent is gone.
|
|
865
|
+
* The shared decision of `recordHeartbeat` and `recordHeartbeatAsync`: buffer a steady-state beat (returns
|
|
866
|
+
* the result) or hand back the lifecycle delta a presence-critical beat must write now.
|
|
835
867
|
*/
|
|
836
|
-
export function
|
|
837
|
-
id: string,
|
|
838
|
-
opts: {
|
|
839
|
-
guard?: AgentSessionGuard;
|
|
840
|
-
/** Semantic status reported by the beat (bus path); undefined for the runtime-less API path. */
|
|
841
|
-
status?: AgentCard["status"];
|
|
842
|
-
runtime?: { providerCapabilities?: ProviderCapabilities; context?: ContextState; quota?: QuotaState };
|
|
843
|
-
now?: number;
|
|
844
|
-
} = {},
|
|
845
|
-
): boolean {
|
|
868
|
+
export function bufferOrBypassHeartbeat(id: string, opts: HeartbeatInput): boolean | AgentLifecycleUpdate {
|
|
846
869
|
if (!validateAgentSession(id, opts.guard).ok) return false;
|
|
847
870
|
const current = getAgent(id);
|
|
848
871
|
if (!current) return false;
|
|
@@ -861,9 +884,9 @@ export function recordHeartbeat(
|
|
|
861
884
|
opts.runtime.context.lifecycleState !== current.context?.lifecycleState;
|
|
862
885
|
const prev = pendingPresence.get(id);
|
|
863
886
|
if (resurrection || statusChanged || lifecycleTransition) {
|
|
864
|
-
// Merge any buffered-but-unflushed runtime cols into this
|
|
865
|
-
//
|
|
866
|
-
//
|
|
887
|
+
// Merge any buffered-but-unflushed runtime cols into this write so the bypass never DISCARDS a
|
|
888
|
+
// coalesced advance (a beat that carried context but was buffered, then a status change arrives
|
|
889
|
+
// carrying none). Per column: this beat wins, else the pending value.
|
|
867
890
|
const runtime = prev
|
|
868
891
|
? {
|
|
869
892
|
providerCapabilities: opts.runtime?.providerCapabilities ?? prev.providerCapabilities,
|
|
@@ -871,16 +894,10 @@ export function recordHeartbeat(
|
|
|
871
894
|
quota: opts.runtime?.quota ?? prev.quota,
|
|
872
895
|
}
|
|
873
896
|
: opts.runtime;
|
|
874
|
-
pendingPresence.delete(id); // this
|
|
875
|
-
// #2111 —
|
|
876
|
-
//
|
|
877
|
-
|
|
878
|
-
return applyAgentLifecycleUpdate(
|
|
879
|
-
id,
|
|
880
|
-
{ status: statusChanged ? opts.status : undefined, resurrectOnBeat: true, runtime },
|
|
881
|
-
opts.guard,
|
|
882
|
-
now,
|
|
883
|
-
).changed;
|
|
897
|
+
pendingPresence.delete(id); // this write supersedes any buffered advance
|
|
898
|
+
// #2111 — one consolidated update carries both the beat and the explicit status, which overrides
|
|
899
|
+
// the resurrection CASE.
|
|
900
|
+
return { status: statusChanged ? opts.status : undefined, resurrectOnBeat: true, runtime };
|
|
884
901
|
}
|
|
885
902
|
|
|
886
903
|
// Steady state: buffer. Merge context advisory markers now (matches heartbeat()'s per-beat merge),
|
|
@@ -1192,6 +1209,10 @@ export function deleteAgent(id: string): { ok: boolean; error?: string } {
|
|
|
1192
1209
|
return deleted ? { ok: true } : { ok: false, error: "agent not found" };
|
|
1193
1210
|
}
|
|
1194
1211
|
|
|
1212
|
+
export type OrphanedTokenRevocation = { agentId: string; jtis: string[] };
|
|
1213
|
+
/** One page of the orphan sweep: agents after `afterId` in id order, up to `limit` (#3776: one short write each). */
|
|
1214
|
+
export type OrphanedTokenPage = { revoked: OrphanedTokenRevocation[]; nextAfterId: string | null };
|
|
1215
|
+
|
|
1195
1216
|
// #1057(1) — close the orphan-token window #1051 left open. A dead runner with no replacement
|
|
1196
1217
|
// used to keep its token valid until the 24h offline prune (~720× the pre-#1051 ~2min horizon).
|
|
1197
1218
|
// This sweep revokes ("stale", re-mintable — #484) the runtime tokens of agents whose process the
|
|
@@ -1204,12 +1225,18 @@ export function deleteAgent(id: string): { ok: boolean; error?: string } {
|
|
|
1204
1225
|
// `lagGraceMs` mirrors the stale reaper's #1048/#1055 discipline: while the relay's own event
|
|
1205
1226
|
// loop was stalled, a live claimant's heartbeats may not have been processed yet, so the window
|
|
1206
1227
|
// widens by the observed lag rather than misreading the backlog as post-mortem silence.
|
|
1207
|
-
export function revokeOrphanedRuntimeTokens(
|
|
1228
|
+
export function revokeOrphanedRuntimeTokens(
|
|
1229
|
+
graceMs: number = STALE_TTL_MS,
|
|
1230
|
+
lagGraceMs = 0,
|
|
1231
|
+
now = Date.now(),
|
|
1232
|
+
page: { afterId?: string; limit?: number } = {},
|
|
1233
|
+
): OrphanedTokenPage {
|
|
1208
1234
|
const cutoff = now - graceMs - Math.max(0, lagGraceMs);
|
|
1235
|
+
const limit = page.limit ?? -1;
|
|
1209
1236
|
const rows = getDb()
|
|
1210
|
-
.query(`SELECT id, meta, last_seen FROM agents WHERE status = 'offline' AND meta LIKE '%lastExitReceivedAt%' AND NOT ${reservedAgentIdSql()}
|
|
1211
|
-
.all() as Array<{ id: string; meta: string | null; last_seen: number }>;
|
|
1212
|
-
const revoked:
|
|
1237
|
+
.query(`SELECT id, meta, last_seen FROM agents WHERE status = 'offline' AND meta LIKE '%lastExitReceivedAt%' AND NOT ${reservedAgentIdSql()} AND id > ? ORDER BY id LIMIT ?`)
|
|
1238
|
+
.all(page.afterId ?? "", limit) as Array<{ id: string; meta: string | null; last_seen: number }>;
|
|
1239
|
+
const revoked: OrphanedTokenRevocation[] = [];
|
|
1213
1240
|
for (const row of rows) {
|
|
1214
1241
|
const meta = parseJson<Record<string, unknown>>(row.meta ?? "{}", {});
|
|
1215
1242
|
const receivedAt = typeof meta.lastExitReceivedAt === "number" ? meta.lastExitReceivedAt : undefined;
|
|
@@ -1218,7 +1245,7 @@ export function revokeOrphanedRuntimeTokens(graceMs: number = STALE_TTL_MS, lagG
|
|
|
1218
1245
|
const jtis = revokeProviderRuntimeTokensByJti(runtimeTokenJtisFromMeta(meta), "stale", now);
|
|
1219
1246
|
if (jtis.length > 0) revoked.push({ agentId: row.id, jtis });
|
|
1220
1247
|
}
|
|
1221
|
-
return revoked;
|
|
1248
|
+
return { revoked, nextAfterId: limit > 0 && rows.length === limit ? rows[rows.length - 1]!.id : null };
|
|
1222
1249
|
}
|
|
1223
1250
|
|
|
1224
1251
|
// #484: `reason` is stamped on each revocation so a stale-on-restart revocation
|
|
@@ -35,15 +35,15 @@ const write = <T>(op: string, args: unknown, idempotencyKey?: string): Promise<T
|
|
|
35
35
|
export function upsertWatchStateAsync(input: Parameters<typeof upsertWatchState>[0]): Promise<ReturnType<typeof upsertWatchState>> {
|
|
36
36
|
return write("ciIncidents.watch.upsert", input);
|
|
37
37
|
}
|
|
38
|
-
/** One gateway writer transaction for the complete watch + incident lifecycle decision. */
|
|
39
|
-
export function ingestCiObservationTransitionAsync(input: CiObservationTransitionInput): Promise<CiObservationTransitionResult> {
|
|
40
|
-
return write("ciIncidents.observation.ingest", input);
|
|
38
|
+
/** One gateway writer transaction for the complete watch + incident lifecycle decision. `now` pins a test clock. */
|
|
39
|
+
export function ingestCiObservationTransitionAsync(input: CiObservationTransitionInput, now?: number): Promise<CiObservationTransitionResult> {
|
|
40
|
+
return write("ciIncidents.observation.ingest", now === undefined ? input : { ...input, now });
|
|
41
41
|
}
|
|
42
42
|
export function markCiWatchNeverMatchedNotifiedAsync(watchKey: string): Promise<ReturnType<typeof markCiWatchNeverMatchedNotified>> {
|
|
43
43
|
return write("ciIncidents.watch.markNeverMatched", { watchKey });
|
|
44
44
|
}
|
|
45
|
-
export function markCiRootMissingNotifiedAsync(repository: string): Promise<ReturnType<typeof markCiRootMissingNotified>> {
|
|
46
|
-
return write("ciIncidents.root.markMissing", { repository });
|
|
45
|
+
export function markCiRootMissingNotifiedAsync(repository: string, now?: number): Promise<ReturnType<typeof markCiRootMissingNotified>> {
|
|
46
|
+
return write("ciIncidents.root.markMissing", now === undefined ? { repository } : { repository, now });
|
|
47
47
|
}
|
|
48
48
|
export function openOrDedupeIncidentAsync(input: Parameters<typeof openOrDedupeIncident>[0]): Promise<ReturnType<typeof openOrDedupeIncident>> {
|
|
49
49
|
return write("ciIncidents.openOrDedupe", input);
|
|
@@ -27,7 +27,7 @@ export const CI_INCIDENTS_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Ob
|
|
|
27
27
|
"ciIncidents.observation.ingest": {
|
|
28
28
|
offLoop: true,
|
|
29
29
|
idempotent: true,
|
|
30
|
-
fn: (a: Parameters<typeof applyCiObservationTransitionInternal>[0], ctx) => applyCiObservationTransitionInternal(a, ctx.now),
|
|
30
|
+
fn: (a: Parameters<typeof applyCiObservationTransitionInternal>[0] & { now?: number }, ctx) => applyCiObservationTransitionInternal(a, a.now ?? ctx.now),
|
|
31
31
|
},
|
|
32
32
|
"ciIncidents.gateRun.record": { offLoop: true, idempotent: true, fn: (a: Parameters<typeof recordCiGateRuns>[0]) => recordCiGateRuns(a) },
|
|
33
33
|
"ciIncidents.gateBackfill.start": { offLoop: true, idempotent: true, fn: (a: Parameters<typeof startCiGateBackfill>[0]) => startCiGateBackfill(a) },
|
|
@@ -36,7 +36,7 @@ export const CI_INCIDENTS_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Ob
|
|
|
36
36
|
"ciIncidents.sha.prune": { offLoop: true, idempotent: true, fn: (_a: { now: number }, ctx) => pruneCiShaObservations(ctx.now) },
|
|
37
37
|
"ciIncidents.watch.upsert": { offLoop: true, idempotent: true, fn: (a: Parameters<typeof upsertWatchState>[0], ctx) => upsertWatchState(a, ctx.now) },
|
|
38
38
|
"ciIncidents.watch.markNeverMatched": { offLoop: true, idempotent: true, fn: (a: { watchKey: string }, ctx) => markCiWatchNeverMatchedNotified(a.watchKey, ctx.now) },
|
|
39
|
-
"ciIncidents.root.markMissing": { offLoop: true, idempotent: true, fn: (a: { repository: string }, ctx) => markCiRootMissingNotified(a.repository, ctx.now) },
|
|
39
|
+
"ciIncidents.root.markMissing": { offLoop: true, idempotent: true, fn: (a: { repository: string; now?: number }, ctx) => markCiRootMissingNotified(a.repository, a.now ?? ctx.now) },
|
|
40
40
|
"ciIncidents.openOrDedupe": { offLoop: true, idempotent: true, fn: (a: Parameters<typeof openOrDedupeIncidentInternal>[0], ctx) => openOrDedupeIncidentInternal(a, ctx.now, false) },
|
|
41
41
|
// Guarded transitions throw after they have advanced; use the durable retry ledger rather than
|
|
42
42
|
// misclassifying that as safe replay. Distinct steward attempts carry distinct request ids.
|
package/src/db/command-row.ts
CHANGED
|
@@ -41,13 +41,15 @@ export interface CommandRow {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
/** `slim` (#3391): listings omit `params.agentProfile` (~0.5 MB per spawn row) instead of hydrating it;
|
|
45
|
+
* `GET /api/commands/:id` returns the full row. */
|
|
46
|
+
export function rowToCommand(row: CommandRow, options: { slim?: boolean } = {}): Command {
|
|
45
47
|
return {
|
|
46
48
|
id: row.id,
|
|
47
49
|
type: row.type,
|
|
48
50
|
source: row.source,
|
|
49
51
|
target: row.target,
|
|
50
|
-
params: hydrateCommandAgentProfile(parseObject(row.params), row.agent_profile_hash),
|
|
52
|
+
params: options.slim ? withoutAgentProfile(parseObject(row.params)) : hydrateCommandAgentProfile(parseObject(row.params), row.agent_profile_hash),
|
|
51
53
|
status: row.status,
|
|
52
54
|
result: row.result ? parseObject(row.result) : undefined,
|
|
53
55
|
error: row.error ?? undefined,
|
|
@@ -78,6 +80,12 @@ export function rowToCommand(row: CommandRow): Command {
|
|
|
78
80
|
};
|
|
79
81
|
}
|
|
80
82
|
|
|
83
|
+
function withoutAgentProfile(params: Record<string, unknown>): Record<string, unknown> {
|
|
84
|
+
if (!("agentProfile" in params)) return params;
|
|
85
|
+
const { agentProfile: _omitted, ...rest } = params;
|
|
86
|
+
return rest;
|
|
87
|
+
}
|
|
88
|
+
|
|
81
89
|
function parseObject(raw: string): Record<string, unknown> {
|
|
82
90
|
try {
|
|
83
91
|
const parsed = JSON.parse(raw);
|
|
@@ -9,6 +9,8 @@ export interface CommandFilters {
|
|
|
9
9
|
dispatchableOnly?: boolean;
|
|
10
10
|
/** #2663 — oldest-first for the executor DISPATCH feed, whose window is capped. See `getCommands`. */
|
|
11
11
|
oldestFirst?: boolean;
|
|
12
|
+
/** #3391 — omit `params.agentProfile` from rows (listing views; fetch the row by id for the profile). */
|
|
13
|
+
slim?: boolean;
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
export function buildListCommandsQuery(filters: CommandFilters = {}): { sql: string; params: unknown[] } {
|
|
@@ -29,5 +29,5 @@ export const IN_FLIGHT_SPAWNS_BY_PARENT_SQL = `SELECT * FROM commands INDEXED BY
|
|
|
29
29
|
/** One parent's agent.spawn commands still in flight, newest first. */
|
|
30
30
|
export function listInFlightSpawnCommands(parentId: string, limit = 500): Command[] {
|
|
31
31
|
const rows = getDb().query(IN_FLIGHT_SPAWNS_BY_PARENT_SQL).all(...SPAWN_IN_FLIGHT_STATUSES, parentId, limit) as CommandRow[];
|
|
32
|
-
return rows.map(rowToCommand);
|
|
32
|
+
return rows.map((row) => rowToCommand(row));
|
|
33
33
|
}
|
|
@@ -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
|
|
17
|
-
//
|
|
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. */
|
|
@@ -46,6 +47,10 @@ export const CONFIG_STORE_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Ob
|
|
|
46
47
|
offLoop: true,
|
|
47
48
|
idempotent: false,
|
|
48
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
|
+
},
|
|
49
54
|
fn: (args: SetConfigOpArgs, ctx: WriterOpContext): ConfigEntry =>
|
|
50
55
|
setConfigInternal(args.namespace, args.key, args.value, args.updatedBy, ctx.now),
|
|
51
56
|
},
|
|
@@ -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,4 +1,5 @@
|
|
|
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({
|
|
@@ -14,4 +15,10 @@ export const CONTINUATION_ARCHIVES_WRITE_OPS: Readonly<Record<string, WriterOpSp
|
|
|
14
15
|
fn: (args: { input: Parameters<typeof archiveAndAdvanceContinuation>[0] }, ctx: WriterOpContext) =>
|
|
15
16
|
archiveAndAdvanceContinuation({ ...args.input, now: args.input.now ?? ctx.now }),
|
|
16
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
|
+
},
|
|
17
24
|
});
|
package/src/db/continuations.ts
CHANGED
|
@@ -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),
|
|
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(
|
|
43
|
+
conn.run(`PRAGMA busy_timeout = ${writerBusyTimeoutMs()}`);
|
|
37
44
|
setDb(conn, dbPath);
|
|
38
45
|
}
|
|
39
46
|
|
|
@@ -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),
|
package/src/db/deploy-events.ts
CHANGED
|
@@ -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:
|
|
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
|
-
|
|
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>;
|
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
|
|
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 {
|
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";
|