@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
|
@@ -5,18 +5,20 @@ import { listOrchestratorsAsync } from "./db/orchestrators-async";
|
|
|
5
5
|
import { listHostViewsAsync } from "./db/hosts-async";
|
|
6
6
|
import { providerQuotaBandsFor, advertisedProvidersFrom } from "./spawn-targets";
|
|
7
7
|
import { dispatchPolicyFor } from "./routing-policy-store";
|
|
8
|
-
import { providerPlacementReady } from "./spawn-placement";
|
|
8
|
+
import { providerPlacementReady, spawnHostCapAdmission } from "./spawn-placement";
|
|
9
9
|
import { apiOverflowContextAsync, CLAUDE_API_PROVIDER } from "./claude-api-overflow";
|
|
10
10
|
import { apiExpiryPaceAsync } from "./claude-api-expiry";
|
|
11
11
|
import { liveTargetAdmission } from "./dispatch-availability";
|
|
12
12
|
|
|
13
13
|
/** Reader-gateway shell for production callers. The ranking core remains the pure sync function. */
|
|
14
|
-
export type DispatchSnapshot = DispatchContext & Required<Pick<DispatchContext, "now" | "catalog" | "bands" | "policy" | "preferences" | "advertisedProviders" | "targetAvailable">> & { probeUncertainty: Set<string
|
|
14
|
+
export type DispatchSnapshot = DispatchContext & Required<Pick<DispatchContext, "now" | "catalog" | "bands" | "policy" | "preferences" | "advertisedProviders" | "targetAvailable">> & { probeUncertainty: Set<string>; pinnedHostAtAgentCap?: boolean };
|
|
15
15
|
|
|
16
16
|
export async function captureDispatchContext(ctx: Omit<DispatchContext, "policy" | "bands" | "catalog"> = {}): Promise<DispatchSnapshot> {
|
|
17
17
|
const now = ctx.now ?? Date.now();
|
|
18
18
|
const [snapshot, orchestrators, hostViews] = await Promise.all([providerQuotaRoutingSnapshotAsync(now), listOrchestratorsAsync(), listHostViewsAsync()]);
|
|
19
19
|
const { targetAdmission: assess, targetUnavailableReason } = liveTargetAdmission(orchestrators, hostViews, snapshot.catalog, ctx.orchestratorId, ctx.spawnCap);
|
|
20
|
+
const pinnedHost = orchestrators.find(host => host.id === ctx.orchestratorId);
|
|
21
|
+
const pinnedView = pinnedHost && hostViews.find(view => view.id === pinnedHost.hostId);
|
|
20
22
|
const probeUncertainty = new Set<string>();
|
|
21
23
|
const targetAdmission = (target: Parameters<typeof assess>[0]) => {
|
|
22
24
|
const result = assess(target);
|
|
@@ -42,6 +44,7 @@ export async function captureDispatchContext(ctx: Omit<DispatchContext, "policy"
|
|
|
42
44
|
targetUnavailableReason,
|
|
43
45
|
targetAdmission,
|
|
44
46
|
probeUncertainty,
|
|
47
|
+
pinnedHostAtAgentCap: Boolean(pinnedView && spawnHostCapAdmission(ctx.spawnCap)(pinnedView)),
|
|
45
48
|
bands,
|
|
46
49
|
policy: dispatchPolicyFor(snapshot.policy),
|
|
47
50
|
catalog: snapshot.catalog,
|
package/src/task-dispatcher.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isOpenAiApiModel } from "@humwick/sdk";
|
|
1
|
+
import { isOpenAiApiModel, ValidationError } from "@humwick/sdk";
|
|
2
2
|
// #839 — the declarative task dispatcher.
|
|
3
3
|
//
|
|
4
4
|
// A pure function from (Task work-shape + live system state) → a concrete spawn
|
|
@@ -311,7 +311,7 @@ export function dispatchTask(shape: TaskDispatch | undefined, pin: DispatchPin =
|
|
|
311
311
|
if (ctx.mustResolveAvailable && bandRank(bandFor(target.provider, bands)) > bandRank("normal")) warnings.push(`[CRITICAL] mustResolveAvailable: degraded to ${target.provider} (${bandFor(target.provider, bands)} band). Contention likely.`);
|
|
312
312
|
}
|
|
313
313
|
if (ctx.targetAvailable && !ctx.targetAvailable(target)) {
|
|
314
|
-
throw new
|
|
314
|
+
throw new ValidationError(ctx.targetUnavailableReason?.(target) ?? `No eligible online host runs ${target.provider}/${target.model ?? "default"}${target.effort ? ` at effort ${target.effort}` : ""}.`);
|
|
315
315
|
}
|
|
316
316
|
if (factors.preferences) factors.preferences = { ...factors.preferences, excluded: preferenceExclusions(preferences, factors.ranking, (p) => eligibility(p) ?? (!advertised.has(p) ? "not-advertised" : undefined)) };
|
|
317
317
|
// Record the FINAL provider's band + the chosen model's window for transparency.
|
package/src/token-db.ts
CHANGED
|
@@ -324,8 +324,8 @@ export function getTokenProfile(id: string): TokenProfile | null {
|
|
|
324
324
|
return row ? rowToProfile(row) : null;
|
|
325
325
|
}
|
|
326
326
|
|
|
327
|
-
export function upsertTokenProfile(input: CreateTokenProfileInput): TokenProfile {
|
|
328
|
-
const now =
|
|
327
|
+
export function upsertTokenProfile(input: CreateTokenProfileInput, submittedAt: number = Date.now()): TokenProfile {
|
|
328
|
+
const now = submittedAt;
|
|
329
329
|
const id = input.id ?? slugify(input.name);
|
|
330
330
|
const existing = getTokenProfile(id);
|
|
331
331
|
if (existing?.builtIn) throw new Error("built-in token profiles cannot be modified");
|
package/src/token-usage-async.ts
CHANGED
|
@@ -5,12 +5,10 @@ import type { Message } from "./types";
|
|
|
5
5
|
// of src/db directly (the transport-thinness ratchet, #348).
|
|
6
6
|
import { getGateway } from "./db/gateway.ts";
|
|
7
7
|
import type { SpawnBudgetReadback, WorkflowRunBudgetReadback } from "./db/spawn-budget.ts";
|
|
8
|
-
import type { aggregateSessionTokenUsage, recordSessionTokenUsage, SessionTokenUsageAggregate
|
|
8
|
+
import type { aggregateSessionTokenUsage, recordSessionTokenUsage, SessionTokenUsageAggregate } from "./db/token-usage.ts";
|
|
9
9
|
|
|
10
10
|
export const recordSessionTokenUsageAsync = async (input: Parameters<typeof recordSessionTokenUsage>[0]) => {
|
|
11
|
-
const { budgetNotices, ...result } = await getGateway().write("tokenUsage.record", { input }, { critical: false }) as unknown as {
|
|
12
|
-
record: SessionTokenUsageRecord;
|
|
13
|
-
inserted: boolean;
|
|
11
|
+
const { budgetNotices, ...result } = await getGateway().write("tokenUsage.record", { input }, { critical: false }) as unknown as ReturnType<typeof recordSessionTokenUsage> & {
|
|
14
12
|
budgetNotices: Message[];
|
|
15
13
|
};
|
|
16
14
|
publishBudgetNotices(budgetNotices);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { framed } from "./hub-metrics";
|
|
1
2
|
import { userSinkId, canonicalHumanAgentId } from "@humwick/sdk";
|
|
2
3
|
import { listUsers } from "./db/users";
|
|
3
|
-
import { listPendingReplyObligationsAsync } from "./db/message-reads-async";
|
|
4
|
-
import { getLatestMessageIdAsync } from "./db/message-reads-async";
|
|
4
|
+
import { listPendingReplyObligationsAsync, getLatestMessageIdAsync } from "./db/message-reads-async";
|
|
5
5
|
import { optedInNotificationRecipients } from "./db/notification-subscriptions";
|
|
6
6
|
import { routeNotification } from "./notification-router";
|
|
7
7
|
import { subscribeRelayEvents } from "./events";
|
|
@@ -12,7 +12,7 @@ export async function reconcileUserAwaitingReply(previous?: string): Promise<str
|
|
|
12
12
|
const [pending, latestId] = await Promise.all([Promise.all(listUsers().map(user => listPendingReplyObligationsAsync(canonicalHumanAgentId(userSinkId(user.id)), 1, true))).then(rows => rows.flat()), getLatestMessageIdAsync()]);
|
|
13
13
|
const signature = JSON.stringify([recipients, pending.length > 0]);
|
|
14
14
|
if (signature === previous) return signature;
|
|
15
|
-
routeNotification({
|
|
15
|
+
await routeNotification({
|
|
16
16
|
type: "user.awaiting_reply", scope: {}, recipients: [],
|
|
17
17
|
message: {
|
|
18
18
|
body: pending.length ? "The operator has an unanswered message." : "The operator has no unanswered messages.",
|
|
@@ -30,13 +30,13 @@ export function startUserAwaitingReply(): () => void {
|
|
|
30
30
|
const unsubscribe = subscribeRelayEvents(event => {
|
|
31
31
|
if (event.type.startsWith("message.") && event.data.to !== "hue:office") dirty = true;
|
|
32
32
|
});
|
|
33
|
-
const timer = setInterval(() => {
|
|
33
|
+
const timer = setInterval(framed("timer user-awaiting-reply", () => {
|
|
34
34
|
ticks++;
|
|
35
35
|
if ((!dirty && ticks % 30 !== 0) || running) return;
|
|
36
36
|
dirty = false; running = true;
|
|
37
37
|
void reconcileUserAwaitingReply(signature).then(value => { signature = value; }).catch(error => {
|
|
38
38
|
dirty = true; console.error("user.awaiting_reply reconciliation failed", error);
|
|
39
39
|
}).finally(() => { running = false; });
|
|
40
|
-
}, 1000);
|
|
40
|
+
}), 1000);
|
|
41
41
|
return () => { clearInterval(timer); unsubscribe(); };
|
|
42
42
|
}
|
package/src/utils.ts
CHANGED
|
@@ -40,3 +40,10 @@ export function isOrchestratorLocalToRelay(orchestrator: { hostname?: string | n
|
|
|
40
40
|
const self = hostname();
|
|
41
41
|
return sameHostIdentity(orchestrator.hostname, self) || sameHostIdentity(orchestrator.id, self);
|
|
42
42
|
}
|
|
43
|
+
|
|
44
|
+
/** An orchestrator id from a hostname or operator input: dots and other separators become dashes. */
|
|
45
|
+
export function sanitizeOrchestratorId(value: string): string {
|
|
46
|
+
const clean = value.trim().replace(/\./g, "-").replace(/[^A-Za-z0-9_.-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
47
|
+
if (!clean) throw new Error("--id must contain at least one letter or number");
|
|
48
|
+
return clean.slice(0, 120);
|
|
49
|
+
}
|
package/src/workspace-actions.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { prepareCommandWriteAsync, createCommandAsync, submitPreparedCommandAsync } from "./commands-write";
|
|
2
|
+
import type { ReviewCleanupInput } from "./db/review-publish-write";
|
|
3
|
+
import { getGateway } from "./db/gateway";
|
|
1
4
|
import { recordWorkspaceAudit } from "./workspace-action-audit";
|
|
2
5
|
import { WORKSPACE_MERGE_HOLD_MESSAGE } from "./db/workspace-merge-hold";
|
|
3
6
|
import { stewardGenerationCanceled } from "./db/workspace-steward-fence";
|
|
@@ -14,22 +17,13 @@ import { stringValue } from "@humwick/sdk";
|
|
|
14
17
|
// `requestWorkspaceMerge`, the core does not emit the command event; it returns
|
|
15
18
|
// the Command and the caller emits via its own `emitCommand` (kept module-local).
|
|
16
19
|
|
|
17
|
-
|
|
20
|
+
|
|
18
21
|
import { requestWorkspaceRekey } from "./workspace-rekey";
|
|
19
22
|
import { isWorkspaceSteward, workspaceStewardRefusal } from "./workspace-steward-authority";
|
|
20
23
|
import { getStewardConfig } from "./config-store";
|
|
21
24
|
import { cancelPendingLegacyLandingsAsync } from "./commands-landing-async";
|
|
22
25
|
import { workspaceOwnerOrchestrator } from "./workspace-host";
|
|
23
|
-
import {
|
|
24
|
-
getWorkspace,
|
|
25
|
-
claimWorkspaceMetadataAsync,
|
|
26
|
-
listOrchestrators,
|
|
27
|
-
patchWorkspaceMetadataAsync,
|
|
28
|
-
releaseWorkspaceClaimMetadataAsync,
|
|
29
|
-
setWorkspaceMergeHoldAsync,
|
|
30
|
-
transitionWorkspaceLandStateAsync,
|
|
31
|
-
updateWorkspaceStatusAsync,
|
|
32
|
-
} from "./db";
|
|
26
|
+
import { getWorkspace, claimWorkspaceMetadataAsync, listOrchestrators, patchWorkspaceMetadataAsync, releaseWorkspaceClaimMetadataAsync, setWorkspaceMergeHoldAsync, transitionWorkspaceLandStateAsync, updateWorkspaceStatusAsync, REVIEW_HOLD_REJECT_MESSAGE, workspaceLandBarrier } from "./db";
|
|
33
27
|
import { isOwnerAlive, requestWorkspaceMerge } from "./workspace-merge";
|
|
34
28
|
import { buildWorkspaceCleanupCommand } from "./workspace-cleanup-stale";
|
|
35
29
|
// Re-export the land-strategy contract so the MCP land tool validates against the same
|
|
@@ -38,15 +32,13 @@ export { LAND_STRATEGIES } from "./workspace-merge";
|
|
|
38
32
|
import { claimMetadataPatch, workspaceActiveClaim, workspaceStewardHandbackPending } from "./workspace-claim";
|
|
39
33
|
import { DIRTY_WORKTREE_LAND_SKIP_REASON, LAND_IN_FLIGHT_STATUSES, READY_TO_LAND_STATUSES, TERMINAL_WORKSPACE_STATUSES, worktreeReapable } from "./workspace-phase";
|
|
40
34
|
import { LAND_BLOCK_REJECT_MESSAGE, stripReservedWorkspaceMetadataKeys } from "./land-block";
|
|
41
|
-
import { REVIEW_HOLD_REJECT_MESSAGE, workspaceLandBarrier } from "./db";
|
|
42
35
|
import { CLEAR_MERGE_NO_PROGRESS, scheduleRepoMerge } from "./workspace-auto-merge";
|
|
43
36
|
import { archiveLandAttempt, CLEAR_LAST_ATTEMPT_VERDICT, CLEAR_MERGE_RETRY } from "./workspace-merge-progress";
|
|
44
37
|
import { fetchHostMergePreview, refreshWorkspaceGitState } from "./workspace-probe";
|
|
45
38
|
import { recordLandStopped } from "./workspace-land-stopped";
|
|
46
39
|
import { finishWorkspaceStewardHandoff } from "./steward-handoff";
|
|
47
40
|
import { stewardHandoffIdentity } from "./steward-provenance";
|
|
48
|
-
import type { Command, WorkspaceAutoMergePolicy, WorkspaceLandingPolicy, WorkspaceMergeStrategy, WorkspaceRecord, WorkspaceStatus } from "./types";
|
|
49
|
-
import type { WorkspaceLandGateLevel } from "./types";
|
|
41
|
+
import type { Command, WorkspaceAutoMergePolicy, WorkspaceLandingPolicy, WorkspaceMergeStrategy, WorkspaceRecord, WorkspaceStatus, WorkspaceLandGateLevel } from "./types";
|
|
50
42
|
import type { WorkspaceLandGateAuth } from "./workspace-land-gate";
|
|
51
43
|
|
|
52
44
|
// Single source of truth for the action verb set. The route's `optionalEnum` and
|
|
@@ -326,7 +318,7 @@ export async function applyWorkspaceAction(workspace: WorkspaceRecord, input: Ap
|
|
|
326
318
|
return { ok: false, httpStatus: 422, error: `workspace ${workspace.id} has no isolated worktree to refresh` };
|
|
327
319
|
}
|
|
328
320
|
const checkOnly = input.checkOnly === true;
|
|
329
|
-
const built = buildWorkspaceDepsRefreshCommand(workspace, agentId ?? "agent", checkOnly);
|
|
321
|
+
const built = (await buildWorkspaceDepsRefreshCommand(workspace, agentId ?? "agent", checkOnly));
|
|
330
322
|
if (!built.ok) return { ok: false, httpStatus: built.status, error: built.error };
|
|
331
323
|
await recordWorkspaceAudit({
|
|
332
324
|
action,
|
|
@@ -416,11 +408,11 @@ export async function applyWorkspaceAction(workspace: WorkspaceRecord, input: Ap
|
|
|
416
408
|
error: `workspace ${workspace.id} cannot be cleaned up safely: ${safety.error}; pass force:true with a detail reason only if intentionally discarding work`,
|
|
417
409
|
};
|
|
418
410
|
}
|
|
419
|
-
const built = buildWorkspaceCleanupCommand(workspace, agentId ?? "dashboard", {
|
|
411
|
+
const built = (await buildWorkspaceCleanupCommand(workspace, agentId ?? "dashboard", {
|
|
420
412
|
force: input.force === true,
|
|
421
413
|
reason: detail,
|
|
422
414
|
autoReclaim: input.autoReclaim,
|
|
423
|
-
});
|
|
415
|
+
}));
|
|
424
416
|
if (!built.ok) return { ok: false, httpStatus: built.status, error: built.error };
|
|
425
417
|
command = built.command;
|
|
426
418
|
}
|
|
@@ -496,17 +488,17 @@ export async function applyWorkspaceAction(workspace: WorkspaceRecord, input: Ap
|
|
|
496
488
|
// captured delivered `sha`. #1452 round-5 H4: the relay deliberately does NOT derive or return a
|
|
497
489
|
// ref — the AUTHORITATIVE ref is whatever the HOST reports back on the command result (the host may
|
|
498
490
|
// run a different sanitizer version), and the ledger records it only after that settlement.
|
|
499
|
-
export function buildWorkspacePublishReviewCommand(
|
|
491
|
+
export async function buildWorkspacePublishReviewCommand(
|
|
500
492
|
workspace: WorkspaceRecord,
|
|
501
493
|
requestedBy: string,
|
|
502
|
-
opts: { taskId?: number; sha?: string; reviewRef?: string } = {},
|
|
503
|
-
): { ok: true; command: Command } | { ok: false; status: number; error: string } {
|
|
494
|
+
opts: { taskId?: number; sha?: string; reviewRef?: string; cleanup?: ReviewCleanupInput } = {},
|
|
495
|
+
): Promise<{ ok: true; command: Command } | { ok: false; status: number; error: string }> {
|
|
504
496
|
if (workspace.mode !== "isolated" || !workspace.worktreePath) {
|
|
505
497
|
return { ok: false, status: 422, error: `workspace ${workspace.id} has no worktree to publish for review` };
|
|
506
498
|
}
|
|
507
499
|
const owner = workspaceOwnerOrchestrator(workspace, listOrchestrators(), { onlineOnly: false });
|
|
508
500
|
if (!owner) return { ok: false, status: 409, error: "no orchestrator owns this workspace path" };
|
|
509
|
-
const
|
|
501
|
+
const prepared = await prepareCommandWriteAsync({
|
|
510
502
|
type: "workspace.publish-review",
|
|
511
503
|
source: "system",
|
|
512
504
|
target: owner.agentId,
|
|
@@ -527,6 +519,9 @@ export function buildWorkspacePublishReviewCommand(
|
|
|
527
519
|
queued: owner.status !== "online",
|
|
528
520
|
},
|
|
529
521
|
});
|
|
522
|
+
const command = opts.cleanup
|
|
523
|
+
? await getGateway().write("commands.createReviewPublish", { command: prepared, cleanup: opts.cleanup }) as unknown as Command
|
|
524
|
+
: await submitPreparedCommandAsync(prepared);
|
|
530
525
|
return { ok: true, command };
|
|
531
526
|
}
|
|
532
527
|
|
|
@@ -537,10 +532,10 @@ export function buildWorkspacePublishReviewCommand(
|
|
|
537
532
|
// caller passes the EXACT host-confirmed `ref` from the ledger and the host deletes THAT ref verbatim
|
|
538
533
|
// (after asserting it is under refs/review/*) — it no longer re-derives the target from branch/
|
|
539
534
|
// workspaceId, which could diverge across a relay/host sanitizer skew and leak the real ref.
|
|
540
|
-
export function buildReviewRefUnpublishCommandFromFields(
|
|
535
|
+
export async function buildReviewRefUnpublishCommandFromFields(
|
|
541
536
|
fields: { workspaceId: string; repoRoot: string; worktreePath?: string; branch?: string; orchestratorId?: string; ref?: string },
|
|
542
537
|
requestedBy: string,
|
|
543
|
-
): { ok: true; command: Command } | { ok: false; status: number; error: string } {
|
|
538
|
+
): Promise<{ ok: true; command: Command } | { ok: false; status: number; error: string }> {
|
|
544
539
|
const synthetic = {
|
|
545
540
|
orchestratorId: fields.orchestratorId,
|
|
546
541
|
sourceCwd: fields.repoRoot,
|
|
@@ -550,7 +545,7 @@ export function buildReviewRefUnpublishCommandFromFields(
|
|
|
550
545
|
} as unknown as WorkspaceRecord;
|
|
551
546
|
const owner = workspaceOwnerOrchestrator(synthetic, listOrchestrators(), { onlineOnly: false });
|
|
552
547
|
if (!owner) return { ok: false, status: 409, error: "no orchestrator owns this repo path" };
|
|
553
|
-
const command =
|
|
548
|
+
const command = (await createCommandAsync({
|
|
554
549
|
type: "workspace.unpublish-review",
|
|
555
550
|
source: "system",
|
|
556
551
|
target: owner.agentId,
|
|
@@ -566,20 +561,20 @@ export function buildReviewRefUnpublishCommandFromFields(
|
|
|
566
561
|
requestedAt: Date.now(),
|
|
567
562
|
queued: owner.status !== "online",
|
|
568
563
|
},
|
|
569
|
-
});
|
|
564
|
+
}));
|
|
570
565
|
return { ok: true, command };
|
|
571
566
|
}
|
|
572
567
|
|
|
573
568
|
// Build a `workspace.deps-refresh` command for a worktree's owning orchestrator
|
|
574
569
|
// (issue #51). checkOnly just reports staleness. Same owner-resolution as cleanup.
|
|
575
|
-
function buildWorkspaceDepsRefreshCommand(
|
|
570
|
+
async function buildWorkspaceDepsRefreshCommand(
|
|
576
571
|
workspace: WorkspaceRecord,
|
|
577
572
|
requestedBy: string,
|
|
578
573
|
checkOnly: boolean,
|
|
579
|
-
): { ok: true; command: Command } | { ok: false; status: number; error: string } {
|
|
574
|
+
): Promise<{ ok: true; command: Command } | { ok: false; status: number; error: string }> {
|
|
580
575
|
const owner = workspaceOwnerOrchestrator(workspace, listOrchestrators(), { onlineOnly: false });
|
|
581
576
|
if (!owner) return { ok: false, status: 409, error: "no online orchestrator owns this workspace path" };
|
|
582
|
-
const command =
|
|
577
|
+
const command = (await createCommandAsync({
|
|
583
578
|
type: "workspace.deps-refresh",
|
|
584
579
|
source: "system",
|
|
585
580
|
target: owner.agentId,
|
|
@@ -594,7 +589,7 @@ function buildWorkspaceDepsRefreshCommand(
|
|
|
594
589
|
requestedAt: Date.now(),
|
|
595
590
|
queued: owner.status !== "online",
|
|
596
591
|
},
|
|
597
|
-
});
|
|
592
|
+
}));
|
|
598
593
|
return { ok: true, command };
|
|
599
594
|
}
|
|
600
595
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { workspaceLandRefused } from "./workspace-land-refusal";
|
|
2
|
+
import { refreshPendingLandQueueLeaseAsync } from "./db/runtime-tail-async";
|
|
1
3
|
// Event-driven workspace merge-back engine (#237).
|
|
2
4
|
//
|
|
3
5
|
// The branch merge-back used to be driven entirely by a 2-min maintenance poll, so an
|
|
@@ -30,8 +32,8 @@ import {
|
|
|
30
32
|
patchWorkspaceMetadataAsync,
|
|
31
33
|
updateWorkspaceStatus,
|
|
32
34
|
workspaceLandBarrier, updateWorkspaceStatusAsync} from "./db";
|
|
33
|
-
import {
|
|
34
|
-
|
|
35
|
+
import { findLatestCommandByCorrelationAsync } from "./commands-write";
|
|
36
|
+
|
|
35
37
|
import { notifyBranchLandFailed } from "./branch-landed";
|
|
36
38
|
import { resolveStewardIdentity } from "./steward-identity";
|
|
37
39
|
import { emitCommandEvent } from "./command-events";
|
|
@@ -144,14 +146,14 @@ async function escalateNoProgressMerge(ws: WorkspaceRecord, _tip?: string): Prom
|
|
|
144
146
|
const payload = { kind: "workspace.auto-merge-stuck", workspaceId: ws.id, repoRoot: ws.repoRoot, branch: ws.branch, baseRef: ws.baseRef, attempts: MAX_NO_PROGRESS_MERGE_ATTEMPTS, reason: mergeError };
|
|
145
147
|
let escalated = false;
|
|
146
148
|
if (recipients.length) {
|
|
147
|
-
notifyBranchLandFailed({ workspace: ws,
|
|
149
|
+
await notifyBranchLandFailed({ workspace: ws,
|
|
148
150
|
commandId: typeof meta.mergeCommandId === "string" ? meta.mergeCommandId : undefined,
|
|
149
151
|
recipients,
|
|
150
152
|
reason: `auto-merge backed off after ${MAX_NO_PROGRESS_MERGE_ATTEMPTS} no-progress attempts (${mergeError})`,
|
|
151
153
|
});
|
|
152
154
|
escalated = true;
|
|
153
155
|
} else {
|
|
154
|
-
const human = notifyWorkspaceHumanEscalation({ subject, body, payload, source: "workspace-auto-merge" });
|
|
156
|
+
const human = (await notifyWorkspaceHumanEscalation({ subject, body, payload, source: "workspace-auto-merge" }));
|
|
155
157
|
escalated = human.accepted;
|
|
156
158
|
if (!human.accepted) {
|
|
157
159
|
console.warn(`[workspace-auto-merge] no-progress escalation for ${ws.id} had zero agent recipients and no human channel: ${human.reason ?? "unknown reason"}`);
|
|
@@ -180,7 +182,7 @@ async function notifyProtectedTipRestore(ws: WorkspaceRecord, preview: Workspace
|
|
|
180
182
|
// whose delivered tip was discarded CANNOT reach landed-on-main by any land path until the carry is
|
|
181
183
|
// explicitly confirmed. Returns ok=false if it could NOT durably record the obligation — the caller
|
|
182
184
|
// fails closed on that (withholds the steward hand-off) rather than risk a silent divergent land.
|
|
183
|
-
const carry = markWorkspaceTasksCarryPending(ws, discardedHead);
|
|
185
|
+
const carry = await markWorkspaceTasksCarryPending(ws, discardedHead);
|
|
184
186
|
const body = `Workspace \`${ws.branch ?? ws.id}\` restored protected tip \`${preview.protectedTip?.slice(0, 12) ?? "unknown"}\`; discarded HEAD \`${discardedHead.slice(0, 12)}\` was preserved at \`${discardedRef}\`.`;
|
|
185
187
|
await createActivityEventAsync({
|
|
186
188
|
clientId: `workspace-protected-tip-restore-${ws.id}-${Date.now()}`,
|
|
@@ -198,7 +200,7 @@ async function notifyProtectedTipRestore(ws: WorkspaceRecord, preview: Workspace
|
|
|
198
200
|
const { coordinatorAgentId } = resolveStewardIdentity(current);
|
|
199
201
|
const recipients = [...new Set([coordinatorAgentId, current.ownerAgentId].filter((r): r is string => Boolean(r)))];
|
|
200
202
|
if (recipients.length) {
|
|
201
|
-
routeNotification({
|
|
203
|
+
await routeNotification({
|
|
202
204
|
type: "workspace.land_gate_warning",
|
|
203
205
|
scope: { workspaceId: current.id, repoRoot: current.repoRoot, author: current.ownerAgentId, parent: coordinatorAgentId ?? undefined },
|
|
204
206
|
recipients,
|
|
@@ -492,7 +494,7 @@ async function landCandidates(
|
|
|
492
494
|
}
|
|
493
495
|
}
|
|
494
496
|
if (p.conflict === true || protectedTipRestored) {
|
|
495
|
-
emitWorkspaceTaskSemanticNotifications({
|
|
497
|
+
await emitWorkspaceTaskSemanticNotifications({
|
|
496
498
|
workspace: ws,
|
|
497
499
|
transition: "land-conflict",
|
|
498
500
|
actor: "workspace-auto-merge",
|
|
@@ -616,7 +618,7 @@ function landableWorkspaces(repoRoot?: string, baseRef?: string): WorkspaceRecor
|
|
|
616
618
|
// `review_requested` with NO authorization; after the coordinator's clear the flag is off
|
|
617
619
|
// and the promoted delivery lands normally). Covers all three sweep triggers (event,
|
|
618
620
|
// backstop, watchdog) and the conflict→steward hand-off at this single selection filter.
|
|
619
|
-
&& !workspaceLandBarrier(ws).blocked
|
|
621
|
+
&& !workspaceLandBarrier(ws).blocked && !workspaceLandRefused(ws)
|
|
620
622
|
// #1473 — nor is a workspace bound to a deliverable Task that has NOT authorized a land: a pushed
|
|
621
623
|
// `agent/*` branch in a ready-to-land status is not, by itself, a land authorization (a "HOLD" is
|
|
622
624
|
// coordinator convention only). Excluding it here keeps it out of BOTH the auto-land dispatch and
|
|
@@ -761,9 +763,9 @@ function commandIsTerminal(command: Command | null): boolean {
|
|
|
761
763
|
return Boolean(command && TERMINAL_COMMAND_STATUSES.has(command.status));
|
|
762
764
|
}
|
|
763
765
|
|
|
764
|
-
function latestWorkspaceCommand(ws: WorkspaceRecord): Command | null {
|
|
766
|
+
async function latestWorkspaceCommand(ws: WorkspaceRecord): Promise<Command | null> {
|
|
765
767
|
const type = LAND_IN_FLIGHT_STATUSES.has(ws.status) ? "workspace.merge" : "workspace.cleanup";
|
|
766
|
-
return
|
|
768
|
+
return findLatestCommandByCorrelationAsync(type, ws.id);
|
|
767
769
|
}
|
|
768
770
|
|
|
769
771
|
async function recoverAgedInFlightWorkspaces(now: number, signal?: AbortSignal): Promise<Record<string, unknown>> {
|
|
@@ -803,9 +805,9 @@ async function recoverAgedInFlightWorkspaces(now: number, signal?: AbortSignal):
|
|
|
803
805
|
// young merge_planned row the 20-min age gate would otherwise skip. Only a merge_planned row can be
|
|
804
806
|
// executor-orphaned, so a young cleanup_requested/non-merge row still pays NO command lookup (the
|
|
805
807
|
// pre-#2094 cost) — fetch only when the row is old enough for the existing recovery OR could be an orphan.
|
|
806
|
-
let command = (LAND_IN_FLIGHT_STATUSES.has(ws.status) || oldEnough(ws)) ? latestWorkspaceCommand(ws) : null;
|
|
808
|
+
let command = (LAND_IN_FLIGHT_STATUSES.has(ws.status) || oldEnough(ws)) ? await latestWorkspaceCommand(ws) : null;
|
|
807
809
|
if (command?.status === "pending" && command.type === "workspace.merge") {
|
|
808
|
-
if (
|
|
810
|
+
if ((await refreshPendingLandQueueLeaseAsync(command.id, now))) {
|
|
809
811
|
stillActive.push(ws.id);
|
|
810
812
|
continue;
|
|
811
813
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ChangedPathDiff } from "@humwick/sdk";
|
|
2
2
|
import { decideDiffGate, type DiffGateDecision } from "./services/diff-gate";
|
|
3
3
|
import { fetchOwningHostJson } from "./workspace-host-client";
|
|
4
|
+
import { workspaceHeadAlreadyOnBase } from "./services/task-nothing-to-land";
|
|
4
5
|
import type { WorkspaceRecord } from "./types";
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -87,11 +88,21 @@ export async function resolveDeliveryDiffDecision(
|
|
|
87
88
|
...(reprobe.ok ? { headSha: reprobe.headSha } : {}),
|
|
88
89
|
};
|
|
89
90
|
}
|
|
90
|
-
return
|
|
91
|
+
return await classify(workspace, reprobe);
|
|
91
92
|
}
|
|
93
|
+
return await classify(workspace, probe);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** A head the owning host proves is already on the freshly fetched base lands nothing, so no
|
|
97
|
+
* gate can protect anything: accept it without one (#3214). Probed only on the would-gate path. */
|
|
98
|
+
async function classify(workspace: WorkspaceRecord, probe: ChangedPathDiff): Promise<DeliveryDiffEvidence> {
|
|
92
99
|
const decision = decideDiffGate(probe);
|
|
93
|
-
return {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
100
|
+
if (!probe.ok || !probe.headSha) return { decision };
|
|
101
|
+
if (decision.requireGates && await workspaceHeadAlreadyOnBase(workspace, probe.headSha)) {
|
|
102
|
+
return {
|
|
103
|
+
decision: { requireGates: false, reason: `delivered head ${probe.headSha.slice(0, 12)} is already on ${workspace.baseRef ?? "main"}: nothing lands`, paths: probe.paths },
|
|
104
|
+
headSha: probe.headSha,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
return { decision, headSha: probe.headSha };
|
|
97
108
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createCommandAsync } from "./commands-write";
|
|
1
2
|
// Cleanup-stale workspace reconciliation: builds the `workspace.cleanup` command and runs the
|
|
2
3
|
// `workspace cleanup-stale --check/--execute` sweep (#208/#965/#1066). Kept as a standalone leaf
|
|
3
4
|
// module (no workspace-auto-merge.ts dependency) so the transient-agent reap lifecycle (#1477)
|
|
@@ -7,7 +8,7 @@
|
|
|
7
8
|
|
|
8
9
|
import { statSync } from "node:fs";
|
|
9
10
|
import { emitCommandEvent } from "./command-events";
|
|
10
|
-
|
|
11
|
+
|
|
11
12
|
import { getAgent, listWorkspaces, updateWorkspaceStatusAsync } from "./db";
|
|
12
13
|
import { fetchWorkspaceGitState } from "./workspace-git-state-client";
|
|
13
14
|
import { workspaceOwnerOrchestrator } from "./workspace-host";
|
|
@@ -15,15 +16,15 @@ import { workspaceActiveClaim } from "./workspace-claim";
|
|
|
15
16
|
import { isOwnerAlive, isOwnerProcessConfirmedGone, liveAgentCwds, worktreeInUseByLiveAgent } from "./workspace-merge";
|
|
16
17
|
import { refResolvesInRepo, verifyLandedOrEquivalentContent } from "./services/landed-ancestry";
|
|
17
18
|
import { TERMINAL_WORKSPACE_STATUSES } from "./workspace-phase";
|
|
18
|
-
import type { Command, WorkspaceRecord } from "./types";
|
|
19
|
+
import type { Command, WorkspaceRecord, WorkspaceOrphan } from "./types";
|
|
19
20
|
|
|
20
21
|
// Build a `workspace.cleanup` command for the worktree's owning orchestrator.
|
|
21
22
|
// Moved here (from routes.ts) so the MCP surface reaches it without importing routes.
|
|
22
|
-
export function buildWorkspaceCleanupCommand(
|
|
23
|
+
export async function buildWorkspaceCleanupCommand(
|
|
23
24
|
workspace: WorkspaceRecord,
|
|
24
25
|
requestedBy: string,
|
|
25
26
|
opts: { force?: boolean; reason?: string; autoReclaim?: "origin-captured" } = {},
|
|
26
|
-
): { ok: true; command: Command } | { ok: false; status: number; error: string } {
|
|
27
|
+
): Promise<{ ok: true; command: Command } | { ok: false; status: number; error: string }> {
|
|
27
28
|
if (
|
|
28
29
|
opts.force !== true &&
|
|
29
30
|
workspace.mode === "isolated" &&
|
|
@@ -38,7 +39,7 @@ export function buildWorkspaceCleanupCommand(
|
|
|
38
39
|
}
|
|
39
40
|
const owner = workspaceOwnerOrchestrator(workspace);
|
|
40
41
|
if (!owner) return { ok: false, status: 409, error: "no online orchestrator owns this workspace path; retry when the host is online or use DELETE /api/workspaces/:id to purge the record" };
|
|
41
|
-
const command =
|
|
42
|
+
const command = (await createCommandAsync({
|
|
42
43
|
type: "workspace.cleanup",
|
|
43
44
|
source: "system",
|
|
44
45
|
target: owner.agentId,
|
|
@@ -58,7 +59,7 @@ export function buildWorkspaceCleanupCommand(
|
|
|
58
59
|
...(opts.autoReclaim === "origin-captured" ? { autoReclaim: "origin-captured" } : {}),
|
|
59
60
|
queued: false,
|
|
60
61
|
},
|
|
61
|
-
});
|
|
62
|
+
}));
|
|
62
63
|
return { ok: true, command };
|
|
63
64
|
}
|
|
64
65
|
|
|
@@ -190,7 +191,7 @@ export async function cleanupStaleWorkspaces(opts: CleanupStaleWorkspacesOptions
|
|
|
190
191
|
} else {
|
|
191
192
|
// Owner re-confirmed offline just above; force past buildWorkspaceCleanupCommand's
|
|
192
193
|
// live-owner guard and carry a reason so the host records an intentional force-discard.
|
|
193
|
-
const built = buildWorkspaceCleanupCommand(ws, "cleanup-stale", forced ? { force: true, reason: "operator force-discard: abandoned branch, owner offline" } : {});
|
|
194
|
+
const built = (await buildWorkspaceCleanupCommand(ws, "cleanup-stale", forced ? { force: true, reason: "operator force-discard: abandoned branch, owner offline" } : {}));
|
|
194
195
|
if (built.ok) {
|
|
195
196
|
await updateWorkspaceStatusAsync(ws.id, "cleanup_requested", { lastWorkspaceAction: "cleanup-stale", lastWorkspaceActionAt: Date.now(), cleanupProof: { ...proof, forced } });
|
|
196
197
|
emitCommandEvent(built.command, "command.requested");
|
|
@@ -229,3 +230,25 @@ export async function cleanupStaleWorkspaces(opts: CleanupStaleWorkspacesOptions
|
|
|
229
230
|
}
|
|
230
231
|
return { dryRun, landedOnly, force, repoRoot, ...(workspaceId ? { workspaceId } : {}), ...(workspaceIds ? { workspaceIds } : {}), scanned: candidates.length, eligible: rows.filter((r) => r.discardable).length, cleaned, candidates: rows };
|
|
231
232
|
}
|
|
233
|
+
|
|
234
|
+
export async function dispatchOrphanCleanup(targetAgentId: string, orphan: WorkspaceOrphan): Promise<string> {
|
|
235
|
+
const command = (await createCommandAsync({
|
|
236
|
+
type: "workspace.cleanup",
|
|
237
|
+
source: "system",
|
|
238
|
+
target: targetAgentId,
|
|
239
|
+
params: {
|
|
240
|
+
action: "cleanup",
|
|
241
|
+
worktreePath: orphan.worktreePath,
|
|
242
|
+
repoRoot: orphan.repoRoot,
|
|
243
|
+
branch: orphan.branch,
|
|
244
|
+
baseRef: orphan.baseRef,
|
|
245
|
+
baseSha: orphan.baseSha,
|
|
246
|
+
deleteBranch: true,
|
|
247
|
+
reclaim: true,
|
|
248
|
+
requestedBy: "workspace-orphan-reaper",
|
|
249
|
+
requestedAt: Date.now(),
|
|
250
|
+
},
|
|
251
|
+
}));
|
|
252
|
+
emitCommandEvent(command, `command.${command.status}`);
|
|
253
|
+
return command.id;
|
|
254
|
+
}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { DELEGATED_LAND_REFUSAL_CODE } from "@humwick/sdk/worktree-config";
|
|
2
|
+
import { refuseWorkspaceLand } from "./workspace-land-refusal";
|
|
3
|
+
import { releaseMergeLeaseAsync, insertLandGateAttestationAsync } from "./db/runtime-tail-async";
|
|
1
4
|
import { commandFailureReason, settleWorkspaceCleanupCommand, type WorkspaceCommandSettleResult } from "./workspace-cleanup-settle";
|
|
2
5
|
export { type WorkspaceCommandSettleResult } from "./workspace-cleanup-settle";
|
|
3
6
|
import { fireAndForget, isRecord, sanitizeFsName, stringValue, type LandGateAttestation, type LandGateRunResult } from "@humwick/sdk";
|
|
4
7
|
import { storeTextArtifact } from "./artifact-text";
|
|
5
8
|
import { announceBranchLanded, notifyBaseWorktreeStrand, notifyBranchLandFailed, notifyLandGateFailed, notifyLandGateSkipped, notifyLandGateWarnings, recordLandingReceipt } from "./branch-landed";
|
|
6
|
-
import { getWorkspace, getArtifactAsync,
|
|
9
|
+
import { getWorkspace, getArtifactAsync, listOrchestrators, markWorkspaceLanded, patchWorkspaceMetadataAsync, setWorkspaceBranchAsync, updateWorkspaceStatusAsync, markWorkspaceLandedAsync } from "./db";
|
|
7
10
|
import { parseOwnerRepoFromRemote, repoFromGitRemote } from "./services/git-remote";
|
|
8
11
|
import { deferLandSettleSideEffect } from "./land-settle-dispatch";
|
|
9
12
|
import { recordConfirmedLandBoundary } from "./services/land-boundary-settle";
|
|
@@ -205,7 +208,7 @@ export async function settleWorkspaceMergeCommand(command: Command, opts: Worksp
|
|
|
205
208
|
command = proven;
|
|
206
209
|
}
|
|
207
210
|
const terminalSettled = WORKSPACE_MERGE_SETTLED_STATUSES.has(command.status);
|
|
208
|
-
if (terminalSettled && command.landingContainmentMode === "legacy-unfenced-v0")
|
|
211
|
+
if (terminalSettled && command.landingContainmentMode === "legacy-unfenced-v0") (await releaseMergeLeaseAsync({ commandId: command.id }));
|
|
209
212
|
const settledRepoRoot = typeof command.params.repoRoot === "string" ? command.params.repoRoot : undefined;
|
|
210
213
|
const settledBaseRef = typeof command.params.baseRef === "string" ? command.params.baseRef : undefined;
|
|
211
214
|
let transitionCompleted = false;
|
|
@@ -360,13 +363,13 @@ async function settleWorkspaceMergeCommandResult(command: Command, markTransitio
|
|
|
360
363
|
if (retryableNonLand) {
|
|
361
364
|
if (ancestryUnconfirmed && landedWorkspace) {
|
|
362
365
|
const ws = transitioned ?? landedWorkspace;
|
|
363
|
-
deferLandSettleSideEffect("land-unconfirmed", () => notifyLandUnconfirmed({ workspace: ws, commandId: command.id, sha: authoritativeLandedSha, targetRef: landTargetRef, kind: landAncestry }));
|
|
366
|
+
deferLandSettleSideEffect("land-unconfirmed", async () => await notifyLandUnconfirmed({ workspace: ws, commandId: command.id, sha: authoritativeLandedSha, targetRef: landTargetRef, kind: landAncestry }));
|
|
364
367
|
}
|
|
365
368
|
return { settled: true };
|
|
366
369
|
}
|
|
367
370
|
const noopBase = nothingBase ?? (mergeResult.noop === true && !resultError && mergeResult.landMechanism !== "plain-git" && (resultStatus === "merged" || resultStatus === "active") && typeof baseSha === "string" && /^[0-9a-f]{40,64}$/.test(baseSha) ? baseSha : undefined);
|
|
368
371
|
if (noopBase && landedWorkspace) await finishWorkspaceStewardHandoff(landedWorkspace);
|
|
369
|
-
if (noopBase && landedWorkspace) deferLandSettleSideEffect("nothing-to-land", () => notifyNothingToLand(landedWorkspace, noopBase, command.id, typeof command.params.expectedHeadSha === "string" ? command.params.expectedHeadSha : undefined));
|
|
372
|
+
if (noopBase && landedWorkspace) deferLandSettleSideEffect("nothing-to-land", async () => await notifyNothingToLand(landedWorkspace, noopBase, command.id, typeof command.params.expectedHeadSha === "string" ? command.params.expectedHeadSha : undefined));
|
|
370
373
|
const landConfirmed = !nothingBase && workspaceMergeProvesLand(mergeResult, command.params);
|
|
371
374
|
const continuationBranch = normalizedStatus === "active" ? landedWorkspace?.branch : undefined;
|
|
372
375
|
// #1790 F1 — build the land notice up front so the durable RECEIPT can be written SYNCHRONOUSLY,
|
|
@@ -393,7 +396,7 @@ async function settleWorkspaceMergeCommandResult(command: Command, markTransitio
|
|
|
393
396
|
...(prLanded || landedCommitCount === undefined ? {} : { landedCommitCount }),
|
|
394
397
|
}
|
|
395
398
|
: undefined;
|
|
396
|
-
if (landedNotice) recordLandingReceipt(landedNotice);
|
|
399
|
+
if (landedNotice) (await recordLandingReceipt(landedNotice));
|
|
397
400
|
// #1744 F1 — record the tree-keyed land-gate attestation. Placed HERE, not at the executor, so
|
|
398
401
|
// that "the full gate passed on tree T" only becomes deployable evidence once the relay has
|
|
399
402
|
// CONFIRMED (ancestry-gated above, #1713) that T actually reached the base ref: a gated attempt
|
|
@@ -408,7 +411,7 @@ async function settleWorkspaceMergeCommandResult(command: Command, markTransitio
|
|
|
408
411
|
const repo = landRepo(landedWorkspace.repoRoot);
|
|
409
412
|
// No resolvable origin ⇒ no shared identity with cd-watcher's detection clone ⇒ no row.
|
|
410
413
|
if (repo) {
|
|
411
|
-
|
|
414
|
+
(await insertLandGateAttestationAsync({
|
|
412
415
|
repo,
|
|
413
416
|
treeSha: attestation.treeSha,
|
|
414
417
|
commitSha: authoritativeLandedSha,
|
|
@@ -419,7 +422,7 @@ async function settleWorkspaceMergeCommandResult(command: Command, markTransitio
|
|
|
419
422
|
gatesRan: attestation.gatesRan,
|
|
420
423
|
workspaceId: landedWorkspace.id,
|
|
421
424
|
...(attestation.suiteCoverage ? { suiteCoverage: attestation.suiteCoverage } : {}),
|
|
422
|
-
});
|
|
425
|
+
}));
|
|
423
426
|
}
|
|
424
427
|
} catch (error) {
|
|
425
428
|
console.warn(`[land-gate-attestation] failed to record tree ${attestation.treeSha.slice(0, 12)} for ${workspaceId}: ${String(error)}`);
|
|
@@ -439,7 +442,7 @@ async function settleWorkspaceMergeCommandResult(command: Command, markTransitio
|
|
|
439
442
|
preservedWipPaths: Array.isArray(baseWorktreeSync.preservedWipPaths) ? baseWorktreeSync.preservedWipPaths.filter((p): p is string => typeof p === "string") : undefined,
|
|
440
443
|
message: typeof baseWorktreeSync.message === "string" ? baseWorktreeSync.message : undefined,
|
|
441
444
|
};
|
|
442
|
-
deferLandSettleSideEffect("base-worktree-strand", () => notifyBaseWorktreeStrand({ workspace: ws, sync: strandSync }));
|
|
445
|
+
deferLandSettleSideEffect("base-worktree-strand", async () => await notifyBaseWorktreeStrand({ workspace: ws, sync: strandSync }));
|
|
443
446
|
}
|
|
444
447
|
// #1713 — reaching here means the land is either ancestry-CONFIRMED, a remote-verified PR land, or
|
|
445
448
|
// a proven zero-commit no-op (handled inside announceBranchLanded, #1620); an unconfirmed
|
|
@@ -450,7 +453,7 @@ async function settleWorkspaceMergeCommandResult(command: Command, markTransitio
|
|
|
450
453
|
// i.e. the serialized writes this issue is about — so it runs off the settle hot path.
|
|
451
454
|
if (landedNotice) {
|
|
452
455
|
const notice = landedNotice;
|
|
453
|
-
deferLandSettleSideEffect("branch-landed", () => announceBranchLanded(notice));
|
|
456
|
+
deferLandSettleSideEffect("branch-landed", async () => await announceBranchLanded(notice));
|
|
454
457
|
}
|
|
455
458
|
const gateSkipped = normalizeGateSkipped(mergeResult.gateSkipped);
|
|
456
459
|
if (gateSkipped && landedWorkspace) {
|
|
@@ -462,7 +465,7 @@ async function settleWorkspaceMergeCommandResult(command: Command, markTransitio
|
|
|
462
465
|
},
|
|
463
466
|
});
|
|
464
467
|
const ws = landedWorkspace;
|
|
465
|
-
deferLandSettleSideEffect("land-gate-skipped", () => notifyLandGateSkipped({ workspace: ws, ...gateSkipped }));
|
|
468
|
+
deferLandSettleSideEffect("land-gate-skipped", async () => await notifyLandGateSkipped({ workspace: ws, ...gateSkipped }));
|
|
466
469
|
}
|
|
467
470
|
const gateWarnings = mergeResult.merged === true ? normalizeGateWarnings(mergeResult.gateWarnings) : [];
|
|
468
471
|
if (gateWarnings.length && landedWorkspace) {
|
|
@@ -500,7 +503,7 @@ async function settleWorkspaceMergeCommandResult(command: Command, markTransitio
|
|
|
500
503
|
console.warn(`land-gate-warnings artifact upload failed for ${warnWorkspace.id}: ${String(error)}`);
|
|
501
504
|
}
|
|
502
505
|
try {
|
|
503
|
-
notifyLandGateWarnings({ workspace: warnWorkspace, warnings: gateWarnings, artifactId, fullOutput: !anyTruncated });
|
|
506
|
+
await notifyLandGateWarnings({ workspace: warnWorkspace, warnings: gateWarnings, artifactId, fullOutput: !anyTruncated });
|
|
504
507
|
} catch (error) {
|
|
505
508
|
console.warn(`land-gate-warnings notify threw for ${warnWorkspace.id}: ${String(error)}`);
|
|
506
509
|
}
|
|
@@ -537,6 +540,11 @@ async function settleWorkspaceMergeCommandResult(command: Command, markTransitio
|
|
|
537
540
|
if (await settleRestartKilledLand(command, current)) markTransitionCompleted();
|
|
538
541
|
return { settled: true };
|
|
539
542
|
}
|
|
543
|
+
if (!alreadyLanded && isRecord(command.result) && command.result.landingBlockerCode === DELEGATED_LAND_REFUSAL_CODE) {
|
|
544
|
+
await refuseWorkspaceLand(current, reason, command.id);
|
|
545
|
+
markTransitionCompleted();
|
|
546
|
+
return { settled: true };
|
|
547
|
+
}
|
|
540
548
|
const transitioned = await updateWorkspaceStatusAsync(command.correlationId, nextStatus, {
|
|
541
549
|
mergeCommandId: command.id,
|
|
542
550
|
landGateQueue: undefined,
|
|
@@ -553,12 +561,12 @@ async function settleWorkspaceMergeCommandResult(command: Command, markTransitio
|
|
|
553
561
|
// announcements off it, so they run off the settle hot path (same as the success path's fan-out).
|
|
554
562
|
if (nextStatus === "conflict" && !alreadyLanded) {
|
|
555
563
|
// #2704 — the settle is the one conflict entry that never woke a steward (case A): give it its resolver.
|
|
556
|
-
deferLandSettleSideEffect("land-conflict-
|
|
557
|
-
deferLandSettleSideEffect("land-conflict-semantic", () => emitWorkspaceTaskSemanticNotifications({
|
|
564
|
+
deferLandSettleSideEffect("land-conflict-semantic", async () => await emitWorkspaceTaskSemanticNotifications({
|
|
558
565
|
workspace: current, transition: "land-conflict", actor: "workspace-merge",
|
|
559
566
|
idempotencyKey: landFailureEventKey(current, command.id),
|
|
560
567
|
outcome: { reason, commandId: command.id, status: nextStatus },
|
|
561
568
|
}));
|
|
569
|
+
deferLandSettleSideEffect("land-conflict-resolver", () => superviseLandConflictLater(current.id, reason, landedHead ? { tip: landedHead } : {}));
|
|
562
570
|
}
|
|
563
571
|
if (gateFailure) {
|
|
564
572
|
const gateFailedAt = Date.now();
|
|
@@ -572,9 +580,9 @@ async function settleWorkspaceMergeCommandResult(command: Command, markTransitio
|
|
|
572
580
|
});
|
|
573
581
|
} else if (!alreadyLanded && nextStatus !== "conflict") {
|
|
574
582
|
// A conflict's owner hears from its resolver instead: the steward hand-off, or the hand-back (#2704).
|
|
575
|
-
deferLandSettleSideEffect("land-failed", () => {
|
|
576
|
-
notifyBranchLandFailed({ workspace: current, commandId: command.id, reason });
|
|
577
|
-
notifyLandUnconfirmed({ workspace: current, commandId: command.id, targetRef: current.baseRef ?? "main", kind: "merge-failed", detail: reason });
|
|
583
|
+
deferLandSettleSideEffect("land-failed", async () => {
|
|
584
|
+
await notifyBranchLandFailed({ workspace: current, commandId: command.id, reason });
|
|
585
|
+
await notifyLandUnconfirmed({ workspace: current, commandId: command.id, targetRef: current.baseRef ?? "main", kind: "merge-failed", detail: reason });
|
|
578
586
|
});
|
|
579
587
|
}
|
|
580
588
|
return { settled: true };
|