@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
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
import { upsertWorkspaceAsync } from "../db/runtime-admin-async";
|
|
1
2
|
import { relayEnv } from "@humwick/sdk/env";
|
|
2
3
|
import { createHash } from "node:crypto";
|
|
3
4
|
import type { LandBoundary, RedMainIncident } from "@humwick/kernel/landing-incident";
|
|
4
5
|
import { errMessage, isRecord, stringValue, type Command } from "@humwick/sdk";
|
|
5
|
-
import { findCommandByCorrelationId
|
|
6
|
+
import { findCommandByCorrelationId } from "../commands-db";
|
|
7
|
+
import { findLatestCommandByCorrelationAsync } from "../commands-write";
|
|
6
8
|
import { getCommandAsync } from "../commands-write";
|
|
7
9
|
import { isTerminalCommandStatus } from "../db/command-terminal-status";
|
|
8
|
-
import { getWorkspace
|
|
10
|
+
import { getWorkspace } from "../db/workspaces";
|
|
9
11
|
import { landBoundaryById, landBoundaryForCommand, redMainIncidentById } from "../db/land-boundaries";
|
|
10
12
|
import { readFailedTestFiles } from "../ci/failed-test-files";
|
|
11
13
|
import type { GitHubApi } from "../issue-tracker/github";
|
|
@@ -70,13 +72,13 @@ async function advanceAutomaticRevert(incident: RedMainIncident, boundary: LandB
|
|
|
70
72
|
/** One direct, pinned, gated land per prepared red-main workspace: the revert's or the quarantine's. */
|
|
71
73
|
export async function landRedMainWorkspace(id: string, boundary: LandBoundary, prepared: Record<string, unknown>,
|
|
72
74
|
metadata: Record<string, unknown>, recoveryTestFiles?: string[]): Promise<Command> {
|
|
73
|
-
const existing =
|
|
75
|
+
const existing = await findLatestCommandByCorrelationAsync("workspace.merge", id);
|
|
74
76
|
if (existing) return existing;
|
|
75
77
|
const source = getWorkspace(boundary.workspaceId)!;
|
|
76
|
-
const workspace = getWorkspace(id) ??
|
|
78
|
+
const workspace = getWorkspace(id) ?? (await upsertWorkspaceAsync({ id, repoRoot: source.repoRoot, sourceCwd: source.repoRoot,
|
|
77
79
|
worktreePath: String(prepared.worktreePath), branch: String(prepared.branch), baseRef: boundary.baseRef,
|
|
78
80
|
baseSha: String(prepared.baseSha), mode: "isolated", status: "ready", orchestratorId: source.orchestratorId,
|
|
79
|
-
metadata: { ...metadata, landStrategy: "direct" } });
|
|
81
|
+
metadata: { ...metadata, landStrategy: "direct" } }));
|
|
80
82
|
const requested = await requestWorkspaceMerge(workspace, { requestedBy: "red-main-controller", strategy: "direct",
|
|
81
83
|
expectedHeadSha: String(prepared.headSha), deleteBranch: true, ...(recoveryTestFiles ? { recoveryTestFiles } : {}) });
|
|
82
84
|
if (!requested.ok) throw new Error(requested.error);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// #3363 — the red-main controller's production deps: GitHub for CI verdicts and reruns, Tasks and
|
|
2
2
|
// messages for escalation. Every CI observation uses the boundary's authoritative watch snapshot.
|
|
3
|
-
import type
|
|
3
|
+
import { MAX_INCIDENT_FAILING_TESTS, type LandBoundary, type RedMainIncident } from "@humwick/kernel/landing-incident";
|
|
4
4
|
import { cleanLandGatesConfigFull } from "@humwick/sdk/land-gates";
|
|
5
|
-
import { isRecord } from "@humwick/sdk";
|
|
5
|
+
import { ciRunUrl, errMessage, isRecord } from "@humwick/sdk";
|
|
6
6
|
import { judgeExactSha, rerunBoundaryCi, type BoundaryCiObservation } from "../ci/land-boundary-ci.ts";
|
|
7
7
|
import { type GitHubRepository } from "../ci/monitor.ts";
|
|
8
8
|
import { getTaskDetailAsync } from "../db/task-entity-async.ts";
|
|
@@ -12,6 +12,8 @@ import { sendMessageServiceAsync } from "./send-message.ts";
|
|
|
12
12
|
import { setTaskEntityStatusAsync } from "./task-entity.ts";
|
|
13
13
|
import { automaticRedMainQuarantine } from "./red-main-quarantine";
|
|
14
14
|
import { automaticRedMainRevert, automaticRevertOutcome } from "./red-main-auto-revert";
|
|
15
|
+
import { readFailedTestCases } from "../ci/failed-test-files.ts";
|
|
16
|
+
import { recordRedMainIncidentFailures } from "./red-main-incident.ts";
|
|
15
17
|
|
|
16
18
|
/** Twice a typical main CI run: a run missing or cancelled past this leaves its boundary unverified. */
|
|
17
19
|
export const RED_MAIN_GRACE_MS = 60 * 60_000;
|
|
@@ -31,6 +33,20 @@ function describe(incident: RedMainIncident, observation: BoundaryCiObservation)
|
|
|
31
33
|
return `Red main confirmed on lane ${incident.laneKey} (incident ${incident.id}): the re-run did not produce a successful verdict.${jobs}${observation.runUrl ? ` Run: ${observation.runUrl}` : ""}`;
|
|
32
34
|
}
|
|
33
35
|
|
|
36
|
+
/** #3362 §11: the confirmed attempt's failing cases; a rerun that never produced logs falls back to the first failure.
|
|
37
|
+
* Best effort, read once: unreadable logs record nothing, so delivery gates hold nothing and every failure blocks. */
|
|
38
|
+
export async function recordIncidentFailingTests(incident: RedMainIncident, boundary: LandBoundary, attempt: number, api: GitHubApi): Promise<void> {
|
|
39
|
+
let cases;
|
|
40
|
+
try {
|
|
41
|
+
cases = await readFailedTestCases(boundary, incident.ciRunId, attempt, api);
|
|
42
|
+
if (!cases.length && attempt > 1) cases = await readFailedTestCases(boundary, incident.ciRunId, attempt - 1, api);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.warn(`[red-main] ${incident.id}: failing cases unreadable; none recorded: ${errMessage(error)}`);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
await recordRedMainIncidentFailures(incident.id, cases.slice(0, MAX_INCIDENT_FAILING_TESTS).map(({ file, name }) => ({ file, name })));
|
|
48
|
+
}
|
|
49
|
+
|
|
34
50
|
export function createRedMainControllerDeps(api: GitHubApi = ghApi, now: () => number = Date.now): RedMainControllerDeps {
|
|
35
51
|
return {
|
|
36
52
|
now,
|
|
@@ -62,10 +78,11 @@ export function createRedMainControllerDeps(api: GitHubApi = ghApi, now: () => n
|
|
|
62
78
|
if (!incident) throw new Error(`Effect ${effect.key} needs its incident`);
|
|
63
79
|
if (effect.kind === "auto-revert") return incident.mainFailurePolicy === "advisory" ? undefined : automaticRedMainRevert(incident, boundary, api);
|
|
64
80
|
if (effect.kind === "quarantine") return automaticRedMainQuarantine(incident, boundary, effect, api);
|
|
81
|
+
if (effect.kind === "record-failures") return recordIncidentFailingTests(incident, boundary, effect.attempt, api);
|
|
65
82
|
const task = await getTaskDetailAsync(effect.taskId);
|
|
66
83
|
if (!task) throw new Error(`Effect ${effect.key}: T${effect.taskId} is missing`);
|
|
67
84
|
const observation: BoundaryCiObservation = { status: "failure", runId: incident.ciRunId,
|
|
68
|
-
runUrl:
|
|
85
|
+
runUrl: ciRunUrl(boundary.remote, incident.ciRunId, incident.ciAttempt) };
|
|
69
86
|
const advisory = incident.mainFailurePolicy === "advisory";
|
|
70
87
|
const note = describe(incident, observation);
|
|
71
88
|
if (effect.kind === "reopen-task") {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// #3363 — server seam for the Kernel landing boundary and red-main incident ops
|
|
2
2
|
// (kernel/landing-incident.ts). Every write goes through the singleton landing module host.
|
|
3
3
|
import { landing } from "@humwick/kernel/landing";
|
|
4
|
-
import type { IncidentEffect, IncidentState, LandBoundary, RedMainIncident } from "@humwick/kernel/landing-incident";
|
|
4
|
+
import type { IncidentEffect, IncidentFailingTest, IncidentState, LandBoundary, RedMainIncident } from "@humwick/kernel/landing-incident";
|
|
5
5
|
import { activeLandCommandIdForWorkspace } from "../db/land-incident-eligibility.ts";
|
|
6
6
|
import { startKernelLandingIngress } from "./kernel-landing-ingress.ts";
|
|
7
7
|
|
|
@@ -26,6 +26,8 @@ export const transitionRedMainIncident = (input: { id: string; from: IncidentSta
|
|
|
26
26
|
export const settleRedMainIncidentEffect = (id: string, key: string) =>
|
|
27
27
|
invoke<RedMainIncident>("settleIncidentEffect", { id, key });
|
|
28
28
|
export const ensureRedMainIncidentEffects = (id: string) => invoke<RedMainIncident>("ensureIncidentEffects", { id });
|
|
29
|
+
export const recordRedMainIncidentFailures = (id: string, failingTests: IncidentFailingTest[]) =>
|
|
30
|
+
invoke<RedMainIncident>("recordIncidentFailures", { id, failingTests });
|
|
29
31
|
export const failAutomaticRedMainRevert = (id: string, reason: string) => invoke<RedMainIncident>("failAutomaticRevert", { id, reason });
|
|
30
32
|
export const queueBoundaryRerun = (id: string, effect: IncidentEffect) => invoke<LandBoundary>("queueBoundaryRerun", { id, effect });
|
|
31
33
|
export const settleBoundaryEffect = (id: string, key: string) => invoke<LandBoundary>("settleBoundaryEffect", { id, key });
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// like any change, and the list's issue gets the audit record. Anything short of that becomes a Task.
|
|
5
5
|
import { createHash } from "node:crypto";
|
|
6
6
|
import type { IncidentEffect, LandBoundary, RedMainIncident } from "@humwick/kernel/landing-incident";
|
|
7
|
-
import { isRecord, stringValue } from "@humwick/sdk";
|
|
7
|
+
import { ciRunUrl, isRecord, stringValue } from "@humwick/sdk";
|
|
8
8
|
import { readFailedTestCases } from "../ci/failed-test-files";
|
|
9
9
|
import { findCommandByCorrelationId } from "../commands-db";
|
|
10
10
|
import { isTerminalCommandStatus } from "../db/command-terminal-status";
|
|
@@ -19,8 +19,7 @@ type Case = { id?: string; file: string; name: string; why?: string };
|
|
|
19
19
|
|
|
20
20
|
export const automaticQuarantineWorkspaceId = (id: string) => `red-main-q-${createHash("sha256").update(id).digest("hex").slice(0, 24)}`;
|
|
21
21
|
const prepareKey = (id: string) => `kcmd:red-main:${id}:prepare-quarantine`;
|
|
22
|
-
const runUrl = (boundary: LandBoundary, incident: RedMainIncident, attempt?: number) =>
|
|
23
|
-
`https://github.com/${boundary.remote}/actions/runs/${incident.ciRunId}${attempt ? `/attempts/${attempt}` : ""}`;
|
|
22
|
+
const runUrl = (boundary: LandBoundary, incident: RedMainIncident, attempt?: number) => ciRunUrl(boundary.remote, incident.ciRunId, attempt);
|
|
24
23
|
|
|
25
24
|
function evidence(incident: RedMainIncident, boundary: LandBoundary, attempt: number): string {
|
|
26
25
|
const failed = `main ${boundary.landedTipSha.slice(0, 12)} run ${incident.ciRunId} attempt ${attempt} failed`;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { stampSpawnPrincipalAsync, markSpawnRegistrationConfirmedAsync, seedSpawnPromptMirrorAsync } from "../db/lifecycle-tail-async";
|
|
1
2
|
// Transport convergence (epic #342, slice #345) — the agent-registration service.
|
|
2
3
|
//
|
|
3
4
|
// ONE home for "an agent registered" — previously inlined in HTTP `postAgent` and
|
|
@@ -11,14 +12,13 @@
|
|
|
11
12
|
// the first-registration audit row.
|
|
12
13
|
import { errMessage, fireAndForget, isReservedAgentId } from "@humwick/sdk";
|
|
13
14
|
import { normalizeAgentSystemTags } from "../agent-system-tags";
|
|
14
|
-
import { ValidationError, agentPrincipalUserId,
|
|
15
|
+
import { ValidationError, agentPrincipalUserId, createActivityEventAsync, deliverableTaskIdsBySpawnRequestId, getAgent, getTaskDetail, readSpawnRegistrationMeta, seedSpawnReplyObligation, spawnRegistrationMeta, upsertAgentAsync } from "../db";
|
|
15
16
|
import { readSpawnSystemTags } from "../commands-db";
|
|
16
17
|
import { channelProviderForInput, expectedChannelAgentId } from "../db/channel-identity";
|
|
17
18
|
import { emitAgentStatus, emitNewMessage } from "../sse";
|
|
18
19
|
import { noteAgentTimelineEvent } from "../compaction-watch";
|
|
19
20
|
import { agentProviderTimelineEvent } from "../provider-timeline-event";
|
|
20
21
|
import { notifyAgentReady, notifyAgentRegistered } from "../agent-lifecycle-events";
|
|
21
|
-
import { markSpawnRegistrationConfirmed } from "../commands-db";
|
|
22
22
|
import { resolveSpawnLineage, resolveTeamLineage } from "../security";
|
|
23
23
|
import { flushQueuedDirectMessages, markManagedAgentRunning } from "./managed-running";
|
|
24
24
|
import { acquireTeamOnReady } from "./team";
|
|
@@ -56,7 +56,7 @@ function taskIdsForReadyAgent(agent: AgentCard): number[] {
|
|
|
56
56
|
return [...ids].sort((a, b) => a - b);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
export function bindSpawnedTaskToReadyAgent(agentId: string, actor = "agent-relay"): void {
|
|
59
|
+
export async function bindSpawnedTaskToReadyAgent(agentId: string, actor = "agent-relay"): Promise<void> {
|
|
60
60
|
const agent = getAgent(agentId);
|
|
61
61
|
if (!agent?.ready || isSpawnIntentCanceled(agent.meta?.spawnRequestId)) return;
|
|
62
62
|
const taskIds = taskIdsForReadyAgent(agent);
|
|
@@ -67,9 +67,9 @@ export function bindSpawnedTaskToReadyAgent(agentId: string, actor = "agent-rela
|
|
|
67
67
|
if (before.ownerAgentId && before.ownerAgentId !== agent.id && getAgent(before.ownerAgentId)?.status !== "offline") continue;
|
|
68
68
|
const assigned = before.ownerAgentId === agent.id
|
|
69
69
|
? before
|
|
70
|
-
: assignTaskEntityAgent(taskId, agent.id, "worker", agent.spawnedBy ?? actor);
|
|
70
|
+
: (await assignTaskEntityAgent(taskId, agent.id, "worker", agent.spawnedBy ?? actor));
|
|
71
71
|
if (assigned.status === "open" || assigned.status === "claimed" || assigned.status === "orphaned") {
|
|
72
|
-
setTaskEntityStatus(taskId, {
|
|
72
|
+
await setTaskEntityStatus(taskId, {
|
|
73
73
|
status: "in_progress",
|
|
74
74
|
note: `worker ${agent.id} ready`,
|
|
75
75
|
actor: agent.spawnedBy ?? actor,
|
|
@@ -90,9 +90,9 @@ export function bindSpawnedTaskToReadyAgent(agentId: string, actor = "agent-rela
|
|
|
90
90
|
* regardless of which transport carried it — a child readied only over the bus used to die
|
|
91
91
|
* unstamped and get reaped as `agent.spawn_failed`.
|
|
92
92
|
*/
|
|
93
|
-
export function announceSpawnedAgentReady(agentId: string, actor = "agent-relay"): void {
|
|
94
|
-
notifyAgentReady(agentId);
|
|
95
|
-
bindSpawnedTaskToReadyAgent(agentId, actor);
|
|
93
|
+
export async function announceSpawnedAgentReady(agentId: string, actor = "agent-relay"): Promise<void> {
|
|
94
|
+
await notifyAgentReady(agentId);
|
|
95
|
+
await bindSpawnedTaskToReadyAgent(agentId, actor);
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
/** The PRIVILEGED principals that bypass the registration gates — an explicit allowlist, so a
|
|
@@ -238,7 +238,7 @@ function serverRecordedApprovalMode(agentId: string, ctx: AuthContext): SpawnApp
|
|
|
238
238
|
return undefined;
|
|
239
239
|
}
|
|
240
240
|
|
|
241
|
-
export function registerAgent(input: RegisterAgentInput, ctx: AuthContext, options: { emitStatus?: boolean } = {}): AgentCard {
|
|
241
|
+
export async function registerAgent(input: RegisterAgentInput, ctx: AuthContext, options: { emitStatus?: boolean } = {}): Promise<AgentCard> {
|
|
242
242
|
// Reserved built-in identities — the `system` lifecycle sender and the whole `user:` human
|
|
243
243
|
// namespace (legacy bare `'user'`, `user:default`, every `user:<id>`) — are mechanical sinks,
|
|
244
244
|
// never registrable agents. THIS is the one external-registration boundary (HTTP `postAgent`
|
|
@@ -358,7 +358,7 @@ export function registerAgent(input: RegisterAgentInput, ctx: AuthContext, optio
|
|
|
358
358
|
if (Object.keys(spawnMeta).length) registrationInput.meta = { ...registrationInput.meta, ...spawnMeta };
|
|
359
359
|
const rawSpawnSystemTags = readSpawnSystemTags(registeringSpawnRequestId, registrationInput.id);
|
|
360
360
|
const spawnSystemTags = rawSpawnSystemTags === undefined ? undefined : normalizeAgentSystemTags(rawSpawnSystemTags);
|
|
361
|
-
const agent =
|
|
361
|
+
const agent = await upsertAgentAsync(registrationInput, {
|
|
362
362
|
verifiedSpawnRequestId: ctx.constraints?.spawnRequestIds?.length === 1 ? ctx.constraints.spawnRequestIds[0] : undefined,
|
|
363
363
|
...(verifiedKeepJti ? { verifiedKeepJtis: [verifiedKeepJti] } : {}),
|
|
364
364
|
...(spawnSystemTags !== undefined ? { systemTags: spawnSystemTags } : {}),
|
|
@@ -366,20 +366,20 @@ export function registerAgent(input: RegisterAgentInput, ctx: AuthContext, optio
|
|
|
366
366
|
// #3567 — a first registration or a new generation records its principal (a reconnect has
|
|
367
367
|
// nothing new to stamp): the spawn's principal, else the registering caller's.
|
|
368
368
|
if (!existing || metaStr(existing.meta, "spawnRequestId") !== registeringSpawnRequestId) {
|
|
369
|
-
|
|
369
|
+
await stampSpawnPrincipalAsync(agent.id, registeringSpawnRequestId, registeringPrincipal(ctx, agent.id));
|
|
370
370
|
}
|
|
371
371
|
// #3398 — a new instance under this id replaced the provider process: that is a context reset.
|
|
372
372
|
if (existing?.instanceId && agent.instanceId && agent.instanceId !== existing.instanceId) {
|
|
373
|
-
recordContextReset({ agentId: agent.id, resetId: `restart:${agent.instanceId}`, kind: "restart", observedAt: Date.now(), evidence: { previousInstanceId: existing.instanceId } });
|
|
373
|
+
(await recordContextReset({ agentId: agent.id, resetId: `restart:${agent.instanceId}`, kind: "restart", observedAt: Date.now(), evidence: { previousInstanceId: existing.instanceId } }));
|
|
374
374
|
}
|
|
375
375
|
// A parked continuation is durable across runner process loss. Registration is the earliest
|
|
376
376
|
// point a replacement can receive it, so replay here rather than waiting for maintenance.
|
|
377
|
-
if (agent.ready && agent.status !== "offline") replayParkedContinuationInjections(agent.id, `registration:${agent.epoch}`);
|
|
377
|
+
if (agent.ready && agent.status !== "offline") (await replayParkedContinuationInjections(agent.id, `registration:${agent.epoch}`));
|
|
378
378
|
|
|
379
379
|
// Managed came-up-running reconcile — no-op for non-managed agents (the guard lives
|
|
380
380
|
// in markManagedAgentRunning). Clears backoff, flushes the policy queue, transitions
|
|
381
381
|
// managed state to running.
|
|
382
|
-
markManagedAgentRunning(
|
|
382
|
+
(await markManagedAgentRunning(
|
|
383
383
|
{
|
|
384
384
|
policyName: metaStr(agent.meta, "policyName"),
|
|
385
385
|
spawnRequestId: metaStr(agent.meta, "spawnRequestId"),
|
|
@@ -387,7 +387,7 @@ export function registerAgent(input: RegisterAgentInput, ctx: AuthContext, optio
|
|
|
387
387
|
tmuxSession: metaStr(agent.meta, "tmuxSession"),
|
|
388
388
|
},
|
|
389
389
|
ctx,
|
|
390
|
-
);
|
|
390
|
+
));
|
|
391
391
|
|
|
392
392
|
// ONE status broadcast (branch-state-enriched, #236), ONE timeline note. A real
|
|
393
393
|
// PreCompact/SessionStart hook arrives as a latched meta.timelineEvent — clears any
|
|
@@ -407,25 +407,25 @@ export function registerAgent(input: RegisterAgentInput, ctx: AuthContext, optio
|
|
|
407
407
|
const generation = metaStr(agent.meta, "spawnRequestId");
|
|
408
408
|
const newGeneration = generation !== undefined && generation !== metaStr(existing?.meta, "spawnRequestId");
|
|
409
409
|
if (newGeneration) notifyAgentRegistered(agent);
|
|
410
|
-
if (agent.ready && (!existing?.ready || newGeneration)) announceSpawnedAgentReady(agent.id, ctx.actor.id);
|
|
410
|
+
if (agent.ready && (!existing?.ready || newGeneration)) await announceSpawnedAgentReady(agent.id, ctx.actor.id);
|
|
411
411
|
|
|
412
412
|
// #416 — flush queued direct messages when a re-registering agent was previously unavailable
|
|
413
413
|
// (offline, stale, compacting, blocked) or a new agent becomes ready. No-op for brand-new agents
|
|
414
414
|
// (no queued messages yet) and when no queued messages exist.
|
|
415
|
-
if (agent.ready) acquireTeamOnReady(agent.id); // #3107 — a coordinator takes its project's seat before the flush
|
|
416
|
-
if (existing && (existing.status === "offline" || isAgentUnavailable(existing)) && agent.ready) flushQueuedDirectMessages(agent.id);
|
|
415
|
+
if (agent.ready) await acquireTeamOnReady(agent.id); // #3107 — a coordinator takes its project's seat before the flush
|
|
416
|
+
if (existing && (existing.status === "offline" || isAgentUnavailable(existing)) && agent.ready) (await flushQueuedDirectMessages(agent.id));
|
|
417
417
|
|
|
418
418
|
if (!existing) {
|
|
419
419
|
// #413 — a spawned worker with a report-back contract gets a pending reply obligation to its
|
|
420
420
|
// spawner so it can't idle with its verdict undelivered (vent #121). First-registration only.
|
|
421
421
|
const spawnRequestId = metaStr(agent.meta, "spawnRequestId");
|
|
422
|
-
if (spawnRequestId)
|
|
422
|
+
if (spawnRequestId) await markSpawnRegistrationConfirmedAsync(spawnRequestId);
|
|
423
423
|
const provider = metaStr(agent.meta, "provider");
|
|
424
|
-
const promptMirror =
|
|
424
|
+
const promptMirror = await seedSpawnPromptMirrorAsync(agent.id, spawnRequestId);
|
|
425
425
|
if (promptMirror?.created || promptMirror?.updated) emitNewMessage(promptMirror.message);
|
|
426
426
|
seedSpawnReplyObligation(agent.id, agent.spawnedBy, spawnRequestId);
|
|
427
|
-
void fireAndForget(`[register] spawn prompt issue activity for ${agent.id}`, () => recordSpawnPromptIssueActivity(agent, spawnRequestId));
|
|
428
|
-
|
|
427
|
+
void fireAndForget(`[register] spawn prompt issue activity for ${agent.id}`, async () => (await recordSpawnPromptIssueActivity(agent, spawnRequestId)));
|
|
428
|
+
await createActivityEventAsync({
|
|
429
429
|
clientId: `server-agent-${agent.id}-registered`,
|
|
430
430
|
kind: "state",
|
|
431
431
|
title: "Agent registered",
|
|
@@ -54,7 +54,7 @@ export async function restartProjectRole(input: {
|
|
|
54
54
|
if (!base) throw new Error(`it extends unknown profile "${role.extends}"`);
|
|
55
55
|
resolveProjectRoleForProvider(role, input.provider, { providerArgs: input.providerArgs }, {});
|
|
56
56
|
let profile = projectRoleProfile((await withResolvedProvisioningAsync(base, input.provider))!, role);
|
|
57
|
-
if (profile.sources?.project) profile = applyProjectCapabilityGates(profile, input.sourceCwd, undefined, { orchestratorLocal: isOrchestratorLocalToRelay(input.host), pinnedMcpCommit: role.source.sha });
|
|
57
|
+
if (profile.sources?.project) profile = (await applyProjectCapabilityGates(profile, input.sourceCwd, undefined, { orchestratorLocal: isOrchestratorLocalToRelay(input.host), pinnedMcpCommit: role.source.sha }));
|
|
58
58
|
assertProjectRoleMcpTrusted(role, profile);
|
|
59
59
|
assertProjectRoleMcpCwd(role, input.runtimeCwd);
|
|
60
60
|
const defaults = projectRoleSpawnDefaults(builtInProfileSpawnDefaults(base.name), role);
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { runnerRuntimeTokenEnvAsync, SUCCESSOR_LAUNCH, type RunnerRuntimeTokenInput } from "../runtime-tokens";
|
|
2
|
+
import { updateManagedAgentStateAsync } from "../db/lifecycle-tail-async";
|
|
1
3
|
import { createCommandAsync } from "../commands-write";
|
|
2
4
|
import { restartHarnessSubagents } from "./harness-subagents";
|
|
3
5
|
import { RELAY_TOKEN_HEADER, isRecord, isSpawnProvider, stringValue } from "@humwick/sdk";
|
|
@@ -5,7 +7,7 @@ import { recordedLaunchMode } from "../launch-mode";
|
|
|
5
7
|
import type { ProviderEffort } from "@humwick/sdk/provider-catalog";
|
|
6
8
|
import { listCommands } from "../commands-db";
|
|
7
9
|
import { isTrustedOrchestratorApiUrl, relayToken } from "../config";
|
|
8
|
-
import { getAgentProfile, getManagedAgentState, getSpawnPolicy
|
|
10
|
+
import { getAgentProfile, getManagedAgentState, getSpawnPolicy } from "../config-store";
|
|
9
11
|
import { getMergeLease, getOrchestrator, listWorkspaces, ownedIsolatedWorkspace, ValidationError, withResolvedProvisioning, withResolvedProvisioningAsync, workflowRunIdOf } from "../db";
|
|
10
12
|
import { resolveProjectForCanonicalCwd } from "./project";
|
|
11
13
|
import { isProvenStewardAgent, stewardWorktreeCwdPrefixes } from "../steward-spawn-guard";
|
|
@@ -16,7 +18,6 @@ import { resolveWhoamiRole } from "../mcp/identity-projection";
|
|
|
16
18
|
import { listManagedOrchestratorsForAgent } from "../orchestrator-lookup";
|
|
17
19
|
import { applyDefaultSharedMcp } from "../shared-mcp";
|
|
18
20
|
import { buildSpawnCommand, generateSpawnRequestId, resolveSpawnModelParams, resolveSpawnModelParamsAsync } from "../spawn-command";
|
|
19
|
-
import { SUCCESSOR_LAUNCH, runnerRuntimeTokenEnv, runnerRuntimeTokenEnvAsync, type RunnerRuntimeTokenInput } from "../runtime-tokens";
|
|
20
21
|
import { restartEffortState } from "./self-effort";
|
|
21
22
|
// #2033 — from isolated-workspace-placement.ts, NOT ./spawn-identity: that module also exports
|
|
22
23
|
// buildProspectiveSpawnIdentity, which imports mcp/identity.ts → self-resume.ts → restart-spawn.ts,
|
|
@@ -65,13 +66,13 @@ function resumeWorkspaceForRestart(agent: AgentCard): { branch: string; mode: "a
|
|
|
65
66
|
* self-restart. The token is minted here from the CURRENT profile, never cloned from the old
|
|
66
67
|
* runtime token, so changed scopes/tool groups take effect on the successor.
|
|
67
68
|
*/
|
|
68
|
-
function buildRestartSpawnParamsForAgent(
|
|
69
|
+
async function buildRestartSpawnParamsForAgent(
|
|
69
70
|
agent: AgentCard,
|
|
70
71
|
orchestrator: Orchestrator | null,
|
|
71
72
|
policyName?: string,
|
|
72
73
|
predecessorSpawnRequestId?: string,
|
|
73
74
|
opts: RestartOptions = {},
|
|
74
|
-
): Record<string, unknown> | undefined {
|
|
75
|
+
): Promise<Record<string, unknown> | undefined> {
|
|
75
76
|
if (!orchestrator) return undefined;
|
|
76
77
|
refuseSyncProjectRoleRestart(agent);
|
|
77
78
|
const requestId = generateSpawnRequestId();
|
|
@@ -110,14 +111,14 @@ function buildRestartSpawnParamsForAgent(
|
|
|
110
111
|
resumeWorktreePath: restartResumeWorkspace?.worktreePath,
|
|
111
112
|
});
|
|
112
113
|
const params = {
|
|
113
|
-
...buildManagedSpawnParams(relaunchPolicy(policy.value, relaunch.effort), requestId, { createdBy: requestedBy, authority: SUCCESSOR_LAUNCH, identityAgentId: agent.id }),
|
|
114
|
+
...(await buildManagedSpawnParams(relaunchPolicy(policy.value, relaunch.effort), requestId, { createdBy: requestedBy, authority: SUCCESSOR_LAUNCH, identityAgentId: agent.id })),
|
|
114
115
|
...relaunch.carried,
|
|
115
116
|
agentId: agent.id,
|
|
116
117
|
requestedBy,
|
|
117
118
|
// Override the policy builder's normal mint so stable team/project bindings are also
|
|
118
119
|
// carried by the fresh token, not merely preserved incidentally on the agent row.
|
|
119
|
-
env:
|
|
120
|
-
[...stewardExtraCwdPrefixes, ...isolatedExtraCwdPrefixes])),
|
|
120
|
+
env: (await runnerRuntimeTokenEnvAsync(managedRestartTokenInput(agent, policy.value, requestId, requestedBy, projectId,
|
|
121
|
+
[...stewardExtraCwdPrefixes, ...isolatedExtraCwdPrefixes]))),
|
|
121
122
|
...managedRestartOptions(opts, restartResumeWorkspace),
|
|
122
123
|
};
|
|
123
124
|
return opts.resumeId ? withResumeParams(params, opts.resumeId, agent.id) : params;
|
|
@@ -126,7 +127,7 @@ function buildRestartSpawnParamsForAgent(
|
|
|
126
127
|
const provider = stringValue(agent.meta?.provider);
|
|
127
128
|
if (!isSpawnProvider(provider)) return undefined;
|
|
128
129
|
// #2033 review — fold in #2041: for an ISOLATED agent, `agent.meta.cwd` is caller-writable
|
|
129
|
-
// (not an AUTHZ_META_KEY) and this `cwd` feeds `
|
|
130
|
+
// (not an AUTHZ_META_KEY) and this `cwd` feeds `runnerRuntimeTokenEnvAsync({cwd})` below as the
|
|
130
131
|
// successor token's cwdPrefixes[0] — a forged value would mint a token authorizing an arbitrary
|
|
131
132
|
// path. Prefer the same unforgeable orchestrator-written DB row (`ownedIsolatedWorkspace`) G1
|
|
132
133
|
// sources the resume worktree from; it's undefined for a non-isolated (shared/coordinator) agent
|
|
@@ -167,7 +168,7 @@ function buildRestartSpawnParamsForAgent(
|
|
|
167
168
|
};
|
|
168
169
|
const tokenContext = { orchestratorId: orchestrator.id, projectId, extraCwdPrefixes: stewardExtraCwdPrefixes };
|
|
169
170
|
return buildRestartSpawnCommand(agent, predecessorSpawnRequestId, agentProfile, projectedInstructions, opts, options, tokenContext,
|
|
170
|
-
|
|
171
|
+
(await runnerRuntimeTokenEnvAsync(restartTokenInput(agent, options, tokenContext))));
|
|
171
172
|
}
|
|
172
173
|
|
|
173
174
|
/** Worker-safe restart command construction used by HTTP/orchestrator/self-restart flows. */
|
|
@@ -457,11 +458,11 @@ export async function createSelfRestartCommandAsync(
|
|
|
457
458
|
const predecessorSpawnRequestId = stringValue(agent.meta?.spawnRequestId);
|
|
458
459
|
const restartSpawn = await restartSpawnParamsForAgentAsync(agent, orchestrator, policyName, predecessorSpawnRequestId, { continuation, generation, reason });
|
|
459
460
|
if (!restartSpawn) throw new ValidationError("self-restart could not build a fresh successor spawn");
|
|
460
|
-
return createCommandAsync(selfRestartInput(agent, orchestrator, restartSpawn, generation, continuation, fullContextMessageId, reason, now));
|
|
461
|
+
return (await createCommandAsync(selfRestartInput(agent, orchestrator, restartSpawn, generation, continuation, fullContextMessageId, reason, now)));
|
|
461
462
|
}
|
|
462
463
|
|
|
463
464
|
/** Settle managed runtime correlation only after the transactional orchestrator swap succeeds. */
|
|
464
|
-
export function settleSelfRestartManagedState(command: Command): void {
|
|
465
|
+
export async function settleSelfRestartManagedState(command: Command): Promise<void> {
|
|
465
466
|
if (command.type !== "agent.restart" || !isRecord(command.params.selfRestart)) return;
|
|
466
467
|
const policyName = stringValue(command.params.policyName);
|
|
467
468
|
if (!policyName || command.status !== "succeeded") return;
|
|
@@ -472,7 +473,7 @@ export function settleSelfRestartManagedState(command: Command): void {
|
|
|
472
473
|
const spawnRequestId = stringValue(restartSpawn?.spawnRequestId) ?? state?.spawnRequestId;
|
|
473
474
|
const tmuxSession = stringValue(report?.tmuxSession) ?? stringValue(report?.sessionName) ?? state?.tmuxSession;
|
|
474
475
|
if (!agentId || !spawnRequestId) return;
|
|
475
|
-
|
|
476
|
+
(await updateManagedAgentStateAsync(policyName, {
|
|
476
477
|
status: "starting",
|
|
477
478
|
agentId,
|
|
478
479
|
spawnRequestId,
|
|
@@ -480,12 +481,12 @@ export function settleSelfRestartManagedState(command: Command): void {
|
|
|
480
481
|
lastSpawnAt: command.updatedAt,
|
|
481
482
|
lastStopAt: command.updatedAt,
|
|
482
483
|
restartCount: (state?.restartCount ?? 0) + 1,
|
|
483
|
-
});
|
|
484
|
-
markManagedAgentRunning({ policyName, spawnRequestId, agentId, tmuxSession }, authContextFromSystem(), { now: () => command.updatedAt });
|
|
484
|
+
}));
|
|
485
|
+
(await markManagedAgentRunning({ policyName, spawnRequestId, agentId, tmuxSession }, authContextFromSystem(), { now: () => command.updatedAt }));
|
|
485
486
|
}
|
|
486
487
|
|
|
487
|
-
export function restartSpawnParamsForAgent(...args: Parameters<typeof buildRestartSpawnParamsForAgent>) {
|
|
488
|
-
return buildRestartSpawnParamsForAgent(...args);
|
|
488
|
+
export async function restartSpawnParamsForAgent(...args: Parameters<typeof buildRestartSpawnParamsForAgent>) {
|
|
489
|
+
return (await buildRestartSpawnParamsForAgent(...args));
|
|
489
490
|
}
|
|
490
491
|
export async function restartSpawnParamsForAgentAsync(...args: Parameters<typeof buildRestartSpawnParamsForAgentAsync>) {
|
|
491
492
|
return buildRestartSpawnParamsForAgentAsync(...args);
|
|
@@ -17,10 +17,10 @@ export interface ReviewPublishDispatch {
|
|
|
17
17
|
error?: string;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
export function dispatchReviewPublish(
|
|
20
|
+
export async function dispatchReviewPublish(
|
|
21
21
|
workspace: WorkspaceRecord | undefined,
|
|
22
22
|
opts: { requestedBy: string; taskId: number; sha?: string; emit: (command: Command) => void },
|
|
23
|
-
): ReviewPublishDispatch | undefined {
|
|
23
|
+
): Promise<ReviewPublishDispatch | undefined> {
|
|
24
24
|
if (!workspace || workspace.mode !== "isolated" || !workspace.worktreePath) return undefined;
|
|
25
25
|
// #1452 round-6 H2b / round-7 H3 — publishing REQUIRES an immutable delivered COMMIT OID (the host
|
|
26
26
|
// must pin exactly it, and settlement matches result.sha === requested sha). It must be a concrete
|
|
@@ -46,28 +46,11 @@ export function dispatchReviewPublish(
|
|
|
46
46
|
// failing ledger insert was swallowed, leaving a POLLABLE publish command with NO cleanup ledger
|
|
47
47
|
// (a crash/insert-failure orphan). The transaction makes them all-or-nothing: a ledger failure
|
|
48
48
|
// rolls back the command row too, so a reviewer never sees a command with no durable cleanup path.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
// Round-9: PIN the exact ref (relay-derived, host-asserted-equal before push) so cleanup can
|
|
55
|
-
// delete it even if the host's success-result is lost. The row also holds the branch/host/
|
|
56
|
-
// requested-sha so cleanup survives a purge/recycle. On settlement the reconciler re-writes the
|
|
57
|
-
// host-confirmed ref (identical to this one, since the host refuses to push a divergent ref).
|
|
58
|
-
upsertReviewRefCleanup({
|
|
59
|
-
id: built.command.id,
|
|
60
|
-
ref: reviewRef,
|
|
61
|
-
workspaceId: workspace.id,
|
|
62
|
-
repoRoot: workspace.repoRoot,
|
|
63
|
-
worktreePath: workspace.worktreePath,
|
|
64
|
-
branch: workspace.branch,
|
|
65
|
-
orchestratorId: workspace.orchestratorId,
|
|
66
|
-
requestedSha: sha,
|
|
67
|
-
});
|
|
68
|
-
command = built.command;
|
|
69
|
-
});
|
|
70
|
-
if (!command) return { workspaceId: workspace.id, dispatched: false, error: buildError ?? "review publish not dispatched" };
|
|
49
|
+
const built = await buildWorkspacePublishReviewCommand(workspace, opts.requestedBy, { taskId: opts.taskId, sha, reviewRef,
|
|
50
|
+
cleanup: { ref: reviewRef, workspaceId: workspace.id, repoRoot: workspace.repoRoot, worktreePath: workspace.worktreePath,
|
|
51
|
+
branch: workspace.branch, orchestratorId: workspace.orchestratorId, requestedSha: sha } });
|
|
52
|
+
if (!built.ok) return { workspaceId: workspace.id, dispatched: false, error: built.error };
|
|
53
|
+
const command = built.command;
|
|
71
54
|
opts.emit(command); // durable state (command + ledger) is committed before anyone is notified
|
|
72
55
|
return { workspaceId: workspace.id, commandId: command.id, dispatched: true };
|
|
73
56
|
} catch (err) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { cancelPendingCommandAsync, recordReviewRefCleanupDispatchAsync, markReviewRefPublishedAsync, markReviewRefFailedAsync, markReviewRefClosedAsync, markReviewRefAnnouncedAsync, deleteReviewRefCleanupAsync } from "../db/runtime-tail-async";
|
|
1
2
|
// #1452 round-4 — the durable, OFF-critical-path engine that settles the review-ref (refs/review/*)
|
|
2
3
|
// lifecycle and cleans up leaked refs. It reads the review_ref_cleanups ledger (one row per EXACT
|
|
3
4
|
// server-derived ref) — NOT a per-workspace marker — so it fixes every round-4 finding at once:
|
|
@@ -23,18 +24,8 @@
|
|
|
23
24
|
// was never enabled, since rows are only written when a publish is dispatched.
|
|
24
25
|
|
|
25
26
|
import { getWorkspace } from "../db";
|
|
26
|
-
import {
|
|
27
|
-
|
|
28
|
-
getReviewRefFenceToken,
|
|
29
|
-
listReviewRefCleanups,
|
|
30
|
-
markReviewRefAnnounced,
|
|
31
|
-
markReviewRefClosed,
|
|
32
|
-
markReviewRefFailed,
|
|
33
|
-
markReviewRefPublished,
|
|
34
|
-
recordReviewRefCleanupDispatch,
|
|
35
|
-
type ReviewRefCleanup,
|
|
36
|
-
} from "../db/review-ref-cleanups";
|
|
37
|
-
import { cancelPendingCommand, getCommand } from "../commands-db";
|
|
27
|
+
import { getReviewRefFenceToken, listReviewRefCleanups, type ReviewRefCleanup } from "../db/review-ref-cleanups";
|
|
28
|
+
import { getCommand } from "../commands-db";
|
|
38
29
|
import { emitCommand } from "../maintenance/events";
|
|
39
30
|
import { emitRelayEvent } from "../events";
|
|
40
31
|
import { buildReviewRefUnpublishCommandFromFields } from "../workspace-actions";
|
|
@@ -81,16 +72,16 @@ interface ReconcileDeps {
|
|
|
81
72
|
* pending publish can still be externally spoofed to succeeded{pushed:true}. Requiring the token here
|
|
82
73
|
* refuses that phantom before it can announce a ref that was never pushed. Returns null when absent. */
|
|
83
74
|
getFenceToken: (id: string) => string | null;
|
|
84
|
-
markPublished: typeof
|
|
85
|
-
markFailed: typeof
|
|
86
|
-
markAnnounced: typeof
|
|
87
|
-
markClosed: typeof
|
|
88
|
-
recordDispatch: typeof
|
|
89
|
-
deleteCleanup: typeof
|
|
75
|
+
markPublished: typeof markReviewRefPublishedAsync;
|
|
76
|
+
markFailed: typeof markReviewRefFailedAsync;
|
|
77
|
+
markAnnounced: typeof markReviewRefAnnouncedAsync;
|
|
78
|
+
markClosed: typeof markReviewRefClosedAsync;
|
|
79
|
+
recordDispatch: typeof recordReviewRefCleanupDispatchAsync;
|
|
80
|
+
deleteCleanup: typeof deleteReviewRefCleanupAsync;
|
|
90
81
|
buildUnpublish: typeof buildReviewRefUnpublishCommandFromFields;
|
|
91
82
|
/** #1452 round-10 HIGH #1 — atomically cancel the publish command IFF still pending; true means it
|
|
92
83
|
* was never dispatched (proven no push can occur), so a closed round can retire it without racing. */
|
|
93
|
-
cancelPublish: (id: string) => boolean
|
|
84
|
+
cancelPublish: (id: string) => boolean | Promise<boolean>;
|
|
94
85
|
emit: (command: Command) => void;
|
|
95
86
|
/** Durable publish-settled announcement: emitted for a published-but-unannounced row, then marked
|
|
96
87
|
* announced only on success — so a crash/outbox failure re-emits next pass (round-6 H-atomicity). */
|
|
@@ -164,21 +155,21 @@ const realDeps: ReconcileDeps = {
|
|
|
164
155
|
},
|
|
165
156
|
getCommand,
|
|
166
157
|
getFenceToken: getReviewRefFenceToken,
|
|
167
|
-
markPublished:
|
|
168
|
-
markFailed:
|
|
169
|
-
markAnnounced:
|
|
170
|
-
markClosed:
|
|
171
|
-
recordDispatch:
|
|
172
|
-
deleteCleanup:
|
|
158
|
+
markPublished: markReviewRefPublishedAsync,
|
|
159
|
+
markFailed: markReviewRefFailedAsync,
|
|
160
|
+
markAnnounced: markReviewRefAnnouncedAsync,
|
|
161
|
+
markClosed: markReviewRefClosedAsync,
|
|
162
|
+
recordDispatch: recordReviewRefCleanupDispatchAsync,
|
|
163
|
+
deleteCleanup: deleteReviewRefCleanupAsync,
|
|
173
164
|
buildUnpublish: buildReviewRefUnpublishCommandFromFields,
|
|
174
|
-
cancelPublish: (id) =>
|
|
165
|
+
cancelPublish: async (id) => (await cancelPendingCommandAsync(id, "workspace.publish-review", "review publish canceled: round closed before dispatch")),
|
|
175
166
|
emit: emitCommand,
|
|
176
167
|
emitPublished: emitPublishedEvent,
|
|
177
168
|
now: () => Date.now(),
|
|
178
169
|
};
|
|
179
170
|
|
|
180
171
|
/** One reconcile pass over the durable ledger. Pure over its (injectable) deps for testing. */
|
|
181
|
-
export function reconcileReviewRefs(deps: ReconcileDeps = realDeps, signal?: AbortSignal): ReviewRefReconcileResult {
|
|
172
|
+
export async function reconcileReviewRefs(deps: ReconcileDeps = realDeps, signal?: AbortSignal): Promise<ReviewRefReconcileResult> {
|
|
182
173
|
const rows = deps.listCleanups();
|
|
183
174
|
const published: string[] = [];
|
|
184
175
|
const failed: string[] = [];
|
|
@@ -208,7 +199,7 @@ export function reconcileReviewRefs(deps: ReconcileDeps = realDeps, signal?: Abo
|
|
|
208
199
|
const roundClosed = !ws
|
|
209
200
|
|| TERMINAL_WORKSPACE_STATUSES.has(ws.status as any)
|
|
210
201
|
|| Boolean(ws.branch && row.branch && ws.branch !== row.branch);
|
|
211
|
-
if (roundClosed) { deps.markClosed(row.id, now); closed = true; }
|
|
202
|
+
if (roundClosed) { await deps.markClosed(row.id, now); closed = true; }
|
|
212
203
|
}
|
|
213
204
|
|
|
214
205
|
// 1) Settle the publish outcome (H2). A ref is only real once its push command TERMINALLY
|
|
@@ -229,8 +220,8 @@ export function reconcileReviewRefs(deps: ReconcileDeps = realDeps, signal?: Abo
|
|
|
229
220
|
// push can occur) → retire with nothing on origin. If it was already claimed (accepted/running),
|
|
230
221
|
// the host may push, so WAIT for its terminal settlement — cleanup then runs safely AFTER it.
|
|
231
222
|
if (!closed) { inFlight.push(label()); continue; }
|
|
232
|
-
if (!deps.cancelPublish(row.id)) { inFlight.push(label()); continue; } // already claimed → wait
|
|
233
|
-
deps.markFailed(row.id, now, false); // canceled before dispatch → nothing on origin, drop the ref
|
|
223
|
+
if (!await deps.cancelPublish(row.id)) { inFlight.push(label()); continue; } // already claimed → wait
|
|
224
|
+
await deps.markFailed(row.id, now, false); // canceled before dispatch → nothing on origin, drop the ref
|
|
234
225
|
confirmedRef = undefined;
|
|
235
226
|
publishState = "failed";
|
|
236
227
|
failed.push(label());
|
|
@@ -242,7 +233,7 @@ export function reconcileReviewRefs(deps: ReconcileDeps = realDeps, signal?: Abo
|
|
|
242
233
|
// never-claimed pending publish spoofed to succeeded has none, so it never advances to published.
|
|
243
234
|
const ref = confirmedPublishRef(cmd, row.requestedSha, row.ref, deps.getFenceToken(row.id));
|
|
244
235
|
if (ref) {
|
|
245
|
-
deps.markPublished(row.id, ref, now); // sets announced=0 → announced below (H-atomicity)
|
|
236
|
+
await deps.markPublished(row.id, ref, now); // sets announced=0 → announced below (H-atomicity)
|
|
246
237
|
confirmedRef = ref;
|
|
247
238
|
publishState = "published";
|
|
248
239
|
announced = false;
|
|
@@ -253,7 +244,7 @@ export function reconcileReviewRefs(deps: ReconcileDeps = realDeps, signal?: Abo
|
|
|
253
244
|
// timed-out/canceled, a missing/pruned result, or a malformed success) may have pushed before
|
|
254
245
|
// its result was lost ⇒ RETAIN the pinned ref as a tombstone and unpublish before deletion.
|
|
255
246
|
const notPushed = definitivelyNotPushed(cmd);
|
|
256
|
-
deps.markFailed(row.id, now, !notPushed);
|
|
247
|
+
await deps.markFailed(row.id, now, !notPushed);
|
|
257
248
|
if (notPushed) confirmedRef = undefined;
|
|
258
249
|
publishState = "failed";
|
|
259
250
|
failed.push(label());
|
|
@@ -267,7 +258,7 @@ export function reconcileReviewRefs(deps: ReconcileDeps = realDeps, signal?: Abo
|
|
|
267
258
|
if (publishState === "published" && !announced && confirmedRef) {
|
|
268
259
|
try {
|
|
269
260
|
deps.emitPublished({ workspaceId: row.workspaceId, ref: confirmedRef, ...(row.requestedSha ? { sha: row.requestedSha } : {}), ...(row.branch ? { branch: row.branch } : {}) });
|
|
270
|
-
deps.markAnnounced(row.id, now);
|
|
261
|
+
await deps.markAnnounced(row.id, now);
|
|
271
262
|
announced = true;
|
|
272
263
|
} catch { /* emit failed → leave announced=0 so the next pass re-emits (at-least-once) */ }
|
|
273
264
|
}
|
|
@@ -285,7 +276,7 @@ export function reconcileReviewRefs(deps: ReconcileDeps = realDeps, signal?: Abo
|
|
|
285
276
|
|
|
286
277
|
// A closed round with NOTHING on origin — a DEFINITIVELY-not-pushed outcome, or a legacy row that
|
|
287
278
|
// never pinned a ref — has nothing to delete. Retire it directly, no host round-trip, no leak.
|
|
288
|
-
if (!confirmedRef) { deps.deleteCleanup(row.id); cleared.push(label()); continue; }
|
|
279
|
+
if (!confirmedRef) { await deps.deleteCleanup(row.id); cleared.push(label()); continue; }
|
|
289
280
|
|
|
290
281
|
// 3) Drive deletion (H4/M7) of the EXACT host-confirmed ref. CONFIRM a prior attempt first — a
|
|
291
282
|
// succeeded delete clears the row regardless of the backoff window; the backoff gates only a
|
|
@@ -294,20 +285,20 @@ export function reconcileReviewRefs(deps: ReconcileDeps = realDeps, signal?: Abo
|
|
|
294
285
|
const cmd = deps.getCommand(row.cleanupCommandId);
|
|
295
286
|
if (cmd && !COMMAND_TERMINAL.has(cmd.status)) { inFlight.push(confirmedRef); continue; }
|
|
296
287
|
if (cmd && cmd.status === "succeeded" && !cmd.error) {
|
|
297
|
-
deps.deleteCleanup(row.id); // origin confirmed the ref is gone (deleted or already absent)
|
|
288
|
+
await deps.deleteCleanup(row.id); // origin confirmed the ref is gone (deleted or already absent)
|
|
298
289
|
cleared.push(confirmedRef);
|
|
299
290
|
continue;
|
|
300
291
|
}
|
|
301
292
|
// failed / timed_out / missing → re-dispatch below, subject to backoff.
|
|
302
293
|
}
|
|
303
294
|
if (row.nextAttemptAt != null && row.nextAttemptAt > now) { waiting.push(confirmedRef); continue; }
|
|
304
|
-
const built = deps.buildUnpublish(
|
|
295
|
+
const built = await deps.buildUnpublish(
|
|
305
296
|
{ workspaceId: row.workspaceId, repoRoot: row.repoRoot, worktreePath: row.worktreePath, branch: row.branch, orchestratorId: row.orchestratorId, ref: confirmedRef },
|
|
306
297
|
"review-ref-reconciler",
|
|
307
298
|
);
|
|
308
299
|
if (!built.ok) continue; // no owning host reachable yet — retry next tick (no backoff burned)
|
|
309
300
|
deps.emit(built.command);
|
|
310
|
-
deps.recordDispatch(row.id, built.command.id, now + cleanupBackoffMs(row.cleanupAttempts), now);
|
|
301
|
+
await deps.recordDispatch(row.id, built.command.id, now + cleanupBackoffMs(row.cleanupAttempts), now);
|
|
311
302
|
dispatched.push(confirmedRef);
|
|
312
303
|
}
|
|
313
304
|
|
|
@@ -326,7 +317,7 @@ export const REVIEW_REF_RECONCILE_JOB = {
|
|
|
326
317
|
intervalMs: WORKSPACE_ORPHAN_REAPER_INTERVAL_MS,
|
|
327
318
|
runOnStart: false,
|
|
328
319
|
deadlineMs: 60 * 1000,
|
|
329
|
-
handler: (context: { signal?: AbortSignal }) => reconcileReviewRefs(undefined, context.signal) as unknown as Record<string, unknown>,
|
|
320
|
+
handler: async (context: { signal?: AbortSignal }) => (await reconcileReviewRefs(undefined, context.signal)) as unknown as Record<string, unknown>,
|
|
330
321
|
};
|
|
331
322
|
|
|
332
323
|
// Test seam: backoff parameters so tests assert scheduling without duplicating literals.
|