@humwick/server 0.130.2 → 0.130.4
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/bundled/prompts/manifest.json +1 -0
- package/bundled/prompts/task.hydration.md +1 -1
- package/bundled/roles/builder.md +1 -1
- package/docs/guides/sub-coordinators.md +12 -5
- package/docs/guides/tools-coordination.md +1 -0
- package/docs/guides/workflows.md +16 -0
- package/docs/openapi.json +175 -4
- package/package.json +8 -7
- package/public/assets/{MessageBubble-BWLqMGKG.js → MessageBubble-BOkmEvgZ.js} +2 -2
- package/public/assets/{PlanGraphPanel-3TlIQpjk.js → PlanGraphPanel-BkaDJp0r.js} +2 -2
- package/public/assets/{TraceView-Wimm0Czv.js → TraceView-B99Uu2yT.js} +2 -2
- package/public/assets/{VaultDecisionCard-Bau_vNax.js → VaultDecisionCard-DUIfTTFF.js} +2 -2
- package/public/assets/{activity-CrSurn-r.js → activity-ZIX0N1El.js} +2 -2
- package/public/assets/{agent-lineage-CknAJKcb.js → agent-lineage-kq9TPYFw.js} +2 -2
- package/public/assets/agent-liveness-DSQY1Aa_.js +2 -0
- package/public/assets/{agent-name-DKbiIhBo.js → agent-name-C_7F4DFR.js} +2 -2
- package/public/assets/{agent-profiles-DwF6EQ4R.js → agent-profiles-BNOcrGcY.js} +2 -2
- package/public/assets/{agent-project-badge-Diz4Sjjb.js → agent-project-badge-B6yAh70a.js} +2 -2
- package/public/assets/{agent-tools-CRhpRQza.js → agent-tools-CcrhmOzF.js} +2 -2
- package/public/assets/{agent-type-icon-LrUEU3lB.js → agent-type-icon-CXSLrKiq.js} +2 -2
- package/public/assets/{agents-B-v66C-4.js → agents-DIZG97_X.js} +2 -2
- package/public/assets/{analytics-BqCb-LKi.js → analytics-rasmoPjR.js} +2 -2
- package/public/assets/{artifact-viewer-kf4pGuAR.js → artifact-viewer-tBpTrEPb.js} +2 -2
- package/public/assets/{artifacts-DJevXcvg.js → artifacts-BNkrynP1.js} +2 -2
- package/public/assets/{artifacts-D32zyw4Y.js → artifacts-Bjq-ZyBA.js} +2 -2
- package/public/assets/{automation-BpRajx_5.js → automation-Cx422L3L.js} +2 -2
- package/public/assets/{backlog-D_UjTdzX.js → backlog-RuFQ8PUu.js} +2 -2
- package/public/assets/{busy-health-N3VEh4ao.js → busy-health-BvNQ9g5x.js} +2 -2
- package/public/assets/{channels-9jopungq.js → channels-DNbKAS0I.js} +2 -2
- package/public/assets/{chat-CSXrTB3C.js → chat-Bp3Fx73h.js} +3 -3
- package/public/assets/{code-preview-p1NnmqK0.js → code-preview-KIRZA6cz.js} +2 -2
- package/public/assets/{connectors-CZdqlDNL.js → connectors-k7vO4vvc.js} +2 -2
- package/public/assets/{cosmos-BgdsE4dG.js → cosmos-BuVoq1aw.js} +2 -2
- package/public/assets/{crate-agent-30aXTKva.js → crate-agent-irqlt4NH.js} +2 -2
- package/public/assets/{dependency-graph-B_jvw2GE.js → dependency-graph-D_rurNBz.js} +2 -2
- package/public/assets/{deploys-DoN6bAb5.js → deploys-yzZqIpxE.js} +2 -2
- package/public/assets/{display-BrKWFGJz.js → display-BemJG1f_.js} +2 -2
- package/public/assets/{files-D6XEu8Ng.js → files-DXWcrxOz.js} +2 -2
- package/public/assets/{fleet-C7jQWbw2.js → fleet-TEuOCZXE.js} +2 -2
- package/public/assets/{fleet-metrics-BiDs3MLx.js → fleet-metrics-BGoDAFI-.js} +2 -2
- package/public/assets/{formatted-body-impl-DyG0WsQS.js → formatted-body-impl-BB9eq9nN.js} +2 -2
- package/public/assets/{formatted-body-CQBwc1fb.js → formatted-body-qy2nTD_1.js} +3 -3
- package/public/assets/{host-providers-dialog-DKaW31dN.js → host-providers-dialog-BGPHxuwT.js} +2 -2
- package/public/assets/{index-CRHep3M9.js → index-BC5XZc0z.js} +5 -5
- package/public/assets/{index-MYAIwYTX.css → index-D8Rp9pz2.css} +1 -1
- package/public/assets/{insights-CzrtkpOE.js → insights-C-KQG0h-.js} +2 -2
- package/public/assets/{integrations-aVsUmigt.js → integrations-ClPRPyKU.js} +2 -2
- package/public/assets/{issue-ref-chip-BnSlM_95.js → issue-ref-chip-DbH8J9Ak.js} +2 -2
- package/public/assets/{log-viewer-qe7Ee47Z.js → log-viewer-PH4zVn24.js} +2 -2
- package/public/assets/{maintenance-NfjqFZAk.js → maintenance-5lV43uTL.js} +2 -2
- package/public/assets/{managed-agents-BiH0HgaE.js → managed-agents-BdgU35aX.js} +2 -2
- package/public/assets/{markdown-preview-BixaT3Xr.js → markdown-preview-DNT4OUwQ.js} +3 -3
- package/public/assets/{markdown-preview-impl-DNGlWIS3.js → markdown-preview-impl-CnCcPy0A.js} +2 -2
- package/public/assets/{memory-DNsCl6UX.js → memory--GgWH1gH.js} +2 -2
- package/public/assets/{messages-DopU1L1X.js → messages-CzFl_97O.js} +2 -2
- package/public/assets/{orchestrators-Cq7ckLQT.js → orchestrators-CVvJVkzv.js} +2 -2
- package/public/assets/overview-DsrZvE86.js +2 -0
- package/public/assets/{pairs-DE_m6rrE.js → pairs-D2d0d2py.js} +2 -2
- package/public/assets/{profile-icon-B3rfh77F.js → profile-icon-_SMuSbYF.js} +2 -2
- package/public/assets/{project-profile-instructions-DQWZE0kA.js → project-profile-instructions-Apn15y43.js} +2 -2
- package/public/assets/{projects-Br2Bd2h1.js → projects-D3B6kto5.js} +2 -2
- package/public/assets/{prompt-settings-BKvFsvGD.js → prompt-settings-DnPB6Ez6.js} +2 -2
- package/public/assets/provider-display-bWwiuedV.js +2 -0
- package/public/assets/{registry-Cw38_6gW.js → registry-D790cIhN.js} +2 -2
- package/public/assets/run-card-BCro-Odj.js +2 -0
- package/public/assets/{secrets-DxI-vzSq.js → secrets-CVXvfvbU.js} +2 -2
- package/public/assets/{service-access-settings-D9s_VJbd.js → service-access-settings-D3Xxqb9w.js} +2 -2
- package/public/assets/{settings-CKZTVGIx.js → settings-CoUC7Jci.js} +2 -2
- package/public/assets/shipping-D_Lp9n_J.js +2 -0
- package/public/assets/{stats-DyUBKyWI.js → stats-CXiNjB9S.js} +2 -2
- package/public/assets/{status-dot-DYqlEUhJ.js → status-dot-C703YIy1.js} +2 -2
- package/public/assets/store-BgDALGIX.js +9 -0
- package/public/assets/{tasks-Bh7Chr4-.js → tasks-DO0WpJCx.js} +2 -2
- package/public/assets/{teams-bowI7qVY.js → teams-FGkdi_tL.js} +2 -2
- package/public/assets/{terminal-viewer-BgVLHdsU.js → terminal-viewer-Dh8dSC7e.js} +3 -3
- package/public/assets/{terminal-viewer-impl-Dz22UPy6.js → terminal-viewer-impl-B5Zriip3.js} +2 -2
- package/public/assets/{use-agent-terminal-DSQQ-iY7.js → use-agent-terminal-CRUVDvLC.js} +2 -2
- package/public/assets/{use-decisions-samMQhQf.js → use-decisions-CoaqRh4l.js} +2 -2
- package/public/assets/{use-project-list-59LheVP7.js → use-project-list-X-BgT53X.js} +2 -2
- package/public/assets/{use-selectors-BZa25xEB.js → use-selectors-0X_UZkk-.js} +2 -2
- package/public/assets/{user-avatar-DHxlvUvV.js → user-avatar-CkLhVBGu.js} +2 -2
- package/public/assets/{versioned-prompt-editor-BmED5DnJ.js → versioned-prompt-editor-BkTi7eim.js} +2 -2
- package/public/assets/{work-queue-CSvpuNLz.js → work-queue-DDOLEyKo.js} +2 -2
- package/public/assets/{workflow-marker-DAZcj5qt.js → workflow-marker-CzOsB8BM.js} +2 -2
- package/public/assets/{workflows-DdmxRSyg.js → workflows-DJilvmRM.js} +2 -2
- package/public/assets/{workspaces-BxeKfVuF.js → workspaces-CHcfbLR3.js} +2 -2
- package/public/cosmos.html +4 -4
- package/public/index.html +29 -29
- package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/runner/src/adapters/claude-api.ts +1 -1
- package/runner/src/adapters/claude-headless.ts +1 -1
- package/runner/src/adapters/claude-launch-exit.ts +26 -0
- package/runner/src/adapters/claude-session-probe.ts +1 -1
- package/runner/src/adapters/claude-tmux.ts +38 -10
- package/runner/src/adapters/claude.ts +28 -75
- package/runner/src/adapters/provider-quota-harvest.ts +20 -71
- package/runner/src/agent-runner/lifecycle-milestones.ts +2 -2
- package/runner/src/agent-runner/liveness-exchange.ts +104 -0
- package/runner/src/claude-system-prompt.ts +49 -0
- package/runner/src/launch-assembly.ts +14 -10
- package/runner/src/profile-home.ts +1 -1
- package/runner/src/project-instructions.ts +2 -2
- package/runner/src/provider-command.ts +18 -0
- package/runner/src/provider-setup.ts +3 -2
- package/runner/src/runner-core.ts +14 -15
- package/runner/src/runner-helpers.ts +0 -8
- package/runner/src/session-scratch.ts +6 -2
- package/runner/test-support/provisioning-context.ts +4 -2
- package/runner/test-support/runner-harness.ts +2 -1
- package/scripts/steward-policy-cleanup.ts +2 -2
- package/src/agent-authz.ts +2 -2
- package/src/agent-drive-arbiter.ts +3 -8
- package/src/agent-exit-cleanup-async.ts +2 -2
- package/src/agent-exit-cleanup.ts +23 -19
- package/src/agent-lifecycle-events.ts +112 -85
- package/src/agent-ref.ts +3 -2
- package/src/agent-status-events.ts +43 -14
- package/src/auth/login-link.ts +35 -0
- package/src/auth/password.ts +4 -3
- package/src/auth/session-refresh.ts +1 -1
- package/src/auth/session.test-fixture.ts +2 -2
- package/src/auth/session.ts +7 -7
- package/src/automations/crud.ts +8 -81
- package/src/automations/dispatch.ts +33 -58
- package/src/automations/index.ts +1 -1
- package/src/automations/persistence.ts +65 -0
- package/src/automations/precheck.ts +18 -16
- package/src/automations/read-async.ts +11 -0
- package/src/automations/reconcile.ts +30 -39
- package/src/automations/storage.ts +33 -10
- package/src/automations/write-async.ts +33 -0
- package/src/branch-land-notice.ts +3 -3
- package/src/branch-landed.ts +25 -23
- package/src/bus.ts +21 -19
- package/src/cd-deploy-notify.ts +2 -2
- package/src/ci/notifications.ts +4 -4
- package/src/ci/root-missing.ts +3 -3
- package/src/ci/transition-notify.ts +5 -5
- package/src/claude-app/oauth-http.ts +3 -3
- package/src/cli/index.ts +7 -1
- package/src/cli/init.ts +53 -0
- package/src/cli/orchestrator.ts +1 -7
- package/src/cli/setup.ts +6 -4
- package/src/cli/workflow.ts +12 -3
- package/src/commands-db.ts +4 -4
- package/src/commands-write.ts +8 -0
- package/src/compaction-watch.ts +10 -8
- package/src/config-store-write.ts +2 -2
- package/src/config.ts +4 -11
- package/src/connectors.ts +10 -8
- package/src/context-advisory.ts +22 -21
- package/src/db/agent-action-write.ts +36 -0
- package/src/db/agent-control-async.ts +9 -0
- package/src/db/agent-control-write.ts +49 -0
- package/src/db/agent-effort-observe.ts +22 -0
- package/src/db/agent-meta.ts +14 -3
- package/src/db/agents-async.ts +8 -5
- package/src/db/agents.ts +15 -12
- package/src/db/auth-identities.ts +2 -2
- package/src/db/automation-read-ops.ts +11 -0
- package/src/db/automation-write-ops.ts +46 -0
- package/src/db/blackboard.ts +2 -2
- package/src/db/browser-session-write.ts +32 -0
- package/src/db/bus-outbox-write-ops.ts +3 -1
- package/src/db/checkout-refresh-schema.ts +47 -0
- package/src/db/checkout-refresh.ts +5 -5
- package/src/db/command-agent-profile-backfill.ts +39 -0
- package/src/db/command-agent-profiles.ts +5 -1
- package/src/db/command-retention.ts +126 -0
- package/src/db/commands-planner-stats.test-fixture.ts +8 -0
- package/src/db/commands-read-ops.ts +16 -0
- package/src/db/connection.ts +1 -182
- package/src/db/context-carry-write.ts +74 -0
- package/src/db/continuations.ts +4 -4
- package/src/db/db-error-registry.ts +12 -0
- package/src/db/gateway.ts +26 -133
- package/src/db/history-retention-ops.ts +13 -0
- package/src/db/inbox-async.ts +2 -2
- package/src/db/inbox-read-ops.ts +1 -1
- package/src/db/inbox.ts +9 -3
- package/src/db/issue-associations.ts +2 -2
- package/src/db/issue-refs-async.ts +4 -1
- package/src/db/issue-refs-read-ops.ts +6 -0
- package/src/db/issue-refs.ts +8 -8
- package/src/db/kernel-event-log-schema.ts +2 -0
- package/src/db/land-boundaries.ts +51 -5
- package/src/db/land-gate-attestations.ts +2 -2
- package/src/db/landing-receipts.ts +2 -2
- package/src/db/lifecycle-context-write.ts +5 -0
- package/src/db/lifecycle-tail-async.ts +76 -0
- package/src/db/lifecycle-tail-write-ops.ts +62 -0
- package/src/db/lifecycle-write-window.ts +2 -1
- package/src/db/lineage-pair-dedupe-sql.ts +3 -3
- package/src/db/main-connection-mode.ts +11 -0
- package/src/db/main-connection-writes.ts +20 -2
- package/src/db/maintenance-job-runs-async.ts +2 -2
- package/src/db/maintenance-job-write-ops.ts +1 -1
- package/src/db/maintenance-pauses.ts +2 -2
- package/src/db/message-send.ts +4 -0
- package/src/db/messages.ts +31 -25
- package/src/db/migrations.ts +18 -1
- package/src/db/notification-write-ops.ts +19 -0
- package/src/db/operator-answer-write.ts +19 -0
- package/src/db/orchestrators.ts +10 -4
- package/src/db/pairs-async.ts +22 -1
- package/src/db/pairs-write-ops.ts +8 -3
- package/src/db/pairs.ts +41 -48
- package/src/db/plan-dispatch-schema.ts +5 -1
- package/src/db/plan-ready-notices.ts +43 -0
- package/src/db/plan-tools-include-migration.ts +36 -0
- package/src/db/plans-write-ops.ts +2 -0
- package/src/db/presence-buffer.ts +5 -0
- package/src/db/presence-loop.ts +18 -23
- package/src/db/presence-write.ts +0 -6
- package/src/db/project-entries-async.ts +14 -0
- package/src/db/project-entries-read-ops.ts +5 -1
- package/src/db/project-entries-write-ops.ts +8 -0
- package/src/db/project-entries.ts +15 -12
- package/src/db/project-journal.ts +2 -2
- package/src/db/project-trust-approvals.ts +2 -2
- package/src/db/prompt-templates.ts +4 -4
- package/src/db/read-op-marshaling-fixtures.ts +18 -0
- package/src/db/read-ops.ts +4 -0
- package/src/db/retention.ts +13 -2
- package/src/db/review-publish-write.ts +8 -0
- package/src/db/runtime-admin-async.ts +19 -0
- package/src/db/runtime-admin-write-ops.ts +36 -0
- package/src/db/runtime-tail-async.ts +288 -0
- package/src/db/runtime-tail-write-ops.ts +97 -0
- package/src/db/schema.ts +4 -1
- package/src/db/spawn-budget.ts +5 -0
- package/src/db/spawn-delegation.ts +3 -12
- package/src/db/sql-audit-indexes.ts +3 -0
- package/src/db/stats-async.ts +5 -3
- package/src/db/task-entity.ts +5 -4
- package/src/db/team-claim-policy.ts +60 -0
- package/src/db/teams-async.ts +4 -0
- package/src/db/teams-write-ops.ts +4 -0
- package/src/db/terminal-events.ts +15 -0
- package/src/db/timer-cutover-bodies.ts +292 -0
- package/src/db/timer-cutover-core.ts +37 -278
- package/src/db/timer-cutover-state.ts +40 -0
- package/src/db/timer-cutover-write-ops.ts +1 -40
- package/src/db/token-db-write-ops.ts +2 -0
- package/src/db/token-standing-async.ts +12 -0
- package/src/db/token-standing.ts +6 -9
- package/src/db/token-usage-retention.ts +33 -0
- package/src/db/token-usage-write-ops.ts +1 -1
- package/src/db/token-usage.ts +4 -1
- package/src/db/user-invites-async.ts +13 -0
- package/src/db/user-invites.ts +3 -3
- package/src/db/users.ts +14 -6
- package/src/db/workflow-execution.ts +3 -3
- package/src/db/workspaces.ts +8 -5
- package/src/db/write-ops.ts +31 -12
- package/src/db/writer-fault-seam.ts +2 -9
- package/src/db/writer-fault-test-ops.ts +18 -0
- package/src/db/writer-gateway.ts +42 -46
- package/src/dev.ts +12 -16
- package/src/event-loop-lag.ts +4 -2
- package/src/event-loop-yield.ts +2 -2
- package/src/events.ts +10 -8
- package/src/host-provider-store.ts +5 -4
- package/src/hub-metrics-report.ts +5 -1
- package/src/hub-metrics.ts +38 -18
- package/src/init.ts +299 -0
- package/src/injection-sweep-pacing.ts +6 -8
- package/src/invite-link.ts +16 -9
- package/src/land-gate-notifications.ts +11 -10
- package/src/land-gate-queue-progress.ts +3 -3
- package/src/land-settle-dispatch.ts +52 -92
- package/src/lifecycle-context-pressure.ts +11 -10
- package/src/lifecycle-manager.ts +104 -98
- package/src/maintenance/breaker.ts +18 -16
- package/src/maintenance/checkout-refresh.ts +18 -1
- package/src/maintenance/critical-job-watchdog.ts +3 -3
- package/src/maintenance/events.ts +3 -3
- package/src/maintenance/history-retention-job.ts +30 -0
- package/src/maintenance/jobs.ts +29 -67
- package/src/maintenance/long-running-work.ts +3 -1
- package/src/maintenance/offline-agent-prune-job.ts +28 -0
- package/src/maintenance/orphaned-sessions.ts +5 -4
- package/src/maintenance/pauses.ts +4 -10
- package/src/maintenance/plan-ready-notice-job.ts +71 -0
- package/src/maintenance/scheduler.ts +18 -13
- package/src/maintenance/stalled-delivery-pager.ts +4 -4
- package/src/maintenance/teams.ts +19 -30
- package/src/maintenance/workspaces.ts +17 -27
- package/src/managed-policy-escalation.ts +7 -7
- package/src/managed-policy.ts +3 -3
- package/src/managed-provider-gate.ts +5 -4
- package/src/managed-spawn-reservation.ts +5 -4
- package/src/mcp/index.ts +8 -8
- package/src/mcp/output-descriptions.ts +1 -1
- package/src/mcp/presentation.ts +2 -1
- package/src/mcp/runtime-output.ts +6 -1
- package/src/mcp/self-care-visibility.ts +2 -2
- package/src/mcp/spawn-targets-matrix.ts +10 -1
- package/src/mcp/tool-groups.ts +3 -2
- package/src/mcp/tool-roles.ts +81 -0
- package/src/mcp/tools-workspace.ts +1 -1
- package/src/mcp-blackboard-tools.ts +6 -5
- package/src/mcp-decision-tools.ts +7 -7
- package/src/mcp-egress-tools.ts +10 -9
- package/src/mcp-issue-activity-tools.ts +2 -2
- package/src/mcp-issue-association-tools.ts +4 -4
- package/src/mcp-issue-ref-tools.ts +3 -3
- package/src/mcp-merge-tools.ts +5 -5
- package/src/mcp-notification-subscription-tools.ts +14 -15
- package/src/mcp-output-levels.test-fixture.ts +1 -1
- package/src/mcp-profile-trust-tools.ts +1 -1
- package/src/mcp-project-journal-tools.ts +4 -12
- package/src/mcp-project-tools.ts +17 -18
- package/src/mcp-routing-policy-tools.ts +3 -2
- package/src/mcp-team-tools.ts +6 -6
- package/src/mcp-workflow-tools.ts +5 -3
- package/src/memory-area-injection.ts +13 -12
- package/src/memory-boundary-capture.ts +6 -6
- package/src/memory-injection.ts +45 -43
- package/src/merge-pause-maintenance.ts +5 -4
- package/src/notification-router.ts +44 -17
- package/src/notify.ts +5 -5
- package/src/observed-reset-refresh.ts +4 -4
- package/src/operator-alarm.ts +2 -5
- package/src/project-ingest.ts +6 -6
- package/src/project-instructions.ts +8 -1
- package/src/project-trust.ts +12 -11
- package/src/prompt-resolver.ts +3 -3
- package/src/provider-quota-async.ts +5 -7
- package/src/provider-timeline-event.ts +4 -7
- package/src/quota-band-delivery.ts +2 -2
- package/src/recipe-runner.ts +4 -3
- package/src/retention-config-store.ts +4 -3
- package/src/reviewer-pipeline.ts +11 -10
- package/src/routes/_route.ts +4 -4
- package/src/routes/_shared.ts +13 -12
- package/src/routes/agent-effort.ts +5 -5
- package/src/routes/agent-heartbeat-quota.ts +23 -0
- package/src/routes/agent-keep.ts +1 -1
- package/src/routes/agent-reporting.ts +1 -1
- package/src/routes/agent-self-effort.ts +5 -5
- package/src/routes/agent-sessions.ts +24 -24
- package/src/routes/agents-spawn.ts +1 -1
- package/src/routes/agents.ts +17 -119
- package/src/routes/auth.ts +44 -17
- package/src/routes/authority-audit.ts +2 -2
- package/src/routes/automations.ts +6 -5
- package/src/routes/channel-binding-delete.ts +1 -1
- package/src/routes/channel-output.ts +3 -3
- package/src/routes/claude-api-budget.ts +5 -5
- package/src/routes/commands-listing.ts +1 -1
- package/src/routes/commands.ts +29 -27
- package/src/routes/context-resets.ts +2 -2
- package/src/routes/coordinator-teams.ts +1 -1
- package/src/routes/decisions.ts +2 -2
- package/src/routes/host-providers.ts +18 -0
- package/src/routes/host-secret-files.ts +1 -1
- package/src/routes/hosts.ts +11 -11
- package/src/routes/inbox.ts +3 -2
- package/src/routes/index.ts +7 -3
- package/src/routes/integrations.ts +9 -7
- package/src/routes/issue-activity.ts +2 -2
- package/src/routes/issue-associations.ts +4 -4
- package/src/routes/issue-refs.ts +3 -3
- package/src/routes/kernel-timers-operator.ts +1 -1
- package/src/routes/memory.ts +1 -1
- package/src/routes/merges.ts +11 -11
- package/src/routes/messages.ts +23 -17
- package/src/routes/orchestrator-bootstrap.ts +3 -1
- package/src/routes/orchestrator-host-registration.ts +2 -0
- package/src/routes/orchestrator-host-utilization.ts +2 -2
- package/src/routes/orchestrator-proxy.ts +2 -2
- package/src/routes/orchestrator.ts +27 -18
- package/src/routes/pairs.ts +8 -8
- package/src/routes/project-profile-instructions.ts +1 -1
- package/src/routes/project-trust.ts +1 -1
- package/src/routes/projects.ts +16 -16
- package/src/routes/prompt-templates.ts +12 -12
- package/src/routes/provider-quotas.ts +10 -10
- package/src/routes/run-command.ts +1 -1
- package/src/routes/secrets-backup.ts +4 -5
- package/src/routes/secrets-import.ts +3 -4
- package/src/routes/secrets-vault.ts +12 -14
- package/src/routes/settings.ts +1 -1
- package/src/routes/shipping.ts +9 -1
- package/src/routes/spawn-policy.ts +8 -7
- package/src/routes/sse.ts +2 -2
- package/src/routes/team-templates.ts +7 -7
- package/src/routes/teams.ts +10 -10
- package/src/routes/tokens.ts +18 -17
- package/src/routes/users.ts +24 -24
- package/src/routes/viewer-read.ts +3 -3
- package/src/routes/web-tool-calls.ts +4 -4
- package/src/routes/workflows.ts +2 -2
- package/src/routes/workspaces.ts +17 -16
- package/src/routing-policy-push.ts +6 -6
- package/src/runtime-grant.ts +17 -11
- package/src/runtime-install.ts +30 -0
- package/src/runtime-tokens.ts +20 -28
- package/src/secrets-broker/config.ts +1 -1
- package/src/secrets-broker/service.ts +2 -2
- package/src/security.ts +3 -1
- package/src/self-resume-carry.ts +21 -110
- package/src/self-resume-supersede.ts +4 -3
- package/src/self-resume.ts +54 -66
- package/src/server-background.ts +1 -1
- package/src/server.ts +25 -26
- package/src/services/agent-action.ts +12 -31
- package/src/services/agent-effort.ts +7 -32
- package/src/services/agent-hang-watchdog-job.ts +2 -2
- package/src/services/agent-heartbeat.ts +166 -0
- package/src/services/agent-reporting.ts +1 -1
- package/src/services/auth-context.ts +6 -0
- package/src/services/browser-inventory.ts +3 -3
- package/src/services/browser-session-state.ts +3 -2
- package/src/services/browser-session.ts +21 -27
- package/src/services/bus-command-update.ts +9 -9
- package/src/services/checkout-refresh-state.ts +1 -0
- package/src/services/checkout-refresh.test-fixture.ts +3 -2
- package/src/services/command-host.ts +7 -2
- package/src/services/coordinator-digest.ts +3 -2
- package/src/services/dispatch-command.ts +12 -13
- package/src/services/effective-launch-driver.ts +14 -9
- package/src/services/host-changed-paths.test-fixture.ts +5 -6
- package/src/services/host-project-readiness.ts +2 -2
- package/src/services/host-providers.ts +1 -1
- package/src/services/host-utilization.ts +9 -9
- package/src/services/idle-agent-digest.ts +17 -12
- package/src/services/idle-epics.ts +4 -4
- package/src/services/interrupt-agent.ts +12 -9
- package/src/services/issue-activity.ts +3 -3
- package/src/services/issue-associations.ts +15 -18
- package/src/services/issue-lifecycle-comments.ts +3 -2
- package/src/services/issue-lifecycle.ts +14 -13
- package/src/services/issue-refs.ts +4 -5
- package/src/services/kernel-timers-cutover-chaos-worker.ts +1 -1
- package/src/services/kernel-timers-ingress-chaos-worker.ts +6 -1
- package/src/services/kernel-timers-ingress.ts +3 -3
- package/src/services/kernel-timers-message-authority-chaos-worker.ts +3 -10
- package/src/services/kernel-timers-operator-chaos-worker.ts +3 -2
- package/src/services/kernel-timers-outbox.ts +1 -1
- package/src/services/kernel-timers-stage10-operator.ts +1 -1
- package/src/services/kernel-timers.ts +4 -3
- package/src/services/land-gate-placement.ts +11 -2
- package/src/services/land-issue-relations.ts +5 -4
- package/src/services/land-restart-retry.ts +7 -6
- package/src/services/landed-ancestry.ts +6 -5
- package/src/services/legacy-landing-host.ts +4 -1
- package/src/services/legacy-landing-recovery.ts +8 -6
- package/src/services/managed-running.ts +17 -17
- package/src/services/memory-areas.ts +3 -3
- package/src/services/merge-pause.ts +9 -15
- package/src/services/operator-decisions.ts +37 -41
- package/src/services/orchestrator-registration-recovery.ts +6 -2
- package/src/services/orchestrator-upgrade.ts +14 -12
- package/src/services/permission-decision.ts +4 -3
- package/src/services/plan-ci-notice.ts +1 -1
- package/src/services/plan-conformance.ts +4 -4
- package/src/services/plan-dispatch-policy.ts +10 -3
- package/src/services/plan-dispatch.ts +35 -14
- package/src/services/plan-graph.ts +39 -261
- package/src/services/plan-import.ts +1 -1
- package/src/services/plan-live-bindings.ts +8 -37
- package/src/services/plan-task-create.ts +1 -1
- package/src/services/plan-terminal-refs.ts +2 -12
- package/src/services/plan-wake-digest.ts +8 -3
- package/src/services/profile-trust.ts +2 -2
- package/src/services/project.ts +11 -10
- package/src/services/prompt-templates.ts +6 -5
- package/src/services/provider-api-key.ts +73 -0
- package/src/services/provider-hold-nudge.ts +9 -9
- package/src/services/provider-hold-record.ts +13 -12
- package/src/services/provider-state-audit.ts +19 -18
- package/src/services/provisioning-harness/driver.ts +1 -1
- package/src/services/provisioning-harness/fixture-matrix.ts +3 -3
- package/src/services/provisioning-harness/lifecycle.ts +24 -22
- package/src/services/provisioning-harness/prepare.ts +16 -17
- package/src/services/rate-limit-resume.ts +10 -9
- package/src/services/red-main-auto-revert.ts +7 -5
- package/src/services/red-main-controller-deps.ts +20 -3
- package/src/services/red-main-incident.ts +3 -1
- package/src/services/red-main-quarantine.ts +2 -3
- package/src/services/register-agent.ts +22 -22
- package/src/services/restart-project-role.ts +1 -1
- package/src/services/restart-spawn.ts +17 -16
- package/src/services/review-publish.ts +7 -24
- package/src/services/review-ref-reconcile.ts +29 -38
- package/src/services/runtime-dependency-alert.ts +27 -0
- package/src/services/scheduler-command.ts +11 -9
- package/src/services/scheduler.ts +5 -5
- package/src/services/self-effort-grant.ts +5 -25
- package/src/services/send-message.ts +16 -33
- package/src/services/shutdown-agent.ts +17 -14
- package/src/services/spawn-agent.ts +12 -41
- package/src/services/spawn-chain-inspector.ts +1 -1
- package/src/services/spawn-cwd-placement.ts +21 -5
- package/src/services/spawn-issue-activity.ts +8 -6
- package/src/services/spawn-launch.ts +17 -1
- package/src/services/spawn-loss-escalation.ts +1 -1
- package/src/services/spawn-registration-watchdog.ts +2 -2
- package/src/services/spawn-repo-probe.ts +7 -5
- package/src/services/spawn-tree-cleanup.ts +7 -8
- package/src/services/steward-reaper.ts +4 -4
- package/src/services/task-context-hydration.ts +25 -1
- package/src/services/task-delivery.ts +20 -34
- package/src/services/task-entity.ts +40 -203
- package/src/services/task-issue-link.ts +13 -20
- package/src/services/task-issue-weave.ts +12 -11
- package/src/services/task-lifecycle.ts +27 -31
- package/src/services/task-repo-cwd.ts +37 -0
- package/src/services/task-review-hold.ts +4 -17
- package/src/services/task-semantic-events.ts +13 -11
- package/src/services/task-state-reconcile.ts +3 -4
- package/src/services/task-validation.ts +2 -2
- package/src/services/team-templates.ts +5 -5
- package/src/services/team.ts +31 -98
- package/src/services/terminal-provider-exit.ts +6 -5
- package/src/services/timer-message-fire-chaos-worker.ts +7 -3
- package/src/services/timer-message-fire-outbox.ts +1 -1
- package/src/services/transient-agent-reaper.ts +22 -19
- package/src/services/turn-reconcile-audit.ts +9 -9
- package/src/services/user-onboarding.ts +8 -6
- package/src/services/user-profile.ts +6 -9
- package/src/services/work-finality.ts +16 -4
- package/src/services/workflow-auto-start.ts +2 -1
- package/src/services/workflow-caller.ts +61 -4
- package/src/services/workflow-child-proof-worker.ts +1 -1
- package/src/services/workflow-control-proof-worker.ts +1 -1
- package/src/services/workflow-coordinator.ts +8 -2
- package/src/services/workflow-decisions.ts +5 -5
- package/src/services/workflow-deferred-cleanup.ts +1 -1
- package/src/services/workflow-effect-driver.ts +42 -19
- package/src/services/workflow-effects.ts +41 -27
- package/src/services/workflow-pins.ts +7 -4
- package/src/services/workflow-reconcile.ts +1 -0
- package/src/services/workflow-resume-pins.ts +3 -3
- package/src/services/workflow-routing-defaults.ts +18 -0
- package/src/services/workflow-run-view.ts +4 -3
- package/src/services/workflow-run.test-fixture.ts +1 -1
- package/src/services/workflow-runs.ts +34 -22
- package/src/services/workflow-supervisor-state.ts +21 -1
- package/src/services/workflow-supervisor.ts +4 -3
- package/src/services/workspace-recovery.ts +4 -3
- package/src/setup.ts +47 -8
- package/src/spawn-admission.ts +10 -2
- package/src/spawn-placement.ts +27 -9
- package/src/spawn-targets.ts +8 -8
- package/src/sse-agent-status.ts +15 -6
- package/src/sse.ts +49 -24
- package/src/startup-context-injection.ts +61 -57
- package/src/steward-handoff.ts +4 -3
- package/src/steward-policy-cleanup.ts +9 -8
- package/src/steward.ts +2 -2
- package/src/task-dispatch-live.ts +5 -2
- package/src/task-dispatcher.ts +2 -2
- package/src/token-db.ts +2 -2
- package/src/token-usage-async.ts +2 -4
- package/src/user-awaiting-reply.ts +5 -5
- package/src/utils.ts +7 -0
- package/src/workspace-actions.ts +24 -29
- package/src/workspace-auto-merge.ts +14 -12
- package/src/workspace-changed-paths-client.ts +16 -5
- package/src/workspace-cleanup-stale.ts +30 -7
- package/src/workspace-command-settle.ts +24 -16
- package/src/workspace-config.ts +1 -1
- package/src/workspace-conflict-resolver.ts +9 -7
- package/src/workspace-dead-owner-recovery.ts +5 -4
- package/src/workspace-escalation.ts +12 -11
- package/src/workspace-human-escalation.ts +3 -3
- package/src/workspace-land-gate-watchdog.ts +3 -3
- package/src/workspace-land-refusal.ts +20 -0
- package/src/workspace-land-stopped.ts +1 -0
- package/src/workspace-merge.ts +10 -26
- package/src/workspace-nothing-to-land-notice.ts +2 -2
- package/src/workspace-orphans.ts +18 -37
- package/src/workspace-pr-completion.ts +10 -8
- package/src/workspace-rekey.ts +3 -3
- package/src/workspace-steward-failure.ts +2 -2
- package/src/workspace-steward-wake.ts +2 -2
- package/public/assets/agent-liveness-CaNwK0Zf.js +0 -2
- package/public/assets/overview-C1DCTzzV.js +0 -2
- package/public/assets/provider-display-DsuWMvVL.js +0 -2
- package/public/assets/run-card-CpKqRbhg.js +0 -2
- package/public/assets/shipping-B9YtnwtG.js +0 -2
- package/public/assets/store-DcClXLH0.js +0 -9
- package/runner/src/agent-runner/http-liveness.ts +0 -85
- package/src/db/on-loop-write-transaction.ts +0 -34
package/src/workspace-config.ts
CHANGED
|
@@ -3,5 +3,5 @@ import type { WorkspaceConfig } from "@humwick/sdk";
|
|
|
3
3
|
// Single source for the isolated-worktree symlink default (config-store + settings registry).
|
|
4
4
|
// Only the untracked rig config is linked; AGENTS.md is tracked (#3516), so a worktree gets it from git.
|
|
5
5
|
export const WORKSPACE_CONFIG_DEFAULTS: WorkspaceConfig = {
|
|
6
|
-
symlinkPaths: [
|
|
6
|
+
symlinkPaths: [],
|
|
7
7
|
};
|
|
@@ -81,7 +81,8 @@ async function withdrawArmedLand(ws: WorkspaceRecord, reason: string): Promise<"
|
|
|
81
81
|
export async function handBackLandConflict(ws: WorkspaceRecord, now: number, why: string): Promise<ConflictHandback> {
|
|
82
82
|
const meta = ws.metadata as Record<string, unknown>;
|
|
83
83
|
if (conflictHandedBackForArm(meta)) return "skipped";
|
|
84
|
-
const
|
|
84
|
+
const cause = typeof meta.mergeError === "string" ? meta.mergeError : undefined;
|
|
85
|
+
const instructions = handbackInstructions(ws, cause && !why.includes(cause) ? `${why}: ${cause}` : why);
|
|
85
86
|
const withdrawn = await withdrawArmedLand(ws, instructions);
|
|
86
87
|
// The steward's shutdown fences the row out of every other lane until it settles: the sweep
|
|
87
88
|
// retries this pending hand-back (workspace-steward-watchdog.ts), never the steward path.
|
|
@@ -106,7 +107,7 @@ export async function handBackLandConflict(ws: WorkspaceRecord, now: number, why
|
|
|
106
107
|
// One notice per recipient: it also wakes the owner, so no separate `landed-failure` (whose per-arm key
|
|
107
108
|
// the land-conflict Task notice already holds). A notify failure falls through to the operator alarm.
|
|
108
109
|
try {
|
|
109
|
-
routeNotification({
|
|
110
|
+
await routeNotification({
|
|
110
111
|
type: "workspace.stranded_escalation", forceDelivery: true, ttlMs: null,
|
|
111
112
|
scope: { workspaceId: ws.id, repoRoot: ws.repoRoot, author: ws.ownerAgentId, parent: coordinator ?? undefined },
|
|
112
113
|
recipients,
|
|
@@ -130,14 +131,14 @@ export async function handBackLandConflict(ws: WorkspaceRecord, now: number, why
|
|
|
130
131
|
}
|
|
131
132
|
}
|
|
132
133
|
|
|
133
|
-
raiseOperatorAlarm({
|
|
134
|
+
(await raiseOperatorAlarm({
|
|
134
135
|
clientId: `steward-handoff-unescalatable-${ws.id}:${strandKey}`,
|
|
135
136
|
source: "workspace-steward-handoff-watchdog",
|
|
136
137
|
subject: "Land conflict has no one to hand it back to",
|
|
137
138
|
detail: `${branchLabel} in ${ws.repoRoot} has an unresolved land conflict and no owner, coordinator, or fallback to hand it back to (${why}). Configure AGENT_RELAY_WORKSPACE_STEWARD_FALLBACK or land/reclaim the worktree manually.`,
|
|
138
139
|
view: "orchestrators",
|
|
139
140
|
metadata: { maintenanceJobId: "workspace-steward-handoff-watchdog", workspaceId: ws.id, repoRoot: ws.repoRoot, branch: ws.branch, reason: why },
|
|
140
|
-
});
|
|
141
|
+
}));
|
|
141
142
|
await transitionWorkspaceLandStateAsync(ws.id, "merge-error", { stewardHandoffEscalatedAt: now, stewardUnavailable: true, conflictHandbackPending: undefined, ...stewardEnded });
|
|
142
143
|
if (withdrawn === "none") await finishWorkspaceStewardHandoff(ws);
|
|
143
144
|
await createActivityEventAsync({
|
|
@@ -167,14 +168,15 @@ export async function superviseLandConflict(ws: WorkspaceRecord, reason: string,
|
|
|
167
168
|
|
|
168
169
|
const inFlight = new Set<Promise<unknown>>();
|
|
169
170
|
|
|
170
|
-
/**
|
|
171
|
-
export function superviseLandConflictLater(workspaceId: string, reason: string, opts: { tip?: string } = {}): void {
|
|
171
|
+
/** Track the wake so deferred settle queues can drain its committed effects (#1790/#1870). */
|
|
172
|
+
export function superviseLandConflictLater(workspaceId: string, reason: string, opts: { tip?: string } = {}): Promise<void> {
|
|
172
173
|
const ws = getWorkspace(workspaceId);
|
|
173
|
-
if (!ws) return;
|
|
174
|
+
if (!ws) return Promise.resolve();
|
|
174
175
|
const run = superviseLandConflict(ws, reason, opts)
|
|
175
176
|
.catch((error) => console.warn(`[workspace-conflict-resolver] supervising ${workspaceId} failed; the hand-off watchdog retries: ${errMessage(error)}`))
|
|
176
177
|
.finally(() => inFlight.delete(run));
|
|
177
178
|
inFlight.add(run);
|
|
179
|
+
return run.then(() => {});
|
|
178
180
|
}
|
|
179
181
|
|
|
180
182
|
export async function settleConflictSupervisionForTests(): Promise<void> {
|
|
@@ -1,19 +1,20 @@
|
|
|
1
|
+
import { createCommandAsync } from "./commands-write";
|
|
1
2
|
// Host-bound rescue for an unpushed dead-owner workspace. Kept separate from
|
|
2
3
|
// the orphan sweep so command construction remains reusable without inflating
|
|
3
4
|
// the disk⇄DB reconciler.
|
|
4
5
|
import type { WorkspaceRecord } from "./types";
|
|
5
|
-
|
|
6
|
+
|
|
6
7
|
import { emitCommandEvent } from "./command-events";
|
|
7
8
|
|
|
8
|
-
export function dispatchDeadOwnerRescue(orch: { agentId: string }, workspace: WorkspaceRecord): string {
|
|
9
|
-
const command =
|
|
9
|
+
export async function dispatchDeadOwnerRescue(orch: { agentId: string }, workspace: WorkspaceRecord): Promise<string> {
|
|
10
|
+
const command = (await createCommandAsync({
|
|
10
11
|
type: "workspace.reconcile", source: "system", target: orch.agentId, correlationId: workspace.id,
|
|
11
12
|
params: {
|
|
12
13
|
action: "reconcile", workspaceId: workspace.id, repoRoot: workspace.repoRoot,
|
|
13
14
|
worktreePath: workspace.worktreePath, branch: workspace.branch, baseRef: workspace.baseRef,
|
|
14
15
|
baseSha: workspace.baseSha, autoReclaim: "dead-owner", requestedBy: "workspace-dead-owner-reaper", requestedAt: Date.now(),
|
|
15
16
|
},
|
|
16
|
-
});
|
|
17
|
+
}));
|
|
17
18
|
emitCommandEvent(command, `command.${command.status}`);
|
|
18
19
|
return command.id;
|
|
19
20
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { claimWorkspaceHumanEscalationAlertAsync } from "./db/runtime-tail-async";
|
|
1
2
|
import { stewardFallbackTarget } from "./config";
|
|
2
|
-
|
|
3
|
+
|
|
3
4
|
import { routeNotification } from "./notification-router";
|
|
4
5
|
import { raiseOperatorAlarm } from "./operator-alarm";
|
|
5
6
|
import { resolveStewardIdentity } from "./steward-identity";
|
|
@@ -18,7 +19,7 @@ export interface WorkspaceCustodyEscalationInput {
|
|
|
18
19
|
idempotencyKey?: string;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
export function routeWorkspaceCustodyEscalation(input: WorkspaceCustodyEscalationInput): string[] {
|
|
22
|
+
export async function routeWorkspaceCustodyEscalation(input: WorkspaceCustodyEscalationInput): Promise<string[]> {
|
|
22
23
|
const { ownerAgentId, coordinatorAgentId } = resolveStewardIdentity(input.workspace);
|
|
23
24
|
const fallback = stewardFallbackTarget();
|
|
24
25
|
const recipients = [...new Set([ownerAgentId, coordinatorAgentId, fallback].filter((value): value is string => Boolean(value)))];
|
|
@@ -31,7 +32,7 @@ export function routeWorkspaceCustodyEscalation(input: WorkspaceCustodyEscalatio
|
|
|
31
32
|
// the caller's idempotencyKey (which already bakes in the condition
|
|
32
33
|
// fingerprint) so an unchanged condition never re-sends, while a real change
|
|
33
34
|
// (new key) still alerts.
|
|
34
|
-
if (input.idempotencyKey && !
|
|
35
|
+
if (input.idempotencyKey && !(await claimWorkspaceHumanEscalationAlertAsync(input.idempotencyKey))) {
|
|
35
36
|
const channelId = workspaceHumanEscalationChannelId();
|
|
36
37
|
return channelId ? [channelId] : [];
|
|
37
38
|
}
|
|
@@ -41,18 +42,18 @@ export function routeWorkspaceCustodyEscalation(input: WorkspaceCustodyEscalatio
|
|
|
41
42
|
ownerAgentId,
|
|
42
43
|
fallbackTarget: fallback || undefined,
|
|
43
44
|
};
|
|
44
|
-
const human = notifyWorkspaceHumanEscalation({
|
|
45
|
+
const human = (await notifyWorkspaceHumanEscalation({
|
|
45
46
|
subject: input.subject,
|
|
46
47
|
body: input.body,
|
|
47
48
|
payload,
|
|
48
49
|
source: "workspace-custody",
|
|
49
|
-
});
|
|
50
|
+
}));
|
|
50
51
|
if (human.accepted && human.channelId) return [human.channelId];
|
|
51
52
|
console.warn(`[workspace-escalation] zero agent recipients and no human channel for workspace ${input.workspace.id}: ${human.reason ?? "unknown reason"}`);
|
|
52
53
|
return [];
|
|
53
54
|
}
|
|
54
55
|
|
|
55
|
-
routeNotification({
|
|
56
|
+
await routeNotification({
|
|
56
57
|
type: input.type ?? "workspace.stranded_escalation",
|
|
57
58
|
scope: {
|
|
58
59
|
workspaceId: input.workspace.id,
|
|
@@ -84,13 +85,13 @@ export function routeWorkspaceCustodyEscalation(input: WorkspaceCustodyEscalatio
|
|
|
84
85
|
// undefined) and most `review_requested` auto-abandons have no agent to page by default,
|
|
85
86
|
// so without this the notice is swallowed and the strand loops in the dark. Returns the
|
|
86
87
|
// (possibly empty) recipient list so the caller can still decide whether to stamp state.
|
|
87
|
-
export function routeWorkspaceCustodyEscalationOrAlarm(
|
|
88
|
+
export async function routeWorkspaceCustodyEscalationOrAlarm(
|
|
88
89
|
input: WorkspaceCustodyEscalationInput & { source: string },
|
|
89
|
-
): string[] {
|
|
90
|
+
): Promise<string[]> {
|
|
90
91
|
const { source, ...escalation } = input;
|
|
91
|
-
const targets = routeWorkspaceCustodyEscalation(escalation);
|
|
92
|
+
const targets = await routeWorkspaceCustodyEscalation(escalation);
|
|
92
93
|
if (!targets.length) {
|
|
93
|
-
raiseOperatorAlarm({
|
|
94
|
+
(await raiseOperatorAlarm({
|
|
94
95
|
clientId: `${escalation.idempotencyKey ?? `${escalation.workspace.id}:${escalation.subject}`}:no-recipients`,
|
|
95
96
|
source,
|
|
96
97
|
subject: escalation.subject,
|
|
@@ -101,7 +102,7 @@ export function routeWorkspaceCustodyEscalationOrAlarm(
|
|
|
101
102
|
branch: escalation.workspace.branch,
|
|
102
103
|
status: escalation.workspace.status,
|
|
103
104
|
},
|
|
104
|
-
});
|
|
105
|
+
}));
|
|
105
106
|
}
|
|
106
107
|
return targets;
|
|
107
108
|
}
|
|
@@ -43,7 +43,7 @@ export function warnIfNoHumanReachableEscalationTarget(log: (message: string) =>
|
|
|
43
43
|
return true;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
export function notifyWorkspaceHumanEscalation(input: WorkspaceHumanEscalationInput): WorkspaceHumanEscalationResult {
|
|
46
|
+
export async function notifyWorkspaceHumanEscalation(input: WorkspaceHumanEscalationInput): Promise<WorkspaceHumanEscalationResult> {
|
|
47
47
|
const channelId = workspaceHumanEscalationChannelId();
|
|
48
48
|
if (!channelId) return { accepted: false, reason: "no human escalation channel configured" };
|
|
49
49
|
if (!getChannel(channelId)) return { accepted: false, reason: `human escalation channel ${channelId} is not registered` };
|
|
@@ -59,9 +59,9 @@ export function notifyWorkspaceHumanEscalation(input: WorkspaceHumanEscalationIn
|
|
|
59
59
|
|
|
60
60
|
try {
|
|
61
61
|
if (channelId === DEFAULT_NTFY_CHANNEL_ID) {
|
|
62
|
-
relayNotifyNtfy(event, input.idempotencyKey);
|
|
62
|
+
(await relayNotifyNtfy(event, input.idempotencyKey));
|
|
63
63
|
} else {
|
|
64
|
-
relayEgressToChannel({ channelId, event }, undefined, input.idempotencyKey);
|
|
64
|
+
(await relayEgressToChannel({ channelId, event }, undefined, input.idempotencyKey));
|
|
65
65
|
}
|
|
66
66
|
return { accepted: true, channelId };
|
|
67
67
|
} catch (error) {
|
|
@@ -41,21 +41,21 @@ export async function runLandGateFailureWatchdog(opts: { now?: number; delayMs?:
|
|
|
41
41
|
const coordinator = resolveCoordinatorAgentId(owner);
|
|
42
42
|
const target = coordinator ?? stewardFallbackTarget() ?? owner;
|
|
43
43
|
if (!target) {
|
|
44
|
-
raiseOperatorAlarm({
|
|
44
|
+
(await raiseOperatorAlarm({
|
|
45
45
|
clientId: `land-gate-failed-unescalatable:${fresh.id}:${failure.notifiedAt}`,
|
|
46
46
|
source: "workspace-land-gate-watchdog",
|
|
47
47
|
subject: "Land gate failure has no escalation recipient",
|
|
48
48
|
detail: `${fresh.branch ?? fresh.id} remains blocked by required gate ${failure.gateName ?? "(unknown)"} with no author, coordinator, or configured fallback.`,
|
|
49
49
|
view: "orchestrators",
|
|
50
50
|
metadata: { workspaceId: fresh.id, repoRoot: fresh.repoRoot, branch: fresh.branch, gateName: failure.gateName },
|
|
51
|
-
});
|
|
51
|
+
}));
|
|
52
52
|
await patchWorkspaceMetadataAsync(fresh.id, { landGateFailureEscalatedAt: now });
|
|
53
53
|
escalated.push(fresh.id);
|
|
54
54
|
continue;
|
|
55
55
|
}
|
|
56
56
|
const targetAgent = getAgent(target);
|
|
57
57
|
const enabled = notificationEnabledFor("workspace.land_gate_failed", { agentId: target, teamId: targetAgent?.teamId });
|
|
58
|
-
routeNotification({
|
|
58
|
+
await routeNotification({
|
|
59
59
|
type: "workspace.land_gate_failed",
|
|
60
60
|
scope: { author: owner, parent: coordinator ?? undefined, repoRoot: fresh.repoRoot, workspaceId: fresh.id },
|
|
61
61
|
recipients: [target],
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { WorkspaceRecord } from "@humwick/sdk";
|
|
2
|
+
import { updateWorkspaceStatusAsync } from "./db";
|
|
3
|
+
import { notifyBranchLandFailed } from "./branch-landed";
|
|
4
|
+
import { deferLandSettleSideEffect } from "./land-settle-dispatch";
|
|
5
|
+
import { landStoppedFact, recordLandStopped } from "./workspace-land-stopped";
|
|
6
|
+
|
|
7
|
+
export class LandConfigurationError extends Error {}
|
|
8
|
+
|
|
9
|
+
/** A new delivery arms a new hand-off; base movement cannot repair a refused configuration (#3824). */
|
|
10
|
+
export function workspaceLandRefused(workspace: WorkspaceRecord): boolean {
|
|
11
|
+
const stop = landStoppedFact(workspace.metadata);
|
|
12
|
+
return stop?.reason === "refused" && stop.handoff === workspace.metadata.landRequestAttemptAt;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function refuseWorkspaceLand(workspace: WorkspaceRecord, reason: string, commandId?: string): Promise<void> {
|
|
16
|
+
if (workspaceLandRefused(workspace)) return;
|
|
17
|
+
const current = await updateWorkspaceStatusAsync(workspace.id, "review_requested", { mergeError: reason });
|
|
18
|
+
await recordLandStopped(workspace.id, "refused", reason, { final: true });
|
|
19
|
+
deferLandSettleSideEffect("land-refused", () => notifyBranchLandFailed({ workspace: current ?? workspace, commandId, reason }));
|
|
20
|
+
}
|
package/src/workspace-merge.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { acquireMergeLeaseAsync, releaseMergeLeaseAsync } from "./db/runtime-tail-async";
|
|
2
|
+
import { LandConfigurationError, refuseWorkspaceLand } from "./workspace-land-refusal";
|
|
1
3
|
import { capturedDeliveryGateRefusal } from "./services/captured-delivery-gate";
|
|
2
4
|
import { ValidationError } from "./db/connection";
|
|
3
5
|
import { WORKSPACE_MERGE_HOLD_MESSAGE } from "./db/workspace-merge-hold";
|
|
@@ -6,19 +8,7 @@ import { resolve } from "node:path";
|
|
|
6
8
|
import { randomUUID } from "node:crypto";
|
|
7
9
|
import { errMessage, isRecord, stringValue, type LandingContainmentMode } from "@humwick/sdk";
|
|
8
10
|
import { cancelPendingLegacyLandingsAsync, createLegacyLandingAsync } from "./commands-landing-async";
|
|
9
|
-
import {
|
|
10
|
-
acquireMergeLease,
|
|
11
|
-
exitCertificateMatchesInstance,
|
|
12
|
-
getActiveMergePause,
|
|
13
|
-
getAgent,
|
|
14
|
-
listAgents,
|
|
15
|
-
listOrchestrators,
|
|
16
|
-
patchWorkspaceMetadataAsync,
|
|
17
|
-
releaseMergeLease,
|
|
18
|
-
REVIEW_HOLD_REJECT_MESSAGE,
|
|
19
|
-
updateWorkspaceStatusAsync,
|
|
20
|
-
workspaceLandBarrier,
|
|
21
|
-
} from "./db";
|
|
11
|
+
import { exitCertificateMatchesInstance, getActiveMergePause, getAgent, listAgents, listOrchestrators, patchWorkspaceMetadataAsync, REVIEW_HOLD_REJECT_MESSAGE, updateWorkspaceStatusAsync, workspaceLandBarrier } from "./db";
|
|
22
12
|
import { ensureLegacyOpenLandingOwnershipAsync } from "./db/landing-ingress-async";
|
|
23
13
|
import { OFFLINE_PRUNE_MS } from "./config";
|
|
24
14
|
import { getLandingConfig } from "./config-store";
|
|
@@ -44,8 +34,6 @@ import type { LandingLaneIdentity } from "./landing-lane-key";
|
|
|
44
34
|
import { archiveLandAttempt, CLEAR_LAST_ATTEMPT_VERDICT, CLEAR_MERGE_RETRY, heavyGateQueuePriorityForRetry } from "./workspace-merge-progress";
|
|
45
35
|
import { coalesceWorkspaceMerge } from "./workspace-merge-dispatch";
|
|
46
36
|
|
|
47
|
-
// Re-export so consumers can import the tuple from the strategy home without a
|
|
48
|
-
// second import hop.
|
|
49
37
|
export { AUTO_MERGE_POLICIES } from "./types";
|
|
50
38
|
|
|
51
39
|
// One home for the land-strategy contract, shared by BOTH land surfaces — the HTTP
|
|
@@ -393,12 +381,7 @@ async function dispatchWorkspaceMerge(workspace: WorkspaceRecord, opts: RequestW
|
|
|
393
381
|
// unauthorized one is refused outright instead of being silently ignored.
|
|
394
382
|
const gate = resolveWorkspaceLandGate(workspace, { level: opts.gateLevel, reason: opts.gateReason, auth: opts.gateAuth });
|
|
395
383
|
if (!gate.ok) return { ok: false, status: gate.status, error: gate.error };
|
|
396
|
-
// #638 —
|
|
397
|
-
// repo. The auto-merge job already honors it (workspace-auto-merge.ts), but gate the
|
|
398
|
-
// shared dispatcher too as defense-in-depth: never let an auto-merge-originated request
|
|
399
|
-
// acquire the lease + storm while paused, regardless of which caller dispatched it. A
|
|
400
|
-
// manual/steward land (any other `requestedBy`) is the intended RECOVERY path during a
|
|
401
|
-
// pause, so it's allowed through.
|
|
384
|
+
// #638 — auto-merge honors the repo pause here too; manual/steward lands remain a recovery path.
|
|
402
385
|
if (!plainGit && opts.requestedBy === "auto-merge" && getActiveMergePause(workspace.repoRoot, workspace.baseRef ?? "main")) {
|
|
403
386
|
return { ok: false, status: 409, error: `auto-merge paused for ${workspace.repoRoot}` };
|
|
404
387
|
}
|
|
@@ -420,9 +403,10 @@ async function dispatchWorkspaceMerge(workspace: WorkspaceRecord, opts: RequestW
|
|
|
420
403
|
let gatePlacement: Awaited<ReturnType<typeof selectLandGateHost>>;
|
|
421
404
|
try {
|
|
422
405
|
gatePlacement = await selectLandGateHost(workspace, onlineOwner, laneIdentity.remoteIdentity,
|
|
423
|
-
effectiveStrategy !== "pr" && effectiveStrategy !== "auto" && gate.gate.level !== "none" ? laneIdentity.gateRequirements : undefined);
|
|
406
|
+
effectiveStrategy !== "pr" && effectiveStrategy !== "auto" && gate.gate.level !== "none" ? laneIdentity.gateRequirements : undefined, laneIdentity.gateDepsStrategy);
|
|
424
407
|
} catch (error) {
|
|
425
|
-
|
|
408
|
+
if (error instanceof LandConfigurationError) await refuseWorkspaceLand(workspace, error.message);
|
|
409
|
+
return { ok: false, status: error instanceof LandConfigurationError ? 422 : 409, error: errMessage(error) };
|
|
426
410
|
}
|
|
427
411
|
const gateHost = gatePlacement.orchestrator;
|
|
428
412
|
const mergeLeaseKey = laneIdentity.remoteIdentity.startsWith("remote:") ? laneIdentity.remoteIdentity : `remote:${laneIdentity.remoteIdentity}`;
|
|
@@ -438,7 +422,7 @@ async function dispatchWorkspaceMerge(workspace: WorkspaceRecord, opts: RequestW
|
|
|
438
422
|
let leaseAcquired = false;
|
|
439
423
|
let commandCreated = false;
|
|
440
424
|
if (!plainGit || gatePlacement.delegated) {
|
|
441
|
-
const lease =
|
|
425
|
+
const lease = (await acquireMergeLeaseAsync(mergeLeaseKey, workspace.id, opts.requestedBy));
|
|
442
426
|
if (!lease.ok) {
|
|
443
427
|
if (containmentMode === "cgroup-v2-close-v1") {
|
|
444
428
|
await cleanupLegacyLandingReservationOnHost(gateHost, executionTreeHandle!).catch(() => false);
|
|
@@ -493,7 +477,7 @@ async function dispatchWorkspaceMerge(workspace: WorkspaceRecord, opts: RequestW
|
|
|
493
477
|
lastWorkspaceActionAt: armAt,
|
|
494
478
|
});
|
|
495
479
|
if (!updated) {
|
|
496
|
-
if (leaseAcquired)
|
|
480
|
+
if (leaseAcquired) (await releaseMergeLeaseAsync({ repoRoot: mergeLeaseKey, workspaceId: workspace.id }));
|
|
497
481
|
if (containmentMode === "cgroup-v2-close-v1") {
|
|
498
482
|
await cleanupLegacyLandingReservationOnHost(gateHost, executionTreeHandle!).catch(() => false);
|
|
499
483
|
}
|
|
@@ -604,7 +588,7 @@ async function dispatchWorkspaceMerge(workspace: WorkspaceRecord, opts: RequestW
|
|
|
604
588
|
} catch (e) {
|
|
605
589
|
// Acquired the lease but failed before dispatch — release it or the repo
|
|
606
590
|
// stays blocked until the TTL expires.
|
|
607
|
-
if (!commandCreated && leaseAcquired)
|
|
591
|
+
if (!commandCreated && leaseAcquired) (await releaseMergeLeaseAsync({ repoRoot: mergeLeaseKey, workspaceId: workspace.id }));
|
|
608
592
|
if (!commandCreated && containmentMode === "cgroup-v2-close-v1") {
|
|
609
593
|
await cleanupLegacyLandingReservationOnHost(gateHost, executionTreeHandle!).catch(() => false);
|
|
610
594
|
}
|
|
@@ -6,14 +6,14 @@ import { withWorkspaceTaskOwnerRoles } from "./task-owner-notices";
|
|
|
6
6
|
import { resolveLandedTaskIds } from "./services/landed-task-attribution";
|
|
7
7
|
import type { WorkspaceRecord } from "./types";
|
|
8
8
|
|
|
9
|
-
export function notifyNothingToLand(workspace: WorkspaceRecord, sha: string, commandId: string, sourceHeadSha?: string): void {
|
|
9
|
+
export async function notifyNothingToLand(workspace: WorkspaceRecord, sha: string, commandId: string, sourceHeadSha?: string): Promise<void> {
|
|
10
10
|
const author = workspace.ownerAgentId;
|
|
11
11
|
const coordinator = resolveCoordinatorAgentId(author);
|
|
12
12
|
const taskOwners = resolveLandedTaskIds({ workspace, sourceHeadSha }).map(id => resolveTaskOwnerAgentId(getTaskDetail(id)));
|
|
13
13
|
const recipients = withWorkspaceTaskOwnerRoles([author, coordinator, ...taskOwners], workspace);
|
|
14
14
|
if (!recipients.length) return;
|
|
15
15
|
const base = workspace.baseRef ?? "base";
|
|
16
|
-
routeNotification({
|
|
16
|
+
await routeNotification({
|
|
17
17
|
type: "branch.landed",
|
|
18
18
|
scope: { author, parent: coordinator ?? undefined, repoRoot: workspace.repoRoot, workspaceId: workspace.id },
|
|
19
19
|
recipients, forceDelivery: true, ttlMs: null,
|
package/src/workspace-orphans.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { pruneWorkspaceOrphanAlertsAsync, pruneStaleWorkspaceHumanEscalationAlertsAsync, claimWorkspaceOrphanAlertAsync } from "./db/runtime-tail-async";
|
|
2
|
+
import { dispatchOrphanCleanup } from "./workspace-cleanup-stale";
|
|
3
|
+
import { emitCommandEvent } from "./command-events";
|
|
1
4
|
// Orphaned-worktree reconciliation (#244). An isolated worktree on disk with no
|
|
2
5
|
// live DB row is "orphaned" — left behind when a session ended without a clean
|
|
3
6
|
// teardown (crash, killed runner, a reaped row). Symmetrically, a live row whose
|
|
@@ -20,8 +23,8 @@
|
|
|
20
23
|
|
|
21
24
|
import { resolve } from "node:path";
|
|
22
25
|
import type { WorkspaceMergePreview, WorkspaceOrphan, WorkspaceProbe, WorkspaceProbeWorktree, WorkspaceRecord, WorkspaceStatus } from "./types";
|
|
23
|
-
import {
|
|
24
|
-
|
|
26
|
+
import { createActivityEventAsync, getWorkspace, listOrchestrators, listWorkspaces, updateWorkspaceStatusAsync } from "./db";
|
|
27
|
+
|
|
25
28
|
import { isPathWithinBase } from "./utils";
|
|
26
29
|
import { IN_FLIGHT_WORKSPACE_STATUSES, TERMINAL_WORKSPACE_STATUSES, worktreeReapable, type WorktreeReapState } from "./workspace-phase";
|
|
27
30
|
import { isOwnerAlive, isOwnerProcessConfirmedGone, liveAgentCwds, worktreeInUseByLiveAgent } from "./workspace-merge";
|
|
@@ -33,7 +36,6 @@ import { READY_TO_LAND_ESCALATION_STATUSES, routeWorkspaceCustodyEscalationOrAla
|
|
|
33
36
|
import { workspaceLandBlocked } from "./land-block";
|
|
34
37
|
import { dispatchDeadOwnerRescue } from "./workspace-dead-owner-recovery";
|
|
35
38
|
import { runWithConcurrency, sortLeastRecentlyProbed } from "./maintenance/workspaces";
|
|
36
|
-
import { emitCommandEvent } from "./command-events";
|
|
37
39
|
import {
|
|
38
40
|
WORKSPACE_ORPHAN_REAPER_DEAD_OWNER_PROBE_BATCH_SIZE,
|
|
39
41
|
WORKSPACE_ORPHAN_REAPER_MISSING_PROBE_BATCH_SIZE,
|
|
@@ -421,27 +423,6 @@ export async function collectWorkspaceOrphans(
|
|
|
421
423
|
return { orphans, missingWorktrees, deadOwnerWorkspaces, ...(moreOrphanEnrichmentWork ? { moreOrphanEnrichmentWork } : {}) };
|
|
422
424
|
}
|
|
423
425
|
|
|
424
|
-
function dispatchCleanup(orch: OnlineOrchestrator, orphan: WorkspaceOrphan): string {
|
|
425
|
-
const command = createCommand({
|
|
426
|
-
type: "workspace.cleanup",
|
|
427
|
-
source: "system",
|
|
428
|
-
target: orch.agentId,
|
|
429
|
-
params: {
|
|
430
|
-
action: "cleanup",
|
|
431
|
-
worktreePath: orphan.worktreePath,
|
|
432
|
-
repoRoot: orphan.repoRoot,
|
|
433
|
-
branch: orphan.branch,
|
|
434
|
-
baseRef: orphan.baseRef,
|
|
435
|
-
baseSha: orphan.baseSha,
|
|
436
|
-
deleteBranch: true,
|
|
437
|
-
reclaim: true,
|
|
438
|
-
requestedBy: "workspace-orphan-reaper",
|
|
439
|
-
requestedAt: Date.now(),
|
|
440
|
-
},
|
|
441
|
-
});
|
|
442
|
-
emitCommandEvent(command, `command.${command.status}`);
|
|
443
|
-
return command.id;
|
|
444
|
-
}
|
|
445
426
|
|
|
446
427
|
/**
|
|
447
428
|
* Scheduled reaper (maintenance job). Auto-removes orphaned worktrees that are
|
|
@@ -520,7 +501,7 @@ export async function reapOrphanedWorktrees(context: { signal?: AbortSignal } =
|
|
|
520
501
|
if (!reapEnabled) continue; // detect-only mode
|
|
521
502
|
if (nonTerminalRowsByPath.has(resolve(orphan.worktreePath))) continue; // still claimed — never reap (#278)
|
|
522
503
|
if (worktreeInUseByLiveAgent(orphan.worktreePath, liveCwds)) continue; // a live agent runs in it — never reap its cwd
|
|
523
|
-
const commandId =
|
|
504
|
+
const commandId = (await dispatchOrphanCleanup(orch.agentId, orphan));
|
|
524
505
|
reaped.push(orphan.worktreePath);
|
|
525
506
|
await createActivityEventAsync({
|
|
526
507
|
clientId: `workspace-orphan-reaped-${orphan.worktreePath}-${now}`,
|
|
@@ -541,7 +522,7 @@ export async function reapOrphanedWorktrees(context: { signal?: AbortSignal } =
|
|
|
541
522
|
const alertKey = `disk:${resolve(orphan.worktreePath)}`;
|
|
542
523
|
liveAlertKeys.add(alertKey);
|
|
543
524
|
const fingerprint = orphanAlertFingerprint({ headSha: orphan.headSha, ahead: orphan.ahead, unmergedAhead: orphan.unmergedAhead, dirty: orphan.dirty, safeToReap: orphan.safeToReap });
|
|
544
|
-
if (!
|
|
525
|
+
if (!(await claimWorkspaceOrphanAlertAsync(alertKey, fingerprint, now))) continue;
|
|
545
526
|
flagged.push(orphan.worktreePath);
|
|
546
527
|
const detail = orphan.safeToReap === undefined
|
|
547
528
|
? "host could not be probed for land-state"
|
|
@@ -549,7 +530,7 @@ export async function reapOrphanedWorktrees(context: { signal?: AbortSignal } =
|
|
|
549
530
|
? "uncommitted changes in the worktree"
|
|
550
531
|
: `${orphan.unmergedAhead ?? orphan.ahead ?? "?"} un-landed commit(s)`;
|
|
551
532
|
if (orphanHasWorkEvidence(orphan)) {
|
|
552
|
-
routeWorkspaceCustodyEscalationOrAlarm({
|
|
533
|
+
await routeWorkspaceCustodyEscalationOrAlarm({
|
|
553
534
|
source: "workspace-orphan-reaper",
|
|
554
535
|
workspace: { id: `orphan:${orphan.worktreePath}`, repoRoot: orphan.repoRoot, branch: orphan.branch, status: "review_requested", ownerAgentId: undefined },
|
|
555
536
|
subject: "Stranded worktree needs attention",
|
|
@@ -586,9 +567,9 @@ export async function reapOrphanedWorktrees(context: { signal?: AbortSignal } =
|
|
|
586
567
|
const inFlight = IN_FLIGHT_WORKSPACE_STATUSES.has(workspace.status);
|
|
587
568
|
if (ownerAlive || inFlight) {
|
|
588
569
|
liveAlertKeys.add(key);
|
|
589
|
-
if (!
|
|
570
|
+
if (!(await claimWorkspaceOrphanAlertAsync(key, orphanAlertFingerprint({ status: workspace.status, ownerAlive, inFlight }), now))) continue;
|
|
590
571
|
if (workspaceNeedsCustodyEscalation(workspace, false)) {
|
|
591
|
-
routeWorkspaceCustodyEscalationOrAlarm({
|
|
572
|
+
await routeWorkspaceCustodyEscalationOrAlarm({
|
|
592
573
|
source: "workspace-orphan-reaper",
|
|
593
574
|
workspace,
|
|
594
575
|
subject: "Workspace row has no worktree on disk",
|
|
@@ -658,7 +639,7 @@ export async function reapOrphanedWorktrees(context: { signal?: AbortSignal } =
|
|
|
658
639
|
if (!updated) continue;
|
|
659
640
|
autoAbandoned.push(workspace.id);
|
|
660
641
|
if (workspaceNeedsCustodyEscalation(workspace, previewHasWorkEvidence(probe.kind === "preview" ? probe.preview : null))) {
|
|
661
|
-
routeWorkspaceCustodyEscalationOrAlarm({
|
|
642
|
+
await routeWorkspaceCustodyEscalationOrAlarm({
|
|
662
643
|
source: "workspace-orphan-reaper",
|
|
663
644
|
workspace,
|
|
664
645
|
type: "workspace.auto_abandoned",
|
|
@@ -690,7 +671,7 @@ export async function reapOrphanedWorktrees(context: { signal?: AbortSignal } =
|
|
|
690
671
|
|
|
691
672
|
liveAlertKeys.add(key);
|
|
692
673
|
const missingFingerprint = orphanAlertFingerprint({ status: workspace.status, probe: probe.kind, recoverableTip: probe.kind === "preview" ? probe.preview.recoverableTip : undefined, headSha: probe.kind === "preview" ? probe.preview.headSha : undefined, ahead: probe.kind === "preview" ? probe.preview.ahead : undefined, unmergedAhead: probe.kind === "preview" ? probe.preview.unmergedAhead : undefined });
|
|
693
|
-
if (!
|
|
674
|
+
if (!(await claimWorkspaceOrphanAlertAsync(key, missingFingerprint, now))) continue;
|
|
694
675
|
flaggedMissingWorktrees.push(workspace.id);
|
|
695
676
|
const hasWorkEvidence = probe.kind === "preview" && previewHasWorkEvidence(probe.preview);
|
|
696
677
|
const detail = probe.kind === "preview"
|
|
@@ -701,7 +682,7 @@ export async function reapOrphanedWorktrees(context: { signal?: AbortSignal } =
|
|
|
701
682
|
? `host could not confirm whether branch ${workspace.branch} has landed`
|
|
702
683
|
: "host could not confirm whether recoverable branch work remains";
|
|
703
684
|
if (workspaceNeedsCustodyEscalation(workspace, hasWorkEvidence)) {
|
|
704
|
-
routeWorkspaceCustodyEscalationOrAlarm({
|
|
685
|
+
await routeWorkspaceCustodyEscalationOrAlarm({
|
|
705
686
|
source: "workspace-orphan-reaper",
|
|
706
687
|
workspace,
|
|
707
688
|
subject: "Missing-worktree workspace needs attention",
|
|
@@ -846,10 +827,10 @@ export async function reapOrphanedWorktrees(context: { signal?: AbortSignal } =
|
|
|
846
827
|
liveDeadOwnerKeys.add(flagKey);
|
|
847
828
|
liveAlertKeys.add(flagKey);
|
|
848
829
|
const deadOwnerFingerprint = orphanAlertFingerprint({ status: workspace.status, headSha: preview?.headSha, ahead: preview?.ahead, unmergedAhead: preview?.unmergedAhead, dirtyCount: preview?.dirtyCount, probeAvailable: Boolean(preview) });
|
|
849
|
-
if (!
|
|
830
|
+
if (!(await claimWorkspaceOrphanAlertAsync(flagKey, deadOwnerFingerprint, now))) continue;
|
|
850
831
|
deadOwnerFlagged.push(workspace.id);
|
|
851
832
|
const rescueCommandId = preview?.originCaptureChecked === true && preview.originCaptured === false && orch
|
|
852
|
-
? dispatchDeadOwnerRescue(orch, workspace)
|
|
833
|
+
? (await dispatchDeadOwnerRescue(orch, workspace))
|
|
853
834
|
: undefined;
|
|
854
835
|
const detail = safeToReap === undefined
|
|
855
836
|
? "host could not be probed for land-state"
|
|
@@ -858,7 +839,7 @@ export async function reapOrphanedWorktrees(context: { signal?: AbortSignal } =
|
|
|
858
839
|
: `${preview?.unmergedAhead ?? preview?.ahead ?? "?"} un-landed commit(s)`;
|
|
859
840
|
const hasWorkEvidence = previewHasWorkEvidence(preview);
|
|
860
841
|
if (workspaceNeedsCustodyEscalation(workspace, hasWorkEvidence)) {
|
|
861
|
-
routeWorkspaceCustodyEscalationOrAlarm({
|
|
842
|
+
await routeWorkspaceCustodyEscalationOrAlarm({
|
|
862
843
|
source: "workspace-orphan-reaper",
|
|
863
844
|
workspace,
|
|
864
845
|
subject: "Dead-owner worktree needs attention",
|
|
@@ -896,8 +877,8 @@ export async function reapOrphanedWorktrees(context: { signal?: AbortSignal } =
|
|
|
896
877
|
// still waiting it out and re-fire orphan alerts already claimed for this condition.
|
|
897
878
|
if (!signal?.aborted) {
|
|
898
879
|
for (const key of deadOwnerTracker.keys()) if (!liveDeadOwnerKeys.has(key)) deadOwnerTracker.delete(key);
|
|
899
|
-
|
|
900
|
-
|
|
880
|
+
(await pruneWorkspaceOrphanAlertsAsync(liveAlertKeys));
|
|
881
|
+
(await pruneStaleWorkspaceHumanEscalationAlertsAsync(now, HUMAN_ESCALATION_ALERT_MAX_AGE_MS));
|
|
901
882
|
}
|
|
902
883
|
|
|
903
884
|
// #2408 — true whenever ANY of the three per-tick probe budgets left candidates un-probed this run.
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { createActivityEventAsync } from "./db/activity-async";
|
|
2
|
+
import { createCommandAsync } from "./commands-write";
|
|
1
3
|
import { isRecord } from "@humwick/sdk";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
+
import { getCommand } from "./commands-db";
|
|
5
|
+
import { patchWorkspaceMetadataAsync, workspaceLandBarrier } from "./db";
|
|
4
6
|
import { handleReviewerPipelineScan, isAwaitingReviewerApproval } from "./reviewer-pipeline";
|
|
5
7
|
import { isOwnerAlive, requestWorkspaceMerge } from "./workspace-merge";
|
|
6
8
|
import type { Command, WorkspaceAutoMergePolicy, WorkspaceMergePreview, WorkspaceRecord } from "./types";
|
|
@@ -39,7 +41,7 @@ export async function reconcileLandedPr(workspace: WorkspaceRecord, preview: Wor
|
|
|
39
41
|
},
|
|
40
42
|
});
|
|
41
43
|
if (recycle.ok) {
|
|
42
|
-
|
|
44
|
+
(await createActivityEventAsync({
|
|
43
45
|
clientId: `workspace-pr-recycle-${workspace.id}-${Date.now()}`,
|
|
44
46
|
kind: "state",
|
|
45
47
|
title: "Workspace recycling after PR land",
|
|
@@ -48,7 +50,7 @@ export async function reconcileLandedPr(workspace: WorkspaceRecord, preview: Wor
|
|
|
48
50
|
icon: "ti-git-merge",
|
|
49
51
|
view: "orchestrators",
|
|
50
52
|
metadata: { source: "server", maintenanceJobId: "workspace-conflict-scan", workspaceId: workspace.id, commandId: recycle.command.id, landedVia: "pr" },
|
|
51
|
-
});
|
|
53
|
+
}));
|
|
52
54
|
return { kind: "recycle", command: recycle.command };
|
|
53
55
|
}
|
|
54
56
|
// 409 = lease held by an in-flight merge — retry next sweep, don't finalize (re-strands a
|
|
@@ -199,7 +201,7 @@ async function dispatchRelayPrMergeCommand(workspace: WorkspaceRecord, preview:
|
|
|
199
201
|
const now = Date.now();
|
|
200
202
|
const prNumber = preview.prNumber ?? (typeof workspace.metadata.prNumber === "number" ? workspace.metadata.prNumber : undefined);
|
|
201
203
|
const prUrl = preview.prUrl ?? text(workspace.metadata.prUrl);
|
|
202
|
-
const command =
|
|
204
|
+
const command = (await createCommandAsync({
|
|
203
205
|
type: "workspace.pr-merge",
|
|
204
206
|
source: "system",
|
|
205
207
|
target: orchestratorAgentId,
|
|
@@ -209,7 +211,7 @@ async function dispatchRelayPrMergeCommand(workspace: WorkspaceRecord, preview:
|
|
|
209
211
|
branch: workspace.branch, baseRef: workspace.baseRef, prNumber, prUrl, policy,
|
|
210
212
|
requestedBy: "relay-pr-completion", requestedAt: now,
|
|
211
213
|
},
|
|
212
|
-
});
|
|
214
|
+
}));
|
|
213
215
|
await patchWorkspaceMetadataAsync(workspace.id, {
|
|
214
216
|
relayMerged: true, relayMergeCommandId: command.id, relayMergeDispatchedAt: now, relayMergePolicy: policy,
|
|
215
217
|
relayMergeError: undefined, relayMergeFailedAt: undefined,
|
|
@@ -221,7 +223,7 @@ async function dispatchRelayPrRefreshCommand(workspace: WorkspaceRecord, preview
|
|
|
221
223
|
const now = Date.now();
|
|
222
224
|
const prNumber = preview.prNumber ?? (typeof workspace.metadata.prNumber === "number" ? workspace.metadata.prNumber : undefined);
|
|
223
225
|
const prUrl = preview.prUrl ?? text(workspace.metadata.prUrl);
|
|
224
|
-
const command =
|
|
226
|
+
const command = (await createCommandAsync({
|
|
225
227
|
type: "workspace.pr-refresh",
|
|
226
228
|
source: "system",
|
|
227
229
|
target: orchestratorAgentId,
|
|
@@ -231,7 +233,7 @@ async function dispatchRelayPrRefreshCommand(workspace: WorkspaceRecord, preview
|
|
|
231
233
|
branch: workspace.branch, baseRef: workspace.baseRef, prNumber, prUrl, policy, refreshKey, attempts,
|
|
232
234
|
requestedBy: "relay-pr-completion", requestedAt: now,
|
|
233
235
|
},
|
|
234
|
-
});
|
|
236
|
+
}));
|
|
235
237
|
await patchWorkspaceMetadataAsync(workspace.id, {
|
|
236
238
|
prRefreshCommandId: command.id, prRefreshDispatchedAt: now, prRefreshPolicy: policy, prRefreshPrNumber: prNumber,
|
|
237
239
|
prRefreshKey: refreshKey, prRefreshAttempts: attempts, prRefreshError: undefined, prRefreshFailedAt: undefined,
|
package/src/workspace-rekey.ts
CHANGED
|
@@ -55,7 +55,7 @@ export function fenceWorkspaceRekeyCommand(commandId: string): Promise<{ fenced:
|
|
|
55
55
|
export async function drainStewardRekeyNotices(): Promise<void> {
|
|
56
56
|
for (const row of pendingStewardRekeyNotices()) {
|
|
57
57
|
try {
|
|
58
|
-
const routed = notifyTaskCoordinatorOfRekey(row);
|
|
58
|
+
const routed = await notifyTaskCoordinatorOfRekey(row);
|
|
59
59
|
if (routed) await getGateway().write("workspaceRekey.noticeSent", { eventId: row.id });
|
|
60
60
|
} catch (error) {
|
|
61
61
|
console.warn(`[steward-rekey] notice for task event ${row.id} failed; retrying later: ${error}`);
|
|
@@ -63,7 +63,7 @@ export async function drainStewardRekeyNotices(): Promise<void> {
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
function notifyTaskCoordinatorOfRekey(row: StewardRekeyNoticeRow): boolean {
|
|
66
|
+
async function notifyTaskCoordinatorOfRekey(row: StewardRekeyNoticeRow): Promise<boolean> {
|
|
67
67
|
const task = getTaskDetail(row.task_id);
|
|
68
68
|
if (!task) return false;
|
|
69
69
|
const m = parseJson<Record<string, unknown>>(row.metadata, {});
|
|
@@ -82,7 +82,7 @@ function notifyTaskCoordinatorOfRekey(row: StewardRekeyNoticeRow): boolean {
|
|
|
82
82
|
...(rewritten.length ? [`${rewritten.length} commit(s) changed patch identity`] : []),
|
|
83
83
|
...(merges.length ? [`${merges.length} merge commit(s) were re-resolved and cannot be verified by patch-id`] : []),
|
|
84
84
|
];
|
|
85
|
-
const delivered = routeNotification({
|
|
85
|
+
const delivered = await routeNotification({
|
|
86
86
|
type: "task.steward_rekey",
|
|
87
87
|
scope: {
|
|
88
88
|
taskId: String(task.id), transition: "steward-rekey", workspaceId,
|
|
@@ -30,13 +30,13 @@ export async function recordStewardWakeFailure(workspace: WorkspaceRecord, error
|
|
|
30
30
|
const body = `Workspace \`${ws.id}\` (\`${ws.branch ?? ws.id}\` → \`${ws.baseRef ?? "base"}\`): ${error}. Relay will retry after ${Math.ceil(STEWARD_HANDOFF_WATCHDOG_MS / 60_000)} minutes. ${stewardRecoveryAdvice(ws.baseRef ?? "main", capExhausted)}`;
|
|
31
31
|
const payload = { kind: "workspace.steward-spawn-failed", workspaceId: ws.id, branch: ws.branch, reason: error };
|
|
32
32
|
if (recipients.length) {
|
|
33
|
-
routeNotification({
|
|
33
|
+
await routeNotification({
|
|
34
34
|
type: "workspace.stranded_escalation", forceDelivery: true, ttlMs: null,
|
|
35
35
|
scope: { workspaceId: ws.id, repoRoot: ws.repoRoot, author: ownerAgentId, parent: coordinatorAgentId ?? undefined },
|
|
36
36
|
recipients, message: { subject, body, payload, idempotencyKey: `steward-spawn-failed:${ws.id}:${failureId}` },
|
|
37
37
|
});
|
|
38
38
|
} else {
|
|
39
|
-
raiseOperatorAlarm({ clientId: `steward-spawn-failed:${ws.id}:${failureId}`, source: "workspace-steward-failure", subject, detail: body, dedupeEvent: true, metadata: payload });
|
|
39
|
+
(await raiseOperatorAlarm({ clientId: `steward-spawn-failed:${ws.id}:${failureId}`, source: "workspace-steward-failure", subject, detail: body, dedupeEvent: true, metadata: payload }));
|
|
40
40
|
}
|
|
41
41
|
await patchWorkspaceMetadataAsync(ws.id, { stewardWakeFailureNotifiedAt: now });
|
|
42
42
|
}
|
|
@@ -92,7 +92,7 @@ export async function wakeRepoSteward(ws: WorkspaceRecord, reason: string, opts:
|
|
|
92
92
|
stewardHandoffEscalatedAt: undefined, stewardRewakeCount: undefined,
|
|
93
93
|
} : {}),
|
|
94
94
|
});
|
|
95
|
-
routeNotification({
|
|
95
|
+
await routeNotification({
|
|
96
96
|
type: "workspace.steward_task",
|
|
97
97
|
scope: { workspaceId: ws.id, repoRoot: ws.repoRoot, author: ownerAgentId, parent: coordinatorAgentId ?? undefined },
|
|
98
98
|
recipients: [`policy:${policyName}`],
|
|
@@ -139,7 +139,7 @@ export async function wakeRepoSteward(ws: WorkspaceRecord, reason: string, opts:
|
|
|
139
139
|
}, attempt < STEWARD_WAKE_STAMP_ATTEMPTS ? current : undefined);
|
|
140
140
|
if (!stamped) continue;
|
|
141
141
|
if (current.landState === "merge-error") await transitionWorkspaceLandStateAsync(ws.id, "steward-requested");
|
|
142
|
-
notifyStewardHandoff({ workspace: ws, stewardId: policyName, coordinatorAgentId, reason });
|
|
142
|
+
await notifyStewardHandoff({ workspace: ws, stewardId: policyName, coordinatorAgentId, reason });
|
|
143
143
|
return { ok: true, policy: policyName };
|
|
144
144
|
}
|
|
145
145
|
} catch (e) {
|