@humwick/server 0.130.2 → 0.130.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundled/prompts/manifest.json +1 -0
- package/bundled/prompts/task.hydration.md +1 -1
- package/bundled/roles/builder.md +1 -1
- package/docs/guides/sub-coordinators.md +12 -5
- package/docs/guides/tools-coordination.md +1 -0
- package/docs/guides/workflows.md +16 -0
- package/docs/openapi.json +175 -4
- package/package.json +8 -7
- package/public/assets/{MessageBubble-BWLqMGKG.js → MessageBubble-BOkmEvgZ.js} +2 -2
- package/public/assets/{PlanGraphPanel-3TlIQpjk.js → PlanGraphPanel-BkaDJp0r.js} +2 -2
- package/public/assets/{TraceView-Wimm0Czv.js → TraceView-B99Uu2yT.js} +2 -2
- package/public/assets/{VaultDecisionCard-Bau_vNax.js → VaultDecisionCard-DUIfTTFF.js} +2 -2
- package/public/assets/{activity-CrSurn-r.js → activity-ZIX0N1El.js} +2 -2
- package/public/assets/{agent-lineage-CknAJKcb.js → agent-lineage-kq9TPYFw.js} +2 -2
- package/public/assets/agent-liveness-DSQY1Aa_.js +2 -0
- package/public/assets/{agent-name-DKbiIhBo.js → agent-name-C_7F4DFR.js} +2 -2
- package/public/assets/{agent-profiles-DwF6EQ4R.js → agent-profiles-BNOcrGcY.js} +2 -2
- package/public/assets/{agent-project-badge-Diz4Sjjb.js → agent-project-badge-B6yAh70a.js} +2 -2
- package/public/assets/{agent-tools-CRhpRQza.js → agent-tools-CcrhmOzF.js} +2 -2
- package/public/assets/{agent-type-icon-LrUEU3lB.js → agent-type-icon-CXSLrKiq.js} +2 -2
- package/public/assets/{agents-B-v66C-4.js → agents-DIZG97_X.js} +2 -2
- package/public/assets/{analytics-BqCb-LKi.js → analytics-rasmoPjR.js} +2 -2
- package/public/assets/{artifact-viewer-kf4pGuAR.js → artifact-viewer-tBpTrEPb.js} +2 -2
- package/public/assets/{artifacts-DJevXcvg.js → artifacts-BNkrynP1.js} +2 -2
- package/public/assets/{artifacts-D32zyw4Y.js → artifacts-Bjq-ZyBA.js} +2 -2
- package/public/assets/{automation-BpRajx_5.js → automation-Cx422L3L.js} +2 -2
- package/public/assets/{backlog-D_UjTdzX.js → backlog-RuFQ8PUu.js} +2 -2
- package/public/assets/{busy-health-N3VEh4ao.js → busy-health-BvNQ9g5x.js} +2 -2
- package/public/assets/{channels-9jopungq.js → channels-DNbKAS0I.js} +2 -2
- package/public/assets/{chat-CSXrTB3C.js → chat-Bp3Fx73h.js} +3 -3
- package/public/assets/{code-preview-p1NnmqK0.js → code-preview-KIRZA6cz.js} +2 -2
- package/public/assets/{connectors-CZdqlDNL.js → connectors-k7vO4vvc.js} +2 -2
- package/public/assets/{cosmos-BgdsE4dG.js → cosmos-BuVoq1aw.js} +2 -2
- package/public/assets/{crate-agent-30aXTKva.js → crate-agent-irqlt4NH.js} +2 -2
- package/public/assets/{dependency-graph-B_jvw2GE.js → dependency-graph-D_rurNBz.js} +2 -2
- package/public/assets/{deploys-DoN6bAb5.js → deploys-yzZqIpxE.js} +2 -2
- package/public/assets/{display-BrKWFGJz.js → display-BemJG1f_.js} +2 -2
- package/public/assets/{files-D6XEu8Ng.js → files-DXWcrxOz.js} +2 -2
- package/public/assets/{fleet-C7jQWbw2.js → fleet-TEuOCZXE.js} +2 -2
- package/public/assets/{fleet-metrics-BiDs3MLx.js → fleet-metrics-BGoDAFI-.js} +2 -2
- package/public/assets/{formatted-body-impl-DyG0WsQS.js → formatted-body-impl-BB9eq9nN.js} +2 -2
- package/public/assets/{formatted-body-CQBwc1fb.js → formatted-body-qy2nTD_1.js} +3 -3
- package/public/assets/{host-providers-dialog-DKaW31dN.js → host-providers-dialog-BGPHxuwT.js} +2 -2
- package/public/assets/{index-CRHep3M9.js → index-BC5XZc0z.js} +5 -5
- package/public/assets/{index-MYAIwYTX.css → index-D8Rp9pz2.css} +1 -1
- package/public/assets/{insights-CzrtkpOE.js → insights-C-KQG0h-.js} +2 -2
- package/public/assets/{integrations-aVsUmigt.js → integrations-ClPRPyKU.js} +2 -2
- package/public/assets/{issue-ref-chip-BnSlM_95.js → issue-ref-chip-DbH8J9Ak.js} +2 -2
- package/public/assets/{log-viewer-qe7Ee47Z.js → log-viewer-PH4zVn24.js} +2 -2
- package/public/assets/{maintenance-NfjqFZAk.js → maintenance-5lV43uTL.js} +2 -2
- package/public/assets/{managed-agents-BiH0HgaE.js → managed-agents-BdgU35aX.js} +2 -2
- package/public/assets/{markdown-preview-BixaT3Xr.js → markdown-preview-DNT4OUwQ.js} +3 -3
- package/public/assets/{markdown-preview-impl-DNGlWIS3.js → markdown-preview-impl-CnCcPy0A.js} +2 -2
- package/public/assets/{memory-DNsCl6UX.js → memory--GgWH1gH.js} +2 -2
- package/public/assets/{messages-DopU1L1X.js → messages-CzFl_97O.js} +2 -2
- package/public/assets/{orchestrators-Cq7ckLQT.js → orchestrators-CVvJVkzv.js} +2 -2
- package/public/assets/overview-DsrZvE86.js +2 -0
- package/public/assets/{pairs-DE_m6rrE.js → pairs-D2d0d2py.js} +2 -2
- package/public/assets/{profile-icon-B3rfh77F.js → profile-icon-_SMuSbYF.js} +2 -2
- package/public/assets/{project-profile-instructions-DQWZE0kA.js → project-profile-instructions-Apn15y43.js} +2 -2
- package/public/assets/{projects-Br2Bd2h1.js → projects-D3B6kto5.js} +2 -2
- package/public/assets/{prompt-settings-BKvFsvGD.js → prompt-settings-DnPB6Ez6.js} +2 -2
- package/public/assets/provider-display-bWwiuedV.js +2 -0
- package/public/assets/{registry-Cw38_6gW.js → registry-D790cIhN.js} +2 -2
- package/public/assets/run-card-BCro-Odj.js +2 -0
- package/public/assets/{secrets-DxI-vzSq.js → secrets-CVXvfvbU.js} +2 -2
- package/public/assets/{service-access-settings-D9s_VJbd.js → service-access-settings-D3Xxqb9w.js} +2 -2
- package/public/assets/{settings-CKZTVGIx.js → settings-CoUC7Jci.js} +2 -2
- package/public/assets/shipping-D_Lp9n_J.js +2 -0
- package/public/assets/{stats-DyUBKyWI.js → stats-CXiNjB9S.js} +2 -2
- package/public/assets/{status-dot-DYqlEUhJ.js → status-dot-C703YIy1.js} +2 -2
- package/public/assets/store-BgDALGIX.js +9 -0
- package/public/assets/{tasks-Bh7Chr4-.js → tasks-DO0WpJCx.js} +2 -2
- package/public/assets/{teams-bowI7qVY.js → teams-FGkdi_tL.js} +2 -2
- package/public/assets/{terminal-viewer-BgVLHdsU.js → terminal-viewer-Dh8dSC7e.js} +3 -3
- package/public/assets/{terminal-viewer-impl-Dz22UPy6.js → terminal-viewer-impl-B5Zriip3.js} +2 -2
- package/public/assets/{use-agent-terminal-DSQQ-iY7.js → use-agent-terminal-CRUVDvLC.js} +2 -2
- package/public/assets/{use-decisions-samMQhQf.js → use-decisions-CoaqRh4l.js} +2 -2
- package/public/assets/{use-project-list-59LheVP7.js → use-project-list-X-BgT53X.js} +2 -2
- package/public/assets/{use-selectors-BZa25xEB.js → use-selectors-0X_UZkk-.js} +2 -2
- package/public/assets/{user-avatar-DHxlvUvV.js → user-avatar-CkLhVBGu.js} +2 -2
- package/public/assets/{versioned-prompt-editor-BmED5DnJ.js → versioned-prompt-editor-BkTi7eim.js} +2 -2
- package/public/assets/{work-queue-CSvpuNLz.js → work-queue-DDOLEyKo.js} +2 -2
- package/public/assets/{workflow-marker-DAZcj5qt.js → workflow-marker-CzOsB8BM.js} +2 -2
- package/public/assets/{workflows-DdmxRSyg.js → workflows-DJilvmRM.js} +2 -2
- package/public/assets/{workspaces-BxeKfVuF.js → workspaces-CHcfbLR3.js} +2 -2
- package/public/cosmos.html +4 -4
- package/public/index.html +29 -29
- package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/runner/src/adapters/claude-api.ts +1 -1
- package/runner/src/adapters/claude-headless.ts +1 -1
- package/runner/src/adapters/claude-launch-exit.ts +26 -0
- package/runner/src/adapters/claude-session-probe.ts +1 -1
- package/runner/src/adapters/claude-tmux.ts +38 -10
- package/runner/src/adapters/claude.ts +28 -75
- package/runner/src/adapters/provider-quota-harvest.ts +20 -71
- package/runner/src/agent-runner/lifecycle-milestones.ts +2 -2
- package/runner/src/agent-runner/liveness-exchange.ts +104 -0
- package/runner/src/claude-system-prompt.ts +49 -0
- package/runner/src/launch-assembly.ts +14 -10
- package/runner/src/profile-home.ts +1 -1
- package/runner/src/project-instructions.ts +2 -2
- package/runner/src/provider-command.ts +18 -0
- package/runner/src/provider-setup.ts +3 -2
- package/runner/src/runner-core.ts +14 -15
- package/runner/src/runner-helpers.ts +0 -8
- package/runner/src/session-scratch.ts +6 -2
- package/runner/test-support/provisioning-context.ts +4 -2
- package/runner/test-support/runner-harness.ts +2 -1
- package/scripts/steward-policy-cleanup.ts +2 -2
- package/src/agent-authz.ts +2 -2
- package/src/agent-drive-arbiter.ts +3 -8
- package/src/agent-exit-cleanup-async.ts +2 -2
- package/src/agent-exit-cleanup.ts +23 -19
- package/src/agent-lifecycle-events.ts +112 -85
- package/src/agent-ref.ts +3 -2
- package/src/agent-status-events.ts +43 -14
- package/src/auth/login-link.ts +35 -0
- package/src/auth/password.ts +4 -3
- package/src/auth/session-refresh.ts +1 -1
- package/src/auth/session.test-fixture.ts +2 -2
- package/src/auth/session.ts +7 -7
- package/src/automations/crud.ts +8 -81
- package/src/automations/dispatch.ts +33 -58
- package/src/automations/index.ts +1 -1
- package/src/automations/persistence.ts +65 -0
- package/src/automations/precheck.ts +18 -16
- package/src/automations/read-async.ts +11 -0
- package/src/automations/reconcile.ts +30 -39
- package/src/automations/storage.ts +33 -10
- package/src/automations/write-async.ts +33 -0
- package/src/branch-land-notice.ts +3 -3
- package/src/branch-landed.ts +25 -23
- package/src/bus.ts +21 -19
- package/src/cd-deploy-notify.ts +2 -2
- package/src/ci/notifications.ts +4 -4
- package/src/ci/root-missing.ts +3 -3
- package/src/ci/transition-notify.ts +5 -5
- package/src/claude-app/oauth-http.ts +3 -3
- package/src/cli/index.ts +7 -1
- package/src/cli/init.ts +53 -0
- package/src/cli/orchestrator.ts +1 -7
- package/src/cli/setup.ts +6 -4
- package/src/cli/workflow.ts +12 -3
- package/src/commands-db.ts +4 -4
- package/src/commands-write.ts +8 -0
- package/src/compaction-watch.ts +10 -8
- package/src/config-store-write.ts +2 -2
- package/src/config.ts +4 -11
- package/src/connectors.ts +10 -8
- package/src/context-advisory.ts +22 -21
- package/src/db/agent-action-write.ts +36 -0
- package/src/db/agent-control-async.ts +9 -0
- package/src/db/agent-control-write.ts +49 -0
- package/src/db/agent-effort-observe.ts +22 -0
- package/src/db/agent-meta.ts +14 -3
- package/src/db/agents-async.ts +8 -5
- package/src/db/agents.ts +15 -12
- package/src/db/auth-identities.ts +2 -2
- package/src/db/automation-read-ops.ts +11 -0
- package/src/db/automation-write-ops.ts +46 -0
- package/src/db/blackboard.ts +2 -2
- package/src/db/browser-session-write.ts +32 -0
- package/src/db/bus-outbox-write-ops.ts +3 -1
- package/src/db/checkout-refresh-schema.ts +47 -0
- package/src/db/checkout-refresh.ts +5 -5
- package/src/db/command-agent-profile-backfill.ts +39 -0
- package/src/db/command-agent-profiles.ts +5 -1
- package/src/db/command-retention.ts +126 -0
- package/src/db/commands-planner-stats.test-fixture.ts +8 -0
- package/src/db/commands-read-ops.ts +16 -0
- package/src/db/connection.ts +1 -182
- package/src/db/context-carry-write.ts +74 -0
- package/src/db/continuations.ts +4 -4
- package/src/db/db-error-registry.ts +12 -0
- package/src/db/gateway.ts +26 -133
- package/src/db/history-retention-ops.ts +13 -0
- package/src/db/inbox-async.ts +2 -2
- package/src/db/inbox-read-ops.ts +1 -1
- package/src/db/inbox.ts +9 -3
- package/src/db/issue-associations.ts +2 -2
- package/src/db/issue-refs-async.ts +4 -1
- package/src/db/issue-refs-read-ops.ts +6 -0
- package/src/db/issue-refs.ts +8 -8
- package/src/db/kernel-event-log-schema.ts +2 -0
- package/src/db/land-boundaries.ts +51 -5
- package/src/db/land-gate-attestations.ts +2 -2
- package/src/db/landing-receipts.ts +2 -2
- package/src/db/lifecycle-context-write.ts +5 -0
- package/src/db/lifecycle-tail-async.ts +76 -0
- package/src/db/lifecycle-tail-write-ops.ts +62 -0
- package/src/db/lifecycle-write-window.ts +2 -1
- package/src/db/lineage-pair-dedupe-sql.ts +3 -3
- package/src/db/main-connection-mode.ts +11 -0
- package/src/db/main-connection-writes.ts +20 -2
- package/src/db/maintenance-job-runs-async.ts +2 -2
- package/src/db/maintenance-job-write-ops.ts +1 -1
- package/src/db/maintenance-pauses.ts +2 -2
- package/src/db/message-send.ts +4 -0
- package/src/db/messages.ts +31 -25
- package/src/db/migrations.ts +18 -1
- package/src/db/notification-write-ops.ts +19 -0
- package/src/db/operator-answer-write.ts +19 -0
- package/src/db/orchestrators.ts +10 -4
- package/src/db/pairs-async.ts +22 -1
- package/src/db/pairs-write-ops.ts +8 -3
- package/src/db/pairs.ts +41 -48
- package/src/db/plan-dispatch-schema.ts +5 -1
- package/src/db/plan-ready-notices.ts +43 -0
- package/src/db/plan-tools-include-migration.ts +36 -0
- package/src/db/plans-write-ops.ts +2 -0
- package/src/db/presence-buffer.ts +5 -0
- package/src/db/presence-loop.ts +18 -23
- package/src/db/presence-write.ts +0 -6
- package/src/db/project-entries-async.ts +14 -0
- package/src/db/project-entries-read-ops.ts +5 -1
- package/src/db/project-entries-write-ops.ts +8 -0
- package/src/db/project-entries.ts +15 -12
- package/src/db/project-journal.ts +2 -2
- package/src/db/project-trust-approvals.ts +2 -2
- package/src/db/prompt-templates.ts +4 -4
- package/src/db/read-op-marshaling-fixtures.ts +18 -0
- package/src/db/read-ops.ts +4 -0
- package/src/db/retention.ts +13 -2
- package/src/db/review-publish-write.ts +8 -0
- package/src/db/runtime-admin-async.ts +19 -0
- package/src/db/runtime-admin-write-ops.ts +36 -0
- package/src/db/runtime-tail-async.ts +288 -0
- package/src/db/runtime-tail-write-ops.ts +97 -0
- package/src/db/schema.ts +4 -1
- package/src/db/spawn-budget.ts +5 -0
- package/src/db/spawn-delegation.ts +3 -12
- package/src/db/sql-audit-indexes.ts +3 -0
- package/src/db/stats-async.ts +5 -3
- package/src/db/task-entity.ts +5 -4
- package/src/db/team-claim-policy.ts +60 -0
- package/src/db/teams-async.ts +4 -0
- package/src/db/teams-write-ops.ts +4 -0
- package/src/db/terminal-events.ts +15 -0
- package/src/db/timer-cutover-bodies.ts +292 -0
- package/src/db/timer-cutover-core.ts +37 -278
- package/src/db/timer-cutover-state.ts +40 -0
- package/src/db/timer-cutover-write-ops.ts +1 -40
- package/src/db/token-db-write-ops.ts +2 -0
- package/src/db/token-standing-async.ts +12 -0
- package/src/db/token-standing.ts +6 -9
- package/src/db/token-usage-retention.ts +33 -0
- package/src/db/token-usage-write-ops.ts +1 -1
- package/src/db/token-usage.ts +4 -1
- package/src/db/user-invites-async.ts +13 -0
- package/src/db/user-invites.ts +3 -3
- package/src/db/users.ts +14 -6
- package/src/db/workflow-execution.ts +3 -3
- package/src/db/workspaces.ts +8 -5
- package/src/db/write-ops.ts +31 -12
- package/src/db/writer-fault-seam.ts +2 -9
- package/src/db/writer-fault-test-ops.ts +18 -0
- package/src/db/writer-gateway.ts +42 -46
- package/src/dev.ts +12 -16
- package/src/event-loop-lag.ts +4 -2
- package/src/event-loop-yield.ts +2 -2
- package/src/events.ts +10 -8
- package/src/host-provider-store.ts +5 -4
- package/src/hub-metrics-report.ts +5 -1
- package/src/hub-metrics.ts +38 -18
- package/src/init.ts +299 -0
- package/src/injection-sweep-pacing.ts +6 -8
- package/src/invite-link.ts +16 -9
- package/src/land-gate-notifications.ts +11 -10
- package/src/land-gate-queue-progress.ts +3 -3
- package/src/land-settle-dispatch.ts +52 -92
- package/src/lifecycle-context-pressure.ts +11 -10
- package/src/lifecycle-manager.ts +104 -98
- package/src/maintenance/breaker.ts +18 -16
- package/src/maintenance/checkout-refresh.ts +18 -1
- package/src/maintenance/critical-job-watchdog.ts +3 -3
- package/src/maintenance/events.ts +3 -3
- package/src/maintenance/history-retention-job.ts +30 -0
- package/src/maintenance/jobs.ts +29 -67
- package/src/maintenance/long-running-work.ts +3 -1
- package/src/maintenance/offline-agent-prune-job.ts +28 -0
- package/src/maintenance/orphaned-sessions.ts +5 -4
- package/src/maintenance/pauses.ts +4 -10
- package/src/maintenance/plan-ready-notice-job.ts +71 -0
- package/src/maintenance/scheduler.ts +18 -13
- package/src/maintenance/stalled-delivery-pager.ts +4 -4
- package/src/maintenance/teams.ts +19 -30
- package/src/maintenance/workspaces.ts +17 -27
- package/src/managed-policy-escalation.ts +7 -7
- package/src/managed-policy.ts +3 -3
- package/src/managed-provider-gate.ts +5 -4
- package/src/managed-spawn-reservation.ts +5 -4
- package/src/mcp/index.ts +8 -8
- package/src/mcp/output-descriptions.ts +1 -1
- package/src/mcp/presentation.ts +2 -1
- package/src/mcp/runtime-output.ts +6 -1
- package/src/mcp/self-care-visibility.ts +2 -2
- package/src/mcp/spawn-targets-matrix.ts +10 -1
- package/src/mcp/tool-groups.ts +3 -2
- package/src/mcp/tool-roles.ts +81 -0
- package/src/mcp/tools-workspace.ts +1 -1
- package/src/mcp-blackboard-tools.ts +6 -5
- package/src/mcp-decision-tools.ts +7 -7
- package/src/mcp-egress-tools.ts +10 -9
- package/src/mcp-issue-activity-tools.ts +2 -2
- package/src/mcp-issue-association-tools.ts +4 -4
- package/src/mcp-issue-ref-tools.ts +3 -3
- package/src/mcp-merge-tools.ts +5 -5
- package/src/mcp-notification-subscription-tools.ts +14 -15
- package/src/mcp-output-levels.test-fixture.ts +1 -1
- package/src/mcp-profile-trust-tools.ts +1 -1
- package/src/mcp-project-journal-tools.ts +4 -12
- package/src/mcp-project-tools.ts +17 -18
- package/src/mcp-routing-policy-tools.ts +3 -2
- package/src/mcp-team-tools.ts +6 -6
- package/src/mcp-workflow-tools.ts +5 -3
- package/src/memory-area-injection.ts +13 -12
- package/src/memory-boundary-capture.ts +6 -6
- package/src/memory-injection.ts +45 -43
- package/src/merge-pause-maintenance.ts +5 -4
- package/src/notification-router.ts +44 -17
- package/src/notify.ts +5 -5
- package/src/observed-reset-refresh.ts +4 -4
- package/src/operator-alarm.ts +2 -5
- package/src/project-ingest.ts +6 -6
- package/src/project-instructions.ts +8 -1
- package/src/project-trust.ts +12 -11
- package/src/prompt-resolver.ts +3 -3
- package/src/provider-quota-async.ts +5 -7
- package/src/provider-timeline-event.ts +4 -7
- package/src/quota-band-delivery.ts +2 -2
- package/src/recipe-runner.ts +4 -3
- package/src/retention-config-store.ts +4 -3
- package/src/reviewer-pipeline.ts +11 -10
- package/src/routes/_route.ts +4 -4
- package/src/routes/_shared.ts +13 -12
- package/src/routes/agent-effort.ts +5 -5
- package/src/routes/agent-heartbeat-quota.ts +23 -0
- package/src/routes/agent-keep.ts +1 -1
- package/src/routes/agent-reporting.ts +1 -1
- package/src/routes/agent-self-effort.ts +5 -5
- package/src/routes/agent-sessions.ts +24 -24
- package/src/routes/agents-spawn.ts +1 -1
- package/src/routes/agents.ts +17 -119
- package/src/routes/auth.ts +44 -17
- package/src/routes/authority-audit.ts +2 -2
- package/src/routes/automations.ts +6 -5
- package/src/routes/channel-binding-delete.ts +1 -1
- package/src/routes/channel-output.ts +3 -3
- package/src/routes/claude-api-budget.ts +5 -5
- package/src/routes/commands-listing.ts +1 -1
- package/src/routes/commands.ts +29 -27
- package/src/routes/context-resets.ts +2 -2
- package/src/routes/coordinator-teams.ts +1 -1
- package/src/routes/decisions.ts +2 -2
- package/src/routes/host-providers.ts +18 -0
- package/src/routes/host-secret-files.ts +1 -1
- package/src/routes/hosts.ts +11 -11
- package/src/routes/inbox.ts +3 -2
- package/src/routes/index.ts +7 -3
- package/src/routes/integrations.ts +9 -7
- package/src/routes/issue-activity.ts +2 -2
- package/src/routes/issue-associations.ts +4 -4
- package/src/routes/issue-refs.ts +3 -3
- package/src/routes/kernel-timers-operator.ts +1 -1
- package/src/routes/memory.ts +1 -1
- package/src/routes/merges.ts +11 -11
- package/src/routes/messages.ts +23 -17
- package/src/routes/orchestrator-bootstrap.ts +3 -1
- package/src/routes/orchestrator-host-registration.ts +2 -0
- package/src/routes/orchestrator-host-utilization.ts +2 -2
- package/src/routes/orchestrator-proxy.ts +2 -2
- package/src/routes/orchestrator.ts +27 -18
- package/src/routes/pairs.ts +8 -8
- package/src/routes/project-profile-instructions.ts +1 -1
- package/src/routes/project-trust.ts +1 -1
- package/src/routes/projects.ts +16 -16
- package/src/routes/prompt-templates.ts +12 -12
- package/src/routes/provider-quotas.ts +10 -10
- package/src/routes/run-command.ts +1 -1
- package/src/routes/secrets-backup.ts +4 -5
- package/src/routes/secrets-import.ts +3 -4
- package/src/routes/secrets-vault.ts +12 -14
- package/src/routes/settings.ts +1 -1
- package/src/routes/shipping.ts +9 -1
- package/src/routes/spawn-policy.ts +8 -7
- package/src/routes/sse.ts +2 -2
- package/src/routes/team-templates.ts +7 -7
- package/src/routes/teams.ts +10 -10
- package/src/routes/tokens.ts +18 -17
- package/src/routes/users.ts +24 -24
- package/src/routes/viewer-read.ts +3 -3
- package/src/routes/web-tool-calls.ts +4 -4
- package/src/routes/workflows.ts +2 -2
- package/src/routes/workspaces.ts +17 -16
- package/src/routing-policy-push.ts +6 -6
- package/src/runtime-grant.ts +17 -11
- package/src/runtime-install.ts +30 -0
- package/src/runtime-tokens.ts +20 -28
- package/src/secrets-broker/config.ts +1 -1
- package/src/secrets-broker/service.ts +2 -2
- package/src/security.ts +3 -1
- package/src/self-resume-carry.ts +21 -110
- package/src/self-resume-supersede.ts +4 -3
- package/src/self-resume.ts +54 -66
- package/src/server-background.ts +1 -1
- package/src/server.ts +25 -26
- package/src/services/agent-action.ts +12 -31
- package/src/services/agent-effort.ts +7 -32
- package/src/services/agent-hang-watchdog-job.ts +2 -2
- package/src/services/agent-heartbeat.ts +166 -0
- package/src/services/agent-reporting.ts +1 -1
- package/src/services/auth-context.ts +6 -0
- package/src/services/browser-inventory.ts +3 -3
- package/src/services/browser-session-state.ts +3 -2
- package/src/services/browser-session.ts +21 -27
- package/src/services/bus-command-update.ts +9 -9
- package/src/services/checkout-refresh-state.ts +1 -0
- package/src/services/checkout-refresh.test-fixture.ts +3 -2
- package/src/services/command-host.ts +7 -2
- package/src/services/coordinator-digest.ts +3 -2
- package/src/services/dispatch-command.ts +12 -13
- package/src/services/effective-launch-driver.ts +14 -9
- package/src/services/host-changed-paths.test-fixture.ts +5 -6
- package/src/services/host-project-readiness.ts +2 -2
- package/src/services/host-providers.ts +1 -1
- package/src/services/host-utilization.ts +9 -9
- package/src/services/idle-agent-digest.ts +17 -12
- package/src/services/idle-epics.ts +4 -4
- package/src/services/interrupt-agent.ts +12 -9
- package/src/services/issue-activity.ts +3 -3
- package/src/services/issue-associations.ts +15 -18
- package/src/services/issue-lifecycle-comments.ts +3 -2
- package/src/services/issue-lifecycle.ts +14 -13
- package/src/services/issue-refs.ts +4 -5
- package/src/services/kernel-timers-cutover-chaos-worker.ts +1 -1
- package/src/services/kernel-timers-ingress-chaos-worker.ts +6 -1
- package/src/services/kernel-timers-ingress.ts +3 -3
- package/src/services/kernel-timers-message-authority-chaos-worker.ts +3 -10
- package/src/services/kernel-timers-operator-chaos-worker.ts +3 -2
- package/src/services/kernel-timers-outbox.ts +1 -1
- package/src/services/kernel-timers-stage10-operator.ts +1 -1
- package/src/services/kernel-timers.ts +4 -3
- package/src/services/land-gate-placement.ts +11 -2
- package/src/services/land-issue-relations.ts +5 -4
- package/src/services/land-restart-retry.ts +7 -6
- package/src/services/landed-ancestry.ts +6 -5
- package/src/services/legacy-landing-host.ts +4 -1
- package/src/services/legacy-landing-recovery.ts +8 -6
- package/src/services/managed-running.ts +17 -17
- package/src/services/memory-areas.ts +3 -3
- package/src/services/merge-pause.ts +9 -15
- package/src/services/operator-decisions.ts +37 -41
- package/src/services/orchestrator-registration-recovery.ts +6 -2
- package/src/services/orchestrator-upgrade.ts +14 -12
- package/src/services/permission-decision.ts +4 -3
- package/src/services/plan-ci-notice.ts +1 -1
- package/src/services/plan-conformance.ts +4 -4
- package/src/services/plan-dispatch-policy.ts +10 -3
- package/src/services/plan-dispatch.ts +35 -14
- package/src/services/plan-graph.ts +39 -261
- package/src/services/plan-import.ts +1 -1
- package/src/services/plan-live-bindings.ts +8 -37
- package/src/services/plan-task-create.ts +1 -1
- package/src/services/plan-terminal-refs.ts +2 -12
- package/src/services/plan-wake-digest.ts +8 -3
- package/src/services/profile-trust.ts +2 -2
- package/src/services/project.ts +11 -10
- package/src/services/prompt-templates.ts +6 -5
- package/src/services/provider-api-key.ts +73 -0
- package/src/services/provider-hold-nudge.ts +9 -9
- package/src/services/provider-hold-record.ts +13 -12
- package/src/services/provider-state-audit.ts +19 -18
- package/src/services/provisioning-harness/driver.ts +1 -1
- package/src/services/provisioning-harness/fixture-matrix.ts +3 -3
- package/src/services/provisioning-harness/lifecycle.ts +24 -22
- package/src/services/provisioning-harness/prepare.ts +16 -17
- package/src/services/rate-limit-resume.ts +10 -9
- package/src/services/red-main-auto-revert.ts +7 -5
- package/src/services/red-main-controller-deps.ts +20 -3
- package/src/services/red-main-incident.ts +3 -1
- package/src/services/red-main-quarantine.ts +2 -3
- package/src/services/register-agent.ts +22 -22
- package/src/services/restart-project-role.ts +1 -1
- package/src/services/restart-spawn.ts +17 -16
- package/src/services/review-publish.ts +7 -24
- package/src/services/review-ref-reconcile.ts +29 -38
- package/src/services/runtime-dependency-alert.ts +27 -0
- package/src/services/scheduler-command.ts +11 -9
- package/src/services/scheduler.ts +5 -5
- package/src/services/self-effort-grant.ts +5 -25
- package/src/services/send-message.ts +16 -33
- package/src/services/shutdown-agent.ts +17 -14
- package/src/services/spawn-agent.ts +12 -41
- package/src/services/spawn-chain-inspector.ts +1 -1
- package/src/services/spawn-cwd-placement.ts +21 -5
- package/src/services/spawn-issue-activity.ts +8 -6
- package/src/services/spawn-launch.ts +17 -1
- package/src/services/spawn-loss-escalation.ts +1 -1
- package/src/services/spawn-registration-watchdog.ts +2 -2
- package/src/services/spawn-repo-probe.ts +7 -5
- package/src/services/spawn-tree-cleanup.ts +7 -8
- package/src/services/steward-reaper.ts +4 -4
- package/src/services/task-context-hydration.ts +25 -1
- package/src/services/task-delivery.ts +20 -34
- package/src/services/task-entity.ts +40 -203
- package/src/services/task-issue-link.ts +13 -20
- package/src/services/task-issue-weave.ts +12 -11
- package/src/services/task-lifecycle.ts +27 -31
- package/src/services/task-repo-cwd.ts +37 -0
- package/src/services/task-review-hold.ts +4 -17
- package/src/services/task-semantic-events.ts +13 -11
- package/src/services/task-state-reconcile.ts +3 -4
- package/src/services/task-validation.ts +2 -2
- package/src/services/team-templates.ts +5 -5
- package/src/services/team.ts +31 -98
- package/src/services/terminal-provider-exit.ts +6 -5
- package/src/services/timer-message-fire-chaos-worker.ts +7 -3
- package/src/services/timer-message-fire-outbox.ts +1 -1
- package/src/services/transient-agent-reaper.ts +22 -19
- package/src/services/turn-reconcile-audit.ts +9 -9
- package/src/services/user-onboarding.ts +8 -6
- package/src/services/user-profile.ts +6 -9
- package/src/services/work-finality.ts +16 -4
- package/src/services/workflow-auto-start.ts +2 -1
- package/src/services/workflow-caller.ts +61 -4
- package/src/services/workflow-child-proof-worker.ts +1 -1
- package/src/services/workflow-control-proof-worker.ts +1 -1
- package/src/services/workflow-coordinator.ts +8 -2
- package/src/services/workflow-decisions.ts +5 -5
- package/src/services/workflow-deferred-cleanup.ts +1 -1
- package/src/services/workflow-effect-driver.ts +42 -19
- package/src/services/workflow-effects.ts +41 -27
- package/src/services/workflow-pins.ts +7 -4
- package/src/services/workflow-reconcile.ts +1 -0
- package/src/services/workflow-resume-pins.ts +3 -3
- package/src/services/workflow-routing-defaults.ts +18 -0
- package/src/services/workflow-run-view.ts +4 -3
- package/src/services/workflow-run.test-fixture.ts +1 -1
- package/src/services/workflow-runs.ts +34 -22
- package/src/services/workflow-supervisor-state.ts +21 -1
- package/src/services/workflow-supervisor.ts +4 -3
- package/src/services/workspace-recovery.ts +4 -3
- package/src/setup.ts +47 -8
- package/src/spawn-admission.ts +10 -2
- package/src/spawn-placement.ts +27 -9
- package/src/spawn-targets.ts +8 -8
- package/src/sse-agent-status.ts +15 -6
- package/src/sse.ts +49 -24
- package/src/startup-context-injection.ts +61 -57
- package/src/steward-handoff.ts +4 -3
- package/src/steward-policy-cleanup.ts +9 -8
- package/src/steward.ts +2 -2
- package/src/task-dispatch-live.ts +5 -2
- package/src/task-dispatcher.ts +2 -2
- package/src/token-db.ts +2 -2
- package/src/token-usage-async.ts +2 -4
- package/src/user-awaiting-reply.ts +5 -5
- package/src/utils.ts +7 -0
- package/src/workspace-actions.ts +24 -29
- package/src/workspace-auto-merge.ts +14 -12
- package/src/workspace-changed-paths-client.ts +16 -5
- package/src/workspace-cleanup-stale.ts +30 -7
- package/src/workspace-command-settle.ts +24 -16
- package/src/workspace-config.ts +1 -1
- package/src/workspace-conflict-resolver.ts +9 -7
- package/src/workspace-dead-owner-recovery.ts +5 -4
- package/src/workspace-escalation.ts +12 -11
- package/src/workspace-human-escalation.ts +3 -3
- package/src/workspace-land-gate-watchdog.ts +3 -3
- package/src/workspace-land-refusal.ts +20 -0
- package/src/workspace-land-stopped.ts +1 -0
- package/src/workspace-merge.ts +10 -26
- package/src/workspace-nothing-to-land-notice.ts +2 -2
- package/src/workspace-orphans.ts +18 -37
- package/src/workspace-pr-completion.ts +10 -8
- package/src/workspace-rekey.ts +3 -3
- package/src/workspace-steward-failure.ts +2 -2
- package/src/workspace-steward-wake.ts +2 -2
- package/public/assets/agent-liveness-CaNwK0Zf.js +0 -2
- package/public/assets/overview-C1DCTzzV.js +0 -2
- package/public/assets/provider-display-DsuWMvVL.js +0 -2
- package/public/assets/run-card-CpKqRbhg.js +0 -2
- package/public/assets/shipping-B9YtnwtG.js +0 -2
- package/public/assets/store-DcClXLH0.js +0 -9
- package/runner/src/agent-runner/http-liveness.ts +0 -85
- package/src/db/on-loop-write-transaction.ts +0 -34
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
|
+
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { createPlanRow, deletePlan, replacePlanIfVersion, setPlanArchived } from "./plans.ts";
|
|
2
|
+
import { PLAN_READY_NOTICE_WRITE_OPS } from "./plan-ready-notices.ts";
|
|
2
3
|
import type { WriterOpContext, WriterOpSpec } from "./write-ops.ts";
|
|
3
4
|
|
|
4
5
|
export const PLANS_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = Object.freeze({
|
|
6
|
+
...PLAN_READY_NOTICE_WRITE_OPS,
|
|
5
7
|
"plans.create": { offLoop: true, idempotent: false, fn: (args: { input: Parameters<typeof createPlanRow>[0] }, ctx: WriterOpContext) => createPlanRow(args.input, ctx.now) },
|
|
6
8
|
"plans.replace": { offLoop: true, idempotent: false, fn: (args: { input: Parameters<typeof replacePlanIfVersion>[0]; changedBy?: string }, ctx: WriterOpContext) => replacePlanIfVersion(args.input, args.changedBy, ctx.now, false) },
|
|
7
9
|
"plans.setArchived": { offLoop: true, idempotent: true, fn: (args: { id: string; archived: boolean }, ctx: WriterOpContext) => setPlanArchived(args.id, args.archived, ctx.now) },
|
|
@@ -104,6 +104,11 @@ export function snapshotBufferedPresence(): PresenceSnapshot {
|
|
|
104
104
|
* current, and the caller's surrounding status/ready predicates constrain the match. */
|
|
105
105
|
export function bufferedLiveAgentIds(cutoff: number): string[] {
|
|
106
106
|
const ids: string[] = [];
|
|
107
|
+
const snapshot = writerPresenceSnapshot();
|
|
108
|
+
if (snapshot) {
|
|
109
|
+
for (const beat of snapshot) if (beat.lastSeen > cutoff) ids.push(beat.id);
|
|
110
|
+
return ids;
|
|
111
|
+
}
|
|
107
112
|
for (const [id, pending] of pendingPresence) {
|
|
108
113
|
if (pending.lastSeen > cutoff) ids.push(id);
|
|
109
114
|
}
|
package/src/db/presence-loop.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { framed } from "../hub-metrics";
|
|
1
2
|
import { assertMainThreadState } from "./main-thread-state";
|
|
2
3
|
// Lifecycle glue for the coalesced presence buffer. Kept outside agents.ts so its writer-op body
|
|
3
4
|
// never depends on the gateway-facing facade (ADR 0001 §3.1).
|
|
@@ -6,53 +7,47 @@ import { errMessage } from "@humwick/sdk";
|
|
|
6
7
|
import { PRESENCE_FLUSH_MS } from "../config";
|
|
7
8
|
import { drainPendingLandSettleSideEffects } from "../land-settle-dispatch";
|
|
8
9
|
import { onShutdown } from "../shutdown";
|
|
9
|
-
import {
|
|
10
|
+
import { flushPresenceAsync } from "./presence-write.ts";
|
|
10
11
|
import { pendingPresence } from "./presence-buffer.ts";
|
|
11
12
|
|
|
12
13
|
let presenceFlushTimer: ReturnType<typeof setInterval> | undefined;
|
|
13
|
-
let asyncPresenceFlusher: (() => Promise<{ flushed: number }>) | undefined;
|
|
14
14
|
let removeShutdownHook: (() => void) | undefined;
|
|
15
15
|
|
|
16
|
-
/** Attach the gateway-backed normal-tick flusher without importing the gateway into agents.ts. */
|
|
17
|
-
export function setAsyncPresenceFlusher(flusher: () => Promise<{ flushed: number }>): void {
|
|
18
|
-
assertMainThreadState("presence-loop.ts");
|
|
19
|
-
asyncPresenceFlusher = flusher;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
16
|
/** Start the periodic presence-buffer flush loop (server boot). */
|
|
23
17
|
export function startPresenceFlushLoop(intervalMs: number = PRESENCE_FLUSH_MS): void {
|
|
24
18
|
assertMainThreadState("presence-loop.ts");
|
|
25
19
|
if (presenceFlushTimer) return;
|
|
26
|
-
presenceFlushTimer = setInterval(() =>
|
|
27
|
-
|
|
28
|
-
if (!flush) return flushPresenceQuietly(); // Bootstrap/isolated compatibility fallback.
|
|
29
|
-
void flush().catch((err) => console.warn(`[presence] flush failed: ${errMessage(err)}`));
|
|
30
|
-
}, intervalMs);
|
|
20
|
+
presenceFlushTimer = setInterval(framed("timer presence-flush", () =>
|
|
21
|
+
flushPresenceAsync().catch((err) => console.warn(`[presence] flush failed: ${errMessage(err)}`))), intervalMs);
|
|
31
22
|
presenceFlushTimer.unref?.();
|
|
32
|
-
process.once("beforeExit",
|
|
33
|
-
removeShutdownHook = onShutdown("presence", () => {
|
|
34
|
-
drainPendingLandSettleSideEffects();
|
|
35
|
-
stopPresenceFlushLoop();
|
|
23
|
+
process.once("beforeExit", flushPresenceBeforeExit);
|
|
24
|
+
removeShutdownHook = onShutdown("presence", async () => {
|
|
25
|
+
await drainPendingLandSettleSideEffects();
|
|
26
|
+
await stopPresenceFlushLoop();
|
|
36
27
|
});
|
|
37
28
|
}
|
|
38
29
|
|
|
39
30
|
/** Stop the loop and persist whatever is buffered (graceful shutdown / test teardown). */
|
|
40
|
-
|
|
31
|
+
function detachPresenceLoop(): void {
|
|
41
32
|
assertMainThreadState("presence-loop.ts");
|
|
42
33
|
removeShutdownHook?.();
|
|
43
34
|
removeShutdownHook = undefined;
|
|
44
|
-
process.removeListener("beforeExit",
|
|
35
|
+
process.removeListener("beforeExit", flushPresenceBeforeExit);
|
|
45
36
|
if (presenceFlushTimer) clearInterval(presenceFlushTimer);
|
|
46
37
|
presenceFlushTimer = undefined;
|
|
47
|
-
flushPresenceQuietly();
|
|
48
38
|
}
|
|
49
39
|
|
|
50
|
-
function
|
|
51
|
-
|
|
40
|
+
export async function stopPresenceFlushLoop(): Promise<void> {
|
|
41
|
+
detachPresenceLoop();
|
|
42
|
+
await flushPresenceAsync();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function flushPresenceBeforeExit(): void {
|
|
46
|
+
void flushPresenceAsync().catch(err => console.warn(`[presence] flush failed: ${errMessage(err)}`));
|
|
52
47
|
}
|
|
53
48
|
|
|
54
49
|
/** Test-only: drop the buffered presence and stop the flush loop. */
|
|
55
50
|
export function resetPresenceBufferForTests(): void {
|
|
56
51
|
pendingPresence.clear();
|
|
57
|
-
|
|
52
|
+
detachPresenceLoop();
|
|
58
53
|
}
|
package/src/db/presence-write.ts
CHANGED
|
@@ -3,7 +3,6 @@ import { assertMainThreadState } from "./main-thread-state";
|
|
|
3
3
|
// Kept outside agents.ts because the writer registry imports the op body from that module; importing
|
|
4
4
|
// the gateway there would make the writer-stack cycle gateway → write-ops → agents → gateway.
|
|
5
5
|
|
|
6
|
-
import { setAsyncPresenceFlusher } from "./presence-loop.ts";
|
|
7
6
|
import { settlePendingPresenceFlush, snapshotPendingPresenceFlush, type PresenceFlushPayload } from "./presence-buffer.ts";
|
|
8
7
|
import { getGateway } from "./gateway.ts";
|
|
9
8
|
|
|
@@ -31,8 +30,3 @@ export function flushPresenceAsync(now: number = Date.now()): Promise<{ flushed:
|
|
|
31
30
|
.finally(() => { activeFlush = undefined; });
|
|
32
31
|
return activeFlush;
|
|
33
32
|
}
|
|
34
|
-
|
|
35
|
-
// The timer remains owned by presence-loop.ts for its synchronous shutdown guarantee, while normal ticks
|
|
36
|
-
// arrive here through an injected callback. This direction deliberately avoids a writer-op → gateway
|
|
37
|
-
// dependency cycle; the callback carries no DB closure across the worker seam.
|
|
38
|
-
setAsyncPresenceFlusher(flushPresenceAsync);
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import type { writeProjectEntry, promoteBlackboardEntry } from "./project-entries";
|
|
1
3
|
// Async project-entries facades (T4060 hub on-loop). Kept outside
|
|
2
4
|
// project-entries.ts to avoid a gateway cycle.
|
|
3
5
|
import { getGateway } from "./gateway.ts";
|
|
@@ -16,3 +18,15 @@ export const searchProjectEntriesAsync = (
|
|
|
16
18
|
query: string,
|
|
17
19
|
opts: ProjectEntrySearchOptions = {},
|
|
18
20
|
): Promise<ProjectEntrySearchResult[]> => read("projectEntries.search", { projectId, query, ...opts });
|
|
21
|
+
|
|
22
|
+
export const getProjectEntryAsync = (id: string): Promise<ProjectEntry | null> => read("projectEntries.get", { id });
|
|
23
|
+
export const getProjectEntryChainAsync = (id: string): Promise<ProjectEntry[]> => read("projectEntries.chain", { id });
|
|
24
|
+
export const listActiveEntriesInAreaAsync = (projectId: string, area: string): Promise<ProjectEntry[]> => read("projectEntries.inArea", { projectId, area });
|
|
25
|
+
export const countActivePinnedEntriesInAreaAsync = (projectId: string, area: string): Promise<number> => read("projectEntries.countPinnedInArea", { projectId, area });
|
|
26
|
+
|
|
27
|
+
export const writeProjectEntryAsync = (input: Parameters<typeof writeProjectEntry>[0], requestId: string = randomUUID()): Promise<ProjectEntry> =>
|
|
28
|
+
getGateway().write("projectEntries.write", input, { idempotencyKey: requestId }) as unknown as Promise<ProjectEntry>;
|
|
29
|
+
export const retractProjectEntryAsync = (id: string): Promise<ProjectEntry | null> =>
|
|
30
|
+
getGateway().write("projectEntries.retract", { id }) as unknown as Promise<ProjectEntry | null>;
|
|
31
|
+
export const promoteBlackboardEntryAsync = (input: Parameters<typeof promoteBlackboardEntry>[0], requestId: string = randomUUID()): Promise<ProjectEntry> =>
|
|
32
|
+
getGateway().write("projectEntries.promoteBlackboard", input, { idempotencyKey: requestId }) as unknown as Promise<ProjectEntry>;
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
// Project-entries reader operations (T4060 hub on-loop). The MCP memory/project
|
|
2
2
|
// read tools ran these scans synchronously on the event loop — including the
|
|
3
3
|
// temp-B-TREE display ORDER BY over cells whose rows are ~95% retracted.
|
|
4
|
-
import { listProjectEntries, searchProjectEntries } from "./project-entries.ts";
|
|
4
|
+
import { countActivePinnedEntriesInArea, getProjectEntry, getProjectEntryChain, listActiveEntriesInArea, listProjectEntries, searchProjectEntries } from "./project-entries.ts";
|
|
5
5
|
import type { ProjectEntryKind, ProjectEntryStatus, ProjectEntrySearchOptions } from "../types";
|
|
6
6
|
|
|
7
7
|
export const PROJECT_ENTRIES_READ_OPS = {
|
|
8
|
+
"projectEntries.get": (args: { id: string }) => getProjectEntry(args.id),
|
|
9
|
+
"projectEntries.chain": (args: { id: string }) => getProjectEntryChain(args.id),
|
|
10
|
+
"projectEntries.inArea": (args: { projectId: string; area: string }) => listActiveEntriesInArea(args.projectId, args.area),
|
|
11
|
+
"projectEntries.countPinnedInArea": (args: { projectId: string; area: string }) => countActivePinnedEntriesInArea(args.projectId, args.area),
|
|
8
12
|
"projectEntries.list": (args: {
|
|
9
13
|
projectId: string;
|
|
10
14
|
kind?: ProjectEntryKind;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { promoteBlackboardEntry, retractProjectEntry, writeProjectEntry } from "./project-entries";
|
|
2
|
+
import type { WriterOpSpec } from "./write-ops";
|
|
3
|
+
|
|
4
|
+
export const PROJECT_ENTRIES_WRITE_OPS: Readonly<Record<string, WriterOpSpec>> = {
|
|
5
|
+
"projectEntries.write": { offLoop: true, idempotent: false, fn: (args: Parameters<typeof writeProjectEntry>[0], ctx) => writeProjectEntry(args, ctx.now) },
|
|
6
|
+
"projectEntries.retract": { offLoop: true, idempotent: true, fn: (args: { id: string }, ctx) => retractProjectEntry(args.id, ctx.now) },
|
|
7
|
+
"projectEntries.promoteBlackboard": { offLoop: true, idempotent: false, fn: (args: Parameters<typeof promoteBlackboardEntry>[0], ctx) => promoteBlackboardEntry(args, ctx.now) },
|
|
8
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { isolateMemoryEntry } from "../memory-entry-safety";
|
|
2
2
|
import { validateProjectEntryText } from "../validation";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
|
-
import { getDb,
|
|
4
|
+
import { getDb, inWriteTransaction, ValidationError } from "./connection.ts";
|
|
5
5
|
import { getEntry as getBlackboardEntry, listEntries as listBlackboardEntries } from "./blackboard.ts";
|
|
6
6
|
import { parseJson } from "../utils";
|
|
7
7
|
import {
|
|
@@ -211,7 +211,7 @@ function assertProjectKnowledge(projectId: string): void {
|
|
|
211
211
|
}
|
|
212
212
|
}
|
|
213
213
|
|
|
214
|
-
export function writeProjectEntry(input: WriteProjectEntryInput): ProjectEntry {
|
|
214
|
+
export function writeProjectEntry(input: WriteProjectEntryInput, now: number = Date.now()): ProjectEntry {
|
|
215
215
|
validateProjectEntryText(input);
|
|
216
216
|
assertProjectKnowledge(input.projectId);
|
|
217
217
|
assertKind(input.kind);
|
|
@@ -234,8 +234,7 @@ export function writeProjectEntry(input: WriteProjectEntryInput): ProjectEntry {
|
|
|
234
234
|
supersedes = latestEntryByTitle(input.projectId, input.kind, input.title)?.id;
|
|
235
235
|
}
|
|
236
236
|
const id = projectEntryId();
|
|
237
|
-
|
|
238
|
-
writeTransaction(() => {
|
|
237
|
+
inWriteTransaction(() => {
|
|
239
238
|
let inheritedAreas: string[] | null = null;
|
|
240
239
|
if (supersedes) {
|
|
241
240
|
const previous = getProjectEntry(supersedes);
|
|
@@ -404,21 +403,25 @@ export function countActivePinnedEntriesInArea(projectId: string, area: string):
|
|
|
404
403
|
}
|
|
405
404
|
|
|
406
405
|
/** #3410 — active entries of one cell that name `area`, newest first. Same index as the count above. */
|
|
407
|
-
export function
|
|
408
|
-
|
|
406
|
+
export function buildActiveEntriesInAreaQuery(projectId: string, area: string): { sql: string; params: string[] } {
|
|
407
|
+
return { sql: `
|
|
409
408
|
SELECT * FROM project_entries
|
|
410
409
|
WHERE project_id = ? AND status = 'active' AND areas LIKE ? ESCAPE '\\'
|
|
411
410
|
ORDER BY updated_at DESC, created_at DESC, id DESC
|
|
412
|
-
|
|
413
|
-
return rows.map(rowToEntry);
|
|
411
|
+
`, params: [projectId, `%${escapeLike(JSON.stringify(area))}%`] };
|
|
414
412
|
}
|
|
415
413
|
|
|
416
|
-
export function
|
|
414
|
+
export function listActiveEntriesInArea(projectId: string, area: string): ProjectEntry[] {
|
|
415
|
+
const { sql, params } = buildActiveEntriesInAreaQuery(projectId, area);
|
|
416
|
+
return (getDb().query(sql).all(...params) as ProjectEntryRow[]).map(rowToEntry);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
export function retractProjectEntry(id: string, now: number = Date.now()): ProjectEntry | null {
|
|
417
420
|
const existing = getProjectEntry(id);
|
|
418
421
|
if (!existing) return null;
|
|
419
422
|
assertStatus("retracted");
|
|
420
423
|
getDb().query("UPDATE project_entries SET status = 'retracted', updated_at = ? WHERE id = ?")
|
|
421
|
-
.run(
|
|
424
|
+
.run(now, id);
|
|
422
425
|
return getProjectEntry(id);
|
|
423
426
|
}
|
|
424
427
|
|
|
@@ -472,7 +475,7 @@ function activePromotedEntry(projectId: string, originRef: string): ProjectEntry
|
|
|
472
475
|
* project supersedes the prior promoted copy rather than duplicating it, so the durable
|
|
473
476
|
* ledger holds at most one active entry per promoted finding while keeping the full
|
|
474
477
|
* supersede chain for audit. */
|
|
475
|
-
export function promoteBlackboardEntry(input: PromoteBlackboardEntryInput): ProjectEntry {
|
|
478
|
+
export function promoteBlackboardEntry(input: PromoteBlackboardEntryInput, now: number = Date.now()): ProjectEntry {
|
|
476
479
|
const source = getBlackboardEntry(input.blackboardEntryId);
|
|
477
480
|
if (!source) throw new ValidationError(`blackboard entry ${input.blackboardEntryId} not found`);
|
|
478
481
|
if (source.status !== "active") {
|
|
@@ -489,7 +492,7 @@ export function promoteBlackboardEntry(input: PromoteBlackboardEntryInput): Proj
|
|
|
489
492
|
source: "promoted",
|
|
490
493
|
originRef: source.id,
|
|
491
494
|
...(existing ? { supersedes: existing.id } : {}),
|
|
492
|
-
});
|
|
495
|
+
}, now);
|
|
493
496
|
}
|
|
494
497
|
|
|
495
498
|
export interface UnpromotedBlackboardSummary {
|
|
@@ -203,7 +203,7 @@ export function setProjectJournalFields(input: {
|
|
|
203
203
|
fields: Record<string, string | null>;
|
|
204
204
|
changedBy: string;
|
|
205
205
|
now?: number;
|
|
206
|
-
}): ProjectJournalFieldChange[] {
|
|
206
|
+
}, submittedAt: number = Date.now()): ProjectJournalFieldChange[] {
|
|
207
207
|
const entries = Object.entries(input.fields);
|
|
208
208
|
if (!entries.length) throw new ValidationError("fields must name at least one journal field");
|
|
209
209
|
const plan: Array<{ field: ProjectJournalField; value: string | null; previous: string | null }> = [];
|
|
@@ -222,7 +222,7 @@ export function setProjectJournalFields(input: {
|
|
|
222
222
|
plan.push({ field, value: value === "" ? null : value, previous });
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
-
const now = input.now ??
|
|
225
|
+
const now = input.now ?? submittedAt;
|
|
226
226
|
const changes: ProjectJournalFieldChange[] = [];
|
|
227
227
|
writeTransaction(() => {
|
|
228
228
|
for (const { field, value, previous } of plan) {
|
|
@@ -68,9 +68,9 @@ export interface UpsertProjectTrustApprovalInput {
|
|
|
68
68
|
/** Records `approvedValue` as the trusted content for this (profile, repo, capability), replacing
|
|
69
69
|
* any prior approval — used both by the operator-triggered dashboard approve action and by the
|
|
70
70
|
* gate's own grandfather-on-first-encounter path (decision #4). */
|
|
71
|
-
export function upsertProjectTrustApproval(input: UpsertProjectTrustApprovalInput): ProjectTrustApproval {
|
|
71
|
+
export function upsertProjectTrustApproval(input: UpsertProjectTrustApprovalInput, submittedAt: number = Date.now()): ProjectTrustApproval {
|
|
72
72
|
return writeTransaction(() => {
|
|
73
|
-
const now =
|
|
73
|
+
const now = submittedAt;
|
|
74
74
|
const existing = getDb().query(`
|
|
75
75
|
SELECT id FROM project_trust_approvals WHERE profile_name = ? AND repo_key = ? AND capability = ?
|
|
76
76
|
`).get(input.profileName, input.repoKey, input.capability) as { id: string } | null;
|
|
@@ -74,8 +74,8 @@ export function listTemplates(category?: string): PromptTemplateRecord[] {
|
|
|
74
74
|
return templates;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
export function upsertTemplate(slug: string, template: string): void {
|
|
78
|
-
const now =
|
|
77
|
+
export function upsertTemplate(slug: string, template: string, submittedAt: number = Date.now()): void {
|
|
78
|
+
const now = submittedAt;
|
|
79
79
|
if (versionedPromptSlot(slug)) {
|
|
80
80
|
throw new ValidationError("Use the versioned prompt writer with expectedVersion and actor");
|
|
81
81
|
}
|
|
@@ -83,11 +83,11 @@ export function upsertTemplate(slug: string, template: string): void {
|
|
|
83
83
|
if ((result.changes ?? 0) === 0) throw new ValidationError(`prompt template ${slug} not found`);
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
export function resetTemplate(slug: string): void {
|
|
86
|
+
export function resetTemplate(slug: string, submittedAt: number = Date.now()): void {
|
|
87
87
|
if (versionedPromptSlot(slug)) {
|
|
88
88
|
throw new ValidationError("Use the versioned prompt writer with expectedVersion and actor");
|
|
89
89
|
}
|
|
90
|
-
const now =
|
|
90
|
+
const now = submittedAt;
|
|
91
91
|
const result = getDb().query("UPDATE prompt_templates SET template = default_template, updated_at = ? WHERE slug = ?").run(now, slug);
|
|
92
92
|
if ((result.changes ?? 0) === 0) throw new ValidationError(`prompt template ${slug} not found`);
|
|
93
93
|
}
|