@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
package/src/db/messages.ts
CHANGED
|
@@ -59,9 +59,9 @@ export { runningAgentForPolicy } from "./policy-runtime.ts";
|
|
|
59
59
|
|
|
60
60
|
export function findMessageByIdempotencyKey(from: string, key: string): Message | null {
|
|
61
61
|
const row = getDb()
|
|
62
|
-
.query(
|
|
63
|
-
.get(from, key) as
|
|
64
|
-
return row ?
|
|
62
|
+
.query("SELECT m.id FROM messages m WHERE m.from_agent = ? AND m.idempotency_key = ? LIMIT 1")
|
|
63
|
+
.get(from, key) as { id: number } | null;
|
|
64
|
+
return row ? getMessage(row.id) : null;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
function sessionStepId(payload: Record<string, unknown> | undefined): string | undefined {
|
|
@@ -162,7 +162,7 @@ function findRecentDuplicateReply(input: SendMessageInput, threadId: number | nu
|
|
|
162
162
|
if (!input.replyTo || threadId === null || hasAttachments) return null;
|
|
163
163
|
const kind = inferMessageKind(input);
|
|
164
164
|
const row = getDb().query(`
|
|
165
|
-
|
|
165
|
+
SELECT m.id FROM messages m
|
|
166
166
|
WHERE m.from_agent = ?
|
|
167
167
|
AND m.to_target = ?
|
|
168
168
|
AND m.kind = ?
|
|
@@ -183,8 +183,8 @@ function findRecentDuplicateReply(input: SendMessageInput, threadId: number | nu
|
|
|
183
183
|
input.subject ?? null,
|
|
184
184
|
input.channel ?? null,
|
|
185
185
|
now - REPLY_DUPLICATE_WINDOW_MS,
|
|
186
|
-
) as
|
|
187
|
-
return row ?
|
|
186
|
+
) as { id: number } | null;
|
|
187
|
+
return row ? getMessage(row.id) : null;
|
|
188
188
|
}
|
|
189
189
|
|
|
190
190
|
function sessionMeta(input: Message | SendMessageInput): Record<string, unknown> | undefined {
|
|
@@ -252,8 +252,8 @@ function findLineageReportByTurn(input: {
|
|
|
252
252
|
lineageKey,
|
|
253
253
|
`response-report:${input.from}:%:${input.turnId}`,
|
|
254
254
|
input.turnId,
|
|
255
|
-
) as
|
|
256
|
-
return row ?
|
|
255
|
+
) as { id: number } | null;
|
|
256
|
+
return row ? getMessage(row.id) : null;
|
|
257
257
|
}
|
|
258
258
|
|
|
259
259
|
function findRecentLineageReport(input: SendMessageInput, now: number): Message | null {
|
|
@@ -275,8 +275,8 @@ function findRecentLineageReport(input: SendMessageInput, now: number): Message
|
|
|
275
275
|
if (duplicate) return duplicate;
|
|
276
276
|
}
|
|
277
277
|
if (responseCapture) return null;
|
|
278
|
-
const row = getDb().query(RECENT_LINEAGE_REPORT_SQL).get(from, to, input.body, now - LINEAGE_REPORT_DUPLICATE_WINDOW_MS, `response-report:${from}:%`) as
|
|
279
|
-
return row ?
|
|
278
|
+
const row = getDb().query(RECENT_LINEAGE_REPORT_SQL).get(from, to, input.body, now - LINEAGE_REPORT_DUPLICATE_WINDOW_MS, `response-report:${from}:%`) as { id: number } | null;
|
|
279
|
+
return row ? getMessage(row.id) : null;
|
|
280
280
|
}
|
|
281
281
|
|
|
282
282
|
// T4458 (Edin's rule #2844 1a) — the sender's currently-open provider turn: the newest
|
|
@@ -340,10 +340,10 @@ function findSameTurnExplicitReport(input: {
|
|
|
340
340
|
input.to,
|
|
341
341
|
...(input.turnId ? [input.from, input.turnId, fallback] : [fallback]),
|
|
342
342
|
input.at + LINEAGE_REPORT_DUPLICATE_WINDOW_MS,
|
|
343
|
-
) as
|
|
343
|
+
) as Array<{ id: number; body: string | null }>;
|
|
344
344
|
for (const row of rows) {
|
|
345
345
|
const explicit = normBody(String(row?.body ?? ""));
|
|
346
|
-
if (explicit.length >= SAME_TURN_MIN_CHARS && explicit.includes(body)) return
|
|
346
|
+
if (explicit.length >= SAME_TURN_MIN_CHARS && explicit.includes(body)) return getMessage(row.id);
|
|
347
347
|
}
|
|
348
348
|
return null;
|
|
349
349
|
}
|
|
@@ -361,9 +361,9 @@ function findTurnExplicitReport(input: {
|
|
|
361
361
|
captureLen: number;
|
|
362
362
|
captureOccurredAt: number;
|
|
363
363
|
}): Message | null {
|
|
364
|
-
const row = getDb().query(TURN_EXPLICIT_REPORT_SQL).get(input.from, input.to, input.turnId, input.before) as
|
|
365
|
-
|
|
366
|
-
|
|
364
|
+
const row = getDb().query(TURN_EXPLICIT_REPORT_SQL).get(input.from, input.to, input.turnId, input.before) as { id: number } | null;
|
|
365
|
+
const message = row ? getMessage(row.id) : null;
|
|
366
|
+
if (!message) return null;
|
|
367
367
|
const explicitLen = normBody(String(message.body ?? "")).length;
|
|
368
368
|
if (explicitLen < SAME_TURN_MIN_CHARS || explicitLen < 0.5 * input.captureLen) return null;
|
|
369
369
|
// MED-1 (r1 review): a stamp is turn inference and can go stale — t1's late final can land
|
|
@@ -409,8 +409,8 @@ function findRecentExplicitLineageDuplicate(input: {
|
|
|
409
409
|
input.to,
|
|
410
410
|
input.body,
|
|
411
411
|
input.since,
|
|
412
|
-
) as
|
|
413
|
-
return row ?
|
|
412
|
+
) as { id: number } | null;
|
|
413
|
+
return row ? getMessage(row.id) : null;
|
|
414
414
|
}
|
|
415
415
|
|
|
416
416
|
// #1084/#3304 — reporting a definitive provider completion does not close its work window.
|
|
@@ -418,9 +418,14 @@ function findRecentExplicitLineageDuplicate(input: {
|
|
|
418
418
|
const LINEAGE_FINAL_GROUP_RECENCY_MS = 10 * 60 * 1000;
|
|
419
419
|
export { __resetDeferredLineageReports, restoreDeferredLineageReports, takeDeferredLineageReports } from "./deferred-lineage-reports.ts";
|
|
420
420
|
|
|
421
|
+
export interface LineageReportResult {
|
|
422
|
+
message: Message;
|
|
423
|
+
created: boolean;
|
|
424
|
+
lifecycleSignals?: Array<{ recipient: string; subjectKey: string }>;
|
|
425
|
+
}
|
|
421
426
|
export interface WorkFinalFlushResult {
|
|
422
427
|
/** The chat report(s) forwarded (or found already-forwarded) to the spawner, oldest first. */
|
|
423
|
-
forwarded:
|
|
428
|
+
forwarded: LineageReportResult[];
|
|
424
429
|
/** The original session-mirror captures retroactively tagged `payload.finality` (for the
|
|
425
430
|
* `message.finalized` SSE upgrade of already-rendered bubbles). */
|
|
426
431
|
finalizedOriginals: Message[];
|
|
@@ -533,12 +538,11 @@ function edgeFallbackReportScore(body: string): number {
|
|
|
533
538
|
// redundant "Task status pulse" for the same child/task is suppressed rather than double-waking.
|
|
534
539
|
// #1038 — a report-up conveys "the child is progressing", so it is state-keyed to `working`: it
|
|
535
540
|
// supersedes only a redundant `working` pulse, never a later distinct milestone (committed/landed/done).
|
|
536
|
-
function
|
|
537
|
-
stampLifecycleSignal(
|
|
538
|
-
for (const taskId of childTaskIds(child)) stampLifecycleSignal(parent, taskSignalKey(taskId, LIFECYCLE_STATE_WORKING));
|
|
541
|
+
export function applyLineageReportEffects(result: LineageReportResult | null): void {
|
|
542
|
+
for (const signal of result?.lifecycleSignals ?? []) stampLifecycleSignal(signal.recipient, signal.subjectKey);
|
|
539
543
|
}
|
|
540
544
|
|
|
541
|
-
export function promoteLineageCapturedResponse(message: Message
|
|
545
|
+
export function promoteLineageCapturedResponse(message: Message, deferred?: Array<{ childId: string; messageId: number }>): LineageReportResult | null {
|
|
542
546
|
if (message.kind !== "session" || isReportSuppressed(message)) return null;
|
|
543
547
|
const session = sessionMeta(message);
|
|
544
548
|
if (!session) return null;
|
|
@@ -556,7 +560,8 @@ export function promoteLineageCapturedResponse(message: Message): { message: Mes
|
|
|
556
560
|
// #3304 — provider completion is enough to report a turn, without declaring the Task done.
|
|
557
561
|
// Keep the ring for later work-final metadata; addressed captures already have a runner reply.
|
|
558
562
|
if (stream === "responses" && verbosity !== "verbose") {
|
|
559
|
-
|
|
563
|
+
if (deferred) deferred.push({ childId: child.id, messageId: message.id });
|
|
564
|
+
else deferLineageReport(child.id, message.id);
|
|
560
565
|
if (session.confidence !== "definitive" || session.endedBy !== "completed" || message.replyTo) return null;
|
|
561
566
|
if (lineageDeliveryCoversCapture(message, child, parent, message.createdAt)) return null;
|
|
562
567
|
}
|
|
@@ -576,7 +581,7 @@ function forwardLineageStreamCapture(
|
|
|
576
581
|
child: AgentCard,
|
|
577
582
|
parent: string,
|
|
578
583
|
finality?: MessageWorkFinality,
|
|
579
|
-
):
|
|
584
|
+
): LineageReportResult | null {
|
|
580
585
|
const parentAgentId = parent;
|
|
581
586
|
parent = canonicalLineageReportTarget(child, parent);
|
|
582
587
|
const idempotencyKey = lineageStreamReportKey(message, session, stream);
|
|
@@ -679,7 +684,8 @@ function forwardLineageStreamCapture(
|
|
|
679
684
|
occurredAt: message.occurredAt ?? message.createdAt,
|
|
680
685
|
});
|
|
681
686
|
if (stream === "responses" && result.created) {
|
|
682
|
-
|
|
687
|
+
return { ...result, lifecycleSignals: [childSignalKey(child.id), ...childTaskIds(child).map(id => taskSignalKey(id, LIFECYCLE_STATE_WORKING))]
|
|
688
|
+
.map(subjectKey => ({ recipient: parentAgentId, subjectKey })) };
|
|
683
689
|
}
|
|
684
690
|
return result;
|
|
685
691
|
}
|
package/src/db/migrations.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { COMMAND_RETENTION_SCHEMA, ensureCommandRetentionReferenceIndexes } from "./command-retention";
|
|
2
|
+
import { migrateCheckoutRefreshHeads } from "./checkout-refresh-schema";
|
|
3
|
+
import { TOKEN_USAGE_RETENTION_SCHEMA } from "./token-usage-retention";
|
|
1
4
|
import { PLAN_DISPATCH_SCHEMA_SQL } from "./plan-dispatch-schema";
|
|
2
5
|
import { PLAN_IMPORT_SCHEMA_SQL } from "./plan-import-schema";
|
|
3
6
|
import { ensureDurablePayloadSchema } from "./durable-payloads"; import { redactProviderConfigHistory } from "./provider-config-history"; import { retireMemoryStorage } from "./memory-retirement";
|
|
@@ -66,6 +69,7 @@ import { migrateTeamProjectSeats } from "./team-project.ts";
|
|
|
66
69
|
import { ensureArtifactCreatorAgentColumn } from "./artifacts.ts";
|
|
67
70
|
import { rewriteEmptyToolGroupsToAbsent } from "./tool-groups-empty-migration.ts";
|
|
68
71
|
import { backfillProjectMemoryToolGroup } from "./project-memory-group-migration.ts";
|
|
72
|
+
import { backfillPlanToolsIntoToolIncludes } from "./plan-tools-include-migration.ts";
|
|
69
73
|
import { removeLegacyProfileProjectInstructions } from "./profile-legacy-project-instructions-migration.ts";
|
|
70
74
|
import { migrateProfileHostSources } from "./profile-host-sources-migration.ts";
|
|
71
75
|
import { migrateProfileProjectAcks } from "./profile-project-ack-migration.ts";
|
|
@@ -169,7 +173,15 @@ export function applyMigrations(): void {
|
|
|
169
173
|
)
|
|
170
174
|
`);
|
|
171
175
|
getDb().run("CREATE INDEX IF NOT EXISTS idx_issue_refs_external ON issue_refs(provider, repo, external_key)");
|
|
172
|
-
|
|
176
|
+
// #3799 — matches the listing ORDER BY exactly, so a list is a bounded index walk with no sort; supersedes (updated_at DESC).
|
|
177
|
+
getDb().run("CREATE INDEX IF NOT EXISTS idx_issue_refs_listing ON issue_refs(updated_at DESC, created_at DESC, id DESC)");
|
|
178
|
+
getDb().run("DROP INDEX IF EXISTS idx_issue_refs_updated");
|
|
179
|
+
// #3799 — automation_runs list/scan indexes: status+created_at supersedes the plain status index, created_at serves the
|
|
180
|
+
// unfiltered listing, and the partial index holds exactly the open task-bound runs the reconcile pass walks.
|
|
181
|
+
getDb().run("CREATE INDEX IF NOT EXISTS idx_automation_runs_status_created ON automation_runs(status, created_at)");
|
|
182
|
+
getDb().run("DROP INDEX IF EXISTS idx_automation_runs_status");
|
|
183
|
+
getDb().run("CREATE INDEX IF NOT EXISTS idx_automation_runs_created ON automation_runs(created_at DESC)");
|
|
184
|
+
getDb().run("CREATE INDEX IF NOT EXISTS idx_automation_runs_open ON automation_runs(created_at) WHERE status IN ('scheduled', 'dispatching', 'waiting_agent', 'running') AND task_id IS NOT NULL");
|
|
173
185
|
// #2520 — the GitHub close classification (completed|not_planned) cached beside the status so
|
|
174
186
|
// plan-node terminal propagation can tell a completed close from a not-planned one. The guarded
|
|
175
187
|
// ALTER converges databases created before the column existed with the CREATE above.
|
|
@@ -666,6 +678,7 @@ export function applyMigrations(): void {
|
|
|
666
678
|
ensureIngestedMemoryIndex();
|
|
667
679
|
rewriteEmptyToolGroupsToAbsent(); // #1963 N2 — persisted relay.tools.groups:[] meant ALL; honoring [] as NONE requires rewriting existing [] → absent first
|
|
668
680
|
backfillProjectMemoryToolGroup(); // #2154 follow-up — stored/overridden profiles do not pick up the built-in `project-memory` group on their own
|
|
681
|
+
backfillPlanToolsIntoToolIncludes(); // #3812 — lean per-tool allowlists (coordinator) never gained relay_plan_start/import
|
|
669
682
|
removeLegacyProfileProjectInstructions(); // #2133 — remove retired profile policy fields before profile override migration reads legacy rows
|
|
670
683
|
backfillCustomProfileArtifacts(); // #3171 — Artifact is off by default now; keep the approved custom brainstorm exception
|
|
671
684
|
migrateAgentProfileOverridesToV2(); // #1963 N10 — back up + migrate persisted override rows to the v2 matrix (D-T=off; G6/G7 leave-untouched-and-report; flag-gated rollback)
|
|
@@ -1733,6 +1746,10 @@ export function applyMigrations(): void {
|
|
|
1733
1746
|
getDb().run(AGENT_EXIT_REPORT_SCHEMA);
|
|
1734
1747
|
getDb().run(CONTEXT_RESET_SCHEMA); // #3398
|
|
1735
1748
|
getDb().run(TASK_TOKEN_TOTALS_SCHEMA);
|
|
1749
|
+
getDb().run(TOKEN_USAGE_RETENTION_SCHEMA);
|
|
1750
|
+
getDb().run(COMMAND_RETENTION_SCHEMA);
|
|
1751
|
+
migrateCheckoutRefreshHeads(getDb());
|
|
1752
|
+
ensureCommandRetentionReferenceIndexes(getDb());
|
|
1736
1753
|
getDb().run(SHIPPING_STATS_SCHEMA);
|
|
1737
1754
|
getDb().run(CI_GATE_BACKFILL_SCHEMA); getDb().run(AGENT_GATE_RUNS_SCHEMA); ensureAgentGateCommandSchema(); repairRerunGateTimings(); ensureCiShaObservationsSchema();
|
|
1738
1755
|
migrateOperatorActionsSchema();
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { sendMessageWithResultInternal } from "./messages";
|
|
2
|
+
import { insertAgentNoticeSlot, type AgentNoticeSlot } from "./terminal-events";
|
|
3
|
+
import type { SendMessageInput } from "../types";
|
|
4
|
+
import type { WriterOpContext, WriterOpSpec } from "./write-ops";
|
|
5
|
+
|
|
6
|
+
const send = (inputs: SendMessageInput[], now: number) => inputs.map(input => sendMessageWithResultInternal(input, false, {}, now));
|
|
7
|
+
export const NOTIFICATION_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze({
|
|
8
|
+
"notifications.route": {
|
|
9
|
+
offLoop: true, idempotent: false,
|
|
10
|
+
fn: ({ inputs }: { inputs: SendMessageInput[] }, ctx: WriterOpContext) => send(inputs, ctx.now),
|
|
11
|
+
},
|
|
12
|
+
"notifications.agentNotice": {
|
|
13
|
+
offLoop: true, idempotent: false,
|
|
14
|
+
fn: ({ slot, inputs }: { slot: AgentNoticeSlot; inputs: SendMessageInput[] }, ctx: WriterOpContext) => {
|
|
15
|
+
const created = insertAgentNoticeSlot(slot, ctx.now);
|
|
16
|
+
return { created, results: created ? send(inputs, ctx.now) : [] };
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { answerOpenOperatorDecision, getOperatorDecision, getWorkflowOperatorDecision, insertOperatorDecision, setOperatorDecisionAnswerMessage } from "./operator-decisions";
|
|
2
|
+
import { sendMessageWithResultInternal } from "./messages";
|
|
3
|
+
import type { SendMessageInput } from "../types";
|
|
4
|
+
export interface OperatorAnswerWrite {
|
|
5
|
+
id: number;
|
|
6
|
+
answer: Parameters<typeof answerOpenOperatorDecision>[1];
|
|
7
|
+
message: SendMessageInput;
|
|
8
|
+
}
|
|
9
|
+
export function answerOperatorDecisionBody({ id, answer, message }: OperatorAnswerWrite) {
|
|
10
|
+
if (!answerOpenOperatorDecision(id, answer)) return null;
|
|
11
|
+
const result = sendMessageWithResultInternal(message, false, {}, answer.now);
|
|
12
|
+
setOperatorDecisionAnswerMessage(id, result.message.id);
|
|
13
|
+
return { decision: getOperatorDecision(id)!, result };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function fileWorkflowDecisionBody(input: Parameters<typeof insertOperatorDecision>[0]) {
|
|
17
|
+
const existing = getWorkflowOperatorDecision(input.workflow!.effectId);
|
|
18
|
+
return existing ? { decision: existing, created: false } : { decision: insertOperatorDecision(input), created: true };
|
|
19
|
+
}
|
package/src/db/orchestrators.ts
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
type RuntimeContracts,
|
|
12
12
|
} from "../contracts";
|
|
13
13
|
import { STALE_TTL_MS } from "../config";
|
|
14
|
-
import {
|
|
14
|
+
import { deleteAgentInternal, heartbeat, upsertAgent, upsertAgentInternal, type UpsertAgentTxResult } from "./agents.ts";
|
|
15
15
|
import { evictPendingPresence } from "./presence-buffer.ts";
|
|
16
16
|
import { getDb, writeTransaction } from "./connection.ts";
|
|
17
17
|
import { fencedColumnSql, nextLifecycleSeq } from "./lifecycle-seq.ts";
|
|
@@ -386,11 +386,17 @@ export function reportOrchestratorRosterRow(id: string, agents: ManagedAgent[],
|
|
|
386
386
|
}
|
|
387
387
|
|
|
388
388
|
export function deleteOrchestrator(id: string): boolean {
|
|
389
|
+
const result = deleteOrchestratorRow(id, Date.now());
|
|
390
|
+
if (result.agentId) evictPendingPresence(result.agentId);
|
|
391
|
+
return result.deleted;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
export function deleteOrchestratorRow(id: string, now: number): { deleted: boolean; agentId?: string } {
|
|
389
395
|
const orch = getOrchestrator(id);
|
|
390
|
-
if (!orch) return false;
|
|
396
|
+
if (!orch) return { deleted: false };
|
|
391
397
|
getDb().query("DELETE FROM orchestrators WHERE id = ?").run(id);
|
|
392
|
-
|
|
393
|
-
return true;
|
|
398
|
+
deleteAgentInternal(orch.agentId, now);
|
|
399
|
+
return { deleted: true, agentId: orch.agentId };
|
|
394
400
|
}
|
|
395
401
|
|
|
396
402
|
export function hasStaleOrchestrator(cutoff: number): boolean {
|
package/src/db/pairs-async.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import type { createPair, acceptPair, rejectPair, endPair, sendPairMessage, getPair, listPairs } from "./pairs";
|
|
1
3
|
import { getGateway } from "./gateway";
|
|
2
|
-
import type { getPair, listPairs } from "./pairs";
|
|
3
4
|
|
|
4
5
|
export function getPairAsync(id: string): Promise<ReturnType<typeof getPair>> {
|
|
5
6
|
return getGateway().write("pairs.getCurrent", { id }) as Promise<ReturnType<typeof getPair>>;
|
|
@@ -8,3 +9,23 @@ export function getPairAsync(id: string): Promise<ReturnType<typeof getPair>> {
|
|
|
8
9
|
export function listPairsAsync(filter?: Parameters<typeof listPairs>[0]): Promise<ReturnType<typeof listPairs>> {
|
|
9
10
|
return getGateway().write("pairs.listCurrent", filter) as unknown as Promise<ReturnType<typeof listPairs>>;
|
|
10
11
|
}
|
|
12
|
+
|
|
13
|
+
export function createPairAsync(...args: Parameters<typeof createPair>): Promise<ReturnType<typeof createPair>> {
|
|
14
|
+
return getGateway().write("pairs.createPair", args, { idempotencyKey: randomUUID() }) as unknown as Promise<ReturnType<typeof createPair>>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function acceptPairAsync(...args: Parameters<typeof acceptPair>): Promise<ReturnType<typeof acceptPair>> {
|
|
18
|
+
return getGateway().write("pairs.acceptPair", args, { idempotencyKey: randomUUID() }) as unknown as Promise<ReturnType<typeof acceptPair>>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function rejectPairAsync(...args: Parameters<typeof rejectPair>): Promise<ReturnType<typeof rejectPair>> {
|
|
22
|
+
return getGateway().write("pairs.rejectPair", args, { idempotencyKey: randomUUID() }) as unknown as Promise<ReturnType<typeof rejectPair>>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function endPairAsync(...args: Parameters<typeof endPair>): Promise<ReturnType<typeof endPair>> {
|
|
26
|
+
return getGateway().write("pairs.endPair", args, { idempotencyKey: randomUUID() }) as unknown as Promise<ReturnType<typeof endPair>>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function sendPairMessageAsync(...args: Parameters<typeof sendPairMessage>): Promise<ReturnType<typeof sendPairMessage>> {
|
|
30
|
+
return getGateway().write("pairs.sendPairMessage", args, { idempotencyKey: randomUUID() }) as unknown as Promise<ReturnType<typeof sendPairMessage>>;
|
|
31
|
+
}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
import { getPair, listPairs } from "./pairs";
|
|
1
|
+
import { createPair, acceptPair, rejectPair, endPair, sendPairMessage, getPair, listPairs } from "./pairs";
|
|
2
2
|
import type { WriterOpSpec } from "./write-ops";
|
|
3
3
|
|
|
4
4
|
// Pair reads settle expired requests before returning the current view.
|
|
5
5
|
export const PAIRS_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = {
|
|
6
|
-
"pairs.getCurrent": { idempotent: true, offLoop: true, fn: (args: { id: string }) => getPair(args.id) },
|
|
7
|
-
"pairs.listCurrent": { idempotent: true, offLoop: true, fn: (args: Parameters<typeof listPairs>[0]) => listPairs(args) },
|
|
6
|
+
"pairs.getCurrent": { idempotent: true, offLoop: true, fn: (args: { id: string }, ctx) => getPair(args.id, ctx.now) },
|
|
7
|
+
"pairs.listCurrent": { idempotent: true, offLoop: true, fn: (args: Parameters<typeof listPairs>[0], ctx) => listPairs(args, ctx.now) },
|
|
8
|
+
"pairs.createPair": { offLoop: true, idempotent: false, fn: (a: Parameters<typeof createPair>, ctx) => createPair(a[0], a[1] ?? ctx.now) },
|
|
9
|
+
"pairs.acceptPair": { offLoop: true, idempotent: false, fn: (a: Parameters<typeof acceptPair>, ctx) => acceptPair(a[0], a[1], a[2] ?? ctx.now) },
|
|
10
|
+
"pairs.rejectPair": { offLoop: true, idempotent: false, fn: (a: Parameters<typeof rejectPair>, ctx) => rejectPair(a[0], a[1], a[2] ?? ctx.now) },
|
|
11
|
+
"pairs.endPair": { offLoop: true, idempotent: false, fn: (a: Parameters<typeof endPair>, ctx) => endPair(a[0], a[1], a[2] ?? ctx.now) },
|
|
12
|
+
"pairs.sendPairMessage": { offLoop: true, idempotent: false, fn: (a: Parameters<typeof sendPairMessage>, ctx) => sendPairMessage(a[0], a[1], a[2] ?? ctx.now) },
|
|
8
13
|
};
|
package/src/db/pairs.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { matchAgents } from "../agent-ref";
|
|
|
4
4
|
import { getAgent, listAgents } from "./agents.ts";
|
|
5
5
|
import { getDb } from "./connection.ts";
|
|
6
6
|
import { rowToPair } from "./mappers.ts";
|
|
7
|
-
import {
|
|
7
|
+
import { sendMessageWithResultInternal } from "./messages.ts";
|
|
8
8
|
export { closeOpenPairsForAgent } from "./pair-maintenance.ts";
|
|
9
9
|
import type {
|
|
10
10
|
AgentCard,
|
|
@@ -26,8 +26,8 @@ function expirePendingPairs(now: number = Date.now()): void {
|
|
|
26
26
|
.run(now, now, now);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
function getOpenPairForAgent(agentId: string): PairSession | null {
|
|
30
|
-
expirePendingPairs();
|
|
29
|
+
function getOpenPairForAgent(agentId: string, now: number = Date.now()): PairSession | null {
|
|
30
|
+
expirePendingPairs(now);
|
|
31
31
|
const row = getDb().query(`
|
|
32
32
|
SELECT * FROM pairs
|
|
33
33
|
WHERE status IN ('pending', 'active') AND (requester_id = ? OR target_id = ?)
|
|
@@ -45,7 +45,7 @@ function pairPeer(pair: PairSession, agentId: string): string {
|
|
|
45
45
|
return pair.requesterId === agentId ? pair.targetId : pair.requesterId;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
function resolvePairTarget(target: string, requesterId: string): {
|
|
48
|
+
function resolvePairTarget(target: string, requesterId: string, now: number = Date.now()): {
|
|
49
49
|
ok: true;
|
|
50
50
|
agent: AgentCard;
|
|
51
51
|
} | {
|
|
@@ -64,7 +64,7 @@ function resolvePairTarget(target: string, requesterId: string): {
|
|
|
64
64
|
const busy: Array<{ agent: AgentCard; pair: PairSession }> = [];
|
|
65
65
|
const available: AgentCard[] = [];
|
|
66
66
|
for (const agent of live) {
|
|
67
|
-
const openPair = getOpenPairForAgent(agent.id);
|
|
67
|
+
const openPair = getOpenPairForAgent(agent.id, now);
|
|
68
68
|
if (openPair) busy.push({ agent, pair: openPair });
|
|
69
69
|
else available.push(agent);
|
|
70
70
|
}
|
|
@@ -81,8 +81,8 @@ function resolvePairTarget(target: string, requesterId: string): {
|
|
|
81
81
|
return { ok: true, agent: available[0]! };
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
function pairSystemMessage(pair: PairSession, to: string, event: string, subject: string, body: string): Message {
|
|
85
|
-
return
|
|
84
|
+
function pairSystemMessage(pair: PairSession, to: string, event: string, subject: string, body: string, now: number = Date.now()): Message {
|
|
85
|
+
return sendMessageWithResultInternal({
|
|
86
86
|
from: "system",
|
|
87
87
|
to,
|
|
88
88
|
kind: "pair",
|
|
@@ -95,17 +95,17 @@ function pairSystemMessage(pair: PairSession, to: string, event: string, subject
|
|
|
95
95
|
targetId: pair.targetId,
|
|
96
96
|
pairStatus: pair.status,
|
|
97
97
|
},
|
|
98
|
-
});
|
|
98
|
+
}, false, {}, now).message;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
export function getPair(id: string): PairSession | null {
|
|
102
|
-
expirePendingPairs();
|
|
101
|
+
export function getPair(id: string, now: number = Date.now()): PairSession | null {
|
|
102
|
+
expirePendingPairs(now);
|
|
103
103
|
const row = getDb().query("SELECT * FROM pairs WHERE id = ?").get(id) as any;
|
|
104
104
|
return row ? rowToPair(row) : null;
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
export function listPairs(filter?: { agentId?: string; status?: PairStatus }): PairSession[] {
|
|
108
|
-
expirePendingPairs();
|
|
107
|
+
export function listPairs(filter?: { agentId?: string; status?: PairStatus }, now: number = Date.now()): PairSession[] {
|
|
108
|
+
expirePendingPairs(now);
|
|
109
109
|
const conditions: string[] = [];
|
|
110
110
|
const params: any[] = [];
|
|
111
111
|
if (filter?.agentId) {
|
|
@@ -120,7 +120,7 @@ export function listPairs(filter?: { agentId?: string; status?: PairStatus }): P
|
|
|
120
120
|
return (getDb().query(`SELECT * FROM pairs ${where} ORDER BY updated_at DESC LIMIT 100`).all(...params) as any[]).map(rowToPair);
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
export function createPair(input: CreatePairInput): {
|
|
123
|
+
export function createPair(input: CreatePairInput, now: number = Date.now()): {
|
|
124
124
|
ok: true;
|
|
125
125
|
pair: PairSession;
|
|
126
126
|
invite: Message;
|
|
@@ -131,25 +131,23 @@ export function createPair(input: CreatePairInput): {
|
|
|
131
131
|
matches?: AgentCard[];
|
|
132
132
|
busy?: Array<{ agent: AgentCard; pair: PairSession }>;
|
|
133
133
|
} {
|
|
134
|
-
expirePendingPairs();
|
|
134
|
+
expirePendingPairs(now);
|
|
135
135
|
const requester = getAgent(input.from);
|
|
136
136
|
if (!requester) return { ok: false, code: "invalid", error: `requester agent ${input.from} not registered` };
|
|
137
137
|
if (requester.status === "offline" || !requester.ready) return { ok: false, code: "offline", error: `requester agent ${input.from} is not online and ready` };
|
|
138
|
-
const requesterPair = getOpenPairForAgent(input.from);
|
|
138
|
+
const requesterPair = getOpenPairForAgent(input.from, now);
|
|
139
139
|
if (requesterPair) return { ok: false, code: "busy", error: `requester is already paired`, busy: [{ agent: requester, pair: requesterPair }] };
|
|
140
140
|
|
|
141
|
-
const resolved = resolvePairTarget(input.target, input.from);
|
|
141
|
+
const resolved = resolvePairTarget(input.target, input.from, now);
|
|
142
142
|
if (!resolved.ok) return resolved;
|
|
143
143
|
if (resolved.agent.id === input.from) return { ok: false, code: "invalid", error: "cannot pair with yourself" };
|
|
144
|
-
|
|
145
|
-
const now = Date.now();
|
|
146
144
|
const ttlMs = Math.min(Math.max(input.ttlMs ?? DEFAULT_PAIR_TTL_MS, 10_000), MAX_PAIR_TTL_MS);
|
|
147
145
|
const id = randomUUID();
|
|
148
146
|
getDb().query(`
|
|
149
147
|
INSERT INTO pairs (id, requester_id, target_id, status, objective, meta, created_at, updated_at, expires_at)
|
|
150
148
|
VALUES (?, ?, ?, 'pending', ?, ?, ?, ?, ?)
|
|
151
149
|
`).run(id, input.from, resolved.agent.id, input.objective ?? null, JSON.stringify(input.meta ?? {}), now, now, now + ttlMs);
|
|
152
|
-
const pair = getPair(id)!;
|
|
150
|
+
const pair = getPair(id, now)!;
|
|
153
151
|
const objective = pair.objective ? `\n\nObjective:\n${pair.objective}` : "";
|
|
154
152
|
const invite = pairSystemMessage(
|
|
155
153
|
pair,
|
|
@@ -163,14 +161,14 @@ export function createPair(input: CreatePairInput): {
|
|
|
163
161
|
"Accept with POST /api/pairs/{id}/accept using your agentId.",
|
|
164
162
|
"Reject with POST /api/pairs/{id}/reject using your agentId.",
|
|
165
163
|
"Pairing is exclusive: each agent can be in at most one pending or active pair.",
|
|
166
|
-
].join("\n"),
|
|
164
|
+
].join("\n"), now
|
|
167
165
|
);
|
|
168
166
|
return { ok: true, pair, invite };
|
|
169
167
|
}
|
|
170
168
|
|
|
171
|
-
export function acceptPair(id: string, input: PairActionInput): { ok: true; pair: PairSession; notices: Message[] } | { ok: false; error: string; code: "not_found" | "forbidden" | "busy" | "invalid" } {
|
|
172
|
-
expirePendingPairs();
|
|
173
|
-
const pair = getPair(id);
|
|
169
|
+
export function acceptPair(id: string, input: PairActionInput, now: number = Date.now()): { ok: true; pair: PairSession; notices: Message[] } | { ok: false; error: string; code: "not_found" | "forbidden" | "busy" | "invalid" } {
|
|
170
|
+
expirePendingPairs(now);
|
|
171
|
+
const pair = getPair(id, now);
|
|
174
172
|
if (!pair) return { ok: false, code: "not_found", error: "pair not found" };
|
|
175
173
|
if (pair.status !== "pending") return { ok: false, code: "invalid", error: `pair is ${pair.status}` };
|
|
176
174
|
if (pair.targetId !== input.agentId) return { ok: false, code: "forbidden", error: "only the target agent can accept this pair" };
|
|
@@ -180,61 +178,56 @@ export function acceptPair(id: string, input: PairActionInput): { ok: true; pair
|
|
|
180
178
|
if (!requester || requester.status === "offline" || !requester.ready) return { ok: false, code: "invalid", error: "requester agent is no longer online and ready" };
|
|
181
179
|
|
|
182
180
|
for (const agentId of [pair.requesterId, pair.targetId]) {
|
|
183
|
-
const open = getOpenPairForAgent(agentId);
|
|
181
|
+
const open = getOpenPairForAgent(agentId, now);
|
|
184
182
|
if (open && open.id !== pair.id) return { ok: false, code: "busy", error: `${agentId} is already paired` };
|
|
185
183
|
}
|
|
186
|
-
|
|
187
|
-
const now = Date.now();
|
|
188
184
|
getDb().query("UPDATE pairs SET status = 'active', accepted_at = ?, updated_at = ? WHERE id = ? AND status = 'pending'")
|
|
189
185
|
.run(now, now, id);
|
|
190
|
-
const active = getPair(id)!;
|
|
186
|
+
const active = getPair(id, now)!;
|
|
191
187
|
const notices = [
|
|
192
|
-
pairSystemMessage(active, active.requesterId, "accepted", `Pair accepted by ${active.targetId}`, `Pair ${active.id} is active. Send pair messages with POST /api/pairs/${active.id}/messages
|
|
193
|
-
pairSystemMessage(active, active.targetId, "accepted", `Pair active with ${active.requesterId}`, `Pair ${active.id} is active. Send pair messages with POST /api/pairs/${active.id}/messages
|
|
188
|
+
pairSystemMessage(active, active.requesterId, "accepted", `Pair accepted by ${active.targetId}`, `Pair ${active.id} is active. Send pair messages with POST /api/pairs/${active.id}/messages.`, now),
|
|
189
|
+
pairSystemMessage(active, active.targetId, "accepted", `Pair active with ${active.requesterId}`, `Pair ${active.id} is active. Send pair messages with POST /api/pairs/${active.id}/messages.`, now),
|
|
194
190
|
];
|
|
195
191
|
return { ok: true, pair: active, notices };
|
|
196
192
|
}
|
|
197
193
|
|
|
198
|
-
export function rejectPair(id: string, input: PairActionInput): { ok: true; pair: PairSession; notice: Message } | { ok: false; error: string; code: "not_found" | "forbidden" | "invalid" } {
|
|
199
|
-
expirePendingPairs();
|
|
200
|
-
const pair = getPair(id);
|
|
194
|
+
export function rejectPair(id: string, input: PairActionInput, now: number = Date.now()): { ok: true; pair: PairSession; notice: Message } | { ok: false; error: string; code: "not_found" | "forbidden" | "invalid" } {
|
|
195
|
+
expirePendingPairs(now);
|
|
196
|
+
const pair = getPair(id, now);
|
|
201
197
|
if (!pair) return { ok: false, code: "not_found", error: "pair not found" };
|
|
202
198
|
if (pair.status !== "pending") return { ok: false, code: "invalid", error: `pair is ${pair.status}` };
|
|
203
199
|
if (pair.targetId !== input.agentId) return { ok: false, code: "forbidden", error: "only the target agent can reject this pair" };
|
|
204
|
-
const now = Date.now();
|
|
205
200
|
getDb().query("UPDATE pairs SET status = 'rejected', ended_at = ?, ended_by = ?, updated_at = ? WHERE id = ?")
|
|
206
201
|
.run(now, input.agentId, now, id);
|
|
207
|
-
const rejected = getPair(id)!;
|
|
202
|
+
const rejected = getPair(id, now)!;
|
|
208
203
|
const reason = input.reason ? `\n\nReason:\n${input.reason}` : "";
|
|
209
|
-
const notice = pairSystemMessage(rejected, rejected.requesterId, "rejected", `Pair rejected by ${input.agentId}`, `Pair ${rejected.id} was rejected.${reason}
|
|
204
|
+
const notice = pairSystemMessage(rejected, rejected.requesterId, "rejected", `Pair rejected by ${input.agentId}`, `Pair ${rejected.id} was rejected.${reason}`, now);
|
|
210
205
|
return { ok: true, pair: rejected, notice };
|
|
211
206
|
}
|
|
212
207
|
|
|
213
|
-
export function endPair(id: string, input: PairActionInput): { ok: true; pair: PairSession; notice?: Message } | { ok: false; error: string; code: "not_found" | "forbidden" | "invalid" } {
|
|
214
|
-
expirePendingPairs();
|
|
215
|
-
const pair = getPair(id);
|
|
208
|
+
export function endPair(id: string, input: PairActionInput, now: number = Date.now()): { ok: true; pair: PairSession; notice?: Message } | { ok: false; error: string; code: "not_found" | "forbidden" | "invalid" } {
|
|
209
|
+
expirePendingPairs(now);
|
|
210
|
+
const pair = getPair(id, now);
|
|
216
211
|
if (!pair) return { ok: false, code: "not_found", error: "pair not found" };
|
|
217
212
|
if (!pairParticipant(pair, input.agentId)) return { ok: false, code: "forbidden", error: "only pair participants can hang up" };
|
|
218
213
|
if (!OPEN_PAIR_STATUSES.includes(pair.status as any)) return { ok: false, code: "invalid", error: `pair is ${pair.status}` };
|
|
219
|
-
const now = Date.now();
|
|
220
214
|
getDb().query("UPDATE pairs SET status = 'ended', ended_at = ?, ended_by = ?, updated_at = ? WHERE id = ?")
|
|
221
215
|
.run(now, input.agentId, now, id);
|
|
222
|
-
const ended = getPair(id)!;
|
|
216
|
+
const ended = getPair(id, now)!;
|
|
223
217
|
const reason = input.reason ? `\n\nReason:\n${input.reason}` : "";
|
|
224
218
|
const peer = pairPeer(ended, input.agentId);
|
|
225
|
-
const notice = pairSystemMessage(ended, peer, "ended", `Pair ended by ${input.agentId}`, `Pair ${ended.id} ended.${reason}
|
|
219
|
+
const notice = pairSystemMessage(ended, peer, "ended", `Pair ended by ${input.agentId}`, `Pair ${ended.id} ended.${reason}`, now);
|
|
226
220
|
return { ok: true, pair: ended, notice };
|
|
227
221
|
}
|
|
228
222
|
|
|
229
|
-
export function sendPairMessage(id: string, input: PairMessageInput): { ok: true; pair: PairSession; message: Message } | { ok: false; error: string; code: "not_found" | "forbidden" | "invalid" } {
|
|
230
|
-
expirePendingPairs();
|
|
231
|
-
const pair = getPair(id);
|
|
223
|
+
export function sendPairMessage(id: string, input: PairMessageInput, now: number = Date.now()): { ok: true; pair: PairSession; message: Message } | { ok: false; error: string; code: "not_found" | "forbidden" | "invalid" } {
|
|
224
|
+
expirePendingPairs(now);
|
|
225
|
+
const pair = getPair(id, now);
|
|
232
226
|
if (!pair) return { ok: false, code: "not_found", error: "pair not found" };
|
|
233
227
|
if (pair.status !== "active") return { ok: false, code: "invalid", error: `pair is ${pair.status}` };
|
|
234
228
|
if (!pairParticipant(pair, input.from)) return { ok: false, code: "forbidden", error: "only pair participants can send pair messages" };
|
|
235
229
|
const to = pairPeer(pair, input.from);
|
|
236
|
-
const
|
|
237
|
-
const message = sendMessage({
|
|
230
|
+
const message = sendMessageWithResultInternal({
|
|
238
231
|
from: input.from,
|
|
239
232
|
to,
|
|
240
233
|
kind: "pair",
|
|
@@ -246,7 +239,7 @@ export function sendPairMessage(id: string, input: PairMessageInput): { ok: true
|
|
|
246
239
|
requesterId: pair.requesterId,
|
|
247
240
|
targetId: pair.targetId,
|
|
248
241
|
},
|
|
249
|
-
});
|
|
242
|
+
}, false, {}, now).message;
|
|
250
243
|
getDb().query("UPDATE pairs SET last_message_at = ?, updated_at = ? WHERE id = ?").run(now, now, id);
|
|
251
|
-
return { ok: true, pair: getPair(id)!, message };
|
|
244
|
+
return { ok: true, pair: getPair(id, now)!, message };
|
|
252
245
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// #3660 — plan autopilot tables (see plan-dispatch-store.ts); dependency-free so the startup catalog can include it.
|
|
1
|
+
// #3660 — plan autopilot tables (see plan-dispatch-store.ts) and ready-node notice state (plan-ready-notices.ts); dependency-free so the startup catalog can include it.
|
|
2
2
|
export const PLAN_DISPATCH_SCHEMA_SQL = `
|
|
3
3
|
CREATE TABLE IF NOT EXISTS task_executions (task_id INTEGER PRIMARY KEY, execution TEXT NOT NULL, updated_at INTEGER NOT NULL);
|
|
4
4
|
CREATE TABLE IF NOT EXISTS plan_dispatch (
|
|
@@ -14,4 +14,8 @@ export const PLAN_DISPATCH_SCHEMA_SQL = `
|
|
|
14
14
|
CREATE TABLE IF NOT EXISTS plan_ci_blocks (
|
|
15
15
|
run_id TEXT PRIMARY KEY, plan_id TEXT NOT NULL, task_id INTEGER NOT NULL, sha TEXT NOT NULL,
|
|
16
16
|
incident_ids TEXT NOT NULL, wait_key TEXT NOT NULL, retry_note TEXT NOT NULL, retried_at INTEGER
|
|
17
|
+
);
|
|
18
|
+
CREATE TABLE IF NOT EXISTS plan_ready_notices (
|
|
19
|
+
plan_id TEXT NOT NULL, node_id TEXT NOT NULL, ready_since INTEGER NOT NULL, notified_at INTEGER,
|
|
20
|
+
PRIMARY KEY (plan_id, node_id)
|
|
17
21
|
)`;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// #3660 — durable "ready since / already told" state per plan node, so a hub restart neither
|
|
2
|
+
// loses the unstarted-ready clock nor repeats a notice. A row exists exactly while the node is
|
|
3
|
+
// ready-and-unstarted; leaving ready deletes it, so re-entering ready starts a fresh clock.
|
|
4
|
+
import { getDb } from "./connection.ts";
|
|
5
|
+
|
|
6
|
+
export interface ReadyPlanScan { planId: string; nodeIds: string[] }
|
|
7
|
+
export interface DueReadyNodes { planId: string; nodes: Array<{ nodeId: string; readySince: number }> }
|
|
8
|
+
|
|
9
|
+
/** Reconcile every scanned plan's ready set; plans absent from `scans` (archived, done, deleted,
|
|
10
|
+
* coordinator-less) lose their rows. Returns the nodes ready for >= `dueMs` and not yet notified. */
|
|
11
|
+
export function syncPlanReadyNotices(args: { scans: ReadyPlanScan[]; now: number; dueMs: number }): DueReadyNodes[] {
|
|
12
|
+
const db = getDb();
|
|
13
|
+
const scanned = new Set(args.scans.map(scan => scan.planId));
|
|
14
|
+
for (const { plan_id } of db.query("SELECT DISTINCT plan_id FROM plan_ready_notices").all() as Array<{ plan_id: string }>) {
|
|
15
|
+
if (!scanned.has(plan_id)) db.query("DELETE FROM plan_ready_notices WHERE plan_id = ?").run(plan_id);
|
|
16
|
+
}
|
|
17
|
+
const due: DueReadyNodes[] = [];
|
|
18
|
+
for (const scan of args.scans) {
|
|
19
|
+
const keep = new Set(scan.nodeIds);
|
|
20
|
+
const rows = db.query("SELECT node_id, ready_since, notified_at FROM plan_ready_notices WHERE plan_id = ?").all(scan.planId) as
|
|
21
|
+
Array<{ node_id: string; ready_since: number; notified_at: number | null }>;
|
|
22
|
+
const nodes: DueReadyNodes["nodes"] = [];
|
|
23
|
+
const known = new Set<string>();
|
|
24
|
+
for (const row of rows) {
|
|
25
|
+
if (!keep.has(row.node_id)) { db.query("DELETE FROM plan_ready_notices WHERE plan_id = ? AND node_id = ?").run(scan.planId, row.node_id); continue; }
|
|
26
|
+
known.add(row.node_id);
|
|
27
|
+
if (row.notified_at === null && row.ready_since <= args.now - args.dueMs) nodes.push({ nodeId: row.node_id, readySince: row.ready_since });
|
|
28
|
+
}
|
|
29
|
+
for (const nodeId of keep) if (!known.has(nodeId)) db.query("INSERT INTO plan_ready_notices(plan_id, node_id, ready_since) VALUES(?, ?, ?)").run(scan.planId, nodeId, args.now);
|
|
30
|
+
if (nodes.length) due.push({ planId: scan.planId, nodes });
|
|
31
|
+
}
|
|
32
|
+
return due;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function markPlanReadyNoticed(args: { planId: string; nodeIds: string[]; now: number }): void {
|
|
36
|
+
const stmt = getDb().query("UPDATE plan_ready_notices SET notified_at = ? WHERE plan_id = ? AND node_id = ? AND notified_at IS NULL");
|
|
37
|
+
for (const nodeId of args.nodeIds) stmt.run(args.now, args.planId, nodeId);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const PLAN_READY_NOTICE_WRITE_OPS = {
|
|
41
|
+
"plans.readyNotices.sync": { offLoop: true, idempotent: true, fn: syncPlanReadyNotices },
|
|
42
|
+
"plans.readyNotices.mark": { offLoop: true, idempotent: true, fn: markPlanReadyNoticed },
|
|
43
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// #3812 — backfill relay_plan_start/import into stored per-tool allowlists (the lean coordinator
|
|
2
|
+
// from #3472 froze before they existed, so tools/list never carried them).
|
|
3
|
+
// Narrow: only an include list already holding relay_plan_get, and not excluding the tool.
|
|
4
|
+
// json_insert, not parse+reserialize (#1456); patches `$.relay.tools` and its `sources` mirror, and
|
|
5
|
+
// the -v1-backup namespace, whose bytes are served while the matrix flag is off.
|
|
6
|
+
import { getDb } from "./connection.ts";
|
|
7
|
+
import { runMigrationOnce } from "./migration-marker.ts";
|
|
8
|
+
|
|
9
|
+
const PROFILE_NAMESPACES = ["agent-profile", "agent-profile-override", "agent-profile-override-v1-backup"] as const;
|
|
10
|
+
const SELECTION_PATHS = ["$.relay.tools", "$.sources.agentRelay.mcp"] as const;
|
|
11
|
+
const BACKFILLED_TOOLS = ["relay_plan_start", "relay_plan_import"] as const;
|
|
12
|
+
const EVIDENCE_TOOL = "relay_plan_get";
|
|
13
|
+
|
|
14
|
+
export function backfillPlanToolsIntoToolIncludes(): void {
|
|
15
|
+
runMigrationOnce("plan-tools-include-backfill-v1", () => {
|
|
16
|
+
const db = getDb();
|
|
17
|
+
const namespaces = PROFILE_NAMESPACES.map(() => "?").join(", ");
|
|
18
|
+
// The outer table is aliased: inside the subqueries a bare `value` binds to json_each.value.
|
|
19
|
+
const holds = (path: string, field: "include" | "exclude", tool: string) =>
|
|
20
|
+
`EXISTS (SELECT 1 FROM json_each(c.value, '${path}.${field}') je WHERE je.value = '${tool}')`;
|
|
21
|
+
db.transaction(() => {
|
|
22
|
+
for (const path of SELECTION_PATHS) {
|
|
23
|
+
for (const tool of BACKFILLED_TOOLS) {
|
|
24
|
+
db.query(
|
|
25
|
+
`UPDATE config AS c SET value = json_insert(c.value, '${path}.include[#]', ?)
|
|
26
|
+
WHERE c.namespace IN (${namespaces}) AND json_valid(c.value)
|
|
27
|
+
AND json_type(c.value, '${path}.include') = 'array'
|
|
28
|
+
AND ${holds(path, "include", EVIDENCE_TOOL)}
|
|
29
|
+
AND NOT ${holds(path, "include", tool)}
|
|
30
|
+
AND NOT ${holds(path, "exclude", tool)}`,
|
|
31
|
+
).run(tool, ...PROFILE_NAMESPACES);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
})();
|
|
35
|
+
});
|
|
36
|
+
}
|