@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,20 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
insertTaskEvent,
|
|
5
|
-
markTaskDeliverableEffectsCompleteAsync,
|
|
6
|
-
ownedIsolatedWorkspace,
|
|
7
|
-
recordGateAsync,
|
|
8
|
-
recordTaskDeliverableAsync,
|
|
9
|
-
recordTaskDeliverableWithLandingIngressAsync,
|
|
10
|
-
setTaskStage,
|
|
11
|
-
setTaskStatus,
|
|
12
|
-
setTaskStatusAsync,
|
|
13
|
-
transitionWorkspaceLandStateAsync,
|
|
14
|
-
workspaceLandAuthorization, workspaceLandAuthorizationWithdrawn,
|
|
15
|
-
ValidationError,
|
|
16
|
-
type CompleteDeliveryArmOutcome,
|
|
17
|
-
} from "../db";
|
|
1
|
+
import { setTaskStatusAsync, setTaskStageAsync } from "../db/task-entity-async";
|
|
2
|
+
import { insertTaskEventAsync } from "../db/runtime-tail-async";
|
|
3
|
+
import { getTaskDetail, getWorkspace, markTaskDeliverableEffectsCompleteAsync, ownedIsolatedWorkspace, recordGateAsync, recordTaskDeliverableAsync, recordTaskDeliverableWithLandingIngressAsync, transitionWorkspaceLandStateAsync, workspaceLandAuthorization, workspaceLandAuthorizationWithdrawn, ValidationError, type CompleteDeliveryArmOutcome } from "../db";
|
|
18
4
|
import { refreshWorkspaceGitState } from "../workspace-probe";
|
|
19
5
|
import { pinWorkspaceDeliveryTip } from "../workspace-delivery-tip";
|
|
20
6
|
import { recoverIsolatedWorkspace, type WorkspaceRecoveryHint } from "./workspace-recovery";
|
|
@@ -25,7 +11,7 @@ import { emitTaskChanged, emitNewMessages } from "../sse";
|
|
|
25
11
|
import { agentBoundTaskIds, hasTaskReadAdminScope } from "../task-authz";
|
|
26
12
|
import { applyWorkspaceAction } from "../workspace-actions";
|
|
27
13
|
import { LAND_IN_FLIGHT_STATUSES, LAND_REQUEST_STALE_ON_HEAD_ADVANCE_STATUSES } from "../workspace-phase";
|
|
28
|
-
import {
|
|
14
|
+
import { findLatestCommandByCorrelationAsync } from "../commands-write";
|
|
29
15
|
import { mergeCommandNeedsRetry } from "../workspace-merge-progress";
|
|
30
16
|
import { workspaceLandNeedsRearm } from "../workspace-land-retry";
|
|
31
17
|
import { emitTaskSemanticNotification } from "./task-semantic-events";
|
|
@@ -120,7 +106,7 @@ function shouldRebindToWorkspace(task: TaskDetail, ws: WorkspaceRecord | undefin
|
|
|
120
106
|
return Boolean(task.branch && ws?.branch && task.branch !== ws.branch);
|
|
121
107
|
}
|
|
122
108
|
|
|
123
|
-
function resolveWorkspace(ctx: AuthContext, input: Pick<DeliverTaskInput, "workspaceId" | "workspace" | "idempotencyKey">, taskId: number, opts: { allowRebind?: boolean } = {}): WorkspaceRecord | undefined {
|
|
109
|
+
async function resolveWorkspace(ctx: AuthContext, input: Pick<DeliverTaskInput, "workspaceId" | "workspace" | "idempotencyKey">, taskId: number, opts: { allowRebind?: boolean } = {}): Promise<WorkspaceRecord | undefined> {
|
|
124
110
|
const caller = ctx.callerAgentId;
|
|
125
111
|
const explicit = input.workspaceId?.trim();
|
|
126
112
|
let ws = explicit ? getWorkspace(explicit) : caller ? ownedIsolatedWorkspace(caller) : undefined;
|
|
@@ -133,7 +119,7 @@ function resolveWorkspace(ctx: AuthContext, input: Pick<DeliverTaskInput, "works
|
|
|
133
119
|
// server-authoritative recovery (signed spawn-request association + immutable agent.spawn params +
|
|
134
120
|
// orchestrator baseDir containment); the descriptor is an untrusted hint, never trusted verbatim.
|
|
135
121
|
if (!ws && input.workspace) {
|
|
136
|
-
const recovery = recoverIsolatedWorkspace(ctx, input.workspace, explicit);
|
|
122
|
+
const recovery = (await recoverIsolatedWorkspace(ctx, input.workspace, explicit));
|
|
137
123
|
if (recovery.ok) ws = recovery.workspace;
|
|
138
124
|
}
|
|
139
125
|
if (!ws) return undefined;
|
|
@@ -149,18 +135,18 @@ function resolveWorkspace(ctx: AuthContext, input: Pick<DeliverTaskInput, "works
|
|
|
149
135
|
return ws;
|
|
150
136
|
}
|
|
151
137
|
|
|
152
|
-
function rebindTaskToWorkspaceIfNeeded(task: TaskDetail, ws: WorkspaceRecord | undefined, actor: string): TaskDetail {
|
|
138
|
+
async function rebindTaskToWorkspaceIfNeeded(task: TaskDetail, ws: WorkspaceRecord | undefined, actor: string): Promise<TaskDetail> {
|
|
153
139
|
if (!shouldRebindToWorkspace(task, ws)) return task;
|
|
154
|
-
|
|
140
|
+
(await setTaskStatusAsync(task.id, {
|
|
155
141
|
status: "in_progress",
|
|
156
142
|
note: `fix-forward rebind to workspace ${ws.id} (${ws.branch})`,
|
|
157
143
|
actor,
|
|
158
|
-
});
|
|
159
|
-
return
|
|
144
|
+
}));
|
|
145
|
+
return (await setTaskStageAsync(task.id, {
|
|
160
146
|
deliverableStage: "committed-on-branch",
|
|
161
147
|
branch: ws.branch,
|
|
162
148
|
actor,
|
|
163
|
-
});
|
|
149
|
+
}));
|
|
164
150
|
}
|
|
165
151
|
|
|
166
152
|
function cleanReason(value: unknown, disposition: "blocked" | "failed"): string {
|
|
@@ -189,7 +175,7 @@ function skippedRequiredGateReports(reason: string): Required<Pick<TaskDeliveryG
|
|
|
189
175
|
// Non-landing dispositions (blocked/failed/needs-review, and a verdict-only complete that closes on
|
|
190
176
|
// delivery with no land) keep their honest existing wording. #2518 adds the third shape: a complete
|
|
191
177
|
// delivery ACCEPTED but HELD for coordinator review — it says exactly that and never claims landing.
|
|
192
|
-
function notifyDelivery(taskId: number, eventId: number, metadata: TaskDeliverableEventMetadata, landing: boolean): void {
|
|
178
|
+
async function notifyDelivery(taskId: number, eventId: number, metadata: TaskDeliverableEventMetadata, landing: boolean): Promise<void> {
|
|
193
179
|
const task = getTaskDetail(taskId);
|
|
194
180
|
if (!task) return;
|
|
195
181
|
const linkedIssues = task.issues.map((issue) => ({ provider: issue.provider, repo: issue.repo, externalKey: issue.externalKey, title: issue.title, status: issue.status }));
|
|
@@ -227,7 +213,7 @@ function notifyDelivery(taskId: number, eventId: number, metadata: TaskDeliverab
|
|
|
227
213
|
const notArmedWarning = metadata.landNotArmedReason && unmergedAhead > 0
|
|
228
214
|
? `\n⚠ NOT landed: ${unmergedAhead} unmerged commit(s) remain on ${metadata.workspaceId ?? "the workspace"} (no land armed — ${metadata.landNotArmedReason}). Land it explicitly if this work belongs on main.`
|
|
229
215
|
: "";
|
|
230
|
-
const delivered = routeNotification({
|
|
216
|
+
const delivered = await routeNotification({
|
|
231
217
|
type: "task.deliverable",
|
|
232
218
|
scope: {
|
|
233
219
|
taskId: String(taskId),
|
|
@@ -309,7 +295,7 @@ export async function deliverTask(input: DeliverTaskInput, ctx: AuthContext): Pr
|
|
|
309
295
|
// `complete` changes, from a hard reject to the same owner-authorized resolve the land path had.
|
|
310
296
|
// The TASK-record rebind itself (rebindTaskToWorkspaceIfNeeded) is still gated on `wantsLand`.
|
|
311
297
|
let workspace = input.disposition === "complete" || input.workspaceId
|
|
312
|
-
? resolveWorkspace(ctx, input, taskId, { allowRebind: input.disposition === "complete" })
|
|
298
|
+
? (await resolveWorkspace(ctx, input, taskId, { allowRebind: input.disposition === "complete" }))
|
|
313
299
|
: undefined;
|
|
314
300
|
// A workspace that could actually carry a land: isolated, live on disk, and on a branch. Mirrors
|
|
315
301
|
// the preconditions enforced below (:live isolated workspace / :exact resolved source ref), so a
|
|
@@ -459,7 +445,7 @@ export async function deliverTask(input: DeliverTaskInput, ctx: AuthContext): Pr
|
|
|
459
445
|
}
|
|
460
446
|
if (ahead > 0) landNotArmed = { reason: workspaceLandBlocked(workspace) ? "workspace-land-blocked" : landDecision.reason, unmergedAhead: ahead };
|
|
461
447
|
}
|
|
462
|
-
const currentTask = wantsLand && !noWork ? rebindTaskToWorkspaceIfNeeded(before, workspace, actor) : before;
|
|
448
|
+
const currentTask = wantsLand && !noWork ? (await rebindTaskToWorkspaceIfNeeded(before, workspace, actor)) : before;
|
|
463
449
|
const sourceRef = wantsLand ? replayLand?.sourceRef ?? workspace?.branch?.trim() : undefined;
|
|
464
450
|
// Only the HEAD returned by THIS host probe can authorize ingress. Persisted metadata may be
|
|
465
451
|
// stale, and the caller's commit list is a self-report; neither is authority for a mutable ref.
|
|
@@ -520,7 +506,7 @@ export async function deliverTask(input: DeliverTaskInput, ctx: AuthContext): Pr
|
|
|
520
506
|
// Pin the delivered tip captured at delivery; the host publishes it ONLY if reachable from the
|
|
521
507
|
// verified workspace tip, else REFUSES (H1 — fail closed), so no unrelated local commit rides in.
|
|
522
508
|
const deliveredSha = input.commits?.length ? input.commits[input.commits.length - 1]?.sha : undefined;
|
|
523
|
-
reviewPublish = dispatchReviewPublish(reviewWorkspace, { requestedBy: actor, taskId, sha: deliveredSha, emit: emitCommand });
|
|
509
|
+
reviewPublish = (await dispatchReviewPublish(reviewWorkspace, { requestedBy: actor, taskId, sha: deliveredSha, emit: emitCommand }));
|
|
524
510
|
}
|
|
525
511
|
const deliveryInput = {
|
|
526
512
|
...input,
|
|
@@ -617,9 +603,9 @@ export async function deliverTask(input: DeliverTaskInput, ctx: AuthContext): Pr
|
|
|
617
603
|
const detail = getTaskDetail(taskId) ?? currentTask;
|
|
618
604
|
if (detail) {
|
|
619
605
|
emitTaskChanged(detail, "task.deliverable");
|
|
620
|
-
notifyDelivery(taskId, delivery.event.id, eventMeta ?? (delivery.event.metadata as unknown as TaskDeliverableEventMetadata), wantsLand && armOutcome === "armed");
|
|
606
|
+
await notifyDelivery(taskId, delivery.event.id, eventMeta ?? (delivery.event.metadata as unknown as TaskDeliverableEventMetadata), wantsLand && armOutcome === "armed");
|
|
621
607
|
if (input.disposition === "failed") {
|
|
622
|
-
emitTaskSemanticNotification({ task: detail, transition: "gate-failed", actor, occurrenceKey: `event:${delivery.event.id}`, outcome: { disposition: "failed" } });
|
|
608
|
+
await emitTaskSemanticNotification({ task: detail, transition: "gate-failed", actor, occurrenceKey: `event:${delivery.event.id}`, outcome: { disposition: "failed" } });
|
|
623
609
|
}
|
|
624
610
|
}
|
|
625
611
|
}
|
|
@@ -685,7 +671,7 @@ export async function deliverTask(input: DeliverTaskInput, ctx: AuthContext): Pr
|
|
|
685
671
|
// into same-head `already-requested`; only terminal non-PR attempts qualify,
|
|
686
672
|
// so a currently running merge or a PR waiting on GitHub is never doubled.
|
|
687
673
|
const priorMerge = LAND_IN_FLIGHT_STATUSES.has(liveWorkspace.status)
|
|
688
|
-
?
|
|
674
|
+
? await findLatestCommandByCorrelationAsync("workspace.merge", liveWorkspace.id)
|
|
689
675
|
: null;
|
|
690
676
|
const retryableMergePlan = mergeCommandNeedsRetry(priorMerge);
|
|
691
677
|
const withdrawnLand = !hadLandAuthorization
|
|
@@ -697,7 +683,7 @@ export async function deliverTask(input: DeliverTaskInput, ctx: AuthContext): Pr
|
|
|
697
683
|
// exhausted. applyWorkspaceAction clears the old retry incident before re-arming it.
|
|
698
684
|
|| retryableMergePlan || workspaceLandNeedsRearm(liveWorkspace) || withdrawnLand;
|
|
699
685
|
if (reenterLand) {
|
|
700
|
-
markAgentWorkFinal(actor, "task-deliver");
|
|
686
|
+
(await markAgentWorkFinal(actor, "task-deliver"));
|
|
701
687
|
const result = await applyWorkspaceAction(liveWorkspace, {
|
|
702
688
|
action: "ready",
|
|
703
689
|
agentId: actor,
|
|
@@ -1,54 +1,11 @@
|
|
|
1
|
+
import { setTaskStatusAsync, setTaskStageAsync, markTaskAgentReapedAsync } from "../db/task-entity-async";
|
|
2
|
+
import { confirmWorkspaceCarryPendingForTaskAsync } from "../db/runtime-tail-async";
|
|
3
|
+
import { removeSubscriptionsForTaskAsync } from "../db/lifecycle-tail-async";
|
|
1
4
|
// #834 — service facade for the durable Task entity. Transports (HTTP routes, MCP tools)
|
|
2
5
|
// call THIS, not the db writers directly (transport-thinness, epic #342): the service owns
|
|
3
6
|
// the db mutation + the SSE emission so the transport stays thin. The kind partition and all
|
|
4
7
|
// validation live in the db layer (src/db/task-entity.ts); this layer adds eventing.
|
|
5
|
-
import {
|
|
6
|
-
addTaskDependencyAsync,
|
|
7
|
-
addTaskRelationAsync,
|
|
8
|
-
assignTaskAgent,
|
|
9
|
-
assignTaskAgentAsync,
|
|
10
|
-
clearTaskParentAsync,
|
|
11
|
-
confirmWorkspaceCarryPendingForTask,
|
|
12
|
-
createDeliverableTask,
|
|
13
|
-
createDeliverableTaskAsync,
|
|
14
|
-
deliverableTaskIdsByOwnerAgent,
|
|
15
|
-
deliverableTaskIdsByOwnerAgentAsync,
|
|
16
|
-
deliverableTaskIdsBySpawnRequestId,
|
|
17
|
-
deliverableTaskIdsBySpawnRequestIdAsync,
|
|
18
|
-
getAgent,
|
|
19
|
-
getTaskDetail,
|
|
20
|
-
getTaskDetailAsync, getTaskViewAsync,
|
|
21
|
-
listAgents,
|
|
22
|
-
listDeliverableTaskSummariesAsync,
|
|
23
|
-
listDeliverableTasksAsync,
|
|
24
|
-
listTaskEvents,
|
|
25
|
-
listTaskEventsAsync,
|
|
26
|
-
listWorkspaces,
|
|
27
|
-
markTaskAgentReaped,
|
|
28
|
-
markTaskAgentReapedAsync,
|
|
29
|
-
recordGate,
|
|
30
|
-
recordGateAsync,
|
|
31
|
-
recordTaskDispatchDecisionAsync,
|
|
32
|
-
removeTaskDependencyAsync,
|
|
33
|
-
removeTaskRefAsync,
|
|
34
|
-
removeTaskRelationAsync,
|
|
35
|
-
setTaskParentAsync,
|
|
36
|
-
setTaskStage,
|
|
37
|
-
setTaskStageAsync,
|
|
38
|
-
setTaskStatus,
|
|
39
|
-
setTaskStatusAsync,
|
|
40
|
-
updateDeliverableTask,
|
|
41
|
-
updateDeliverableTaskAsync,
|
|
42
|
-
ValidationError,
|
|
43
|
-
type ListDeliverableTasksFilter,
|
|
44
|
-
type RecordTaskDispatchDecisionInput,
|
|
45
|
-
type RecordGateInput,
|
|
46
|
-
type RemoveTaskRefInput,
|
|
47
|
-
type SetTaskStageInput,
|
|
48
|
-
type SetTaskStatusInput,
|
|
49
|
-
type TaskEntityWriteOptions,
|
|
50
|
-
type UpdateDeliverableTaskInput,
|
|
51
|
-
} from "../db";
|
|
8
|
+
import { addTaskDependencyAsync, addTaskRelationAsync, assignTaskAgent, assignTaskAgentAsync, clearTaskParentAsync, createDeliverableTask, createDeliverableTaskAsync, deliverableTaskIdsByOwnerAgent, deliverableTaskIdsByOwnerAgentAsync, deliverableTaskIdsBySpawnRequestId, deliverableTaskIdsBySpawnRequestIdAsync, getAgent, getTaskDetail, getTaskDetailAsync, getTaskViewAsync, listAgents, listDeliverableTaskSummariesAsync, listDeliverableTasksAsync, listTaskEvents, listTaskEventsAsync, listWorkspaces, recordGate, recordGateAsync, recordTaskDispatchDecisionAsync, removeTaskDependencyAsync, removeTaskRefAsync, removeTaskRelationAsync, setTaskParentAsync, updateDeliverableTask, updateDeliverableTaskAsync, ValidationError, type ListDeliverableTasksFilter, type RecordTaskDispatchDecisionInput, type RecordGateInput, type RemoveTaskRefInput, type SetTaskStageInput, type SetTaskStatusInput, type TaskEntityWriteOptions, type UpdateDeliverableTaskInput } from "../db";
|
|
52
9
|
|
|
53
10
|
import { validationCompletionStatus } from "../task-completion.ts";
|
|
54
11
|
import { findSpawnCommandByRequestId } from "../commands-db";
|
|
@@ -56,7 +13,6 @@ import { findSpawnCommandByRequestId } from "../commands-db";
|
|
|
56
13
|
import { emitTaskChanged } from "../sse";
|
|
57
14
|
import { isCarryPendingBlocker } from "./carry-pending";
|
|
58
15
|
import type { CommandStatus, Task, TaskBlocker, TaskCommitRef, TaskDeliverableEventMetadata, TaskDetail, TaskEvent, TaskGateState, TaskListItem, TaskStatus, TaskTerminalStage, WorkspaceRecord } from "../types";
|
|
59
|
-
import { removeSubscriptionsForTask } from "../db/notification-subscriptions";
|
|
60
16
|
import { emitRelayEvent } from "../events";
|
|
61
17
|
import { routeNotification } from "../notification-router";
|
|
62
18
|
import { isAgentExitDoneWhen } from "./task-done-when";
|
|
@@ -73,19 +29,8 @@ import { taskOwnerTarget } from "../task-owner";
|
|
|
73
29
|
export type { CreateTaskEntityInput } from "./task-issue-link";
|
|
74
30
|
export { addTaskEntityRef, addTaskEntityRefAsync, attachTaskRefs } from "./task-issue-link";
|
|
75
31
|
|
|
76
|
-
export function createTaskEntity(input: CreateTaskEntityInput): TaskDetail {
|
|
77
|
-
|
|
78
|
-
const created = createDeliverableTask(input);
|
|
79
|
-
const detail = addInitialIssueRefs(created.id, input);
|
|
80
|
-
autoSubscribeTaskCoordinator(input.createdBy, detail.id);
|
|
81
|
-
emitTaskChanged(detail, "task.created");
|
|
82
|
-
emitTaskSemanticNotification({
|
|
83
|
-
task: detail,
|
|
84
|
-
transition: "created",
|
|
85
|
-
actor: input.createdBy,
|
|
86
|
-
occurrenceKey: latestTaskEventOccurrence(detail.id, "task.created"),
|
|
87
|
-
});
|
|
88
|
-
return detail;
|
|
32
|
+
export async function createTaskEntity(input: CreateTaskEntityInput): Promise<TaskDetail> {
|
|
33
|
+
return createTaskEntityAsync(input);
|
|
89
34
|
}
|
|
90
35
|
|
|
91
36
|
/** The off-loop twin of createTaskEntity (S3.2 L-D2 #1904) — the sanctioned HTTP/MCP create path. */
|
|
@@ -93,9 +38,9 @@ export async function createTaskEntityAsync(input: CreateTaskEntityInput, option
|
|
|
93
38
|
cleanIssueRefRelation(input.relation);
|
|
94
39
|
const created = await createDeliverableTaskAsync(input, options);
|
|
95
40
|
const detail = await addInitialIssueRefsAsync(created.id, input, options);
|
|
96
|
-
autoSubscribeTaskCoordinator(input.createdBy, detail.id);
|
|
41
|
+
(await autoSubscribeTaskCoordinator(input.createdBy, detail.id));
|
|
97
42
|
emitTaskChanged(detail, "task.created");
|
|
98
|
-
emitTaskSemanticNotification({
|
|
43
|
+
await emitTaskSemanticNotification({
|
|
99
44
|
task: detail,
|
|
100
45
|
transition: "created",
|
|
101
46
|
actor: input.createdBy,
|
|
@@ -118,10 +63,8 @@ export function listTaskEntitySummariesAsync(filter: ListDeliverableTasksFilter
|
|
|
118
63
|
return listDeliverableTaskSummariesAsync(filter);
|
|
119
64
|
}
|
|
120
65
|
|
|
121
|
-
export function updateTaskEntity(id: number, input: UpdateDeliverableTaskInput): TaskDetail {
|
|
122
|
-
|
|
123
|
-
emitTaskChanged(detail, "task.updated");
|
|
124
|
-
return detail;
|
|
66
|
+
export async function updateTaskEntity(id: number, input: UpdateDeliverableTaskInput): Promise<TaskDetail> {
|
|
67
|
+
return updateTaskEntityAsync(id, input);
|
|
125
68
|
}
|
|
126
69
|
|
|
127
70
|
/** The off-loop twin of updateTaskEntity (S3.2 L-D2 #1904). */
|
|
@@ -136,30 +79,22 @@ export async function updateTaskEntityAsync(id: number, input: UpdateDeliverable
|
|
|
136
79
|
// interface that cannot yet await, S3.2 L-D2 #1904 follow-up) and the off-loop path
|
|
137
80
|
// (setTaskEntityStatusAsync) apply IDENTICAL business logic over their differently-sourced `before`/
|
|
138
81
|
// `detail` rows, instead of forking the notification/work-finality logic in two places.
|
|
139
|
-
export function emitStatusChangeEffects(before: TaskDetail | null, detail: TaskDetail, actor: string | undefined, occurrenceKey: string | undefined): void {
|
|
82
|
+
export async function emitStatusChangeEffects(before: TaskDetail | null, detail: TaskDetail, actor: string | undefined, occurrenceKey: string | undefined): Promise<void> {
|
|
140
83
|
emitTaskChanged(detail, "task.status");
|
|
141
84
|
const transition = taskTransitionForStatus(detail.status, detail.blockedBy);
|
|
142
85
|
if (actor !== "task-lifecycle" && transition && shouldEmitStatusTransition(before, detail, transition)) {
|
|
143
|
-
emitTaskSemanticNotification({ task: detail, transition, actor, occurrenceKey });
|
|
86
|
+
await emitTaskSemanticNotification({ task: detail, transition, actor, occurrenceKey });
|
|
144
87
|
}
|
|
145
|
-
if (isTerminalTaskStatus(detail.status))
|
|
88
|
+
if (isTerminalTaskStatus(detail.status)) await removeSubscriptionsForTaskAsync(detail.id);
|
|
146
89
|
// #1084 — a task reaching `done` is an explicit, platform-owned completion signal for its
|
|
147
90
|
// owning agent's work window: close it, forwarding only the held final group. The #2520
|
|
148
91
|
// plan-ref propagation trigger lives with its CALLERS (below): the sync twin runs the
|
|
149
92
|
// in-process op body; the async path awaits the gateway-backed seam.
|
|
150
|
-
if (before?.status !== "done" && detail.status === "done") markAgentWorkFinal(detail.ownerAgentId, "task-done");
|
|
93
|
+
if (before?.status !== "done" && detail.status === "done") (await markAgentWorkFinal(detail.ownerAgentId, "task-done"));
|
|
151
94
|
}
|
|
152
95
|
|
|
153
|
-
export function setTaskEntityStatus(id: number, input: SetTaskStatusInput): TaskDetail {
|
|
154
|
-
|
|
155
|
-
const detail = setTaskStatus(id, input);
|
|
156
|
-
emitStatusChangeEffects(before, detail, input.actor, latestTaskEventOccurrence(detail.id, `status:${detail.status}`));
|
|
157
|
-
if (before?.status !== "done" && detail.status === "done") {
|
|
158
|
-
// #2520 — durable status=done trigger for ref-driven plan-node terminal propagation
|
|
159
|
-
// (in-process twin: the bus-dispatcher seam cannot await; no fire-and-forget promises).
|
|
160
|
-
advanceTerminalPlanRefs({ taskId: detail.id, actor: input.actor });
|
|
161
|
-
}
|
|
162
|
-
return detail;
|
|
96
|
+
export async function setTaskEntityStatus(id: number, input: SetTaskStatusInput): Promise<TaskDetail> {
|
|
97
|
+
return setTaskEntityStatusAsync(id, input);
|
|
163
98
|
}
|
|
164
99
|
|
|
165
100
|
/**
|
|
@@ -175,7 +110,7 @@ export function setTaskEntityStatus(id: number, input: SetTaskStatusInput): Task
|
|
|
175
110
|
export async function setTaskEntityStatusAsync(id: number, input: SetTaskStatusInput, options?: TaskEntityWriteOptions): Promise<TaskDetail> {
|
|
176
111
|
const before = await getTaskDetailAsync(id);
|
|
177
112
|
const detail = await setTaskStatusAsync(id, input, options);
|
|
178
|
-
emitStatusChangeEffects(before, detail, input.actor, await latestTaskEventOccurrenceAsync(detail.id, `status:${detail.status}`));
|
|
113
|
+
await emitStatusChangeEffects(before, detail, input.actor, await latestTaskEventOccurrenceAsync(detail.id, `status:${detail.status}`));
|
|
179
114
|
if (before?.status !== "done" && detail.status === "done") {
|
|
180
115
|
// #2520 — awaited on the sanctioned async path so the trigger's plan writes settle in order.
|
|
181
116
|
await advanceTerminalPlanRefsAsync({ taskId: detail.id, actor: input.actor });
|
|
@@ -188,33 +123,14 @@ export function isTerminalTaskStatus(status: Task["status"]): boolean {
|
|
|
188
123
|
}
|
|
189
124
|
|
|
190
125
|
// #1456 — the un-bypassable carry-pending landing gate is enforced in the LOWEST stage writer
|
|
191
|
-
// (`db/task-entity.ts`
|
|
126
|
+
// (`db/task-entity.ts` setTaskStageAsync), so it covers EVERY entrypoint — the land pipeline, the
|
|
192
127
|
// no-mergedSha out-of-band reconciliation, PR/squash, a manual `relay_task_set_stage`, and the
|
|
193
128
|
// deploy path (`stampDeployedTasks`, which calls the primitive directly). This service wrapper adds
|
|
194
129
|
// only eventing; it does NOT re-check (guarding the wrapper too would be a second, drift-prone code
|
|
195
130
|
// path — round-4: guard the primitive, not the wrapper). The land pipeline (driveTaskOnLand)
|
|
196
131
|
// short-circuits earlier with a superseded event; the primitive throw is the backstop for the rest.
|
|
197
|
-
export function setTaskEntityStage(id: number, input: SetTaskStageInput): TaskDetail {
|
|
198
|
-
|
|
199
|
-
let detail = setTaskStage(id, input);
|
|
200
|
-
emitTaskChanged(detail, "task.stage");
|
|
201
|
-
const transition = taskTransitionForStage(detail.deliverableStage);
|
|
202
|
-
if (input.actor !== "task-lifecycle" && transition && before?.deliverableStage !== detail.deliverableStage) {
|
|
203
|
-
emitTaskSemanticNotification({
|
|
204
|
-
task: detail,
|
|
205
|
-
transition,
|
|
206
|
-
actor: input.actor,
|
|
207
|
-
branch: detail.branch,
|
|
208
|
-
occurrenceKey: latestTaskEventOccurrence(detail.id, `stage:${detail.deliverableStage}`),
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
const deliverableStage = detail.deliverableStage ?? "none";
|
|
212
|
-
if (LANDED_STAGES.has(deliverableStage)) {
|
|
213
|
-
// #2520 — a landed/deploying/deployed stage is terminal success for ref-driven propagation.
|
|
214
|
-
advanceTerminalPlanRefs({ taskId: detail.id, actor: input.actor });
|
|
215
|
-
}
|
|
216
|
-
if (before?.status !== detail.status) emitStatusChangeEffects(before, detail, input.actor, undefined);
|
|
217
|
-
return detail;
|
|
132
|
+
export async function setTaskEntityStage(id: number, input: SetTaskStageInput): Promise<TaskDetail> {
|
|
133
|
+
return setTaskEntityStageAsync(id, input);
|
|
218
134
|
}
|
|
219
135
|
|
|
220
136
|
/** The off-loop twin of setTaskEntityStage (S3.2 L-D2 #1904). */
|
|
@@ -224,7 +140,7 @@ export async function setTaskEntityStageAsync(id: number, input: SetTaskStageInp
|
|
|
224
140
|
emitTaskChanged(detail, "task.stage");
|
|
225
141
|
const transition = taskTransitionForStage(detail.deliverableStage);
|
|
226
142
|
if (input.actor !== "task-lifecycle" && transition && before?.deliverableStage !== detail.deliverableStage) {
|
|
227
|
-
emitTaskSemanticNotification({
|
|
143
|
+
await emitTaskSemanticNotification({
|
|
228
144
|
task: detail,
|
|
229
145
|
transition,
|
|
230
146
|
actor: input.actor,
|
|
@@ -237,7 +153,7 @@ export async function setTaskEntityStageAsync(id: number, input: SetTaskStageInp
|
|
|
237
153
|
// #2520 — the off-loop stage trigger for ref-driven plan-node terminal propagation.
|
|
238
154
|
await advanceTerminalPlanRefsAsync({ taskId: detail.id, actor: input.actor });
|
|
239
155
|
}
|
|
240
|
-
if (before?.status !== detail.status) emitStatusChangeEffects(before, detail, input.actor, undefined);
|
|
156
|
+
if (before?.status !== detail.status) await emitStatusChangeEffects(before, detail, input.actor, undefined);
|
|
241
157
|
return detail;
|
|
242
158
|
}
|
|
243
159
|
|
|
@@ -254,39 +170,11 @@ export async function setTaskEntityStageAsync(id: number, input: SetTaskStageInp
|
|
|
254
170
|
// #831). The `deliverable.superseded` event already alerts the coordinator, who handles any
|
|
255
171
|
// issue-close/reap manually in the rare revert window. Automatic host-side tree-level carry
|
|
256
172
|
// verification (and lifecycle re-drive) is tracked as the #1458 follow-up.
|
|
257
|
-
export function confirmTaskCarry(id: number, opts: { actor?: string; note?: string; commit?: TaskCommitRef; branch?: string } = {}): TaskDetail {
|
|
258
|
-
|
|
259
|
-
if (!task) throw new ValidationError("task not found");
|
|
260
|
-
const blockers = task.blockedBy ?? [];
|
|
261
|
-
const remaining = blockers.filter((b: TaskBlocker) => !isCarryPendingBlocker(b));
|
|
262
|
-
const hadCarryPending = remaining.length !== blockers.length;
|
|
263
|
-
const note = opts.note?.trim() || "delivered change confirmed carried onto the current base (#1456)";
|
|
264
|
-
let detail = task;
|
|
265
|
-
if (hadCarryPending) {
|
|
266
|
-
// `allowCarryPendingClear` authorizes the primitive latch (BLOCKER 1) to drop the carry-pending
|
|
267
|
-
// blocker(s) — this confirm is the ONLY path that may. Scoped: preserves unrelated blockers.
|
|
268
|
-
detail = remaining.length > 0
|
|
269
|
-
? setTaskEntityStatus(id, { status: "blocked", blockedBy: remaining, note, actor: opts.actor, allowCarryPendingClear: true })
|
|
270
|
-
: setTaskEntityStatus(id, { status: "in_progress", note, actor: opts.actor, allowCarryPendingClear: true });
|
|
271
|
-
}
|
|
272
|
-
// Clear the workspace-durable marker too (round-4 BLOCKER 3): a late-bound Task can be gated purely
|
|
273
|
-
// by the workspace's unconfirmed head with no task-level blocker, and the marker would otherwise
|
|
274
|
-
// re-gate the very land this confirm authorizes. Cleared for the Task's bound workspace(s).
|
|
275
|
-
confirmWorkspaceCarryPendingForTask(task);
|
|
276
|
-
// Optionally complete the land the carry-pending gate refused. Both obligation carriers are cleared
|
|
277
|
-
// above, so the stage gate now permits the advance; doneWhen closes the Task as usual.
|
|
278
|
-
if (opts.commit) {
|
|
279
|
-
detail = setTaskEntityStage(id, {
|
|
280
|
-
deliverableStage: "landed-on-main",
|
|
281
|
-
...(opts.branch ? { branch: opts.branch } : {}),
|
|
282
|
-
commit: opts.commit,
|
|
283
|
-
actor: opts.actor,
|
|
284
|
-
});
|
|
285
|
-
}
|
|
286
|
-
return detail;
|
|
173
|
+
export async function confirmTaskCarry(id: number, opts: { actor?: string; note?: string; commit?: TaskCommitRef; branch?: string } = {}): Promise<TaskDetail> {
|
|
174
|
+
return confirmTaskCarryAsync(id, opts);
|
|
287
175
|
}
|
|
288
176
|
|
|
289
|
-
/** The off-loop twin of confirmTaskCarry (S3.2 L-D2 #1904). `
|
|
177
|
+
/** The off-loop twin of confirmTaskCarry (S3.2 L-D2 #1904). `confirmWorkspaceCarryPendingForTaskAsync`
|
|
290
178
|
* (workspaces/carry-pending-marker.ts) is a genuinely cross-domain, not-yet-migrated write — left on the
|
|
291
179
|
* sync facade like the retained sync callers elsewhere in this migration; it is a single small metadata
|
|
292
180
|
* write, not a hot path, so it does not defeat the point of awaiting the task-entity writes around it. */
|
|
@@ -303,7 +191,7 @@ export async function confirmTaskCarryAsync(id: number, opts: { actor?: string;
|
|
|
303
191
|
? await setTaskEntityStatusAsync(id, { status: "blocked", blockedBy: remaining, note, actor: opts.actor, allowCarryPendingClear: true })
|
|
304
192
|
: await setTaskEntityStatusAsync(id, { status: "in_progress", note, actor: opts.actor, allowCarryPendingClear: true });
|
|
305
193
|
}
|
|
306
|
-
|
|
194
|
+
(await confirmWorkspaceCarryPendingForTaskAsync(task));
|
|
307
195
|
if (opts.commit) {
|
|
308
196
|
detail = await setTaskEntityStageAsync(id, {
|
|
309
197
|
deliverableStage: "landed-on-main",
|
|
@@ -335,20 +223,8 @@ function shouldEmitStatusTransition(before: TaskDetail | null, detail: TaskDetai
|
|
|
335
223
|
return false;
|
|
336
224
|
}
|
|
337
225
|
|
|
338
|
-
export function recordTaskEntityGate(id: number, input: RecordGateInput): { gate: TaskGateState; gates: TaskGateState[] } {
|
|
339
|
-
|
|
340
|
-
const detail = getTaskDetail(id);
|
|
341
|
-
if (detail) emitTaskChanged(detail, "task.gate");
|
|
342
|
-
if (detail && gate.status === "fail") {
|
|
343
|
-
emitTaskSemanticNotification({
|
|
344
|
-
task: detail,
|
|
345
|
-
transition: "gate-failed",
|
|
346
|
-
actor: input.actor,
|
|
347
|
-
occurrenceKey: latestTaskEventOccurrence(detail.id, `gate:${gate.gate}`),
|
|
348
|
-
outcome: { gate: gate.gate, status: gate.status, ...(gate.detail ? { detail: gate.detail } : {}) },
|
|
349
|
-
});
|
|
350
|
-
}
|
|
351
|
-
return { gate, gates: detail?.gates ?? [] };
|
|
226
|
+
export async function recordTaskEntityGate(id: number, input: RecordGateInput): Promise<{ gate: TaskGateState; gates: TaskGateState[] }> {
|
|
227
|
+
return recordTaskEntityGateAsync(id, input);
|
|
352
228
|
}
|
|
353
229
|
|
|
354
230
|
/** The off-loop twin of recordTaskEntityGate (S3.2 L-D2 #1904). */
|
|
@@ -357,7 +233,7 @@ export async function recordTaskEntityGateAsync(id: number, input: RecordGateInp
|
|
|
357
233
|
const detail = await getTaskDetailAsync(id);
|
|
358
234
|
if (detail) emitTaskChanged(detail, "task.gate");
|
|
359
235
|
if (detail && gate.status === "fail") {
|
|
360
|
-
emitTaskSemanticNotification({
|
|
236
|
+
await emitTaskSemanticNotification({
|
|
361
237
|
task: detail,
|
|
362
238
|
transition: "gate-failed",
|
|
363
239
|
actor: input.actor,
|
|
@@ -419,10 +295,8 @@ export async function removeTaskEntityRefAsync(id: number, input: RemoveTaskRefI
|
|
|
419
295
|
* `registerAgent` — the same synchronous plugin/hook constraint as `setTaskEntityStatus` above. Every
|
|
420
296
|
* other production caller (routes/tasks.ts, the MCP task tools) now calls `assignTaskEntityAgentAsync`.
|
|
421
297
|
*/
|
|
422
|
-
export function assignTaskEntityAgent(id: number, agentId: string, role?: string, actor?: string): TaskDetail {
|
|
423
|
-
|
|
424
|
-
emitTaskChanged(detail, "task.updated");
|
|
425
|
-
return detail;
|
|
298
|
+
export async function assignTaskEntityAgent(id: number, agentId: string, role?: string, actor?: string): Promise<TaskDetail> {
|
|
299
|
+
return assignTaskEntityAgentAsync(id, agentId, role, actor);
|
|
426
300
|
}
|
|
427
301
|
|
|
428
302
|
/** The off-loop twin of assignTaskEntityAgent (S3.2 L-D2 #1904) — the sanctioned HTTP/MCP path. */
|
|
@@ -600,10 +474,10 @@ async function latestDeliverableEventAsync(taskId: number): Promise<TaskEvent |
|
|
|
600
474
|
return [...(await listTaskEventsAsync(taskId))].reverse().find((event) => event.type === "deliverable");
|
|
601
475
|
}
|
|
602
476
|
|
|
603
|
-
function restoreDeliveredDisposition(task: TaskDetail, event: TaskEvent): void {
|
|
477
|
+
async function restoreDeliveredDisposition(task: TaskDetail, event: TaskEvent): Promise<void> {
|
|
604
478
|
const metadata = event.metadata as unknown as Partial<TaskDeliverableEventMetadata>;
|
|
605
479
|
if (metadata.disposition === "failed" && task.status !== "failed") {
|
|
606
|
-
setTaskEntityStatus(task.id, {
|
|
480
|
+
await setTaskEntityStatus(task.id, {
|
|
607
481
|
status: "failed",
|
|
608
482
|
...(typeof metadata.reason === "string" && metadata.reason.trim() ? { note: metadata.reason } : {}),
|
|
609
483
|
actor: "task-lifecycle",
|
|
@@ -611,7 +485,7 @@ function restoreDeliveredDisposition(task: TaskDetail, event: TaskEvent): void {
|
|
|
611
485
|
return;
|
|
612
486
|
}
|
|
613
487
|
if (metadata.disposition === "blocked" && task.status !== "blocked" && Array.isArray(metadata.blockedBy) && metadata.blockedBy.length > 0) {
|
|
614
|
-
setTaskEntityStatus(task.id, {
|
|
488
|
+
await setTaskEntityStatus(task.id, {
|
|
615
489
|
status: "blocked",
|
|
616
490
|
blockedBy: metadata.blockedBy,
|
|
617
491
|
...(typeof metadata.reason === "string" && metadata.reason.trim() ? { note: metadata.reason } : {}),
|
|
@@ -643,46 +517,9 @@ async function restoreDeliveredDispositionAsync(task: TaskDetail, event: TaskEve
|
|
|
643
517
|
// #889 — finalize bound tasks for a reaped agent whose doneWhen is "agent-exit" / "reaped".
|
|
644
518
|
// Called by the transient reaper after requesting shutdown so ephemeral run-and-exit workers
|
|
645
519
|
// don't leave their auto-scaffolded Task stuck in_progress forever.
|
|
646
|
-
// Never overrides an already-terminal status. Idempotent (reapedAt guard inside
|
|
647
|
-
export function finalizeEphemeralTasksOnReap(agentId: string, opts: { spawnRequestId?: string; reason?: string; producedWork?: boolean; evidence?: string } = {}): number[] {
|
|
648
|
-
|
|
649
|
-
for (const id of deliverableTaskIdsByOwnerAgent(agentId)) ids.add(id);
|
|
650
|
-
if (opts.spawnRequestId) {
|
|
651
|
-
for (const id of deliverableTaskIdsBySpawnRequestId(opts.spawnRequestId)) ids.add(id);
|
|
652
|
-
}
|
|
653
|
-
const finalized: number[] = [];
|
|
654
|
-
for (const taskId of ids) {
|
|
655
|
-
const task = getTaskDetail(taskId);
|
|
656
|
-
if (!task) continue;
|
|
657
|
-
if (task.status === "done" || task.status === "failed" || task.status === "canceled") continue;
|
|
658
|
-
if (!isAgentExitDoneWhen(task.doneWhen)) continue;
|
|
659
|
-
if (opts.producedWork === false) {
|
|
660
|
-
try {
|
|
661
|
-
const deliverable = latestDeliverableEvent(taskId);
|
|
662
|
-
if (deliverable) {
|
|
663
|
-
if (!task.reapedAt) markTaskAgentReaped(taskId, { agentId, reason: opts.reason ?? "agent-exit" });
|
|
664
|
-
restoreDeliveredDisposition(task, deliverable);
|
|
665
|
-
finalized.push(taskId);
|
|
666
|
-
continue;
|
|
667
|
-
}
|
|
668
|
-
if (!task.reapedAt) markTaskAgentReaped(taskId, { agentId, reason: opts.reason ?? "agent-exit" });
|
|
669
|
-
setTaskEntityStatus(taskId, { status: "canceled", note: "worker reaped without landing or producing work; task canceled", actor: "task-lifecycle" });
|
|
670
|
-
notifyEmptyTransientReap(agentId, task, opts);
|
|
671
|
-
finalized.push(taskId);
|
|
672
|
-
} catch {
|
|
673
|
-
// best-effort; failures must not surface into the reaper scan
|
|
674
|
-
}
|
|
675
|
-
continue;
|
|
676
|
-
}
|
|
677
|
-
try {
|
|
678
|
-
if (!task.reapedAt) markTaskAgentReaped(taskId, { agentId, reason: opts.reason ?? "agent-exit" });
|
|
679
|
-
setTaskEntityStatus(taskId, { status: validationCompletionStatus(task.validationItems), note: "doneWhen=agent-exit satisfied by worker reap", actor: "task-lifecycle" });
|
|
680
|
-
finalized.push(taskId);
|
|
681
|
-
} catch {
|
|
682
|
-
// best-effort; failures must not surface into the reaper scan
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
return finalized;
|
|
520
|
+
// Never overrides an already-terminal status. Idempotent (reapedAt guard inside markTaskAgentReapedAsync).
|
|
521
|
+
export async function finalizeEphemeralTasksOnReap(agentId: string, opts: { spawnRequestId?: string; reason?: string; producedWork?: boolean; evidence?: string } = {}): Promise<number[]> {
|
|
522
|
+
return finalizeEphemeralTasksOnReapAsync(agentId, opts);
|
|
686
523
|
}
|
|
687
524
|
|
|
688
525
|
/** The off-loop twin of finalizeEphemeralTasksOnReap (S3.2 L-D2 #1904) — the sanctioned scheduled-job
|
|
@@ -710,7 +547,7 @@ export async function finalizeEphemeralTasksOnReapAsync(agentId: string, opts: {
|
|
|
710
547
|
}
|
|
711
548
|
if (!task.reapedAt) await markTaskAgentReapedAsync(taskId, { agentId, reason: opts.reason ?? "agent-exit" });
|
|
712
549
|
await setTaskEntityStatusAsync(taskId, { status: "canceled", note: "worker reaped without landing or producing work; task canceled", actor: "task-lifecycle" });
|
|
713
|
-
notifyEmptyTransientReap(agentId, task, opts);
|
|
550
|
+
await notifyEmptyTransientReap(agentId, task, opts);
|
|
714
551
|
finalized.push(taskId);
|
|
715
552
|
} catch {
|
|
716
553
|
// best-effort; failures must not surface into the reaper scan
|
|
@@ -728,7 +565,7 @@ export async function finalizeEphemeralTasksOnReapAsync(agentId: string, opts: {
|
|
|
728
565
|
return finalized;
|
|
729
566
|
}
|
|
730
567
|
|
|
731
|
-
function notifyEmptyTransientReap(agentId: string, task: TaskDetail, opts: { spawnRequestId?: string; reason?: string; evidence?: string }): void {
|
|
568
|
+
async function notifyEmptyTransientReap(agentId: string, task: TaskDetail, opts: { spawnRequestId?: string; reason?: string; evidence?: string }): Promise<void> {
|
|
732
569
|
const agent = getAgent(agentId);
|
|
733
570
|
const parent = agent?.spawnedBy;
|
|
734
571
|
const recipients = [...new Set([parent, taskOwnerTarget(task)].filter((value): value is string => Boolean(value && value.trim())))];
|
|
@@ -743,7 +580,7 @@ function notifyEmptyTransientReap(agentId: string, task: TaskDetail, opts: { spa
|
|
|
743
580
|
evidence: opts.evidence ?? "no-produced-work",
|
|
744
581
|
};
|
|
745
582
|
emitRelayEvent({ type: "agent.transient_empty_reap", source: "task-lifecycle", subject: agentId, data: payload });
|
|
746
|
-
routeNotification({
|
|
583
|
+
await routeNotification({
|
|
747
584
|
type: "agent.transient_empty_reap",
|
|
748
585
|
scope: { agentId, parent, taskId: String(task.id) },
|
|
749
586
|
recipients,
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { addTaskRefAsync } from "../db/task-entity-async";
|
|
2
|
+
import { insertTaskEventAsync } from "../db/runtime-tail-async";
|
|
1
3
|
// #834/#2214 — the Task ↔ issue ATTACH seam: everything that turns "this Task is about issue N"
|
|
2
4
|
// into a real `issue_associations` row, split out of the task-entity service (which owns status /
|
|
3
5
|
// stage / gates / attribution) so both stay under the file-size ratchet and this rule has one home.
|
|
@@ -11,18 +13,9 @@
|
|
|
11
13
|
// - after create — `relay_task_add_ref` / `POST /api/tasks/:id/refs` via attachTaskRefs.
|
|
12
14
|
// Both resolve a bare `#N` through the same default-repo chain (see issueRepoForCreate).
|
|
13
15
|
import { errMessage } from "@humwick/sdk";
|
|
14
|
-
import {
|
|
15
|
-
addTaskRef,
|
|
16
|
-
addTaskRefAsync,
|
|
17
|
-
getTaskDetail,
|
|
18
|
-
getTaskDetailAsync,
|
|
19
|
-
ValidationError,
|
|
20
|
-
type AddTaskRefInput,
|
|
21
|
-
type CreateDeliverableTaskInput,
|
|
22
|
-
type TaskEntityWriteOptions,
|
|
23
|
-
} from "../db";
|
|
16
|
+
import { getTaskDetail, getTaskDetailAsync, ValidationError, type AddTaskRefInput, type CreateDeliverableTaskInput, type TaskEntityWriteOptions } from "../db";
|
|
24
17
|
import { isBareIssueRef } from "../db/issue-refs";
|
|
25
|
-
|
|
18
|
+
|
|
26
19
|
import { resolveActorIssueRepo } from "../agent-issue-repo";
|
|
27
20
|
import { emitTaskChanged } from "../sse";
|
|
28
21
|
import { cleanIssueRefRelation } from "../db/task-issue-relation";
|
|
@@ -54,31 +47,31 @@ export function issueRepoForCreate(input: CreateTaskEntityInput): string | undef
|
|
|
54
47
|
// A ref the caller ASKED for that could not be linked must not vanish into a server log: record it
|
|
55
48
|
// on the Task's own ledger so it shows up in history (and in a spawned worker's hydrated brief).
|
|
56
49
|
// Creation itself still succeeds — a Task without its issue link is degraded, not invalid.
|
|
57
|
-
function recordIssueLinkFailure(taskId: number, issue: string, defaultRepo: string | undefined, error: unknown): void {
|
|
50
|
+
async function recordIssueLinkFailure(taskId: number, issue: string, defaultRepo: string | undefined, error: unknown): Promise<void> {
|
|
58
51
|
const message = errMessage(error);
|
|
59
52
|
console.warn(`task ${taskId} issue ref '${issue}' could not be linked on create: ${message}`);
|
|
60
53
|
try {
|
|
61
|
-
|
|
54
|
+
(await insertTaskEventAsync(taskId, {
|
|
62
55
|
source: "agent-relay",
|
|
63
56
|
type: "ref.issue.link_failed",
|
|
64
57
|
severity: "warning",
|
|
65
58
|
title: `Issue ref '${issue}' could not be linked`,
|
|
66
59
|
body: message,
|
|
67
60
|
metadata: { issue, ...(defaultRepo ? { defaultRepo } : {}) },
|
|
68
|
-
}, Date.now());
|
|
61
|
+
}, Date.now()));
|
|
69
62
|
} catch (eventError) {
|
|
70
63
|
console.warn(`task ${taskId} could not record issue-link failure: ${errMessage(eventError)}`);
|
|
71
64
|
}
|
|
72
65
|
}
|
|
73
66
|
|
|
74
|
-
export function addInitialIssueRefs(taskId: number, input: CreateTaskEntityInput): TaskDetail {
|
|
67
|
+
export async function addInitialIssueRefs(taskId: number, input: CreateTaskEntityInput): Promise<TaskDetail> {
|
|
75
68
|
const defaultRepo = issueRepoForCreate(input);
|
|
76
69
|
let detail = getTaskDetail(taskId)!;
|
|
77
70
|
for (const { issue, relation } of initialIssueRefs(input)) {
|
|
78
71
|
try {
|
|
79
|
-
detail =
|
|
72
|
+
detail = (await addTaskRefAsync(taskId, { issue, relation, defaultRepo, actor: input.createdBy }));
|
|
80
73
|
} catch (error) {
|
|
81
|
-
recordIssueLinkFailure(taskId, issue, defaultRepo, error);
|
|
74
|
+
(await recordIssueLinkFailure(taskId, issue, defaultRepo, error));
|
|
82
75
|
}
|
|
83
76
|
}
|
|
84
77
|
return detail;
|
|
@@ -95,7 +88,7 @@ export async function addInitialIssueRefsAsync(taskId: number, input: CreateTask
|
|
|
95
88
|
...(options?.idempotencyKey ? { idempotencyKey: `${options.idempotencyKey}:initial-ref:${index}` } : {}),
|
|
96
89
|
});
|
|
97
90
|
} catch (error) {
|
|
98
|
-
recordIssueLinkFailure(taskId, issue, defaultRepo, error);
|
|
91
|
+
(await recordIssueLinkFailure(taskId, issue, defaultRepo, error));
|
|
99
92
|
}
|
|
100
93
|
}
|
|
101
94
|
return detail;
|
|
@@ -115,8 +108,8 @@ function needsIssueRepo(input: AddTaskRefInput): boolean {
|
|
|
115
108
|
return isBareIssueRef(input.issue) && !input.defaultRepo?.trim();
|
|
116
109
|
}
|
|
117
110
|
|
|
118
|
-
export function addTaskEntityRef(id: number, input: AddTaskRefInput): TaskDetail {
|
|
119
|
-
const detail =
|
|
111
|
+
export async function addTaskEntityRef(id: number, input: AddTaskRefInput): Promise<TaskDetail> {
|
|
112
|
+
const detail = (await addTaskRefAsync(id, needsIssueRepo(input) ? withResolvedIssueRepo(input, getTaskDetail(id)) : input));
|
|
120
113
|
emitTaskChanged(detail, "task.updated");
|
|
121
114
|
return detail;
|
|
122
115
|
}
|