@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,9 +1,10 @@
|
|
|
1
|
+
import { taskLinkedRepoCwd } from "./task-repo-cwd";
|
|
2
|
+
export { taskLinkedRepoCwd } from "./task-repo-cwd";
|
|
1
3
|
import { writeChildEnv } from "@humwick/sdk/env";
|
|
2
4
|
import { bindSpawnProjectCheckout } from "./project-checkout-binding";
|
|
3
|
-
import { REPORT_VERBOSITIES, resolveReportVerbosity } from "@humwick/sdk";
|
|
5
|
+
import { REPORT_VERBOSITIES, resolveReportVerbosity, DEFAULT_USER_ID, errMessage, isRecord, stringValue, type LaunchMode, type ReportVerbosity, type SpawnProvider, type TaskDispatch } from "@humwick/sdk";
|
|
4
6
|
import { cleanCallerSpawnEnv } from "../spawn-env";
|
|
5
|
-
import { optionalEnum } from "../validation";
|
|
6
|
-
import { optionalNonNegativeInt } from "../validation";
|
|
7
|
+
import { optionalEnum, optionalNonNegativeInt } from "../validation";
|
|
7
8
|
import { cleanSpawnBudget } from "../spawn-budget";
|
|
8
9
|
import { teamId as newTeamId } from "../db/teams";
|
|
9
10
|
import { getGateway } from "../db/gateway";
|
|
@@ -35,8 +36,7 @@ import type { WorkflowSpawnQuota } from "../db/spawn-in-flight";
|
|
|
35
36
|
// deliberately out of scope here — this slice converges the two external TRANSPORTS.
|
|
36
37
|
import { resolveSpawnApprovalMode } from "./spawn-approval";
|
|
37
38
|
import { assertOrchestratorSupportsLaunchMode, resolveSpawnLaunchMode } from "../launch-mode";
|
|
38
|
-
import {
|
|
39
|
-
import { addProjectRootAsync, countLiveSpawnedAgents, createActivityEventAsync, getAgent, getProject, getTaskDetailAsync, listAgents, listProjects, listWorkspaces, recordTaskSpawnRequestedAsync, reportedRepoRootForCwd, resolveAgentOwnershipAsync, resolveProjectForCwd, rootsForProject, ValidationError, withResolvedProvisioningAsync } from "../db";
|
|
39
|
+
import { addProjectRootAsync, countLiveSpawnedAgents, createActivityEventAsync, getAgent, getProject, listAgents, listWorkspaces, recordTaskSpawnRequestedAsync, reportedRepoRootForCwd, resolveAgentOwnershipAsync, resolveProjectForCwd, rootsForProject, ValidationError, withResolvedProvisioningAsync } from "../db";
|
|
40
40
|
import { acquireCoordinatorTeam } from "./team";
|
|
41
41
|
import { countInFlightSpawnCommands } from "../commands-db";
|
|
42
42
|
import { createSpawnCommandWithinQuotaAsync } from "../commands-write";
|
|
@@ -54,7 +54,7 @@ import { profileNotFoundMessage } from "../spawn-profile-errors";
|
|
|
54
54
|
import { composeSpawnInstructionsWithEventsAsync } from "../project-instructions";
|
|
55
55
|
import { applyDefaultSharedMcp, type SharedMcpSpawnOverride } from "../shared-mcp";
|
|
56
56
|
import { findGitRepoRoot, isInsideGitWorkTree, pathVisibleLocally } from "./canonical-root";
|
|
57
|
-
import { selectSpawnCwdPlacement } from "./spawn-cwd-placement";
|
|
57
|
+
import { selectSpawnCwdPlacement, spawnBuildWork } from "./spawn-cwd-placement";
|
|
58
58
|
import { isolatedSpawnGitRepoError, resolveSpawnRepoRoot } from "./spawn-repo-probe";
|
|
59
59
|
import { autoSeedProjectForCwdAsync } from "./project"; import { applyProjectCapabilityGates } from "../project-trust";
|
|
60
60
|
import { assertProjectRoleMcpCwd, assertProjectRoleMcpTrusted, projectRoleProfile, projectRoleProvenance, projectRoleSpawnDefaults, withRoleProfile } from "../project-roles";
|
|
@@ -152,35 +152,6 @@ export function acquisitionManifestFor(projectId: string, cwd: string): {
|
|
|
152
152
|
};
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
-
export async function taskLinkedRepoCwd(taskId: number, callerCwd?: string): Promise<string> {
|
|
156
|
-
const task = await getTaskDetailAsync(taskId);
|
|
157
|
-
if (!task) throw new ValidationError(`task ${taskId} not found or not a deliverable task`);
|
|
158
|
-
// #2844 — a Task created with `repo:` but no linked issue still names its project repo.
|
|
159
|
-
const repos = [...new Set([...task.issues.map((issue) => issue.repo), task.repo].filter((repo): repo is string => Boolean(repo)))];
|
|
160
|
-
if (repos.length === 0) {
|
|
161
|
-
throw new ValidationError(`task ${taskId} has no linked issue repo; pass cwd/orchestratorId explicitly`);
|
|
162
|
-
}
|
|
163
|
-
const candidates = listProjects()
|
|
164
|
-
.filter((project) => project.issueRepo && repos.includes(project.issueRepo))
|
|
165
|
-
.flatMap((project) => rootsForProject(project.id).map((root) => ({ project, root })));
|
|
166
|
-
if (candidates.length === 0) {
|
|
167
|
-
// #1627 — this refusal is real and names its own cause: with no registered root, relay has no
|
|
168
|
-
// way to turn "repo owner/name" into a checkout path on a specific host. What made it a trap
|
|
169
|
-
// was the NEXT step: following its advice (pass cwd + orchestratorId) used to hit the isolated
|
|
170
|
-
// precondition's local-filesystem probe and get told the repo was "not a git repo". That lie is
|
|
171
|
-
// fixed at assertIsolatedSpawnHasGitRepo, so the remedy below now works. Name the tool so it is
|
|
172
|
-
// mechanical rather than a step you have to know exists.
|
|
173
|
-
throw new ValidationError(`task ${taskId} links repo ${repos.join(", ")} but no Relay project root is registered for it, so relay cannot resolve a checkout path for it; pass cwd + orchestratorId explicitly, or register the checkout with relay_project_add_root`);
|
|
174
|
-
}
|
|
175
|
-
const matchingCallerRoot = callerCwd
|
|
176
|
-
? candidates
|
|
177
|
-
.filter((candidate) => isPathWithinBase(callerCwd, candidate.root.rootPath))
|
|
178
|
-
.sort((a, b) => b.root.rootPath.length - a.root.rootPath.length)[0]
|
|
179
|
-
: undefined;
|
|
180
|
-
if (matchingCallerRoot) return matchingCallerRoot.root.rootPath;
|
|
181
|
-
if (candidates.length === 1) return candidates[0]!.root.rootPath;
|
|
182
|
-
throw new ValidationError(`task ${taskId} links repo ${repos.join(", ")} with multiple registered roots; pass cwd explicitly`);
|
|
183
|
-
}
|
|
184
155
|
|
|
185
156
|
/**
|
|
186
157
|
* A normalized spawn request — every transport validates its own wire shape (enums,
|
|
@@ -390,7 +361,7 @@ export async function spawnAgent(input: SpawnAgentInput, ctx: AuthContext): Prom
|
|
|
390
361
|
const lineageAgent: AgentCard | undefined = (lineageAgentId ? getAgent(lineageAgentId) : undefined) ?? undefined;
|
|
391
362
|
const callerCwd = stringValue(caller?.meta?.cwd) ?? stringValue(lineageAgent?.meta?.cwd);
|
|
392
363
|
const taskDefaultCwd = input.taskId !== undefined && (!input.orchestratorId || input.role !== undefined) && !input.cwd
|
|
393
|
-
? input.taskRepoCwd ?? await taskLinkedRepoCwd(input.taskId, callerCwd)
|
|
364
|
+
? input.taskRepoCwd ?? await taskLinkedRepoCwd(input.taskId, callerCwd, caller ?? lineageAgent)
|
|
394
365
|
: undefined;
|
|
395
366
|
const requestedCwd = input.cwd ?? taskDefaultCwd ?? (input.role !== undefined ? callerCwd : undefined);
|
|
396
367
|
const roleSpawn = input.roleSnapshot ? { role: input.roleSnapshot, warnings: [] } : input.role === undefined && input.roleSha === undefined ? undefined : await resolveSpawnProjectRole({ role: input.role, roleSha: input.roleSha, profile: input.profile, orchestratorId: input.orchestratorId, cwd: requestedCwd, preferHost: input.roleHost ?? caller?.machine ?? lineageAgent?.machine, allowedOrchestratorIds: ctx.constraints?.orchestrators, canSpawn: canSpawn(ctx) });
|
|
@@ -429,7 +400,7 @@ export async function spawnAgent(input: SpawnAgentInput, ctx: AuthContext): Prom
|
|
|
429
400
|
provider: resolvedProvider, orchestratorId: input.orchestratorId, requestedCwd: repoSource ? undefined : requestedCwd, repoSource,
|
|
430
401
|
resumeWorkspace: input.resumeWorkspace, caller, lineageMachine: lineageAgent?.machine,
|
|
431
402
|
callerCwd, allowedOrchestratorIds: ctx.constraints?.orchestrators,
|
|
432
|
-
shared: workspaceMode === "shared", workspaceMode, canLand: !landBlockResolution.landBlocked, model: modelParams.model, effort: modelParams.effort,
|
|
403
|
+
shared: workspaceMode === "shared", workspaceMode, canLand: !landBlockResolution.landBlocked, buildWork: spawnBuildWork(profileName, (input.dispatch ?? linkedTask?.dispatch ?? profileSpawnDefaults.dispatch)?.type), model: modelParams.model, effort: modelParams.effort,
|
|
433
404
|
});
|
|
434
405
|
|
|
435
406
|
assertOrchestratorSupportsLaunchMode(orchestrator, launchMode, input.providerArgs);
|
|
@@ -486,7 +457,7 @@ export async function spawnAgent(input: SpawnAgentInput, ctx: AuthContext): Prom
|
|
|
486
457
|
|
|
487
458
|
if (shouldAutoScaffoldTask(input, profileName)) {
|
|
488
459
|
const brief = spawnBrief(input)!;
|
|
489
|
-
linkedTask = createTaskEntity({
|
|
460
|
+
linkedTask = await createTaskEntity({
|
|
490
461
|
title: taskTitleFromBrief(brief, input.label),
|
|
491
462
|
intent: brief,
|
|
492
463
|
latestSteer: input.steer,
|
|
@@ -544,7 +515,7 @@ export async function spawnAgent(input: SpawnAgentInput, ctx: AuthContext): Prom
|
|
|
544
515
|
// disk. That's only trustworthy when the spawn's target orchestrator actually shares this
|
|
545
516
|
// filesystem — a remote orchestrator's host can coincidentally have the same path with
|
|
546
517
|
// DIFFERENT content, so the gate is told explicitly rather than inferring from path existence.
|
|
547
|
-
if (agentProfile.sources?.project) agentProfile = applyProjectCapabilityGates(agentProfile, resolvedCwd, registeredProjectBeforeAutoSeed, { orchestratorLocal: isOrchestratorLocalToRelay(orchestrator), ...(projectRole ? { pinnedMcpCommit: projectRole.source.sha } : {}) });
|
|
518
|
+
if (agentProfile.sources?.project) agentProfile = (await applyProjectCapabilityGates(agentProfile, resolvedCwd, registeredProjectBeforeAutoSeed, { orchestratorLocal: isOrchestratorLocalToRelay(orchestrator), ...(projectRole ? { pinnedMcpCommit: projectRole.source.sha } : {}) }));
|
|
548
519
|
if (projectRole) assertProjectRoleMcpTrusted(projectRole, agentProfile);
|
|
549
520
|
|
|
550
521
|
// N7 — project hooks/MCP are executable code and are gated against the project
|
|
@@ -599,7 +570,7 @@ export async function spawnAgent(input: SpawnAgentInput, ctx: AuthContext): Prom
|
|
|
599
570
|
}
|
|
600
571
|
|
|
601
572
|
// #3107 — the lineage coordinator's active team; a coordinator takes its project's seat, never a dissolved id.
|
|
602
|
-
const callerTeam = lineageAgentId ? acquireCoordinatorTeam(lineageAgentId) : undefined;
|
|
573
|
+
const callerTeam = lineageAgentId ? (await acquireCoordinatorTeam(lineageAgentId)) : undefined;
|
|
603
574
|
const privateTeamId = profileName === "sub-coordinator" ? newTeamId() : undefined;
|
|
604
575
|
const launchTeamId = privateTeamId ?? callerTeam?.id;
|
|
605
576
|
|
|
@@ -885,7 +856,7 @@ export async function spawnAgent(input: SpawnAgentInput, ctx: AuthContext): Prom
|
|
|
885
856
|
requestedBy,
|
|
886
857
|
...(currentTask?.latestSteer ? { steer: currentTask.latestSteer } : {}),
|
|
887
858
|
});
|
|
888
|
-
autoSubscribeTaskCoordinator(requestedBy, input.taskId);
|
|
859
|
+
(await autoSubscribeTaskCoordinator(requestedBy, input.taskId));
|
|
889
860
|
} catch (err) {
|
|
890
861
|
console.warn(`task ${input.taskId} spawn attribution skipped: ${errMessage(err)}`);
|
|
891
862
|
}
|
|
@@ -50,7 +50,7 @@ export interface SpawnChainInspection {
|
|
|
50
50
|
settings: Record<string, unknown> | null;
|
|
51
51
|
/** The effective launch argv the adapter would exec. */
|
|
52
52
|
argv: string[];
|
|
53
|
-
/** The launch command (
|
|
53
|
+
/** The launch command (e.g. claude). */
|
|
54
54
|
command: string;
|
|
55
55
|
/** relay-provisioned-hooks/<ref> dir for the baseline guard. */
|
|
56
56
|
baselineHookAssetDir: string | undefined;
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
import { basename, join, relative } from "node:path";
|
|
2
2
|
import { hostname } from "node:os";
|
|
3
|
-
import { normalizeGitRemoteIdentity, stringValue, type AgentCard, type Orchestrator, type SpawnProvider, type WorkspaceMode, type ProjectRoot } from "@humwick/sdk";
|
|
4
|
-
import { listAgents, listOrchestrators, listWorkspaces, resolveProjectForCwd, rootsForProject, ValidationError } from "../db";
|
|
3
|
+
import { normalizeGitRemoteIdentity, stringValue, type AgentCard, type Orchestrator, type SpawnProvider, type WorkspaceMode, type ProjectRoot, type TaskDispatch } from "@humwick/sdk";
|
|
4
|
+
import { getOrchestrator, listAgents, listOrchestrators, listWorkspaces, resolveProjectForCwd, rootsForProject, ValidationError } from "../db";
|
|
5
|
+
import { listHostViews } from "../db/hosts-views";
|
|
5
6
|
import { unboundOnHost, unboundProjectRoots } from "../db/project-lifecycle";
|
|
6
7
|
import { isOrchestratorLocalToRelay, isPathWithinBase, sameHostIdentity } from "../utils";
|
|
7
8
|
import { findGitRepoRoot } from "./canonical-root";
|
|
8
9
|
import { TERMINAL_WORKSPACE_STATUSES } from "../workspace-phase";
|
|
9
|
-
import { selectSpawnPlacement, spawnPlacementCandidates, isRelayHost } from "../spawn-placement";
|
|
10
|
+
import { selectSpawnPlacement, spawnPlacementCandidates, isRelayHost, type SpawnCapOptions } from "../spawn-placement";
|
|
10
11
|
import { remoteUrlFromGitRemote } from "./git-remote";
|
|
11
12
|
import { probeHostRepo, probeLocalSourceRepo } from "./spawn-repo-probe";
|
|
12
13
|
import { hostProjectReadiness } from "./host-project-readiness";
|
|
13
14
|
|
|
15
|
+
export function spawnBuildWork(profile: string, type?: TaskDispatch["type"]): boolean {
|
|
16
|
+
return type ? !["review", "research", "ops"].includes(type) : !["reviewer", "qa", "probe", "sec-ops"].includes(profile.toLowerCase());
|
|
17
|
+
}
|
|
18
|
+
|
|
14
19
|
/** Resolve host and working directory together so capacity routing keeps the caller's repo. */
|
|
15
20
|
export async function selectSpawnCwdPlacement(input: {
|
|
16
21
|
provider: SpawnProvider;
|
|
@@ -26,15 +31,16 @@ export async function selectSpawnCwdPlacement(input: {
|
|
|
26
31
|
shared: boolean;
|
|
27
32
|
workspaceMode?: WorkspaceMode;
|
|
28
33
|
canLand?: boolean;
|
|
34
|
+
buildWork?: boolean;
|
|
29
35
|
model?: string;
|
|
30
36
|
effort?: string;
|
|
31
37
|
}): Promise<{ orchestrator: Orchestrator; placementReason: string; resolvedCwd: string; lazyRoot?: ProjectRoot }> {
|
|
32
38
|
const platformRule = input.workspaceMode === "isolated" && !input.shared && input.resumeWorkspace?.mode !== "attach"
|
|
33
39
|
? await sourcePlacementPlatforms(input) : undefined;
|
|
34
|
-
const capOpts = { ...(input.caller ? { callerAgentId: input.caller.id } : {}), attach: input.resumeWorkspace?.mode === "attach" };
|
|
40
|
+
const capOpts: SpawnCapOptions = { ...(input.caller ? { callerAgentId: input.caller.id } : {}), attach: input.resumeWorkspace?.mode === "attach", buildWork: input.buildWork };
|
|
35
41
|
const earlyPlacement = !input.orchestratorId && input.resumeWorkspace?.mode !== "attach"
|
|
36
42
|
? spawnPlacementCandidates(input.provider, input.requestedCwd, input.allowedOrchestratorIds,
|
|
37
|
-
input.shared, input.model, input.effort, input.workspaceMode, platformRule, input.canLand, capOpts)
|
|
43
|
+
input.shared, input.model, input.effort, input.buildWork === false ? undefined : input.workspaceMode, platformRule, input.canLand, capOpts)
|
|
38
44
|
: undefined;
|
|
39
45
|
const survivors = earlyPlacement?.candidates.map((host) => host.id) ?? input.allowedOrchestratorIds;
|
|
40
46
|
// Preserve selection's detailed exclusion error without probing hosts already ruled out.
|
|
@@ -43,6 +49,16 @@ export async function selectSpawnCwdPlacement(input: {
|
|
|
43
49
|
input.model, input.effort, input.workspaceMode, platformRule, input.canLand, capOpts);
|
|
44
50
|
const { attachWorkspace, hostCwds, allowedOrchestratorIds, lazyRoots, exclusions: checkoutExclusions = [] } = await omittedHostContext({ ...input, allowedOrchestratorIds: survivors });
|
|
45
51
|
const requestedCwd = input.repoSource && !hostCwds ? input.repoSource : input.requestedCwd;
|
|
52
|
+
// Review/QA setup depends on the target checkout HEAD; a caller's policy base cannot vouch for it (#3805).
|
|
53
|
+
if (input.workspaceMode === "isolated" && input.buildWork === false && !input.resumeWorkspace) {
|
|
54
|
+
const views = new Map(listHostViews(Date.now()).map((view) => [view.id, view]));
|
|
55
|
+
const hosts = input.orchestratorId ? [getOrchestrator(input.orchestratorId)].filter((host): host is Orchestrator => !!host) : earlyPlacement?.candidates ?? [];
|
|
56
|
+
const lightHosts = await Promise.all(hosts.filter((host) => isRelayHost(host, host.hostId ? views.get(host.hostId) : undefined)).map(async (host) => {
|
|
57
|
+
const probe = await probeHostRepo(host, requestedCwd ?? hostCwds?.get(host.id) ?? input.callerCwd ?? host.baseDir, undefined, true);
|
|
58
|
+
return probe.kind === "present" && probe.workspaceDepsDisabled === true ? host.id : undefined;
|
|
59
|
+
}));
|
|
60
|
+
capOpts.workspaceDepsDisabledHosts = lightHosts.filter((id): id is string => id !== undefined);
|
|
61
|
+
}
|
|
46
62
|
// #3169/#3625 — capacity ranks only hosts whose own checkout is ready for the project (refresh hook, toolchain).
|
|
47
63
|
const readiness = hostProjectReadiness();
|
|
48
64
|
const readinessExclusions: string[] = [];
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { resolveIssueRefFromStringAsync } from "../db/issue-refs-async";
|
|
2
|
+
|
|
1
3
|
import { isRecord } from "@humwick/sdk";
|
|
2
4
|
import { resolveProjectForCwd, spawnIssuePromptAsync } from "../db";
|
|
3
|
-
|
|
5
|
+
|
|
4
6
|
import { defaultIssueRepo } from "../config";
|
|
5
7
|
import { parseJson } from "../utils";
|
|
6
8
|
import { recordAndEmitIssueActivity } from "./issue-activity";
|
|
@@ -32,8 +34,8 @@ export async function recordSpawnPromptIssueActivity(agent: AgentCard, spawnRequ
|
|
|
32
34
|
if (!num || seen.has(num)) continue;
|
|
33
35
|
seen.add(num);
|
|
34
36
|
try {
|
|
35
|
-
const issueRef =
|
|
36
|
-
recordAndEmitIssueActivity({
|
|
37
|
+
const issueRef = await resolveIssueRefFromStringAsync({ ref: `#${num}`, defaultRepo: issueRepo });
|
|
38
|
+
await recordAndEmitIssueActivity({
|
|
37
39
|
agentId: agent.id,
|
|
38
40
|
action: "edit",
|
|
39
41
|
issueRefId: issueRef.id,
|
|
@@ -58,7 +60,7 @@ export async function recordSpawnPromptIssueActivity(agent: AgentCard, spawnRequ
|
|
|
58
60
|
* as recordSpawnPromptIssueActivity so prompt + branch refs for the same issue
|
|
59
61
|
* are deduplicated at the DB layer.
|
|
60
62
|
*/
|
|
61
|
-
export function recordWorkspaceIssueActivity(workspace: WorkspaceRecord): void {
|
|
63
|
+
export async function recordWorkspaceIssueActivity(workspace: WorkspaceRecord): Promise<void> {
|
|
62
64
|
const agentId = workspace.ownerAgentId;
|
|
63
65
|
if (!agentId || workspace.mode !== "isolated") return;
|
|
64
66
|
|
|
@@ -86,8 +88,8 @@ export function recordWorkspaceIssueActivity(workspace: WorkspaceRecord): void {
|
|
|
86
88
|
|
|
87
89
|
for (const num of seen) {
|
|
88
90
|
try {
|
|
89
|
-
const issueRef =
|
|
90
|
-
recordAndEmitIssueActivity({
|
|
91
|
+
const issueRef = await resolveIssueRefFromStringAsync({ ref: `#${num}`, defaultRepo: issueRepo });
|
|
92
|
+
await recordAndEmitIssueActivity({
|
|
91
93
|
agentId,
|
|
92
94
|
action: "edit",
|
|
93
95
|
issueRefId: issueRef.id,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { memoryRetirementMessage } from "../memory-retirement";
|
|
2
2
|
// #3121 — the provider, model and effort a spawn launches with, resolved in one place: spawnAgent uses it, and a
|
|
3
3
|
// caller that must know a launch before anything spawns (a workflow's start check) asks the same seam.
|
|
4
|
-
import type
|
|
4
|
+
import { errMessage, type SpawnProvider, type TaskDispatch } from "@humwick/sdk";
|
|
5
5
|
import { isProviderEnabled, type ProviderCatalogEntry } from "@humwick/sdk/provider-catalog";
|
|
6
6
|
import { builtInProfileSpawnDefaults, profileProviderRefusal } from "../agent-profile-catalog";
|
|
7
7
|
import { DEFAULT_SPAWN_PROFILE_NAME, getAgentProfile } from "../config-store";
|
|
@@ -14,6 +14,7 @@ import { captureDispatchContext, withProbeUncertainty, type DispatchSnapshot } f
|
|
|
14
14
|
import { finalizeLaunchDecision } from "./spawn-launch-decision";
|
|
15
15
|
import type { SpawnCapOptions } from "../spawn-placement";
|
|
16
16
|
import { resolveProjectRoleForProvider } from "./spawn-project-role";
|
|
17
|
+
import { PinnedHostCapacityError } from "../spawn-admission";
|
|
17
18
|
|
|
18
19
|
export interface SpawnLaunchInput {
|
|
19
20
|
profile?: string;
|
|
@@ -82,6 +83,21 @@ export async function resolveSpawnLaunch(input: SpawnLaunchInput, captured?: Dis
|
|
|
82
83
|
mustResolveAvailable: mustResolveAvailableFor(input.dispatchShape), orchestratorId: input.orchestratorId, spawnCap: input.spawnCap,
|
|
83
84
|
avoidProviders: input.avoidProviders,
|
|
84
85
|
});
|
|
86
|
+
try {
|
|
87
|
+
return await resolveLaunch(input, snapshot);
|
|
88
|
+
} catch (error) {
|
|
89
|
+
if (!input.orchestratorId || !snapshot.pinnedHostAtAgentCap) throw error;
|
|
90
|
+
// #3816 — prove capacity is the only refusal using the same full launch resolver.
|
|
91
|
+
try {
|
|
92
|
+
await resolveSpawnLaunch({ ...input, spawnCap: { ...input.spawnCap, attach: true } });
|
|
93
|
+
} catch {
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
throw new PinnedHostCapacityError(errMessage(error), input.orchestratorId);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function resolveLaunch(input: SpawnLaunchInput, snapshot: DispatchSnapshot): Promise<SpawnLaunch> {
|
|
85
101
|
const { catalog } = snapshot;
|
|
86
102
|
let provider = input.provider;
|
|
87
103
|
const profile = input.profile ? getAgentProfile(input.profile)?.value : undefined;
|
|
@@ -224,7 +224,7 @@ export async function escalateLostSpawnCommand(command: Command): Promise<SpawnL
|
|
|
224
224
|
// A child that DID register is alive regardless of what happened to its command row; never
|
|
225
225
|
// convert a bookkeeping loss into a phantom failure for a running agent.
|
|
226
226
|
if (spawnRequestId && await spawnWatchdogHasRegisteredAsync(spawnRequestId)) return null;
|
|
227
|
-
notifyAgentSpawnFailed({
|
|
227
|
+
await notifyAgentSpawnFailed({
|
|
228
228
|
parent,
|
|
229
229
|
...(spawnRequestId ? { spawnRequestId } : {}),
|
|
230
230
|
...(isRecord(command.params) && stringValue(command.params.provider) ? { provider: stringValue(command.params.provider)! } : {}),
|
|
@@ -209,12 +209,12 @@ export async function reapUnregisteredSpawnCommands(
|
|
|
209
209
|
const representativeId = unconfirmedIds[unconfirmedIds.length - 1] ?? group.rows[group.rows.length - 1]!.id;
|
|
210
210
|
if (undispatchable) {
|
|
211
211
|
lostSpawnCommandIds.push(representativeId);
|
|
212
|
-
notifyAgentSpawnFailed({ parent: group.parent, spawnRequestId: group.spawnRequestId, provider: group.provider, reason });
|
|
212
|
+
await notifyAgentSpawnFailed({ parent: group.parent, spawnRequestId: group.spawnRequestId, provider: group.provider, reason });
|
|
213
213
|
continue;
|
|
214
214
|
}
|
|
215
215
|
if (group.latestUpdatedAt >= notifyCutoff) {
|
|
216
216
|
notifiedUnconfirmedSpawnCommandIds.push(representativeId);
|
|
217
|
-
notifyAgentSpawnUnconfirmed({
|
|
217
|
+
await notifyAgentSpawnUnconfirmed({
|
|
218
218
|
parent: group.parent,
|
|
219
219
|
spawnRequestId: group.spawnRequestId,
|
|
220
220
|
provider: group.provider,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { cleanLandPlacement, readRepoLandPlacement } from "@humwick/sdk/land-gates";
|
|
2
|
+
import { repoWorkspaceDepsDisabled } from "@humwick/sdk/worktree-config";
|
|
2
3
|
import { existsSync } from "node:fs";
|
|
3
4
|
import { isAbsolute } from "node:path";
|
|
4
5
|
import { isRecord, normalizeGitRemoteIdentity, ValidationError, type Orchestrator } from "@humwick/sdk";
|
|
@@ -9,7 +10,7 @@ import { findGitRepoRoot } from "./canonical-root";
|
|
|
9
10
|
import { runGit, toProjectGitResult } from "./git-process";
|
|
10
11
|
import { remoteUrlFromGitRemoteAsync } from "./git-remote";
|
|
11
12
|
|
|
12
|
-
export type RepoProbe = { kind: "present"; repoRoot?: string; remoteIdentity?: string; placementPlatforms?: string[]; toolchainError?: string }
|
|
13
|
+
export type RepoProbe = { kind: "present"; repoRoot?: string; remoteIdentity?: string; placementPlatforms?: string[]; workspaceDepsDisabled?: boolean; toolchainError?: string }
|
|
13
14
|
/** `occupied`: the path exists but is not inside a git checkout, so no clone can land there (#3347). */
|
|
14
15
|
| { kind: "absent"; occupied?: boolean } | { kind: "timeout" } | { kind: "unavailable"; reason?: string };
|
|
15
16
|
|
|
@@ -77,17 +78,18 @@ export async function probeLocalSourceRepo(path: string, baseRef?: string | null
|
|
|
77
78
|
if (!root) return { kind: "absent", occupied: true };
|
|
78
79
|
const remote = await remoteUrlFromGitRemoteAsync(root, { refresh: true });
|
|
79
80
|
let placementPlatforms: string[] | undefined;
|
|
81
|
+
const workspaceDepsDisabled = await repoWorkspaceDepsDisabled(placementGit(root));
|
|
80
82
|
if (baseRef !== undefined) {
|
|
81
83
|
try {
|
|
82
84
|
placementPlatforms = await readRepoLandPlacement(baseRef ?? undefined, placementGit(root));
|
|
83
85
|
} catch (error) { return { kind: "unavailable", reason: String(error) }; }
|
|
84
86
|
}
|
|
85
|
-
return { kind: "present", repoRoot: root, ...(placementPlatforms ? { placementPlatforms } : {}), ...(remote ? { remoteIdentity: normalizeGitRemoteIdentity(remote) } : {}) };
|
|
87
|
+
return { kind: "present", repoRoot: root, workspaceDepsDisabled, ...(placementPlatforms ? { placementPlatforms } : {}), ...(remote ? { remoteIdentity: normalizeGitRemoteIdentity(remote) } : {}) };
|
|
86
88
|
}
|
|
87
89
|
|
|
88
90
|
async function probeRemoteRepo(host: Orchestrator, path: string, baseRef?: string | null, toolchain = false): Promise<RepoProbe> {
|
|
89
91
|
if (!host.apiUrl || !isTrustedOrchestratorApiUrl(host.apiUrl)) return { kind: "unavailable" };
|
|
90
|
-
const result = await fetchHostJson<{ isGitRepo: boolean; definite?: boolean; pathExists?: boolean; repoRoot?: string; remoteIdentity?: string; placementPlatforms?: string[]; placementError?: string; toolchainError?: string }>(host.apiUrl, "/api/repo/identity", {
|
|
92
|
+
const result = await fetchHostJson<{ isGitRepo: boolean; definite?: boolean; pathExists?: boolean; repoRoot?: string; remoteIdentity?: string; placementPlatforms?: string[]; workspaceDepsDisabled?: boolean; placementError?: string; toolchainError?: string }>(host.apiUrl, "/api/repo/identity", {
|
|
91
93
|
query: new URLSearchParams({ path, ...(toolchain ? { toolchain: "1" } : {}), ...(baseRef ? { baseRef } : baseRef === null ? { placement: "1" } : {}) }), timeoutMs: toolchain ? 50_000 : 20_000,
|
|
92
94
|
validate: (body) => {
|
|
93
95
|
if (!isRecord(body) || typeof body.isGitRepo !== "boolean"
|
|
@@ -101,14 +103,14 @@ async function probeRemoteRepo(host: Orchestrator, path: string, baseRef?: strin
|
|
|
101
103
|
let placementPlatforms: string[] | undefined;
|
|
102
104
|
try { placementPlatforms = cleanLandPlacement({ placement: { platforms: body.placementPlatforms } })?.platforms; }
|
|
103
105
|
catch (error) { console.warn(`${host.id}: invalid repository placement: ${String(error)}`); }
|
|
104
|
-
return { ok: true, value: { ...(typeof body.toolchainError === "string" ? { toolchainError: body.toolchainError } : {}), isGitRepo: body.isGitRepo, definite: body.definite === true, pathExists: body.pathExists === true, ...(typeof body.placementError === "string" ? { placementError: body.placementError } : {}), ...(placementPlatforms ? { placementPlatforms } : {}),
|
|
106
|
+
return { ok: true, value: { workspaceDepsDisabled: body.workspaceDepsDisabled === true, ...(typeof body.toolchainError === "string" ? { toolchainError: body.toolchainError } : {}), isGitRepo: body.isGitRepo, definite: body.definite === true, pathExists: body.pathExists === true, ...(typeof body.placementError === "string" ? { placementError: body.placementError } : {}), ...(placementPlatforms ? { placementPlatforms } : {}),
|
|
105
107
|
...(typeof body.repoRoot === "string" ? { repoRoot: body.repoRoot } : {}),
|
|
106
108
|
...(typeof body.remoteIdentity === "string" ? { remoteIdentity: body.remoteIdentity } : {}) } };
|
|
107
109
|
},
|
|
108
110
|
});
|
|
109
111
|
if (result.ok && baseRef !== undefined && result.value.placementError) return { kind: "unavailable", reason: result.value.placementError };
|
|
110
112
|
if (result.ok) return result.value.isGitRepo
|
|
111
|
-
? { kind: "present", ...(result.value.toolchainError ? { toolchainError: result.value.toolchainError } : {}), ...(result.value.repoRoot ? { repoRoot: result.value.repoRoot } : {}), ...(result.value.placementPlatforms ? { placementPlatforms: result.value.placementPlatforms } : {}), ...(result.value.remoteIdentity ? { remoteIdentity: result.value.remoteIdentity } : {}) }
|
|
113
|
+
? { kind: "present", workspaceDepsDisabled: result.value.workspaceDepsDisabled, ...(result.value.toolchainError ? { toolchainError: result.value.toolchainError } : {}), ...(result.value.repoRoot ? { repoRoot: result.value.repoRoot } : {}), ...(result.value.placementPlatforms ? { placementPlatforms: result.value.placementPlatforms } : {}), ...(result.value.remoteIdentity ? { remoteIdentity: result.value.remoteIdentity } : {}) }
|
|
112
114
|
// Old orchestrators also returned false for a killed/timed-out probe (#2715).
|
|
113
115
|
: result.value.definite === true ? { kind: "absent", ...(result.value.pathExists ? { occupied: true } : {}) } : { kind: "unavailable" };
|
|
114
116
|
const failure = result.failure;
|
|
@@ -2,36 +2,35 @@ import { errMessage, stringValue } from "@humwick/sdk";
|
|
|
2
2
|
import type { WorkflowEffect } from "@humwick/kernel/workflow.pilot";
|
|
3
3
|
import { getGateway } from "../db/gateway";
|
|
4
4
|
import { getAgentAsync } from "../db";
|
|
5
|
+
import { isTerminalCommandStatus } from "../db/command-terminal-status";
|
|
5
6
|
import { shutdownAgent } from "./shutdown-agent";
|
|
6
7
|
import { currentStop } from "./workflow-effects";
|
|
7
|
-
import { invoke, supervisorNow, type Supervisor } from "./workflow-supervisor-state";
|
|
8
|
+
import { invoke, retryResult, supervisorNow, type Supervisor } from "./workflow-supervisor-state";
|
|
8
9
|
|
|
9
10
|
/** Every retry resumes the same outbox effect; only host witnesses release reservations. */
|
|
10
11
|
export async function performSpawnTreeCleanup(sup: Supervisor, effect: WorkflowEffect): Promise<boolean> {
|
|
11
12
|
const now = supervisorNow();
|
|
12
|
-
if (typeof effect.result?.nextAttemptAt === "number" && effect.result.nextAttemptAt > now) return false;
|
|
13
13
|
const agentId = String(effect.payload.agentId);
|
|
14
14
|
await getGateway().write("spawnTree.prepareCleanup", { agentId });
|
|
15
15
|
const targets = await getGateway().read("spawnTree.cleanupTargets", { agentId });
|
|
16
16
|
const retry: Array<{ spawnRequestId: string; error: string }> = [];
|
|
17
|
+
let inFlight = false;
|
|
17
18
|
for (const target of targets) {
|
|
18
19
|
if (target.processGone) continue;
|
|
19
20
|
try {
|
|
20
21
|
const agent = await getAgentAsync(target.agentId);
|
|
21
22
|
const generation = stringValue(agent?.meta?.spawnRequestId) ?? target.spawnRequestId;
|
|
22
|
-
const { id, prior } = await currentStop(`${effect.id}:stop:${target.spawnRequestId}:${generation}`,
|
|
23
|
-
command.result?.targetNotFound !== true || now - command.updatedAt >= 15 * 60_000);
|
|
23
|
+
const { id, prior } = await currentStop(`${effect.id}:stop:${target.spawnRequestId}:${generation}`, now);
|
|
24
24
|
const command = prior ?? (await shutdownAgent({
|
|
25
25
|
...(agent ? { agentId: agent.id } : {}), spawnRequestId: generation, orchestratorId: target.orchestratorId,
|
|
26
26
|
commandId: id, requireProcessTreeProof: true, graceful: false, force: true,
|
|
27
27
|
reason: String(effect.payload.reason), requestedVia: "bus", requestedBy: "workflow",
|
|
28
28
|
}, { actor: { id: "workflow", kind: "system" }, scopes: [] })).command;
|
|
29
29
|
if (command.status !== "succeeded" || command.result?.processTreeStopped !== true) retry.push({ spawnRequestId: target.spawnRequestId, error: `shutdown ${command.id}: ${command.status}` });
|
|
30
|
+
if (!isTerminalCommandStatus(command.status)) inFlight = true;
|
|
30
31
|
} catch (error) { retry.push({ spawnRequestId: target.spawnRequestId, error: errMessage(error) }); }
|
|
31
32
|
}
|
|
32
|
-
const
|
|
33
|
-
await invoke(sup, "settleEffect", { id: effect.id, status: retry.length ? "pending" : "done", result:
|
|
34
|
-
targets: targets.length, retry, checkedAt: now, ...(retry.length ? { nextAttemptAt: now + delay } : {}),
|
|
35
|
-
} });
|
|
33
|
+
const result = { targets: targets.length, retry, checkedAt: now, ...(inFlight ? { inFlight } : {}) };
|
|
34
|
+
await invoke(sup, "settleEffect", { id: effect.id, status: retry.length ? "pending" : "done", result: retry.length ? retryResult(effect.result, result, now) : result });
|
|
36
35
|
return retry.length === 0;
|
|
37
36
|
}
|
|
@@ -164,13 +164,13 @@ export async function reapIdleStewards(opts: { now?: number; signal?: AbortSigna
|
|
|
164
164
|
if (entry.attempts >= STEWARD_REAP_MAX_ATTEMPTS) {
|
|
165
165
|
if (!entry.alarmed) {
|
|
166
166
|
entry.alarmed = true;
|
|
167
|
-
raiseOperatorAlarm({
|
|
167
|
+
(await raiseOperatorAlarm({
|
|
168
168
|
clientId: `steward-zombie-blocking-${agent.id}`,
|
|
169
169
|
source: "steward-reaper",
|
|
170
170
|
subject: "Steward zombie is blocking a repo/host steward slot",
|
|
171
171
|
detail: `Steward \`${agent.id}\` (${agent.label ?? "unlabelled"}, host ${agent.machine ?? "unknown"}) ignored ${entry.attempts} shutdown attempt(s) and is holding the one-per-repo/host steward slot — no new steward can spawn there until it is killed. Investigate and terminate it manually.`,
|
|
172
172
|
metadata: { maintenanceJobId: "steward-reaper", agentId: agent.id, label: agent.label, machine: agent.machine, attempts: entry.attempts },
|
|
173
|
-
});
|
|
173
|
+
}));
|
|
174
174
|
}
|
|
175
175
|
reapTracker.set(agent.id, entry);
|
|
176
176
|
alarmed.push(agent.id);
|
|
@@ -192,7 +192,7 @@ export async function reapIdleStewards(opts: { now?: number; signal?: AbortSigna
|
|
|
192
192
|
entry.lastAt = now;
|
|
193
193
|
reapTracker.set(agent.id, entry);
|
|
194
194
|
if (graceful) {
|
|
195
|
-
requestTransientAgentShutdown(agent, {
|
|
195
|
+
(await requestTransientAgentShutdown(agent, {
|
|
196
196
|
reason: disabled ? "steward-disabled" : "steward-orphan-idle",
|
|
197
197
|
requestedBy: "steward-reaper",
|
|
198
198
|
activityTitle: disabled ? "Orphaned steward reaped (spawning disabled)" : "Orphaned steward reaped",
|
|
@@ -201,7 +201,7 @@ export async function reapIdleStewards(opts: { now?: number; signal?: AbortSigna
|
|
|
201
201
|
: "Steward has no live managed policy state and has been idle past the keepalive grace; reaping.",
|
|
202
202
|
landReason: disabled ? "steward-disabled" : "steward-orphan-idle",
|
|
203
203
|
now,
|
|
204
|
-
});
|
|
204
|
+
}));
|
|
205
205
|
} else {
|
|
206
206
|
// A plain agent-targeted `agent.shutdown` — graceful or not — is INERT against a runner that
|
|
207
207
|
// has stopped reading its own commands (runner-core keys kill-hardness off the command TYPE,
|
|
@@ -48,6 +48,10 @@ export interface TaskHydrationFacets {
|
|
|
48
48
|
/** #2728 — the Task's `context` facet (free-text facts a coordinator must not resend by message). */
|
|
49
49
|
context?: string;
|
|
50
50
|
doneWhen?: string;
|
|
51
|
+
/** #3808 — the Task's `terminalStage` (deployed = deploy/live proof happens after land). */
|
|
52
|
+
terminalStage?: string;
|
|
53
|
+
/** #3808 — the Task's `onDeploy` follow-up, run after the deploy, not by the worker. */
|
|
54
|
+
onDeploy?: string;
|
|
51
55
|
issues: ResolvedIssueSpec[];
|
|
52
56
|
/** Decisions/rules: discussion + milestone write-throughs and steers (the narrative ledger). */
|
|
53
57
|
decisions: TaskEvent[];
|
|
@@ -165,12 +169,28 @@ function renderContextFacet(value: string | undefined): string {
|
|
|
165
169
|
return `\nContext (Task data):\n${fence(truncate(text, TASK_CONTEXT_LIMIT))}\n`;
|
|
166
170
|
}
|
|
167
171
|
|
|
172
|
+
/** #3808 — post-deploy criteria rendered apart from the worker's acceptance: they are checked after land
|
|
173
|
+
* (QA / onDeploy) and must never read as unmet delivery criteria. Empty when the Task has none, so the
|
|
174
|
+
* template line collapses to a blank line (the `{{context}}` seam's shape). */
|
|
175
|
+
function renderAfterLandFacet(facets: Pick<TaskHydrationFacets, "terminalStage" | "onDeploy">): string {
|
|
176
|
+
const onDeploy = facets.onDeploy?.trim();
|
|
177
|
+
const deployed = facets.terminalStage === "deployed";
|
|
178
|
+
if (!onDeploy && !deployed) return "";
|
|
179
|
+
const parts = [
|
|
180
|
+
"\nAfter land (checked by QA/onDeploy once the change is live; NOT yours and never blocks delivery — deliver `complete` when code, tests and the delivery gate pass):",
|
|
181
|
+
];
|
|
182
|
+
if (deployed) parts.push("- Terminal stage is deployed: deploy, live-check and independent-reproduction criteria in the acceptance above belong here, not to your delivery.");
|
|
183
|
+
if (onDeploy) parts.push(`- onDeploy follow-up:\n${fence(truncate(onDeploy, TASK_CONTEXT_LIMIT))}`);
|
|
184
|
+
return `${parts.join("\n")}\n`;
|
|
185
|
+
}
|
|
186
|
+
|
|
168
187
|
export function renderTaskHydration(facets: TaskHydrationFacets): string {
|
|
169
188
|
const issues = facets.issues.length ? facets.issues.map(renderIssue).join("\n") : "- none linked";
|
|
170
189
|
const decisions = facets.decisions.length ? facets.decisions.map(renderEvent).join("\n") : "- none recorded";
|
|
171
190
|
const history = facets.history.length ? facets.history.map(renderEvent).join("\n") : "- none yet";
|
|
172
191
|
const memory = facets.memory ? `\n${fence(facets.memory)}\n` : "";
|
|
173
192
|
const context = renderContextFacet(facets.context);
|
|
193
|
+
const afterLand = renderAfterLandFacet(facets);
|
|
174
194
|
const rendered = renderTemplate("task.hydration", {
|
|
175
195
|
taskId: String(facets.taskId),
|
|
176
196
|
title: inlineSafe(facets.title),
|
|
@@ -180,6 +200,7 @@ export function renderTaskHydration(facets: TaskHydrationFacets): string {
|
|
|
180
200
|
intent: fence(facets.intent || "(no intent recorded)"),
|
|
181
201
|
context,
|
|
182
202
|
doneWhen: fence(facets.doneWhen || "(no explicit acceptance recorded)"),
|
|
203
|
+
afterLand,
|
|
183
204
|
issues,
|
|
184
205
|
decisions,
|
|
185
206
|
history,
|
|
@@ -187,7 +208,8 @@ export function renderTaskHydration(facets: TaskHydrationFacets): string {
|
|
|
187
208
|
steer: fence(facets.steer || "(no steer provided — proceed toward the acceptance above)"),
|
|
188
209
|
}).trimEnd();
|
|
189
210
|
// Saved operator templates can predate {{context}}; required Task data must still reach the worker (#3245).
|
|
190
|
-
|
|
211
|
+
const withContext = context && !rendered.includes(context.trim()) ? `${rendered}\n${context}`.trimEnd() : rendered;
|
|
212
|
+
return afterLand && !withContext.includes(afterLand.trim()) ? `${withContext}\n${afterLand}`.trimEnd() : withContext;
|
|
191
213
|
}
|
|
192
214
|
|
|
193
215
|
function hasTaskMemory(envelope: TaskMemoryEnvelope): boolean {
|
|
@@ -346,6 +368,8 @@ export async function hydrateTaskContext(input: HydrateTaskContextInput): Promis
|
|
|
346
368
|
intent: task.intent ?? task.title,
|
|
347
369
|
...(task.context?.trim() ? { context: task.context } : {}),
|
|
348
370
|
...(task.doneWhen ? { doneWhen: task.doneWhen } : {}),
|
|
371
|
+
...(task.terminalStage ? { terminalStage: task.terminalStage } : {}),
|
|
372
|
+
...(task.onDeploy?.trim() ? { onDeploy: task.onDeploy } : {}),
|
|
349
373
|
issues,
|
|
350
374
|
decisions,
|
|
351
375
|
history,
|