@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
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
type PruneExpiredTokenRecordsInput,
|
|
26
26
|
} from "../token-db.ts";
|
|
27
27
|
import type { TokenRecord } from "../types.ts";
|
|
28
|
+
import { recordLaunchFact, type LaunchFact } from "./launch-facts.ts";
|
|
28
29
|
import type { TokenRevokeReason } from "../security.ts";
|
|
29
30
|
import type { WriterOpContext, WriterOpSpec } from "./write-ops.ts";
|
|
30
31
|
|
|
@@ -41,6 +42,11 @@ export interface HandoffTokenOpArgs {
|
|
|
41
42
|
readonly newJti: string;
|
|
42
43
|
}
|
|
43
44
|
|
|
45
|
+
/** Args for `tokens.createRunner`: a runner mint plus, for a fresh launch, its launch fact (#1870 S7). */
|
|
46
|
+
export interface CreateRunnerTokenOpArgs extends CreateTokenOpArgs {
|
|
47
|
+
readonly launch?: { readonly agentId: string; readonly fact: LaunchFact };
|
|
48
|
+
}
|
|
49
|
+
|
|
44
50
|
export interface RevokeTokenOpArgs {
|
|
45
51
|
readonly jti: string;
|
|
46
52
|
readonly reason?: TokenRevokeReason;
|
|
@@ -67,12 +73,25 @@ function stripHandoffToken(result: unknown): unknown {
|
|
|
67
73
|
export const TOKEN_DB_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze({
|
|
68
74
|
// Non-idempotent (mints a new row each apply); the facade supplies `jti` as the ledger key.
|
|
69
75
|
"tokens.create": {
|
|
76
|
+
offLoop: true,
|
|
70
77
|
idempotent: false,
|
|
71
78
|
fn: (args: CreateTokenOpArgs, ctx: WriterOpContext) => createTokenInternal(args.input, args.jti, ctx.now),
|
|
72
79
|
serializeIdempotencyResult: stripCreateToken,
|
|
73
80
|
},
|
|
81
|
+
// A runner mint and the launch fact its successors derive from, in one transaction.
|
|
82
|
+
"tokens.createRunner": {
|
|
83
|
+
offLoop: true,
|
|
84
|
+
idempotent: false,
|
|
85
|
+
fn: (args: CreateRunnerTokenOpArgs, ctx: WriterOpContext) => {
|
|
86
|
+
const issued = createTokenInternal(args.input, args.jti, ctx.now);
|
|
87
|
+
if (args.launch) recordLaunchFact(args.launch.agentId, args.launch.fact, ctx.now);
|
|
88
|
+
return issued;
|
|
89
|
+
},
|
|
90
|
+
serializeIdempotencyResult: stripCreateToken,
|
|
91
|
+
},
|
|
74
92
|
// Non-idempotent (mints and links a successor); folds the eligibility read (ADR §3.4).
|
|
75
93
|
"tokens.handoff": {
|
|
94
|
+
offLoop: true,
|
|
76
95
|
idempotent: false,
|
|
77
96
|
fn: (args: HandoffTokenOpArgs, ctx: WriterOpContext) => handoffTokenInternal(args.input, args.newJti, ctx.now),
|
|
78
97
|
serializeIdempotencyResult: stripHandoffToken,
|
|
@@ -80,11 +99,13 @@ export const TOKEN_DB_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object
|
|
|
80
99
|
// Idempotent by construction (the `revoked_at IS NULL` guard makes a re-apply a no-op). No key, no
|
|
81
100
|
// secret in the boolean result.
|
|
82
101
|
"tokens.revoke": {
|
|
102
|
+
offLoop: true,
|
|
83
103
|
idempotent: true,
|
|
84
104
|
fn: (args: RevokeTokenOpArgs, ctx: WriterOpContext) => revokeTokenInternal(args.jti, args.reason, ctx.now),
|
|
85
105
|
},
|
|
86
106
|
// Idempotent (deleting an already-reaped row is a no-op). Returns pruned jtis — not secret.
|
|
87
107
|
"tokens.pruneExpired": {
|
|
108
|
+
offLoop: true,
|
|
88
109
|
idempotent: true,
|
|
89
110
|
fn: (args: PruneExpiredTokensOpArgs, ctx: WriterOpContext) =>
|
|
90
111
|
pruneExpiredTokenRecordsInternal(args.input, ctx.now),
|
package/src/db/token-standing.ts
CHANGED
|
@@ -61,24 +61,28 @@ export function tokenRevocationReason(jti: string): string | null | undefined {
|
|
|
61
61
|
/** A rotation retires one token for its successor, so it is never a lineage revocation. */
|
|
62
62
|
export type LineageRevokeReason = Exclude<TokenRevokeReason, "rotation">;
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
* stale walk stops at stale members: what follows them is an orchestrator recovery. It never enters a `keep` token. */
|
|
68
|
-
export function revokeTokenLineage(seeds: readonly string[], reason: LineageRevokeReason | null, nowSeconds: number, keep: readonly string[] = []): string[] {
|
|
69
|
-
if (seeds.length === 0) return [];
|
|
70
|
-
const rows = getDb().query(`
|
|
64
|
+
// #3776 — each step must be a key lookup whatever sqlite_stat1 says: CROSS JOIN pins the join order and the unary
|
|
65
|
+
// `+` keeps the predecessor index off the backward step, so stale stats cannot make the walk scan per member.
|
|
66
|
+
export const TOKEN_LINEAGE_REVOKE_SQL = `
|
|
71
67
|
WITH RECURSIVE kept(jti) AS (SELECT value FROM json_each(?1)),
|
|
72
68
|
lineage(jti) AS (
|
|
73
69
|
SELECT value FROM json_each(?2) WHERE value NOT IN kept
|
|
74
|
-
UNION SELECT t.jti FROM lineage l JOIN tokens p ON p.jti = l.jti JOIN tokens t ON t.predecessor_jti = p.jti
|
|
70
|
+
UNION SELECT t.jti FROM lineage l CROSS JOIN tokens p ON p.jti = l.jti CROSS JOIN tokens t ON t.predecessor_jti = p.jti
|
|
75
71
|
WHERE t.jti NOT IN kept AND (?4 IS NOT 'stale' OR p.revoked_reason IS NOT 'stale')
|
|
76
|
-
UNION SELECT p.predecessor_jti FROM lineage l JOIN tokens p ON p.jti = l.jti
|
|
77
|
-
WHERE p.predecessor_jti IS NOT NULL AND p.predecessor_jti NOT IN kept AND (?4 IS NOT 'stale' OR p.revoked_reason IS NOT 'stale')
|
|
72
|
+
UNION SELECT p.predecessor_jti FROM lineage l CROSS JOIN tokens p ON p.jti = l.jti
|
|
73
|
+
WHERE +p.predecessor_jti IS NOT NULL AND +p.predecessor_jti NOT IN kept AND (?4 IS NOT 'stale' OR p.revoked_reason IS NOT 'stale')
|
|
78
74
|
)
|
|
79
75
|
UPDATE tokens SET revoked_at = COALESCE(revoked_at, ?3), revoked_reason = ?4
|
|
80
76
|
WHERE jti IN lineage AND (revoked_at IS NULL OR (?4 IS NOT 'stale' AND revoked_reason = 'stale'))
|
|
81
77
|
RETURNING jti
|
|
82
|
-
|
|
78
|
+
`;
|
|
79
|
+
|
|
80
|
+
/** #1245 — a token and every token handed off from it (token-db.ts handoffTokenInternal) are one credential, so
|
|
81
|
+
* revoking any of them revokes the whole lineage: unretired predecessors, pending successors and their renewals.
|
|
82
|
+
* A hard reason also hardens stale members, so no auth, renewal or orchestrator recovery reaches the lineage. A
|
|
83
|
+
* stale walk stops at stale members: what follows them is an orchestrator recovery. It never enters a `keep` token. */
|
|
84
|
+
export function revokeTokenLineage(seeds: readonly string[], reason: LineageRevokeReason | null, nowSeconds: number, keep: readonly string[] = []): string[] {
|
|
85
|
+
if (seeds.length === 0) return [];
|
|
86
|
+
const rows = getDb().query(TOKEN_LINEAGE_REVOKE_SQL).all(JSON.stringify(keep), JSON.stringify(seeds), nowSeconds, reason) as Array<{ jti: string }>;
|
|
83
87
|
return rows.map((row) => row.jti);
|
|
84
88
|
}
|
|
@@ -36,6 +36,7 @@ export const USER_SETTINGS_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = O
|
|
|
36
36
|
},
|
|
37
37
|
// Idempotent by construction: deleting again finds no row (`existing` null → early false).
|
|
38
38
|
"userSettings.delete": {
|
|
39
|
+
offLoop: true,
|
|
39
40
|
idempotent: true,
|
|
40
41
|
fn: (args: DeleteUserSettingOpArgs) => deleteUserSetting(getDb(), args.userId, args.namespace, args.key),
|
|
41
42
|
},
|
package/src/db/vault-backup.ts
CHANGED
|
@@ -54,6 +54,6 @@ export function syncVaultBackupWarning(args: { kind: VaultBackupWarning | null;
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
export const VAULT_BACKUP_WRITE_OPS = {
|
|
57
|
-
"secretsVault.backup.record": { idempotent: true, fn: recordVaultBackup },
|
|
58
|
-
"secretsVault.backup.warn": { idempotent: true, fn: syncVaultBackupWarning },
|
|
57
|
+
"secretsVault.backup.record": { offLoop: true, idempotent: true, fn: recordVaultBackup },
|
|
58
|
+
"secretsVault.backup.warn": { offLoop: true, idempotent: true, fn: syncVaultBackupWarning },
|
|
59
59
|
};
|
package/src/db/vault-expiry.ts
CHANGED
|
@@ -66,6 +66,6 @@ export function prepareVaultExpiryNotices(args: { now: number }) {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
export const VAULT_EXPIRY_WRITE_OPS = {
|
|
69
|
-
"secretsVault.expiry.prepare": { idempotent: true, fn: prepareVaultExpiryNotices },
|
|
70
|
-
"secretsVault.expiry.sent": { idempotent: true, fn: (args: { id: number }) => { getDb().query("UPDATE vault_expiry_notices SET notified=1 WHERE decision_id=?").run(args.id); } },
|
|
69
|
+
"secretsVault.expiry.prepare": { offLoop: true, idempotent: true, fn: prepareVaultExpiryNotices },
|
|
70
|
+
"secretsVault.expiry.sent": { offLoop: true, idempotent: true, fn: (args: { id: number }) => { getDb().query("UPDATE vault_expiry_notices SET notified=1 WHERE decision_id=?").run(args.id); } },
|
|
71
71
|
};
|
package/src/db/vault-health.ts
CHANGED
|
@@ -85,9 +85,9 @@ export function logPage(args: { profile?: string; before?: { at: number; id: num
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
export const VAULT_HEALTH_WRITE_OPS: Record<string, WriterOpSpec> = {
|
|
88
|
-
"vaultHealth.host": { idempotent: true, fn: (host: ServiceAccessHostHealth, ctx: WriterOpContext) => { writeState("host", host, ctx.now); return null; } },
|
|
89
|
-
"vaultHealth.collect": { idempotent: true, fn: (input: VaultLogCollect, ctx: WriterOpContext) => collectVaultLogs(input, ctx.now) },
|
|
90
|
-
"vaultHealth.prune": { idempotent: true, fn: (args: { now: number }) => pruneVaultLogs(args.now) },
|
|
88
|
+
"vaultHealth.host": { offLoop: true, idempotent: true, fn: (host: ServiceAccessHostHealth, ctx: WriterOpContext) => { writeState("host", host, ctx.now); return null; } },
|
|
89
|
+
"vaultHealth.collect": { offLoop: true, idempotent: true, fn: (input: VaultLogCollect, ctx: WriterOpContext) => collectVaultLogs(input, ctx.now) },
|
|
90
|
+
"vaultHealth.prune": { offLoop: true, idempotent: true, fn: (args: { now: number }) => pruneVaultLogs(args.now) },
|
|
91
91
|
};
|
|
92
92
|
|
|
93
93
|
export const VAULT_HEALTH_READ_OPS = {
|
|
@@ -3,12 +3,12 @@ import { settleVaultToken, revokeVaultTokens, recordOrphanVaultToken, pruneVault
|
|
|
3
3
|
import type { WriterOpContext, WriterOpSpec } from "./write-ops";
|
|
4
4
|
|
|
5
5
|
export const VAULT_TOKEN_WRITE_OPS: Record<string, WriterOpSpec> = {
|
|
6
|
-
"vaultTokens.orphan": { idempotent: true, fn: (row: VaultTokenRow) => recordOrphanVaultToken(row) },
|
|
7
|
-
"vaultTokens.prune": { idempotent: true, fn: (args: { now: number }) => pruneVaultTokens(args.now) },
|
|
8
|
-
"vaultTokens.begin": { idempotent: false, fn: (row: VaultTokenRow) => beginVaultToken(row) },
|
|
9
|
-
"vaultTokens.activate": { idempotent: true, fn: (args: { id: string; sessionId: string; expiresAt: number }, ctx: WriterOpContext) => activateVaultToken(args.id, args.sessionId, args.expiresAt, ctx.now) },
|
|
10
|
-
"vaultTokens.renew": { idempotent: true, fn: (args: { id: string; sessionId: string; expiresAt: number }, ctx: WriterOpContext) => renewVaultToken(args.id, args.sessionId, args.expiresAt, ctx.now) },
|
|
11
|
-
"vaultTokens.check": { idempotent: true, fn: (args: { id: string; now: number }) => checkVaultToken(args.id, args.now) ?? null },
|
|
12
|
-
"vaultTokens.settle": { idempotent: false, fn: (args: { id: string; error?: string }, ctx: WriterOpContext) => { settleVaultToken(args.id, ctx.now, args.error); return null; } },
|
|
13
|
-
"vaultTokens.revoke": { idempotent: true, fn: (args: { agentId: string; instanceId: string; spawnRequestId: string; launchId: string }, ctx: WriterOpContext) => { revokeVaultTokens(args.agentId, ctx.now, "runner_release", args.instanceId, args.spawnRequestId, args.launchId); return null; } },
|
|
6
|
+
"vaultTokens.orphan": { offLoop: true, idempotent: true, fn: (row: VaultTokenRow) => recordOrphanVaultToken(row) },
|
|
7
|
+
"vaultTokens.prune": { offLoop: true, idempotent: true, fn: (args: { now: number }) => pruneVaultTokens(args.now) },
|
|
8
|
+
"vaultTokens.begin": { offLoop: true, idempotent: false, fn: (row: VaultTokenRow) => beginVaultToken(row) },
|
|
9
|
+
"vaultTokens.activate": { offLoop: true, idempotent: true, fn: (args: { id: string; sessionId: string; expiresAt: number }, ctx: WriterOpContext) => activateVaultToken(args.id, args.sessionId, args.expiresAt, ctx.now) },
|
|
10
|
+
"vaultTokens.renew": { offLoop: true, idempotent: true, fn: (args: { id: string; sessionId: string; expiresAt: number }, ctx: WriterOpContext) => renewVaultToken(args.id, args.sessionId, args.expiresAt, ctx.now) },
|
|
11
|
+
"vaultTokens.check": { offLoop: true, idempotent: true, fn: (args: { id: string; now: number }) => checkVaultToken(args.id, args.now) ?? null },
|
|
12
|
+
"vaultTokens.settle": { offLoop: true, idempotent: false, fn: (args: { id: string; error?: string }, ctx: WriterOpContext) => { settleVaultToken(args.id, ctx.now, args.error); return null; } },
|
|
13
|
+
"vaultTokens.revoke": { offLoop: true, idempotent: true, fn: (args: { agentId: string; instanceId: string; spawnRequestId: string; launchId: string }, ctx: WriterOpContext) => { revokeVaultTokens(args.agentId, ctx.now, "runner_release", args.instanceId, args.spawnRequestId, args.launchId); return null; } },
|
|
14
14
|
};
|
|
@@ -9,7 +9,7 @@ export const WEB_TOOL_CALL_READ_OPS = {
|
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
export const WEB_TOOL_CALL_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = {
|
|
12
|
-
"webSearch.reserveBrave": { idempotent: false, fn: (args: { month: string }) => reserveBraveSearch(args.month) },
|
|
13
|
-
"webToolCalls.record": { idempotent: true, fn: (call: WebToolCall) => { recordWebToolCall(call); return null; } },
|
|
14
|
-
"webToolCalls.prune": { idempotent: true, fn: (args: { before: number }) => pruneWebToolCalls(args.before) },
|
|
12
|
+
"webSearch.reserveBrave": { offLoop: true, idempotent: false, fn: (args: { month: string }) => reserveBraveSearch(args.month) },
|
|
13
|
+
"webToolCalls.record": { offLoop: true, idempotent: true, fn: (call: WebToolCall) => { recordWebToolCall(call); return null; } },
|
|
14
|
+
"webToolCalls.prune": { offLoop: true, idempotent: true, fn: (args: { before: number }) => pruneWebToolCalls(args.before) },
|
|
15
15
|
};
|
|
@@ -20,7 +20,7 @@ export interface WorkflowHeldContact { runId: string; reason: WorkflowContactRea
|
|
|
20
20
|
|
|
21
21
|
/** Notices that bypass the step-contact hold: the run's own coordinator notices (including GO awaiting an external
|
|
22
22
|
* hold's release), and a step agent held on a provider block, which waits until someone acts (#3647). */
|
|
23
|
-
const COORDINATOR_NOTICE_KINDS = new Set(["workflow.started", "workflow.landed", "workflow.escalated", "workflow.result", "workflow.review-passed", "agent.spawn_failed", "agent.spawn_recovered", "delegation.budget_exhausted", "agent.provider_blocked", "agent.provider_still_held"]);
|
|
23
|
+
const COORDINATOR_NOTICE_KINDS = new Set(["workflow.started", "workflow.landed", "workflow.escalated", "workflow.result", "workflow.review-passed", "workflow.paused", "workflow.unpaused", "agent.spawn_failed", "agent.spawn_recovered", "delegation.budget_exhausted", "agent.provider_blocked", "agent.provider_still_held"]);
|
|
24
24
|
export const WORKFLOW_SCOPE = "local";
|
|
25
25
|
export const WORKFLOW_RUN_TABLE = "kernel_entity__workflow__run";
|
|
26
26
|
const LINK_TABLE = "kernel_entity__workflow__link";
|
|
@@ -185,3 +185,33 @@ export function openWorkflowRunHolders(): { teamIds: Set<string>; coordinatorIds
|
|
|
185
185
|
}
|
|
186
186
|
return holders;
|
|
187
187
|
}
|
|
188
|
+
|
|
189
|
+
/** #3757 — a running run a coordinator paused, with its input Task's team; `pause` is the stored JSON. */
|
|
190
|
+
export interface PausedWorkflowRunRow {
|
|
191
|
+
runId: string; taskId: number; taskTitle: string | null; coordinator: string; callerAgentId: string | null;
|
|
192
|
+
teamId: string | null; owner: string | null; pause: string;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
let pauseColumnOn: unknown;
|
|
196
|
+
/** The column appears when the workflow module first registers after #3757; until then nothing is paused. */
|
|
197
|
+
function pauseColumnReady(): boolean {
|
|
198
|
+
const db = getDb();
|
|
199
|
+
if (pauseColumnOn === db) return true;
|
|
200
|
+
const ready = (db.query(`PRAGMA table_info(${WORKFLOW_RUN_TABLE})`).all() as Array<{ name: string }>).some((column) => column.name === "pause");
|
|
201
|
+
if (ready) pauseColumnOn = db;
|
|
202
|
+
return ready;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export function pausedWorkflowRuns(): PausedWorkflowRunRow[] {
|
|
206
|
+
if (!workflowTablesReady() || !pauseColumnReady()) return [];
|
|
207
|
+
return getDb().query(`SELECT run.id AS runId, run.taskId AS taskId, task.title AS taskTitle, run.coordinator AS coordinator, run.callerAgentId AS callerAgentId,
|
|
208
|
+
task.team_id AS teamId, task.owner AS owner, run.pause AS pause
|
|
209
|
+
FROM ${WORKFLOW_RUN_TABLE} run LEFT JOIN tasks task ON task.id = run.taskId
|
|
210
|
+
WHERE run.scope = ? AND run.status = 'running' AND run.pause IS NOT NULL ORDER BY run.id`).all(WORKFLOW_SCOPE) as PausedWorkflowRunRow[];
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** #3757 — a paused run keeps its idle step agents: the idle reapers leave them alone until it is unpaused. */
|
|
214
|
+
export function workflowRunPaused(runId: string): boolean {
|
|
215
|
+
if (!workflowTablesReady() || !pauseColumnReady()) return false;
|
|
216
|
+
return Boolean(getDb().query(`SELECT 1 FROM ${WORKFLOW_RUN_TABLE} WHERE scope = ? AND id = ? AND status = 'running' AND pause IS NOT NULL`).get(WORKFLOW_SCOPE, runId));
|
|
217
|
+
}
|
package/src/db/write-ops.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { INTEGRATIONS_WRITE_OPS } from "./integrations-ops.ts";
|
|
1
2
|
import { bufferedLastSeen, type PresenceSnapshot } from "./presence-buffer";
|
|
2
3
|
import { ensureDurablePayloadSchema, readRetryReceipt, storeDurablePayload } from "./durable-payloads";
|
|
3
4
|
import { ledgerSlotPredicate, ledgerSlots, WRITER_IDEMPOTENCY_RETENTION_MS } from "./writer-ledger";
|
|
@@ -5,6 +6,7 @@ import { workspaceChangeListeners } from "./workspaces";
|
|
|
5
6
|
import { withWriterOpState } from "./main-thread-state";
|
|
6
7
|
import { importVaultRows } from "./secrets-vault-import";
|
|
7
8
|
import { PROMPT_VERSION_WRITE_OPS } from "./prompt-versions-ops";
|
|
9
|
+
import { PROJECT_PROFILE_INSTRUCTION_WRITE_OPS } from "./project-profile-instructions";
|
|
8
10
|
import { VAULT_WRITE_OPS } from "./secrets-vault-write";
|
|
9
11
|
import { VAULT_EXPIRY_WRITE_OPS } from "./vault-expiry";
|
|
10
12
|
import { VAULT_BACKUP_WRITE_OPS } from "./vault-backup";
|
|
@@ -117,6 +119,7 @@ import { RESTART_EPOCH_WRITE_OPS } from "./restart-epoch-write-ops.ts";
|
|
|
117
119
|
import { TOKEN_USAGE_WRITE_OPS } from "./token-usage-write-ops.ts";
|
|
118
120
|
// #1870 S3 — the 1 s raw writers: maintenance leases/runs, pending issue closes, workspace lifecycle.
|
|
119
121
|
import { MAINTENANCE_JOB_WRITE_OPS } from "./maintenance-job-write-ops.ts";
|
|
122
|
+
import { ISSUE_REFS_WRITE_OPS } from "./issue-refs-write-ops.ts";
|
|
120
123
|
import { LONG_RUNNING_WORK_WRITE_OPS } from "./long-running-work-write-ops.ts";
|
|
121
124
|
import { WORKSPACE_WRITE_OPS } from "./workspace-write-ops.ts";
|
|
122
125
|
|
|
@@ -260,6 +263,7 @@ export const WRITER_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze(
|
|
|
260
263
|
// Delete all probe rows — a test reset. Deleting again reaches the same end state, so re-applying is
|
|
261
264
|
// safe: idempotent, no key required.
|
|
262
265
|
"seam.reset": {
|
|
266
|
+
offLoop: true,
|
|
263
267
|
idempotent: true,
|
|
264
268
|
fn: () => {
|
|
265
269
|
const before = (getDb().query("SELECT COUNT(*) AS n FROM _writer_seam_probe").get() as { n: number }).n;
|
|
@@ -286,6 +290,7 @@ export const WRITER_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze(
|
|
|
286
290
|
// must NOT enter the retry ledger — the retry re-evaluates fresh and can succeed once the blocker
|
|
287
291
|
// clears — while an applied outcome dedups exactly-once as usual.
|
|
288
292
|
"seam.claimLike": {
|
|
293
|
+
offLoop: true,
|
|
289
294
|
idempotent: false,
|
|
290
295
|
fn: (args: { tag: string; refuse?: boolean }, ctx) => {
|
|
291
296
|
if (args.refuse) return { applied: false };
|
|
@@ -301,6 +306,7 @@ export const WRITER_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze(
|
|
|
301
306
|
// single transaction (or savepoint), the first insert MUST roll back — the atomic-multi-statement
|
|
302
307
|
// proof across the boundary (§3.4). Inserts rows — NON-idempotent, key required.
|
|
303
308
|
"seam.txPair": {
|
|
309
|
+
offLoop: true,
|
|
304
310
|
idempotent: false,
|
|
305
311
|
fn: (args: { a: string; b: string; failSecond?: boolean }, ctx) => {
|
|
306
312
|
const first = getDb()
|
|
@@ -318,6 +324,7 @@ export const WRITER_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze(
|
|
|
318
324
|
// the main thread as a real `ValidationError` (instanceof true), not a bare `Error`. No state change,
|
|
319
325
|
// so re-applying is safe: idempotent, no key required.
|
|
320
326
|
"seam.throwValidation": {
|
|
327
|
+
offLoop: true,
|
|
321
328
|
idempotent: true,
|
|
322
329
|
fn: (args: { message?: string }) => {
|
|
323
330
|
throw new ValidationError(args.message ?? "seam.throwValidation");
|
|
@@ -327,6 +334,7 @@ export const WRITER_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze(
|
|
|
327
334
|
// Insert-once keyed by `idem_key` in its OWN args — dedups by construction (`ON CONFLICT DO
|
|
328
335
|
// NOTHING`), so it is idempotent and needs no gateway key.
|
|
329
336
|
"seam.idempotent": {
|
|
337
|
+
offLoop: true,
|
|
330
338
|
idempotent: true,
|
|
331
339
|
fn: (args: { key: string; value?: string }, ctx) => {
|
|
332
340
|
const inserted = getDb()
|
|
@@ -347,6 +355,7 @@ export const WRITER_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze(
|
|
|
347
355
|
// marshaling seam (H1) clones args before the op and clones the result after, so the caller's object
|
|
348
356
|
// is NEVER mutated and the caller sees only the cloned result — as across a real worker boundary.
|
|
349
357
|
"seam.mutateArg": {
|
|
358
|
+
offLoop: true,
|
|
350
359
|
idempotent: true,
|
|
351
360
|
fn: (args: { n: number }) => {
|
|
352
361
|
args.n += 1000;
|
|
@@ -358,6 +367,7 @@ export const WRITER_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze(
|
|
|
358
367
|
// integration suite to force a writer-job deadline (H4 wedged-writer): Atomics.wait on a private
|
|
359
368
|
// buffer is a true synchronous sleep the gateway deadline fires behind. No write, so idempotent.
|
|
360
369
|
"seam.sleep": {
|
|
370
|
+
offLoop: true,
|
|
361
371
|
idempotent: true,
|
|
362
372
|
fn: (args: { ms: number }) => {
|
|
363
373
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, Math.max(0, args.ms));
|
|
@@ -368,6 +378,7 @@ export const WRITER_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze(
|
|
|
368
378
|
// A writer-side read of committed state, for tests to verify persistence without a reader gateway
|
|
369
379
|
// (the writer reading its own connection trivially sees its own writes — §3.5). Read-only: idempotent.
|
|
370
380
|
"seam.snapshot": {
|
|
381
|
+
offLoop: true,
|
|
371
382
|
idempotent: true,
|
|
372
383
|
fn: () => {
|
|
373
384
|
const rows = getDb().query("SELECT id, tag, value, idem_key FROM _writer_seam_probe ORDER BY id").all();
|
|
@@ -398,6 +409,7 @@ export function allWriterOps(): Readonly<Record<string, WriterOpSpec>> {
|
|
|
398
409
|
...ROUTING_POLICY_WRITE_OPS,
|
|
399
410
|
...USER_SETTINGS_WRITE_OPS,
|
|
400
411
|
...PROMPT_VERSION_WRITE_OPS,
|
|
412
|
+
...PROJECT_PROFILE_INSTRUCTION_WRITE_OPS,
|
|
401
413
|
...TOKEN_DB_WRITE_OPS,
|
|
402
414
|
...DELIVERY_WRITE_OPS,
|
|
403
415
|
...TIMER_WRITE_OPS,
|
|
@@ -432,6 +444,7 @@ export function allWriterOps(): Readonly<Record<string, WriterOpSpec>> {
|
|
|
432
444
|
...SPAWN_TREE_CLEANUP_WRITE_OPS,
|
|
433
445
|
...PROJECTS_WRITE_OPS,
|
|
434
446
|
...CHANNELS_WRITE_OPS,
|
|
447
|
+
...INTEGRATIONS_WRITE_OPS,
|
|
435
448
|
...CI_INCIDENTS_WRITE_OPS,
|
|
436
449
|
...TEAMS_WRITE_OPS,
|
|
437
450
|
...TEAM_SPAWN_DELEGATION_WRITE_OPS,
|
|
@@ -450,7 +463,7 @@ export function allWriterOps(): Readonly<Record<string, WriterOpSpec>> {
|
|
|
450
463
|
...VAULT_WRITE_OPS,
|
|
451
464
|
...VAULT_EXPIRY_WRITE_OPS,
|
|
452
465
|
...VAULT_BACKUP_WRITE_OPS,
|
|
453
|
-
"secretsVault.import": { idempotent: false, fn: importVaultRows },
|
|
466
|
+
"secretsVault.import": { offLoop: true, idempotent: false, fn: importVaultRows },
|
|
454
467
|
...MACOS_PRIVACY_WRITE_OPS,
|
|
455
468
|
...PROVIDER_FLAP_MESSAGES_WRITE_OPS,
|
|
456
469
|
...PROVIDER_CATALOG_WRITE_OPS,
|
|
@@ -460,6 +473,7 @@ export function allWriterOps(): Readonly<Record<string, WriterOpSpec>> {
|
|
|
460
473
|
...TOKEN_USAGE_WRITE_OPS,
|
|
461
474
|
...MAINTENANCE_JOB_WRITE_OPS,
|
|
462
475
|
...LONG_RUNNING_WORK_WRITE_OPS,
|
|
476
|
+
...ISSUE_REFS_WRITE_OPS,
|
|
463
477
|
...WORKSPACE_WRITE_OPS,
|
|
464
478
|
...WEB_TOOL_CALL_WRITE_OPS,
|
|
465
479
|
...CLAUDE_API_BUDGET_WRITE_OPS,
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { mainThreadRegistry } from "./main-thread-state";
|
|
2
1
|
// The writer-side fault-injection seam for relocated db ops (ADR 0001 §3.1 H6, S3 leaf L-C #1899).
|
|
3
2
|
//
|
|
4
3
|
// WHY THIS EXISTS. Several timer ops carry test-only crash hooks that must fire INSIDE the op's
|
|
@@ -37,7 +36,11 @@ interface ArmedWriterFault {
|
|
|
37
36
|
readonly threadId: number;
|
|
38
37
|
}
|
|
39
38
|
|
|
40
|
-
|
|
39
|
+
// Deliberately NOT a mainThreadRegistry (#1870 S6a): off-loop op bodies call `writerFault`, and on the
|
|
40
|
+
// writer thread this map is empty, so the lookup is a harmless no-op there. Delivery is kept honest at
|
|
41
|
+
// dispatch: while a fault is armed the in-process backend runs off-loop ops on the arming thread
|
|
42
|
+
// (writer-gateway `dispatchEntry`), and arming refuses under the worker backend.
|
|
43
|
+
const armed = new Map<string, ArmedWriterFault>();
|
|
41
44
|
|
|
42
45
|
/**
|
|
43
46
|
* Whether a main-thread fault closure can still reach an op body. False once the gateway stands up
|
package/src/db/writer-gateway.ts
CHANGED
|
@@ -43,6 +43,7 @@ import { WriterDrain } from "./writer-drain";
|
|
|
43
43
|
import { errMessage } from "@humwick/sdk";
|
|
44
44
|
import { createWorkerPool, type WorkerPool } from "@humwick/offload";
|
|
45
45
|
import { getDbGeneration, getDbPath } from "./connection.ts";
|
|
46
|
+
import { armedWriterFaultCount } from "./writer-fault-seam.ts";
|
|
46
47
|
import { onLoopWriteTransaction } from "./on-loop-write-transaction.ts";
|
|
47
48
|
// On-loop-block attribution (#1882 / ADR §5). `resolveLoopCallsite` pins the effective callsite onto a
|
|
48
49
|
// submission in the CALLER's context; `captureSyncFrame` times the op body where it actually runs — in
|
|
@@ -173,8 +174,9 @@ export interface WriterStats {
|
|
|
173
174
|
* SQLite lock (H4 §3.6.1). A wedged writer is NOT idle — this is deliberately distinct from
|
|
174
175
|
* `busy:0`, which is how the naive pool-replace path mis-reported the stall. New non-critical
|
|
175
176
|
* writes shed and critical writes are held until the wedged op completes and the FIFO resumes.
|
|
177
|
+
* #3776 — names the op holding the writer and how long it has run, so health says WHICH job; null when not wedged.
|
|
176
178
|
*/
|
|
177
|
-
wedged:
|
|
179
|
+
wedged: { op: string; runningMs: number } | null;
|
|
178
180
|
/**
|
|
179
181
|
* #2276 — how many times the IN-PROCESS drain has yielded the event loop mid-convoy because its
|
|
180
182
|
* time slice was spent. 0 on the worker backend (nothing to slice). A steadily climbing count is
|
|
@@ -258,7 +260,7 @@ let drainYields = 0;
|
|
|
258
260
|
// lock (§3.6.1). Cleared when that in-flight job finally settles (the op completed and released the
|
|
259
261
|
// lock), which re-kicks the FIFO drain. Worker backend only — an in-process op runs synchronously on
|
|
260
262
|
// the main thread, so it cannot be observed mid-stall.
|
|
261
|
-
let
|
|
263
|
+
let wedgedOp: { op: string; startedAt: number } | undefined;
|
|
262
264
|
|
|
263
265
|
// Test-only fault injection for the precise H5 window: the op has COMMITTED, but its
|
|
264
266
|
// acknowledgement is lost before the caller observes the result. One-shot by design so a retry
|
|
@@ -354,14 +356,14 @@ function createFileWriter(dbPath: string): WorkerBackend {
|
|
|
354
356
|
|
|
355
357
|
/** Server shutdown keeps already-admitted writes; test/reset teardown remains immediate. */
|
|
356
358
|
export async function drainWriterOnShutdown(): Promise<void> {
|
|
357
|
-
try { await shutdownDrain.start(() => !draining && !
|
|
359
|
+
try { await shutdownDrain.start(() => !draining && !wedgedOp && pendingWriteDepth() === 0 && inFlightWrites() === 0); }
|
|
358
360
|
finally { await shutdownWriter(); }
|
|
359
361
|
}
|
|
360
362
|
|
|
361
363
|
export async function shutdownWriter(): Promise<void> {
|
|
362
364
|
const current = backend;
|
|
363
365
|
backend = undefined;
|
|
364
|
-
|
|
366
|
+
wedgedOp = undefined;
|
|
365
367
|
// Reject anything still queued at the gateway so no caller hangs across a teardown.
|
|
366
368
|
const stranded = submissionQueue.splice(0, submissionQueue.length);
|
|
367
369
|
for (const entry of stranded) {
|
|
@@ -378,7 +380,8 @@ export function isWriterInitialized(): boolean {
|
|
|
378
380
|
}
|
|
379
381
|
|
|
380
382
|
export function writerStats(): WriterStats {
|
|
381
|
-
if (!backend) return { backend: "uninitialized", queued: 0, busy: 0, highWater: queueHighWater, wedged:
|
|
383
|
+
if (!backend) return { backend: "uninitialized", queued: 0, busy: 0, highWater: queueHighWater, wedged: null, drainYields };
|
|
384
|
+
const wedged = wedgedOp ? { op: wedgedOp.op, runningMs: Math.round(performance.now() - wedgedOp.startedAt) } : null;
|
|
382
385
|
if (backend.kind === "in-process") {
|
|
383
386
|
return { backend: "in-process", queued: pendingWriteDepth(), busy: inFlightWrites(), highWater: queueHighWater, wedged, drainYields };
|
|
384
387
|
}
|
|
@@ -479,7 +482,7 @@ function admitOrThrow(op: string, opts: WriteOpts | undefined, count: number): v
|
|
|
479
482
|
const occupancy = pendingWriteDepth() + inFlightWrites();
|
|
480
483
|
if (occupancy + count > queueHardCap) throw new WriterOverloadedError(op, occupancy);
|
|
481
484
|
if (opts?.critical) return;
|
|
482
|
-
const workerWedged =
|
|
485
|
+
const workerWedged = wedgedOp !== undefined;
|
|
483
486
|
if (workerWedged || occupancy + count > queueHighWater) throw new WriterOverloadedError(op, occupancy);
|
|
484
487
|
}
|
|
485
488
|
|
|
@@ -510,7 +513,7 @@ async function drainLoop(): Promise<void> {
|
|
|
510
513
|
// second concurrent drain and anything submitted meanwhile is picked up by this same loop when
|
|
511
514
|
// it resumes — FIFO is preserved, the queue is only PAUSED.
|
|
512
515
|
let sliceStartedAt = performance.now();
|
|
513
|
-
while (submissionQueue.length > 0 && !
|
|
516
|
+
while (submissionQueue.length > 0 && !wedgedOp) {
|
|
514
517
|
const entry = submissionQueue.shift()!;
|
|
515
518
|
const onLoop = backend?.kind === "in-process";
|
|
516
519
|
await dispatchEntry(entry);
|
|
@@ -520,7 +523,7 @@ async function drainLoop(): Promise<void> {
|
|
|
520
523
|
sliceStartedAt = performance.now();
|
|
521
524
|
continue;
|
|
522
525
|
}
|
|
523
|
-
if (submissionQueue.length === 0 ||
|
|
526
|
+
if (submissionQueue.length === 0 || wedgedOp) continue;
|
|
524
527
|
if (performance.now() - sliceStartedAt < drainSliceMs) continue;
|
|
525
528
|
drainYields += 1;
|
|
526
529
|
await yieldEventLoop();
|
|
@@ -528,7 +531,7 @@ async function drainLoop(): Promise<void> {
|
|
|
528
531
|
}
|
|
529
532
|
} finally {
|
|
530
533
|
draining = false;
|
|
531
|
-
shutdownDrain.notify(!
|
|
534
|
+
shutdownDrain.notify(!wedgedOp && pendingWriteDepth() === 0 && inFlightWrites() === 0);
|
|
532
535
|
}
|
|
533
536
|
}
|
|
534
537
|
|
|
@@ -551,7 +554,9 @@ async function dispatchEntry(entry: Submission): Promise<void> {
|
|
|
551
554
|
back.busy = true;
|
|
552
555
|
try {
|
|
553
556
|
const path = getDbPath();
|
|
554
|
-
|
|
557
|
+
// An armed crash seam is a closure on THIS thread, so while one is armed the op runs here instead
|
|
558
|
+
// of on the file-DB worker (tests only; production never arms). Otherwise the fault would never fire.
|
|
559
|
+
if (path && path !== ":memory:" && isOffLoopWriterOp(entry.op) && armedWriterFaultCount() === 0) {
|
|
555
560
|
if (back.offLoop && back.offLoopGeneration !== getDbGeneration()) {
|
|
556
561
|
await back.offLoop.pool.destroy();
|
|
557
562
|
back.offLoop = undefined;
|
|
@@ -647,6 +652,7 @@ async function dispatchWorker(entry: Submission, back: WorkerBackend): Promise<v
|
|
|
647
652
|
|
|
648
653
|
// retrySafe stays false (default): a write must NEVER be auto-re-dispatched (it would repeat its
|
|
649
654
|
// effect). No pool timeout is armed — the deadline is the gateway's own (§3.6.1).
|
|
655
|
+
const startedAt = performance.now();
|
|
650
656
|
const poolPromise = back.pool.run<WriterJobInput, WriterJobResult>(WRITER_TASK_NAME, input, { labels });
|
|
651
657
|
|
|
652
658
|
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
@@ -662,13 +668,16 @@ async function dispatchWorker(entry: Submission, back: WorkerBackend): Promise<v
|
|
|
662
668
|
// WEDGED (§3.6.1). The op is still running and still holds the lock; do NOT replace the worker.
|
|
663
669
|
// Surface the caller's INDETERMINATE outcome, hold the FIFO, and resume when the op finally
|
|
664
670
|
// settles (it commits/rolls back and releases the lock on the SAME warm connection).
|
|
665
|
-
wedged
|
|
671
|
+
// #3776 — name the op: "writer wedged" alone left an incident to guess which job held the lock.
|
|
672
|
+
wedgedOp = { op: entry.op, startedAt };
|
|
673
|
+
console.warn(`[writer-gateway] writer op ${entry.op} has held the writer past its ${writerTimeoutMs}ms deadline; writes are held until it settles`);
|
|
666
674
|
rejectIndeterminate(entry);
|
|
667
675
|
poolPromise.then(
|
|
668
676
|
() => {},
|
|
669
677
|
() => {},
|
|
670
678
|
).finally(() => {
|
|
671
|
-
wedged
|
|
679
|
+
console.warn(`[writer-gateway] wedged writer op ${entry.op} settled after ${Math.round(performance.now() - startedAt)}ms`);
|
|
680
|
+
wedgedOp = undefined;
|
|
672
681
|
kickDrain();
|
|
673
682
|
}).catch((error) => console.error(`[writer-gateway] resume after wedge failed: ${errMessage(error)}`));
|
|
674
683
|
return;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import type
|
|
1
|
+
import { errMessage, type HostView, type Orchestrator } from "@humwick/sdk";
|
|
2
|
+
import { resolveProviderSelection, type ProviderCatalogEntry } from "@humwick/sdk/provider-catalog";
|
|
3
3
|
import type { DispatchTarget } from "./task-dispatcher";
|
|
4
4
|
import { spawnPlacementRefusal, hostSpawnAdmission, hostIdentitiesById, spawnHostCapAdmission, type SpawnCapOptions } from "./spawn-placement";
|
|
5
5
|
import { liveHostDisabledProviders } from "./host-provider-store";
|
|
@@ -17,13 +17,15 @@ export interface LiveTargetAdmission {
|
|
|
17
17
|
targetUnavailableReason: (target: DispatchTarget) => string | undefined;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
export function liveTargetAdmission(orchestrators: readonly Orchestrator[], views: readonly HostView[], orchestratorId?: string, capOpts: SpawnCapOptions = {}): LiveTargetAdmission {
|
|
20
|
+
export function liveTargetAdmission(orchestrators: readonly Orchestrator[], views: readonly HostView[], catalog: ProviderCatalogEntry[], orchestratorId?: string, capOpts: SpawnCapOptions = {}): LiveTargetAdmission {
|
|
21
21
|
const identities = hostIdentitiesById(views);
|
|
22
22
|
const byId = new Map(views.map((view) => [view.id, view]));
|
|
23
23
|
const disabled = liveHostDisabledProviders();
|
|
24
24
|
const capBlock = spawnHostCapAdmission(capOpts);
|
|
25
25
|
const hosts = orchestrators.filter((host) => !orchestratorId || host.id === orchestratorId);
|
|
26
26
|
const targetAdmission = ({ provider, model, effort }: DispatchTarget): TargetAdmission => {
|
|
27
|
+
try { resolveProviderSelection({ provider, model, effort }, catalog); }
|
|
28
|
+
catch { return { eligible: false, uncertainties: [] }; }
|
|
27
29
|
let eligible = false;
|
|
28
30
|
const uncertainties: TargetUncertainty[] = [];
|
|
29
31
|
for (const host of hosts) {
|
|
@@ -34,9 +36,11 @@ export function liveTargetAdmission(orchestrators: readonly Orchestrator[], view
|
|
|
34
36
|
}
|
|
35
37
|
return { eligible, uncertainties };
|
|
36
38
|
};
|
|
37
|
-
const targetUnavailableReason = (target: DispatchTarget) =>
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
const targetUnavailableReason = (target: DispatchTarget) => {
|
|
40
|
+
try { resolveProviderSelection(target, catalog); }
|
|
41
|
+
catch (error) { return errMessage(error); }
|
|
42
|
+
return spawnPlacementRefusal(orchestrators, views, target.provider, target.model, target.effort, orchestratorId, capOpts);
|
|
43
|
+
};
|
|
40
44
|
return { targetAdmission, targetUnavailableReason };
|
|
41
45
|
}
|
|
42
46
|
|
package/src/ephemeral/{landing-ingress-proof-worker.ts → landing-ingress-proof.test-fixture.ts}
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Precondition setup for the packaged-ephemeral landing-ingress Proof of Life.
|
|
2
2
|
* Delivery and both crash boundaries run through the packaged Relay; this fixture only registers
|
|
3
3
|
* the disposable workspace that its real ephemeral orchestrator will probe. */
|
|
4
|
-
import {
|
|
4
|
+
import { closeDb, openExistingDb } from "../db/connection.ts";
|
|
5
5
|
import { upsertWorkspace } from "../db/workspaces.ts";
|
|
6
6
|
|
|
7
7
|
const dbPath = process.env.DB_PATH;
|
|
@@ -13,7 +13,8 @@ if (!dbPath || !worktreePath || !orchestratorId || !baseSha) {
|
|
|
13
13
|
throw new Error("usage: landing-ingress-proof-worker <worktree-path> <orchestrator-id> <base-sha>");
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
// The installed Relay already owns the schema; a fixture must not migrate its live DB (#3715).
|
|
17
|
+
openExistingDb(dbPath);
|
|
17
18
|
upsertWorkspace({
|
|
18
19
|
id: workspaceId,
|
|
19
20
|
repoRoot: worktreePath,
|
|
@@ -27,4 +28,5 @@ upsertWorkspace({
|
|
|
27
28
|
orchestratorId,
|
|
28
29
|
metadata: {},
|
|
29
30
|
});
|
|
31
|
+
closeDb();
|
|
30
32
|
console.log(JSON.stringify({ workspaceId }));
|