@humwick/server 0.130.2 → 0.130.3
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 +73 -4
- package/package.json +8 -7
- package/public/assets/{MessageBubble-BWLqMGKG.js → MessageBubble-Bq9kr21N.js} +2 -2
- package/public/assets/{PlanGraphPanel-3TlIQpjk.js → PlanGraphPanel-Dqiroy06.js} +2 -2
- package/public/assets/{TraceView-Wimm0Czv.js → TraceView-DYU962-H.js} +2 -2
- package/public/assets/{VaultDecisionCard-Bau_vNax.js → VaultDecisionCard-DhPfzioE.js} +2 -2
- package/public/assets/{activity-CrSurn-r.js → activity-DzCcQTee.js} +2 -2
- package/public/assets/{agent-lineage-CknAJKcb.js → agent-lineage-SgDt9i3T.js} +2 -2
- package/public/assets/agent-liveness-Bq-s73jW.js +2 -0
- package/public/assets/{agent-name-DKbiIhBo.js → agent-name-CcY3Aijy.js} +2 -2
- package/public/assets/{agent-profiles-DwF6EQ4R.js → agent-profiles-cxtRBn6O.js} +2 -2
- package/public/assets/{agent-project-badge-Diz4Sjjb.js → agent-project-badge-DGYbIhaI.js} +2 -2
- package/public/assets/{agent-tools-CRhpRQza.js → agent-tools-DHbftV0N.js} +2 -2
- package/public/assets/{agent-type-icon-LrUEU3lB.js → agent-type-icon-CY113sp-.js} +2 -2
- package/public/assets/{agents-B-v66C-4.js → agents-BPzY7IlQ.js} +2 -2
- package/public/assets/{analytics-BqCb-LKi.js → analytics-DENFPv_a.js} +2 -2
- package/public/assets/{artifact-viewer-kf4pGuAR.js → artifact-viewer-KRvSc7sl.js} +2 -2
- package/public/assets/{artifacts-DJevXcvg.js → artifacts-C9gSmL3s.js} +2 -2
- package/public/assets/{artifacts-D32zyw4Y.js → artifacts-DT-HkI5O.js} +2 -2
- package/public/assets/{automation-BpRajx_5.js → automation-BsmjQhKD.js} +2 -2
- package/public/assets/{backlog-D_UjTdzX.js → backlog-8NYH_62t.js} +2 -2
- package/public/assets/{busy-health-N3VEh4ao.js → busy-health-D3hwvYb4.js} +2 -2
- package/public/assets/{channels-9jopungq.js → channels-DCJ5WL7O.js} +2 -2
- package/public/assets/{chat-CSXrTB3C.js → chat-DAIl357U.js} +3 -3
- package/public/assets/{code-preview-p1NnmqK0.js → code-preview-CzrZfr9U.js} +2 -2
- package/public/assets/{connectors-CZdqlDNL.js → connectors-xTxtlyou.js} +2 -2
- package/public/assets/{cosmos-BgdsE4dG.js → cosmos-BpebFLK2.js} +2 -2
- package/public/assets/{crate-agent-30aXTKva.js → crate-agent-BHvI5wXC.js} +2 -2
- package/public/assets/{dependency-graph-B_jvw2GE.js → dependency-graph-DGT0o3wJ.js} +2 -2
- package/public/assets/{deploys-DoN6bAb5.js → deploys-hqWOHSHE.js} +2 -2
- package/public/assets/{display-BrKWFGJz.js → display-CDeH4I8T.js} +2 -2
- package/public/assets/{files-D6XEu8Ng.js → files-BTeGVmTg.js} +2 -2
- package/public/assets/{fleet-C7jQWbw2.js → fleet-DgXtNgCa.js} +2 -2
- package/public/assets/{fleet-metrics-BiDs3MLx.js → fleet-metrics-BHEt5yc_.js} +2 -2
- package/public/assets/{formatted-body-CQBwc1fb.js → formatted-body-MLtodyPp.js} +3 -3
- package/public/assets/{formatted-body-impl-DyG0WsQS.js → formatted-body-impl-CAe664Z8.js} +2 -2
- package/public/assets/{host-providers-dialog-DKaW31dN.js → host-providers-dialog-mapF4Fj9.js} +2 -2
- package/public/assets/{index-CRHep3M9.js → index-CqqgOI5R.js} +5 -5
- package/public/assets/{insights-CzrtkpOE.js → insights-DNyf7IzF.js} +2 -2
- package/public/assets/{integrations-aVsUmigt.js → integrations-DlYOHq7T.js} +2 -2
- package/public/assets/{issue-ref-chip-BnSlM_95.js → issue-ref-chip-CeWBNWn7.js} +2 -2
- package/public/assets/{log-viewer-qe7Ee47Z.js → log-viewer-Cib8nqdE.js} +2 -2
- package/public/assets/{maintenance-NfjqFZAk.js → maintenance-DSn1cPm7.js} +2 -2
- package/public/assets/{managed-agents-BiH0HgaE.js → managed-agents-C3CkM81m.js} +2 -2
- package/public/assets/{markdown-preview-BixaT3Xr.js → markdown-preview-W4VQQPAv.js} +3 -3
- package/public/assets/{markdown-preview-impl-DNGlWIS3.js → markdown-preview-impl-CMsTdhjq.js} +2 -2
- package/public/assets/{memory-DNsCl6UX.js → memory-BV17qtO4.js} +2 -2
- package/public/assets/{messages-DopU1L1X.js → messages-Bm_1AqSA.js} +2 -2
- package/public/assets/{orchestrators-Cq7ckLQT.js → orchestrators-Dg-vfP09.js} +2 -2
- package/public/assets/{overview-C1DCTzzV.js → overview-DcYCrvlr.js} +2 -2
- package/public/assets/{pairs-DE_m6rrE.js → pairs-8o9nuG3V.js} +2 -2
- package/public/assets/{profile-icon-B3rfh77F.js → profile-icon-BsRyBMea.js} +2 -2
- package/public/assets/{project-profile-instructions-DQWZE0kA.js → project-profile-instructions-DETKFn_E.js} +2 -2
- package/public/assets/{projects-Br2Bd2h1.js → projects-M_m6AHws.js} +2 -2
- package/public/assets/{prompt-settings-BKvFsvGD.js → prompt-settings-C_mNCgeL.js} +2 -2
- package/public/assets/provider-display-BEeLXC_D.js +2 -0
- package/public/assets/{registry-Cw38_6gW.js → registry-DDHHHE5N.js} +2 -2
- package/public/assets/run-card-Dt4I4jaP.js +2 -0
- package/public/assets/{secrets-DxI-vzSq.js → secrets-BkVwxI5O.js} +2 -2
- package/public/assets/{service-access-settings-D9s_VJbd.js → service-access-settings-Crq-6a-B.js} +2 -2
- package/public/assets/{settings-CKZTVGIx.js → settings-CYcavMFu.js} +2 -2
- package/public/assets/shipping-H6gzEeZB.js +2 -0
- package/public/assets/{stats-DyUBKyWI.js → stats-BJVeWkpV.js} +2 -2
- package/public/assets/{status-dot-DYqlEUhJ.js → status-dot-BcHyw1Vw.js} +2 -2
- package/public/assets/store-DKPDIZBe.js +9 -0
- package/public/assets/{tasks-Bh7Chr4-.js → tasks-qJjlQPpx.js} +2 -2
- package/public/assets/{teams-bowI7qVY.js → teams-Cd5Sk1uX.js} +2 -2
- package/public/assets/{terminal-viewer-BgVLHdsU.js → terminal-viewer-CpY5_m7W.js} +3 -3
- package/public/assets/{terminal-viewer-impl-Dz22UPy6.js → terminal-viewer-impl-DI_2llFR.js} +2 -2
- package/public/assets/{use-agent-terminal-DSQQ-iY7.js → use-agent-terminal-0yJA_buM.js} +2 -2
- package/public/assets/{use-decisions-samMQhQf.js → use-decisions-uJXHf34W.js} +2 -2
- package/public/assets/{use-project-list-59LheVP7.js → use-project-list-CpWl6eHQ.js} +2 -2
- package/public/assets/{use-selectors-BZa25xEB.js → use-selectors-BdxBW8mL.js} +2 -2
- package/public/assets/{user-avatar-DHxlvUvV.js → user-avatar-3IndfpOc.js} +2 -2
- package/public/assets/{versioned-prompt-editor-BmED5DnJ.js → versioned-prompt-editor-CIVg7XB1.js} +2 -2
- package/public/assets/{work-queue-CSvpuNLz.js → work-queue-B9rnLwHZ.js} +2 -2
- package/public/assets/{workflow-marker-DAZcj5qt.js → workflow-marker-X8wxyhzC.js} +2 -2
- package/public/assets/{workflows-DdmxRSyg.js → workflows-o5YyMFW7.js} +2 -2
- package/public/assets/{workspaces-BxeKfVuF.js → workspaces-CzEGoAng.js} +2 -2
- package/public/cosmos.html +4 -4
- package/public/index.html +28 -28
- package/runner/plugins/claude/.claude-plugin/plugin.json +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 +2 -1
- 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/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/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 +1 -11
- 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/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 +8 -3
- 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/injection-sweep-pacing.ts +6 -8
- 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 +14 -15
- 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-secret-files.ts +1 -1
- package/src/routes/hosts.ts +11 -11
- package/src/routes/inbox.ts +3 -2
- package/src/routes/index.ts +2 -1
- 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 +1 -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 +2 -2
- package/src/routes/secrets-import.ts +1 -1
- package/src/routes/secrets-vault.ts +7 -7
- 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-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 +21 -22
- 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/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-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/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/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/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,3 +1,5 @@
|
|
|
1
|
+
import { pauseMaintenanceJobAsync } from "../db/runtime-tail-async";
|
|
2
|
+
import { createActivityEventAsync } from "../db/activity-async";
|
|
1
3
|
// #1724 stage 1 — the maintenance circuit breaker's bookkeeping and trip actions.
|
|
2
4
|
//
|
|
3
5
|
// The design (§5.4, §8.5) asks for a breaker that, when a job repeatedly starves the event loop (or
|
|
@@ -15,7 +17,7 @@
|
|
|
15
17
|
|
|
16
18
|
import { errMessage } from "@humwick/sdk";
|
|
17
19
|
import { MAINTENANCE_BREAKER_MAX_STRIKES } from "../config";
|
|
18
|
-
import { AUTO_BREAKER_PAUSE_HOLDER
|
|
20
|
+
import { AUTO_BREAKER_PAUSE_HOLDER } from "../db";
|
|
19
21
|
|
|
20
22
|
// Two strike counters per job, because the two failure signals want different reset semantics — and
|
|
21
23
|
// getting this wrong silently defeats the breaker:
|
|
@@ -81,7 +83,7 @@ export interface BreakerStrikeResult {
|
|
|
81
83
|
* and, on reaching MAINTENANCE_BREAKER_MAX_STRIKES, trips: a non-critical job is durably auto-paused;
|
|
82
84
|
* a critical one is escalated LOUDLY but never paused. Idempotent per runId.
|
|
83
85
|
*/
|
|
84
|
-
export function recordBreakerStrike(input: BreakerStrikeInput): BreakerStrikeResult {
|
|
86
|
+
export async function recordBreakerStrike(input: BreakerStrikeInput): Promise<BreakerStrikeResult> {
|
|
85
87
|
if (!markStruck(input.runId)) {
|
|
86
88
|
return { struck: false, strikes: breakerStrikeCount(input.jobId), tripped: false };
|
|
87
89
|
}
|
|
@@ -92,7 +94,7 @@ export function recordBreakerStrike(input: BreakerStrikeInput): BreakerStrikeRes
|
|
|
92
94
|
if (count < MAINTENANCE_BREAKER_MAX_STRIKES) {
|
|
93
95
|
return { struck: true, strikes: breakerStrikeCount(input.jobId), tripped: false };
|
|
94
96
|
}
|
|
95
|
-
const action = trip(input.jobId, input.critical, input.reason);
|
|
97
|
+
const action = (await trip(input.jobId, input.critical, input.reason));
|
|
96
98
|
return { struck: true, strikes: 0, tripped: true, action };
|
|
97
99
|
}
|
|
98
100
|
|
|
@@ -119,34 +121,34 @@ export function isRunStruck(runId: string): boolean {
|
|
|
119
121
|
return struckRunIds.has(runId);
|
|
120
122
|
}
|
|
121
123
|
|
|
122
|
-
function trip(jobId: string, critical: boolean, reason: string): "paused" | "escalated" | "pause-conflict" {
|
|
124
|
+
async function trip(jobId: string, critical: boolean, reason: string): Promise<"paused" | "escalated" | "pause-conflict"> {
|
|
123
125
|
// Reset BOTH counters first: after a trip the job is either paused (won't run) or escalated; either
|
|
124
126
|
// way the next strike should start a fresh count rather than instantly re-trip on every later run.
|
|
125
127
|
timeoutStrikes.delete(jobId);
|
|
126
128
|
lagStrikes.delete(jobId);
|
|
127
129
|
if (critical) {
|
|
128
|
-
escalateCritical(jobId, reason);
|
|
130
|
+
(await escalateCritical(jobId, reason));
|
|
129
131
|
return "escalated";
|
|
130
132
|
}
|
|
131
|
-
return autoPause(jobId, reason);
|
|
133
|
+
return (await autoPause(jobId, reason));
|
|
132
134
|
}
|
|
133
135
|
|
|
134
|
-
function autoPause(jobId: string, reason: string): "paused" | "pause-conflict" {
|
|
136
|
+
async function autoPause(jobId: string, reason: string): Promise<"paused" | "pause-conflict"> {
|
|
135
137
|
const pauseReason = `auto-paused by circuit breaker: ${reason}`;
|
|
136
138
|
console.warn(`[relay] maintenance circuit breaker TRIPPED — durably auto-pausing ${jobId}: ${reason}`);
|
|
137
139
|
try {
|
|
138
140
|
// Indefinite on purpose: a job that was starving the loop must NOT quietly resume itself on a
|
|
139
141
|
// timer. A human resumes it once it is fixed — and the `maintenance-auto-paused` health arm is
|
|
140
142
|
// what makes sure a human sees it (this pause is otherwise invisible to the critical-only arm).
|
|
141
|
-
const result =
|
|
142
|
-
auditBreakerEvent({
|
|
143
|
+
const result = (await pauseMaintenanceJobAsync({ jobId, holder: AUTO_BREAKER_PAUSE_HOLDER, reason: pauseReason, indefinite: true }));
|
|
144
|
+
(await auditBreakerEvent({
|
|
143
145
|
jobId,
|
|
144
146
|
clientId: `server-maintenance-breaker-pause-${jobId}-${result.pause.createdAt}`,
|
|
145
147
|
title: `Maintenance circuit breaker auto-paused ${jobId}`,
|
|
146
148
|
body: `${jobId} was auto-paused by the event-loop-lag circuit breaker after ${MAINTENANCE_BREAKER_MAX_STRIKES} strikes — ${reason}. It will stay paused until a human resumes it.`,
|
|
147
149
|
icon: "ti-alert-octagon",
|
|
148
150
|
metadata: { breaker: true, pauseId: result.pause.id, alreadyHeld: !result.ok },
|
|
149
|
-
});
|
|
151
|
+
}));
|
|
150
152
|
return result.ok ? "paused" : "pause-conflict";
|
|
151
153
|
} catch (error) {
|
|
152
154
|
console.warn(`[relay] maintenance circuit breaker could not pause ${jobId}: ${errMessage(error)}`);
|
|
@@ -154,27 +156,27 @@ function autoPause(jobId: string, reason: string): "paused" | "pause-conflict" {
|
|
|
154
156
|
}
|
|
155
157
|
}
|
|
156
158
|
|
|
157
|
-
function escalateCritical(jobId: string, reason: string): void {
|
|
159
|
+
async function escalateCritical(jobId: string, reason: string): Promise<void> {
|
|
158
160
|
// §8.5 — the one thing the breaker must never do is silence a critical job the way a bare
|
|
159
161
|
// `enabled=0` silenced cd-watcher for nine hours. So a critical job is escalated, never paused: it
|
|
160
162
|
// KEEPS running (and keeps being reported by criticalMaintenanceJobsCheck if it is genuinely
|
|
161
163
|
// failing), and a loud, attributed event tells a human to intervene deliberately.
|
|
162
164
|
console.warn(`[relay] maintenance circuit breaker: CRITICAL job ${jobId} repeatedly misbehaving (${reason}) — NOT auto-paused (see #1645/§8.5), escalating for a human`);
|
|
163
|
-
auditBreakerEvent({
|
|
165
|
+
(await auditBreakerEvent({
|
|
164
166
|
jobId,
|
|
165
167
|
clientId: `server-maintenance-breaker-escalate-${jobId}-${Date.now()}`,
|
|
166
168
|
title: `Maintenance circuit breaker: critical job ${jobId} needs a human`,
|
|
167
169
|
body: `${jobId} repeatedly starved the event loop or overran its deadline (${reason}). Because it is CRITICAL, the breaker did NOT auto-pause it — a machine silencing a critical job is the #1645 blackout. A human must decide what to do.`,
|
|
168
170
|
icon: "ti-alert-triangle",
|
|
169
171
|
metadata: { breaker: true, critical: true, escalated: true },
|
|
170
|
-
});
|
|
172
|
+
}));
|
|
171
173
|
}
|
|
172
174
|
|
|
173
|
-
function auditBreakerEvent(input: { jobId: string; clientId: string; title: string; body: string; icon: string; metadata: Record<string, unknown> }): void {
|
|
175
|
+
async function auditBreakerEvent(input: { jobId: string; clientId: string; title: string; body: string; icon: string; metadata: Record<string, unknown> }): Promise<void> {
|
|
174
176
|
try {
|
|
175
177
|
// #1912 S3.2: sync-retained — the lag sampler invokes recordBreakerStrike synchronously and
|
|
176
178
|
// cannot await without redesigning the event-loop attribution callback contract.
|
|
177
|
-
|
|
179
|
+
(await createActivityEventAsync({
|
|
178
180
|
clientId: input.clientId,
|
|
179
181
|
kind: "operator",
|
|
180
182
|
title: input.title,
|
|
@@ -183,7 +185,7 @@ function auditBreakerEvent(input: { jobId: string; clientId: string; title: stri
|
|
|
183
185
|
icon: input.icon,
|
|
184
186
|
view: "activity",
|
|
185
187
|
metadata: { source: "server", maintenanceJobId: input.jobId, ...input.metadata },
|
|
186
|
-
});
|
|
188
|
+
}));
|
|
187
189
|
} catch (error) {
|
|
188
190
|
// The pause/escalation itself already happened; losing the timeline entry must not throw back
|
|
189
191
|
// into the scheduler's tick.
|
|
@@ -9,6 +9,7 @@ import { isRecord } from "@humwick/sdk";
|
|
|
9
9
|
import { createHash } from "node:crypto";
|
|
10
10
|
import { sendMessageServiceAsync } from "../services/send-message";
|
|
11
11
|
import { checkoutRefreshEnabled } from "../config";
|
|
12
|
+
import { runGit } from "../services/git-process";
|
|
12
13
|
|
|
13
14
|
async function notice(projectId: string, projectSlug: string, host: string, rootPath: string, reason: string): Promise<void> {
|
|
14
15
|
const key = createHash("sha256").update(JSON.stringify([projectId, host, rootPath, reason])).digest("hex");
|
|
@@ -28,6 +29,7 @@ export function reconcileCheckoutRefresh(signal?: AbortSignal): Promise<Record<s
|
|
|
28
29
|
async function reconcile(signal?: AbortSignal): Promise<Record<string, unknown>> {
|
|
29
30
|
const dispatched: string[] = [];
|
|
30
31
|
const skipped: string[] = [];
|
|
32
|
+
const remoteTips = new Map<string, Promise<string | undefined>>();
|
|
31
33
|
for (const project of await listProjectsAsync()) {
|
|
32
34
|
if (signal?.aborted) break;
|
|
33
35
|
const roots = await rootsForProjectAsync(project.id);
|
|
@@ -55,8 +57,23 @@ async function reconcile(signal?: AbortSignal): Promise<Record<string, unknown>>
|
|
|
55
57
|
if (prior && ["pending", "accepted", "running"].includes(prior.status)) continue;
|
|
56
58
|
// The host reported once that it cannot refresh; re-asking every tick only repeats that (#3629).
|
|
57
59
|
if (health === "unsupported") { skipped.push(`${project.slug}: unsupported host ${hostId} for ${root.rootPath}`); continue; }
|
|
60
|
+
// Completed receipts remain valid until the remote default branch moves (#3813).
|
|
61
|
+
if (prior?.status === "succeeded" && prior.result?.targetSha && ["current", "refreshed", "disabled"].includes(prior.result.status)) {
|
|
62
|
+
let tip = remoteTips.get(root.remoteUrl!);
|
|
63
|
+
if (!tip) {
|
|
64
|
+
tip = runGit(["ls-remote", "--symref", "--", root.remoteUrl!, "HEAD"], 10_000, 4096).then(result => {
|
|
65
|
+
if (result.exitCode !== 0 || result.unavailable || !/^ref: refs\/heads\/.+\tHEAD$/m.test(result.stdout)) return undefined;
|
|
66
|
+
return result.stdout.match(/^([0-9a-f]{40,64})\tHEAD$/m)?.[1];
|
|
67
|
+
});
|
|
68
|
+
remoteTips.set(root.remoteUrl!, tip);
|
|
69
|
+
}
|
|
70
|
+
const sha = await tip;
|
|
71
|
+
if (!sha) { skipped.push(`${project.slug}: remote default branch unavailable`); continue; }
|
|
72
|
+
if (sha === prior.result.targetSha) continue;
|
|
73
|
+
}
|
|
58
74
|
// Leave one tick of backoff after terminal failure, including restart/lease recovery (#3691).
|
|
59
75
|
if (prior && (["failed", "timed_out"].includes(prior.status) || prior.result?.status === "error") && Date.now() < (prior.updatedAt ?? 0) + 60_000) continue;
|
|
76
|
+
if (signal?.aborted) break;
|
|
60
77
|
const command = await createCommandAsync({ type: "project.checkout.refresh", source: "system", target: `orchestrator-${hostId}`,
|
|
61
78
|
params: { projectId: project.id, rootPath: root.rootPath, remoteUrl: root.remoteUrl, orchestratorId: hostId, enabled: true, ...(prior?.result ? { previousResult: prior.result } : {}) } });
|
|
62
79
|
emitCommandEvent(command, "command.requested");
|
|
@@ -66,7 +83,7 @@ async function reconcile(signal?: AbortSignal): Promise<Record<string, unknown>>
|
|
|
66
83
|
return { enabled: true, dispatched, skipped };
|
|
67
84
|
}
|
|
68
85
|
|
|
69
|
-
//
|
|
86
|
+
// Publish hints and external pushes use the same remote-tip comparison.
|
|
70
87
|
subscribeRelayEvents((event) => {
|
|
71
88
|
if (!event.type.startsWith("command.") || !isRecord(event.data.command)) return;
|
|
72
89
|
const command = event.data.command;
|
|
@@ -128,18 +128,18 @@ export async function runCriticalJobWatchdog(now: number = Date.now()): Promise<
|
|
|
128
128
|
|
|
129
129
|
const { subject, body, payload } = composeNotice(degraded, resetRuns, legacyHungRuns, heldRestartEpoch, now);
|
|
130
130
|
const recipients: string[] = [];
|
|
131
|
-
const human = notifyWorkspaceHumanEscalation({
|
|
131
|
+
const human = (await notifyWorkspaceHumanEscalation({
|
|
132
132
|
subject,
|
|
133
133
|
body,
|
|
134
134
|
payload,
|
|
135
135
|
source: "maintenance-critical-job-watchdog",
|
|
136
136
|
tags: ["agent-relay", "maintenance"],
|
|
137
|
-
});
|
|
137
|
+
}));
|
|
138
138
|
if (human.accepted && human.channelId) recipients.push(human.channelId);
|
|
139
139
|
|
|
140
140
|
const agentTarget = stewardFallbackTarget();
|
|
141
141
|
if (agentTarget) {
|
|
142
|
-
const routed = notifyTarget("maintenance.degraded", {}, agentTarget, subject, body, payload);
|
|
142
|
+
const routed = await notifyTarget("maintenance.degraded", {}, agentTarget, subject, body, payload);
|
|
143
143
|
if (routed) recipients.push(routed);
|
|
144
144
|
}
|
|
145
145
|
|
|
@@ -9,7 +9,7 @@ export function emitCommand(command: Command): void {
|
|
|
9
9
|
|
|
10
10
|
// Route a maintenance notification, swallowing failures (a stale/missing/misconfigured
|
|
11
11
|
// target must never break the sweep). Returns the target on success, null otherwise.
|
|
12
|
-
export function notifyTarget(
|
|
12
|
+
export async function notifyTarget(
|
|
13
13
|
type: NotificationType,
|
|
14
14
|
scope: NotificationScope,
|
|
15
15
|
target: string,
|
|
@@ -17,10 +17,10 @@ export function notifyTarget(
|
|
|
17
17
|
body: string,
|
|
18
18
|
payload: Record<string, unknown>,
|
|
19
19
|
idempotencyKey?: string,
|
|
20
|
-
): string | null {
|
|
20
|
+
): Promise<string | null> {
|
|
21
21
|
if (!target) return null;
|
|
22
22
|
try {
|
|
23
|
-
routeNotification({ type, scope, recipients: [target], message: { subject, body, payload, idempotencyKey } });
|
|
23
|
+
await routeNotification({ type, scope, recipients: [target], message: { subject, body, payload, idempotencyKey } });
|
|
24
24
|
return target;
|
|
25
25
|
} catch {
|
|
26
26
|
return null;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { getGateway } from "../db/gateway";
|
|
2
|
+
import type { MaintenanceJobDefinition } from "./types";
|
|
3
|
+
|
|
4
|
+
export const HISTORY_RETENTION_JOB: MaintenanceJobDefinition = {
|
|
5
|
+
id: "history-retention", critical: false,
|
|
6
|
+
title: "Command, kernel replay and token history retention",
|
|
7
|
+
description: "Expire superseded checkout refresh commands after one hour, other non-spawn terminal commands and kernel replay receipts after 30 days, and raw usage after 90 days. Preserve references, expired replay keys and permanent budget spend (#3619, #3797).",
|
|
8
|
+
intervalMs: 60 * 60_000, backlogIntervalMs: 1_000, deadlineMs: 30_000, runOnStart: true,
|
|
9
|
+
async handler(context) {
|
|
10
|
+
const gateway = getGateway();
|
|
11
|
+
const result = { prunedCommands: 0, prunedKernelReceipts: 0, prunedTokenReports: 0, moreWork: false };
|
|
12
|
+
for (const [op, key] of [
|
|
13
|
+
["historyRetention.commands", "prunedCommands"],
|
|
14
|
+
["historyRetention.kernel", "prunedKernelReceipts"],
|
|
15
|
+
["historyRetention.tokens", "prunedTokenReports"],
|
|
16
|
+
] as const) {
|
|
17
|
+
if (context.signal.aborted) return { ...result, aborted: true, moreWork: true };
|
|
18
|
+
const start = performance.now();
|
|
19
|
+
let batch: { pruned: number; moreWork: boolean };
|
|
20
|
+
do {
|
|
21
|
+
if (context.signal.aborted) return { ...result, aborted: true, moreWork: true };
|
|
22
|
+
batch = await gateway.write(op, {}) as typeof batch;
|
|
23
|
+
result[key] += batch.pruned;
|
|
24
|
+
// Each await releases the writer for foreground work; bound the whole catch-up run (#3807).
|
|
25
|
+
} while (op === "historyRetention.commands" && batch.moreWork && performance.now() - start < 250);
|
|
26
|
+
result.moreWork ||= batch.moreWork;
|
|
27
|
+
}
|
|
28
|
+
return result;
|
|
29
|
+
},
|
|
30
|
+
};
|
package/src/maintenance/jobs.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import { OFFLINE_AGENT_PRUNE_JOB } from "./offline-agent-prune-job";
|
|
2
|
+
import { pruneOrphanedSharedWorkspacesAsync, releaseExpiredDriveLeasesAsync } from "../db/runtime-tail-async";
|
|
3
|
+
import { HISTORY_RETENTION_JOB } from "./history-retention-job";
|
|
1
4
|
import { isHumanAgentId } from "@humwick/sdk";
|
|
2
|
-
import {
|
|
5
|
+
import { REAP_INTERVAL_MS, STALE_TTL_MS } from "../config";
|
|
6
|
+
import { IDLE_EPICS_JOB, PENDING_ISSUE_CLOSE_JOB, PLAN_READY_NOTICE_JOB } from "./long-running-work";
|
|
3
7
|
import { CHECKOUT_REFRESH_JOB } from "./checkout-refresh";
|
|
4
8
|
import { CLAUDE_API_BUDGET_RECONCILE_JOB } from "./claude-api-budget-job";
|
|
5
9
|
import { SERVICE_ACCESS_JOBS } from "./service-access-job";
|
|
@@ -15,27 +19,9 @@ import { deriveAgentBusyHealth } from "../agent-busy-health";
|
|
|
15
19
|
import { expireStaleBusAgents } from "../bus";
|
|
16
20
|
import { eventLoopLagGraceMs } from "../event-loop-lag";
|
|
17
21
|
import { pruneOutboxAsync } from "../bus-outbox-async";
|
|
18
|
-
import {
|
|
22
|
+
import { expireCommandsAsync } from "../db/runtime-admin-async";
|
|
19
23
|
import { getRetentionConfig } from "../retention-config-store";
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
createActivityEventAsync,
|
|
23
|
-
evaluatePoolBindingsAsync,
|
|
24
|
-
getAgent,
|
|
25
|
-
listAgents,
|
|
26
|
-
listStaleQueuedDirectTargetsAsync,
|
|
27
|
-
pruneOrphanedSharedWorkspaces,
|
|
28
|
-
pruneOfflineAgentsAsync,
|
|
29
|
-
pruneRetentionBatchAsync,
|
|
30
|
-
pruneProviderQuotaHistoryAsync,
|
|
31
|
-
reapStaleOrchestratorsAsync,
|
|
32
|
-
releaseExpiredClaimsAsync,
|
|
33
|
-
releaseExpiredDriveLeases,
|
|
34
|
-
releaseOrphanedTasksAsync,
|
|
35
|
-
reapStaleAgentsAsync,
|
|
36
|
-
revokeOrphanedRuntimeTokensAsync,
|
|
37
|
-
runDbMaintenanceAsync,
|
|
38
|
-
} from "../db";
|
|
24
|
+
import { createActivityEventAsync, evaluatePoolBindingsAsync, getAgent, listAgents, listStaleQueuedDirectTargetsAsync, pruneRetentionBatchAsync, pruneProviderQuotaHistoryAsync, reapStaleOrchestratorsAsync, releaseExpiredClaimsAsync, releaseOrphanedTasksAsync, reapStaleAgentsAsync, revokeOrphanedRuntimeTokensAsync, runDbMaintenanceAsync } from "../db";
|
|
39
25
|
import { isAgentUnavailable } from "../db/agent-predicates";
|
|
40
26
|
import { resolveTeamCoordinatorAgentId } from "../coordinator-authority";
|
|
41
27
|
import { flushQueuedDirectMessages } from "../services/managed-running";
|
|
@@ -49,7 +35,6 @@ import { getLifecycleManager } from "../lifecycle-manager";
|
|
|
49
35
|
import { applyCommandToRecipe } from "../recipe-runner";
|
|
50
36
|
import { settleWorkspaceCommand } from "../workspace-command-settle";
|
|
51
37
|
import {
|
|
52
|
-
emitAgentRemoved,
|
|
53
38
|
emitAgentStatus,
|
|
54
39
|
emitMessageClaimReleased,
|
|
55
40
|
emitOrchestratorStatus,
|
|
@@ -64,7 +49,6 @@ import { runIdleAgentDigest } from "../services/idle-agent-digest";
|
|
|
64
49
|
import { runShutdownWatchdog } from "../services/shutdown-watchdog";
|
|
65
50
|
import { AGENT_HANG_WATCHDOG_MAINTENANCE_JOB } from "../services/agent-hang-watchdog-job";
|
|
66
51
|
import { reapIdleStewards } from "../services/steward-reaper";
|
|
67
|
-
import { emitWorkFinalityResult } from "../services/work-finality";
|
|
68
52
|
import { escalateExpiredSpawnCommands } from "../services/spawn-loss-escalation";
|
|
69
53
|
import { reapUnregisteredSpawnCommands } from "../services/spawn-registration-watchdog";
|
|
70
54
|
import { resumeRateLimitedAgents } from "../services/rate-limit-resume";
|
|
@@ -182,7 +166,7 @@ export async function runStuckBusyWatchdog(input: { now?: number; signal?: Abort
|
|
|
182
166
|
},
|
|
183
167
|
});
|
|
184
168
|
|
|
185
|
-
const messages = routeNotification({
|
|
169
|
+
const messages = await routeNotification({
|
|
186
170
|
type: "agent.stuck_busy",
|
|
187
171
|
scope: { agentId: agent.id, parent: agent.spawnedBy, teamId: agent.teamId },
|
|
188
172
|
message: {
|
|
@@ -300,9 +284,9 @@ export const definitions: MaintenanceJobDefinition[] = [
|
|
|
300
284
|
runOnStart: true,
|
|
301
285
|
// #1819 — SYNCHRONOUS, one atomic statement, no fan-out: nothing to split, so the pre-flight
|
|
302
286
|
// check is the whole of its cooperation.
|
|
303
|
-
handler(context) {
|
|
287
|
+
async handler(context) {
|
|
304
288
|
if (context.signal?.aborted) return { releasedDriveLeaseAgentIds: [], aborted: true };
|
|
305
|
-
return { releasedDriveLeaseAgentIds:
|
|
289
|
+
return { releasedDriveLeaseAgentIds: (await releaseExpiredDriveLeasesAsync()) };
|
|
306
290
|
},
|
|
307
291
|
},
|
|
308
292
|
{
|
|
@@ -339,7 +323,7 @@ export const definitions: MaintenanceJobDefinition[] = [
|
|
|
339
323
|
// message belongs in the queue (this is hooked to agent availability,
|
|
340
324
|
// not pool-slot reclaim).
|
|
341
325
|
if (!agent || isAgentUnavailable(agent)) continue;
|
|
342
|
-
const flushed = flushQueuedDirectMessages(agent!.id);
|
|
326
|
+
const flushed = await flushQueuedDirectMessages(agent!.id);
|
|
343
327
|
if (flushed.length > 0) reswept[target] = flushed.length;
|
|
344
328
|
}
|
|
345
329
|
return { resweptTargets: reswept };
|
|
@@ -359,19 +343,14 @@ export const definitions: MaintenanceJobDefinition[] = [
|
|
|
359
343
|
// by heartbeat and deleted on removal, so a missing/offline agent record for the
|
|
360
344
|
// target means it will never poll this command again. A merely-busy (online) host is
|
|
361
345
|
// left alone, preserving the #930/#953 "don't sweep before the host can claim it".
|
|
362
|
-
const commands =
|
|
363
|
-
|
|
364
|
-
if (!command.target || command.target === "system") return false;
|
|
365
|
-
const agent = getAgent(command.target);
|
|
366
|
-
return !agent || agent.status === "offline";
|
|
367
|
-
},
|
|
368
|
-
});
|
|
346
|
+
const commands = await expireCommandsAsync();
|
|
347
|
+
|
|
369
348
|
// #1819 — deliberately NOT checkpointed: `expireCommands` already moved these rows terminal
|
|
370
349
|
// and nothing re-runs this settle, so bailing would strand their workspace and recipe forever.
|
|
371
350
|
// Bounded by one expiry sweep.
|
|
372
351
|
for (const command of commands) {
|
|
373
|
-
notifySelfResumeNoOp(command);
|
|
374
|
-
settleContextResetCarry(command);
|
|
352
|
+
await notifySelfResumeNoOp(command);
|
|
353
|
+
(await settleContextResetCarry(command));
|
|
375
354
|
noteCompactionResult(command);
|
|
376
355
|
await settleWorkspaceCommand(command, { onMergeSettled: scheduleRepoMerge });
|
|
377
356
|
await applyCommandToRecipe(command);
|
|
@@ -412,7 +391,7 @@ export const definitions: MaintenanceJobDefinition[] = [
|
|
|
412
391
|
// #1819 — deliberately NOT checkpointed, as in command-expiration: the attachment is already
|
|
413
392
|
// expired, and this is the one place a parent is told its agent died.
|
|
414
393
|
for (const id of expired.agentIds) {
|
|
415
|
-
getLifecycleManager().onAgentDisappeared(id);
|
|
394
|
+
(await getLifecycleManager().onAgentDisappeared(id));
|
|
416
395
|
// #636/#659 — only after the stale reconnect grace expires do we turn a bare
|
|
417
396
|
// heartbeat gap into a terminal parent notification. A live runner that
|
|
418
397
|
// reconnects before this point heals from `stale` without a false exit.
|
|
@@ -420,7 +399,7 @@ export const definitions: MaintenanceJobDefinition[] = [
|
|
|
420
399
|
// when a succeeded agent.shutdown command explains the silence, instead of the
|
|
421
400
|
// heartbeat-lost/transport-loss verdict a graceful reap used to receive.
|
|
422
401
|
const { reason, diagnosis } = classifyBusExpirationDeath(id);
|
|
423
|
-
notifyAgentOffline(id, reason, diagnosis);
|
|
402
|
+
(await notifyAgentOffline(id, reason, diagnosis));
|
|
424
403
|
}
|
|
425
404
|
for (const task of expired.orphanedTasks) emitTaskChanged(task, "task.updated");
|
|
426
405
|
return {
|
|
@@ -464,26 +443,7 @@ export const definitions: MaintenanceJobDefinition[] = [
|
|
|
464
443
|
return { reapedAgentIds };
|
|
465
444
|
},
|
|
466
445
|
},
|
|
467
|
-
|
|
468
|
-
id: "offline-agent-prune",
|
|
469
|
-
critical: false,
|
|
470
|
-
title: "Offline agent prune",
|
|
471
|
-
description: "Delete long-offline provider agents and release their claims.",
|
|
472
|
-
intervalMs: REAP_INTERVAL_MS,
|
|
473
|
-
runOnStart: false,
|
|
474
|
-
async handler(context) {
|
|
475
|
-
if (context.signal?.aborted) return { prunedAgentIds: [], aborted: true };
|
|
476
|
-
// #1084 crash-final-loss fix-forward — defense-in-depth: `notifyAgentOffline` is the primary
|
|
477
|
-
// flush point when an agent first goes offline, so this is normally a no-op by the time an
|
|
478
|
-
// agent reaches prune horizon; the async facade snapshots its held queue and performs the DB
|
|
479
|
-
// finalization in the SAME worker transaction before deleting the agent row.
|
|
480
|
-
const prunedAgentIds = await pruneOfflineAgentsAsync(OFFLINE_PRUNE_MS, {
|
|
481
|
-
onFinality: emitWorkFinalityResult,
|
|
482
|
-
});
|
|
483
|
-
for (const id of prunedAgentIds) emitAgentRemoved(id);
|
|
484
|
-
return { prunedAgentIds };
|
|
485
|
-
},
|
|
486
|
-
},
|
|
446
|
+
OFFLINE_AGENT_PRUNE_JOB,
|
|
487
447
|
AGENT_IDENTITY_ARCHIVE_PRUNE_JOB,
|
|
488
448
|
{
|
|
489
449
|
id: "orphan-token-revoker",
|
|
@@ -510,12 +470,12 @@ export const definitions: MaintenanceJobDefinition[] = [
|
|
|
510
470
|
description: "Stop spawned sessions whose relay agent is offline/gone but whose process the orchestrator still reports running, after a grace period for self-heal.",
|
|
511
471
|
intervalMs: ORPHAN_REAPER_INTERVAL_MS,
|
|
512
472
|
runOnStart: false,
|
|
513
|
-
handler: (context) => reapOrphanedSessions({ signal: context.signal }),
|
|
473
|
+
handler: async (context) => (await reapOrphanedSessions({ signal: context.signal })),
|
|
514
474
|
},
|
|
515
|
-
{ id: "transient-agent-reaper", critical: false, title: "Transient agent reaper", description: "Cleanly shut down transient spawned agents once they are idle and any isolated branch work has landed; also flushes a persistent agent's held work-final report once it has settled idle (#1084 HIGH fix-forward).", intervalMs: REAP_INTERVAL_MS, runOnStart: false, handler: (context) => reapTransientAgents({ signal: context.signal }) },
|
|
475
|
+
{ id: "transient-agent-reaper", critical: false, title: "Transient agent reaper", description: "Cleanly shut down transient spawned agents once they are idle and any isolated branch work has landed; also flushes a persistent agent's held work-final report once it has settled idle (#1084 HIGH fix-forward).", intervalMs: REAP_INTERVAL_MS, runOnStart: false, handler: async (context) => (await reapTransientAgents({ signal: context.signal })) },
|
|
516
476
|
{ id: "idle-agent-digest", critical: false, title: "Idle-agent digest", description: "One digest per coordinator window listing idle children; stops provably safe ones when the team allows it (#3428). Off until the idle-digest setting enables it.", intervalMs: REAP_INTERVAL_MS, runOnStart: false, handler: (context) => runIdleAgentDigest({ signal: context.signal }) },
|
|
517
477
|
{ id: "shutdown-watchdog", critical: false, title: "Shutdown kill-switch watchdog", description: "#1746 — recover a NON-policy agent stuck `shutting-down` past its grace whose shutdown command never executed (owning host offline / command expired): re-issue exactly one forceful shutdown, then surface a terminal `kill-failed` if it is still alive a further grace window later. Policy/steward agents are excluded (the lifecycle-manager owns their retry ladder, #1761).", intervalMs: REAP_INTERVAL_MS, runOnStart: false, handler: (context) => runShutdownWatchdog({ signal: context.signal }) },
|
|
518
|
-
{ id: "steward-reaper", critical: false, title: "Orphan steward reaper", description: "Independent backstop (#1761) for the lifecycle reconcile: reap ORPHANED stewards ONLY — a live steward agent no managed policy state references (left when a respawn overwrote the state's agentId, or handed off after the lifecycle exhausted its forceful stop budget). Reaps an orphan once idle past the dashboard keepalive grace, or immediately when steward spawning is disabled. Never reaps a busy/provider-blocked/mid-land (claim-holding) steward, reaps each orphan at most once (idempotent), and never touches a still-tracked steward — that one is the lifecycle reconcile's (its keepalive idle-timeout, policy-disabled stop, and forceful stopping-wedge escalation), so the two can never race.", intervalMs: REAP_INTERVAL_MS, runOnStart: false, handler: (context) => reapIdleStewards({ signal: context.signal }) },
|
|
478
|
+
{ id: "steward-reaper", critical: false, title: "Orphan steward reaper", description: "Independent backstop (#1761) for the lifecycle reconcile: reap ORPHANED stewards ONLY — a live steward agent no managed policy state references (left when a respawn overwrote the state's agentId, or handed off after the lifecycle exhausted its forceful stop budget). Reaps an orphan once idle past the dashboard keepalive grace, or immediately when steward spawning is disabled. Never reaps a busy/provider-blocked/mid-land (claim-holding) steward, reaps each orphan at most once (idempotent), and never touches a still-tracked steward — that one is the lifecycle reconcile's (its keepalive idle-timeout, policy-disabled stop, and forceful stopping-wedge escalation), so the two can never race.", intervalMs: REAP_INTERVAL_MS, runOnStart: false, handler: async (context) => (await reapIdleStewards({ signal: context.signal })) },
|
|
519
479
|
{
|
|
520
480
|
id: "restart-epoch-reconcile",
|
|
521
481
|
// CRITICAL: if this stops running, an epoch opened by a deploy is never released — every
|
|
@@ -556,7 +516,7 @@ export const definitions: MaintenanceJobDefinition[] = [
|
|
|
556
516
|
description: "Auto-resume agents held on a provider usage/rate-limit stall once their reset window has passed (#286).",
|
|
557
517
|
intervalMs: REAP_INTERVAL_MS,
|
|
558
518
|
runOnStart: false,
|
|
559
|
-
handler: (context) => resumeRateLimitedAgents({ signal: context.signal }),
|
|
519
|
+
handler: async (context) => (await resumeRateLimitedAgents({ signal: context.signal })),
|
|
560
520
|
},
|
|
561
521
|
{ id: "provider-still-held-nudge", critical: false, title: "Provider still-held nudge", description: "Notify a child's parent while it remains provider-blocked, and page the operator once a top-level provider block passes its threshold.", intervalMs: REAP_INTERVAL_MS, runOnStart: false, handler: async (context) => await sendStillHeldNudges(Date.now(), context.signal) },
|
|
562
522
|
{
|
|
@@ -648,6 +608,7 @@ export const definitions: MaintenanceJobDefinition[] = [
|
|
|
648
608
|
prunedMessages: result.prunedMessages,
|
|
649
609
|
prunedActivityEvents: result.prunedActivityEvents,
|
|
650
610
|
prunedSpawnCommands: result.prunedSpawnCommands,
|
|
611
|
+
slimmedSpawnProfiles: result.slimmedSpawnProfiles,
|
|
651
612
|
offThread: result.offThread,
|
|
652
613
|
moreWork: result.moreWork, // #1758 — bounded-progress signal; drives backlogIntervalMs reschedule.
|
|
653
614
|
};
|
|
@@ -662,6 +623,7 @@ export const definitions: MaintenanceJobDefinition[] = [
|
|
|
662
623
|
SNAPSHOT_LEDGER_PRUNE_JOB,
|
|
663
624
|
PROVIDER_SECRET_SCRUB_JOB,
|
|
664
625
|
WEB_TOOL_CALL_PRUNE_JOB,
|
|
626
|
+
HISTORY_RETENTION_JOB,
|
|
665
627
|
CONTINUATION_ARCHIVE_SEARCH_SYNC_JOB,
|
|
666
628
|
SHIPPING_STATS_ROLLUP_JOB,
|
|
667
629
|
REPLY_OBLIGATION_PARITY_JOB,
|
|
@@ -762,9 +724,9 @@ export const definitions: MaintenanceJobDefinition[] = [
|
|
|
762
724
|
intervalMs: REAP_INTERVAL_MS,
|
|
763
725
|
runOnStart: true,
|
|
764
726
|
// #1819 — SYNCHRONOUS, one atomic sweep: pre-flight check only, nothing to split.
|
|
765
|
-
handler(context) {
|
|
727
|
+
async handler(context) {
|
|
766
728
|
if (context.signal?.aborted) return { prunedSharedWorkspaceIds: [], aborted: true };
|
|
767
|
-
const prunedSharedWorkspaceIds =
|
|
729
|
+
const prunedSharedWorkspaceIds = (await pruneOrphanedSharedWorkspacesAsync());
|
|
768
730
|
return { prunedSharedWorkspaceIds };
|
|
769
731
|
},
|
|
770
732
|
},
|
|
@@ -830,7 +792,7 @@ export const definitions: MaintenanceJobDefinition[] = [
|
|
|
830
792
|
intervalMs: WORKSPACE_IDLE_REFRESH_INTERVAL_MS,
|
|
831
793
|
runOnStart: false,
|
|
832
794
|
deadlineMs: 60 * 1000,
|
|
833
|
-
handler: (context) => refreshIdleWorkspaces({ signal: context.signal }),
|
|
795
|
+
handler: async (context) => (await refreshIdleWorkspaces({ signal: context.signal })),
|
|
834
796
|
},
|
|
835
797
|
{
|
|
836
798
|
id: "workspace-gc",
|
|
@@ -844,14 +806,14 @@ export const definitions: MaintenanceJobDefinition[] = [
|
|
|
844
806
|
// otherwise never run it at all — the backstop effectively off exactly when it's needed.
|
|
845
807
|
runOnStart: true,
|
|
846
808
|
deadlineMs: 60 * 1000,
|
|
847
|
-
handler: (context) => workspaceGC({ signal: context.signal }),
|
|
809
|
+
handler: async (context) => (await workspaceGC({ signal: context.signal })),
|
|
848
810
|
},
|
|
849
811
|
{ id: "agent-exit-cleanup", critical: false, title: "Agent exit cleanup",
|
|
850
812
|
description: "Save exited owners on origin, then remove their workspace, branch, outbox and provider home on each host (#2749).",
|
|
851
813
|
intervalMs: 15 * 60_000, runOnStart: true, logRuns: true, deadlineMs: 60_000,
|
|
852
814
|
handler: (context) => requestAgentExitCleanupSweep(context.signal),
|
|
853
815
|
},
|
|
854
|
-
IDLE_EPICS_JOB, PENDING_ISSUE_CLOSE_JOB,
|
|
816
|
+
IDLE_EPICS_JOB, PENDING_ISSUE_CLOSE_JOB, PLAN_READY_NOTICE_JOB,
|
|
855
817
|
REVIEW_REF_RECONCILE_JOB,
|
|
856
818
|
CHECKOUT_REFRESH_JOB,
|
|
857
819
|
ARTIFACT_CHECKOUT_GC_JOB,
|
|
@@ -5,6 +5,8 @@ import { routeNotification } from "../notification-router";
|
|
|
5
5
|
import { DAY_MS } from "./constants";
|
|
6
6
|
import type { MaintenanceJobDefinition } from "./types";
|
|
7
7
|
|
|
8
|
+
export { PLAN_READY_NOTICE_JOB } from "./plan-ready-notice-job";
|
|
9
|
+
|
|
8
10
|
export const PENDING_ISSUE_CLOSE_JOB: MaintenanceJobDefinition = {
|
|
9
11
|
id: "pending-issue-closes", title: "Issues awaiting Task completion", critical: false,
|
|
10
12
|
description: "Close landed delivery issues when their Tasks reach doneWhen and pass validation.",
|
|
@@ -23,7 +25,7 @@ export async function remindIdleEpics(signal: AbortSignal, now = Date.now()): Pr
|
|
|
23
25
|
if (signal.aborted) break;
|
|
24
26
|
const lines = [...renderTeamIdleEpics(team.id), ...teamFailures.map(failure => `- ${failure}`)];
|
|
25
27
|
if (!lines.length) continue;
|
|
26
|
-
routeNotification({
|
|
28
|
+
await routeNotification({
|
|
27
29
|
type: "team.idle", scope: { teamId: team.id }, recipients: [`coordinator:${team.id}`],
|
|
28
30
|
message: { kind: "system", from: "system", subject: "Open idle epics and Plans", body: lines.join("\n"), replyExpected: false,
|
|
29
31
|
payload: { kind: "team.idle-epics", teamId: team.id }, idempotencyKey: `idle-epics:${team.id}:${Math.floor(now / DAY_MS)}` },
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { OFFLINE_PRUNE_MS, REAP_INTERVAL_MS } from "../config";
|
|
2
|
+
import { pruneOfflineAgentsAsync } from "../db/agents-async";
|
|
3
|
+
import { emitAgentRemoved } from "../sse";
|
|
4
|
+
import { emitWorkFinalityResult } from "../services/work-finality";
|
|
5
|
+
import type { MaintenanceJobDefinition } from "./types";
|
|
6
|
+
|
|
7
|
+
export const OFFLINE_AGENT_PRUNE_JOB: MaintenanceJobDefinition = {
|
|
8
|
+
id: "offline-agent-prune",
|
|
9
|
+
critical: false,
|
|
10
|
+
title: "Offline agent prune",
|
|
11
|
+
description: "Delete long-offline provider agents and release their claims.",
|
|
12
|
+
intervalMs: REAP_INTERVAL_MS,
|
|
13
|
+
runOnStart: false,
|
|
14
|
+
async handler(context) {
|
|
15
|
+
if (context.signal?.aborted) return { prunedAgentIds: [], aborted: true };
|
|
16
|
+
// #1084 crash-final-loss fix-forward — defense-in-depth: `notifyAgentOffline` is the primary
|
|
17
|
+
// flush point when an agent first goes offline, so this is normally a no-op by the time an
|
|
18
|
+
// agent reaches prune horizon; the async facade snapshots its held queue and performs the DB
|
|
19
|
+
// finalization in the SAME worker transaction before deleting the agent row.
|
|
20
|
+
let archivePurgeFailures = 0;
|
|
21
|
+
const prunedAgentIds = await pruneOfflineAgentsAsync(OFFLINE_PRUNE_MS, {
|
|
22
|
+
onFinality: emitWorkFinalityResult,
|
|
23
|
+
onArchivePurgeFailure: () => { archivePurgeFailures++; },
|
|
24
|
+
});
|
|
25
|
+
for (const id of prunedAgentIds) emitAgentRemoved(id);
|
|
26
|
+
return { prunedAgentIds, archivePurgeFailures };
|
|
27
|
+
},
|
|
28
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createCommandAsync } from "../commands-write";
|
|
2
|
+
import { listCommands } from "../commands-db";
|
|
2
3
|
import { createActivityEventAsync, getAgent, listOrchestrators } from "../db";
|
|
3
4
|
import type { Command, ManagedAgent } from "../types";
|
|
4
5
|
import { orphanGraceMs, orphanReapCooldownMs, orphanReapEnabled } from "./constants";
|
|
@@ -44,7 +45,7 @@ export async function reapOrphanedSessions(context: { signal?: AbortSignal } = {
|
|
|
44
45
|
let orphaned = 0;
|
|
45
46
|
|
|
46
47
|
for (const orch of listOrchestrators()) {
|
|
47
|
-
// #1819 — checkpoint per orchestrator and per managed session: a reap is `
|
|
48
|
+
// #1819 — checkpoint per orchestrator and per managed session: a reap is `createCommandAsync` +
|
|
48
49
|
// `emitCommandEventIfAbsent` + activity event + `lastReapAt` stamp, indivisible once started.
|
|
49
50
|
if (signal?.aborted) break;
|
|
50
51
|
if (orch.status !== "online") continue;
|
|
@@ -70,7 +71,7 @@ export async function reapOrphanedSessions(context: { signal?: AbortSignal } = {
|
|
|
70
71
|
// logical orphan reap has one command id and one command.requested event.
|
|
71
72
|
const pending = listCommands({ target: orch.agentId, type: "agent.shutdown", limit: 500 })
|
|
72
73
|
.find((command) => isPendingReapForSession(command, session));
|
|
73
|
-
const command = pending ??
|
|
74
|
+
const command = pending ?? (await createCommandAsync({
|
|
74
75
|
type: "agent.shutdown",
|
|
75
76
|
source: "system",
|
|
76
77
|
target: orch.agentId,
|
|
@@ -89,7 +90,7 @@ export async function reapOrphanedSessions(context: { signal?: AbortSignal } = {
|
|
|
89
90
|
requestedAt: now,
|
|
90
91
|
orchestratorId: orch.id,
|
|
91
92
|
},
|
|
92
|
-
});
|
|
93
|
+
}));
|
|
93
94
|
// The command is durable but not externally observable until its audit row commits. A failed
|
|
94
95
|
// audit must never let an orchestrator execute an unaudited reap (#1912 S3.2 class B).
|
|
95
96
|
await createActivityEventAsync({
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resumeMaintenanceJobAsync, pauseMaintenanceJobAsync } from "../db/runtime-tail-async";
|
|
1
2
|
// #1645 — behavior layer for maintenance-job pause/resume: validation, audit, and the drift report.
|
|
2
3
|
//
|
|
3
4
|
// Persistence is src/db/maintenance-pauses.ts; the scheduler gate reads from there. This module is
|
|
@@ -6,14 +7,7 @@
|
|
|
6
7
|
// #1638 pause's justification existed only in one agent's session context and one memory file.
|
|
7
8
|
|
|
8
9
|
import { errMessage } from "@humwick/sdk";
|
|
9
|
-
import {
|
|
10
|
-
createActivityEventAsync,
|
|
11
|
-
latestSuccessfulDeployAsync,
|
|
12
|
-
listMaintenanceJobPauses,
|
|
13
|
-
pauseMaintenanceJob,
|
|
14
|
-
resumeMaintenanceJob,
|
|
15
|
-
type MaintenanceJobPauseRecord,
|
|
16
|
-
} from "../db";
|
|
10
|
+
import { createActivityEventAsync, latestSuccessfulDeployAsync, listMaintenanceJobPauses, type MaintenanceJobPauseRecord } from "../db";
|
|
17
11
|
import { openRestartEpochsAsync } from "../db/restart-epoch-async.ts";
|
|
18
12
|
import { ValidationError } from "../db/connection.ts";
|
|
19
13
|
import { definitions } from "./jobs";
|
|
@@ -61,7 +55,7 @@ export async function pauseJob(input: PauseJobRequest): Promise<PauseJobResult>
|
|
|
61
55
|
throw new ValidationError("pass either ttlMs or indefinite, not both");
|
|
62
56
|
}
|
|
63
57
|
|
|
64
|
-
const result =
|
|
58
|
+
const result = (await pauseMaintenanceJobAsync({ ...input, reason }));
|
|
65
59
|
if (!result.ok) return { ok: false, conflict: true, pause: result.pause };
|
|
66
60
|
|
|
67
61
|
const ownedRestartEpoch = await ownedOpenRestartEpoch(definition.id);
|
|
@@ -126,7 +120,7 @@ export interface ResumeJobResult {
|
|
|
126
120
|
export async function resumeJobAsync(opts: { jobId?: string; id?: string; now?: number }): Promise<ResumeJobResult> {
|
|
127
121
|
if (opts.jobId) requireKnownJob(opts.jobId);
|
|
128
122
|
const now = opts.now ?? Date.now();
|
|
129
|
-
const released =
|
|
123
|
+
const released = (await resumeMaintenanceJobAsync({ jobId: opts.jobId, id: opts.id }));
|
|
130
124
|
if (!released) return { resumed: false };
|
|
131
125
|
const heldForMs = Math.max(0, now - released.createdAt);
|
|
132
126
|
const lapsed = released.expiresAt !== undefined && released.expiresAt <= now;
|