@humwick/server 0.130.0 → 0.130.2
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/README.md +5 -0
- package/docs/guides/tools-administration.md +6 -1
- package/docs/guides/tools-coordination.md +1 -0
- package/docs/guides/tools-tasks.md +8 -0
- package/docs/guides/workflows.md +58 -0
- package/docs/openapi.json +414 -1
- package/package.json +8 -17
- package/public/assets/{MessageBubble-BWpoBjl4.js → MessageBubble-BWLqMGKG.js} +2 -2
- package/public/assets/{PlanGraphPanel-C0auck3n.js → PlanGraphPanel-3TlIQpjk.js} +2 -2
- package/public/assets/{TraceView-DBnOx7AQ.js → TraceView-Wimm0Czv.js} +2 -2
- package/public/assets/{VaultDecisionCard-0kp2oNBC.js → VaultDecisionCard-Bau_vNax.js} +2 -2
- package/public/assets/{activity-yLztXEBt.js → activity-CrSurn-r.js} +2 -2
- package/public/assets/{agent-lineage-BbJDR1dW.js → agent-lineage-CknAJKcb.js} +2 -2
- package/public/assets/agent-liveness-CaNwK0Zf.js +2 -0
- package/public/assets/{agent-name-CTZBrSD9.js → agent-name-DKbiIhBo.js} +2 -2
- package/public/assets/agent-profiles-DwF6EQ4R.js +2 -0
- package/public/assets/{agent-project-badge-9TqzUUYE.js → agent-project-badge-Diz4Sjjb.js} +2 -2
- package/public/assets/{agent-tools-D4n40pn8.js → agent-tools-CRhpRQza.js} +2 -2
- package/public/assets/{agent-type-icon-D6FUarOf.js → agent-type-icon-LrUEU3lB.js} +2 -2
- package/public/assets/{agents-BYDslkJc.js → agents-B-v66C-4.js} +2 -2
- package/public/assets/{analytics-B-XSbEMC.js → analytics-BqCb-LKi.js} +2 -2
- package/public/assets/{artifact-viewer-DdWIfV6M.js → artifact-viewer-kf4pGuAR.js} +2 -2
- package/public/assets/{artifacts-oBOqks-2.js → artifacts-D32zyw4Y.js} +2 -2
- package/public/assets/{artifacts-DPN1q4pA.js → artifacts-DJevXcvg.js} +2 -2
- package/public/assets/{automation-4KcRFXEu.js → automation-BpRajx_5.js} +2 -2
- package/public/assets/{backlog-Cw9xPTCF.js → backlog-D_UjTdzX.js} +2 -2
- package/public/assets/{busy-health-CTu32MZt.js → busy-health-N3VEh4ao.js} +2 -2
- package/public/assets/{channels-BiXzg1G9.js → channels-9jopungq.js} +2 -2
- package/public/assets/{chat-Ctk2qwvA.js → chat-CSXrTB3C.js} +2 -2
- package/public/assets/{code-preview-Bm1wWqAp.js → code-preview-p1NnmqK0.js} +2 -2
- package/public/assets/{connectors-C36ax4Vw.js → connectors-CZdqlDNL.js} +2 -2
- package/public/assets/{cosmos-Jj9LK2Wf.js → cosmos-BgdsE4dG.js} +2 -2
- package/public/assets/{crate-agent-H6S7-B9T.js → crate-agent-30aXTKva.js} +2 -2
- package/public/assets/{dependency-graph-Cd1E23wa.js → dependency-graph-B_jvw2GE.js} +2 -2
- package/public/assets/{deploys-Bwfq146q.js → deploys-DoN6bAb5.js} +2 -2
- package/public/assets/{display-CfO7rTuF.js → display-BrKWFGJz.js} +2 -2
- package/public/assets/{files-DhhZI2Ih.js → files-D6XEu8Ng.js} +2 -2
- package/public/assets/fleet-C7jQWbw2.js +2 -0
- package/public/assets/{fleet-metrics-BkHP-P3R.js → fleet-metrics-BiDs3MLx.js} +2 -2
- package/public/assets/{formatted-body-Cm1RrzmN.js → formatted-body-CQBwc1fb.js} +3 -3
- package/public/assets/{formatted-body-impl-DQiOuOD1.js → formatted-body-impl-DyG0WsQS.js} +2 -2
- package/public/assets/{host-providers-dialog-CHqwXTov.js → host-providers-dialog-DKaW31dN.js} +2 -2
- package/public/assets/index-CRHep3M9.js +15 -0
- package/public/assets/index-MYAIwYTX.css +2 -0
- package/public/assets/{insights-fH1EL-qZ.js → insights-CzrtkpOE.js} +2 -2
- package/public/assets/{integrations-C5Oix6ng.js → integrations-aVsUmigt.js} +2 -2
- package/public/assets/{issue-ref-chip-ODyondIa.js → issue-ref-chip-BnSlM_95.js} +2 -2
- package/public/assets/{log-viewer-Cvfy6PW7.js → log-viewer-qe7Ee47Z.js} +2 -2
- package/public/assets/{maintenance-D8CDH5wZ.js → maintenance-NfjqFZAk.js} +2 -2
- package/public/assets/{managed-agents-egllbpij.js → managed-agents-BiH0HgaE.js} +2 -2
- package/public/assets/{markdown-preview-CYSFgVgK.js → markdown-preview-BixaT3Xr.js} +3 -3
- package/public/assets/{markdown-preview-impl-BteVIjX4.js → markdown-preview-impl-DNGlWIS3.js} +2 -2
- package/public/assets/{memory-C8WmXsyr.js → memory-DNsCl6UX.js} +2 -2
- package/public/assets/{messages-BzQdqlO6.js → messages-DopU1L1X.js} +2 -2
- package/public/assets/{orchestrators-B1HxzkNw.js → orchestrators-Cq7ckLQT.js} +2 -2
- package/public/assets/{overview-DCUnI4Z7.js → overview-C1DCTzzV.js} +2 -2
- package/public/assets/{pairs-ftCd2XcD.js → pairs-DE_m6rrE.js} +2 -2
- package/public/assets/{profile-icon-B2zH8DMH.js → profile-icon-B3rfh77F.js} +2 -2
- package/public/assets/project-profile-instructions-DQWZE0kA.js +2 -0
- package/public/assets/projects-Br2Bd2h1.js +2 -0
- package/public/assets/{prompt-settings-Bftq9czX.js → prompt-settings-BKvFsvGD.js} +2 -2
- package/public/assets/provider-display-DsuWMvVL.js +2 -0
- package/public/assets/{registry-DWCU5c8q.js → registry-Cw38_6gW.js} +2 -2
- package/public/assets/run-card-CpKqRbhg.js +2 -0
- package/public/assets/scroll-text-BKXS1ahr.js +2 -0
- package/public/assets/{secrets-C7GIUATz.js → secrets-DxI-vzSq.js} +2 -2
- package/public/assets/{service-access-settings-C3aujqTG.js → service-access-settings-D9s_VJbd.js} +2 -2
- package/public/assets/{settings-C6sZ9eBa.js → settings-CKZTVGIx.js} +2 -2
- package/public/assets/{shipping-Bu8p1gEw.js → shipping-B9YtnwtG.js} +2 -2
- package/public/assets/{stats-V9Z-GxUd.js → stats-DyUBKyWI.js} +2 -2
- package/public/assets/{status-dot-CVv0iGHL.js → status-dot-DYqlEUhJ.js} +2 -2
- package/public/assets/{store-BvtFdlGX.js → store-DcClXLH0.js} +3 -3
- package/public/assets/{tasks-CXU75HRF.js → tasks-Bh7Chr4-.js} +2 -2
- package/public/assets/{teams-BPf976zb.js → teams-bowI7qVY.js} +2 -2
- package/public/assets/{terminal-viewer-DE9BOe-8.js → terminal-viewer-BgVLHdsU.js} +3 -3
- package/public/assets/{terminal-viewer-impl-D04pjmGh.js → terminal-viewer-impl-Dz22UPy6.js} +2 -2
- package/public/assets/token-estimate-Co-Jdphh.js +2 -0
- package/public/assets/{use-agent-terminal-B1yR456R.js → use-agent-terminal-DSQQ-iY7.js} +2 -2
- package/public/assets/{use-decisions-D-S4tpDf.js → use-decisions-samMQhQf.js} +2 -2
- package/public/assets/use-project-list-59LheVP7.js +2 -0
- package/public/assets/{use-selectors-CnrAk2YP.js → use-selectors-BZa25xEB.js} +2 -2
- package/public/assets/{user-avatar-nBFIcR4M.js → user-avatar-DHxlvUvV.js} +2 -2
- package/public/assets/versioned-prompt-editor-BmED5DnJ.js +8 -0
- package/public/assets/{work-queue-TCjJxeT-.js → work-queue-CSvpuNLz.js} +2 -2
- package/public/assets/{workflow-marker-Cf3Hf2fN.js → workflow-marker-DAZcj5qt.js} +2 -2
- package/public/assets/{workflows-CXQX20Qj.js → workflows-DdmxRSyg.js} +2 -2
- package/public/assets/{workspaces-qSYzKYMA.js → workspaces-BxeKfVuF.js} +2 -2
- package/public/cosmos.html +4 -4
- package/public/index.html +31 -29
- package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/runner/plugins/claude/hooks/gate-run-guard.provisioned.mjs +31 -1
- package/runner/src/adapters/claude-effort.ts +2 -4
- package/runner/src/adapters/codex-launch-config.ts +4 -5
- package/runner/src/adapters/codex.ts +1 -1
- package/runner/src/adapters/opencode-provider.ts +7 -22
- package/runner/src/adapters/opencode.ts +4 -4
- package/runner/src/agent-runner/credentials.ts +42 -12
- package/runner/src/agent-runner/inbox.ts +27 -0
- package/runner/src/agent-runner/lifecycle-milestones.ts +0 -2
- package/runner/src/bin/claude.ts +4 -0
- package/runner/src/bin/codex.ts +4 -0
- package/runner/src/bin/opencode.ts +4 -0
- package/runner/src/index.ts +5 -2
- package/runner/src/provider-setup.ts +34 -0
- package/runner/src/providers.ts +2 -1
- package/runner/src/runner-core.ts +18 -23
- package/runner/src/spawn-input.ts +2 -1
- package/runner/src/test-run-dedup.ts +59 -56
- package/scripts/cd-config.ts +24 -3
- package/scripts/gate-run-lease.ts +10 -7
- package/scripts/gate.sh +5 -1
- package/src/branch-land-notice.ts +93 -0
- package/src/branch-landed.ts +7 -76
- package/src/bus.ts +19 -20
- package/src/cd-deploy-notify.ts +30 -2
- package/src/cd-host-lag-notify.ts +7 -6
- package/src/ci/incidents.ts +2 -2
- package/src/ci/monitor.ts +5 -8
- package/src/ci/root-missing.ts +2 -7
- package/src/ci/transition-notify.ts +7 -39
- package/src/cli/cd-pauses.ts +19 -0
- package/src/cli/orchestrator.ts +1 -1
- package/src/cli/state.ts +2 -19
- package/src/cli/task-edit.ts +22 -0
- package/src/cli/task.ts +3 -1
- package/src/cli/workflow.ts +8 -2
- package/src/commands-db.ts +10 -6
- package/src/commands-write.ts +8 -0
- package/src/config-store-write.ts +13 -1
- package/src/config-store.ts +25 -14
- package/src/db/activity-async.ts +6 -1
- package/src/db/activity-write-ops.ts +13 -1
- package/src/db/activity.ts +1 -14
- package/src/db/agent-lifecycle-write-ops.ts +1 -0
- package/src/db/agent-meta.ts +2 -2
- package/src/db/agent-ownership-write-ops.ts +4 -0
- package/src/db/agent-search.ts +1 -15
- package/src/db/agents-async.ts +58 -7
- package/src/db/agents-write-ops.ts +28 -5
- package/src/db/agents.ts +81 -54
- package/src/db/artifacts-write-ops.ts +8 -3
- package/src/db/bus-outbox-write-ops.ts +1 -0
- package/src/db/channels-write-ops.ts +4 -0
- package/src/db/ci-incidents-async.ts +5 -5
- package/src/db/ci-incidents-write-ops.ts +2 -2
- package/src/db/claude-api-budget-ops.ts +5 -5
- package/src/db/claude-app-oauth.ts +4 -4
- package/src/db/codex-reset-consumes-write-ops.ts +3 -0
- package/src/db/command-row.ts +10 -2
- package/src/db/commands-list-query.ts +2 -0
- package/src/db/commands-spawn-read.ts +1 -1
- package/src/db/commands-write-ops.ts +21 -0
- package/src/db/config-store-write-ops.ts +9 -2
- package/src/db/continuation-archives-async.ts +5 -0
- package/src/db/continuation-archives-write-ops.ts +9 -0
- package/src/db/continuations.ts +2 -2
- package/src/db/db-writer-worker.ts +8 -1
- package/src/db/delivery-write-ops.ts +5 -0
- package/src/db/deploy-events-async.ts +1 -0
- package/src/db/deploy-events-read-ops.ts +2 -1
- package/src/db/deploy-events.ts +35 -4
- package/src/db/hosts-registration.ts +10 -2
- package/src/db/hosts-write-ops.ts +14 -0
- package/src/db/hosts.ts +9 -6
- package/src/db/inbox-write-ops.ts +4 -0
- package/src/db/inbox.ts +0 -14
- package/src/db/index.ts +2 -0
- package/src/db/insights-write-ops.ts +1 -0
- package/src/db/integrations-async.ts +8 -0
- package/src/db/integrations-ops.ts +15 -0
- package/src/db/integrations.ts +15 -21
- package/src/db/issue-lifecycle.ts +3 -4
- package/src/db/issue-refs-async.ts +20 -0
- package/src/db/issue-refs-write-ops.ts +29 -0
- package/src/db/issue-refs.ts +7 -8
- package/src/db/long-running-work.ts +2 -2
- package/src/db/macos-privacy.ts +1 -1
- package/src/db/main-connection-writes.ts +23 -2
- package/src/db/maintenance-job-reconcile.ts +61 -0
- package/src/db/maintenance-job-runs-async.ts +14 -0
- package/src/db/maintenance-job-write-ops.ts +9 -0
- package/src/db/mappers.ts +2 -14
- package/src/db/merge-lease.ts +1 -15
- package/src/db/message-reads-write-ops.ts +3 -0
- package/src/db/message-write-ops.ts +4 -0
- package/src/db/migrations.ts +7 -15
- package/src/db/orchestrators-async.ts +7 -3
- package/src/db/orchestrators-write-ops.ts +7 -1
- package/src/db/orchestrators.ts +21 -77
- package/src/db/pairs.ts +1 -13
- package/src/db/project-profile-instructions.ts +50 -0
- package/src/db/prompt-versions-ops.ts +1 -0
- package/src/db/prompt-versions.ts +9 -2
- package/src/db/provider-catalog-write-ops.ts +4 -4
- package/src/db/provider-config-history.ts +8 -3
- package/src/db/provider-flap-messages-write-ops.ts +1 -0
- package/src/db/provider-model-seed.ts +34 -0
- package/src/db/provider-quota-lease-write-ops.ts +1 -0
- package/src/db/provider-quota-write-ops.ts +2 -0
- package/src/db/provisioning-write-ops.ts +9 -8
- package/src/db/read-op-marshaling-fixtures.ts +5 -0
- package/src/db/read-ops.ts +4 -0
- package/src/db/routing-policy.ts +3 -4
- package/src/db/schema.ts +9 -3
- package/src/db/secrets-broker-write-ops.ts +5 -5
- package/src/db/secrets-vault-write.ts +7 -7
- package/src/db/services-write-ops.ts +3 -0
- package/src/db/spawn-delegation-state.ts +11 -4
- package/src/db/spawn-tree-cleanup.ts +3 -0
- package/src/db/stats-async.ts +4 -3
- package/src/db/task-entity.ts +23 -2
- package/src/db/task-list.ts +3 -1
- package/src/db/tasks.ts +1 -17
- package/src/db/team-spawn-delegation.ts +1 -1
- package/src/db/teams-write-ops.ts +4 -4
- package/src/db/timer-cutover-write-ops.ts +7 -0
- package/src/db/timer-ingress-write-ops.ts +9 -0
- package/src/db/timer-ingress.ts +7 -1
- package/src/db/timer-operator-write-ops.ts +14 -0
- package/src/db/timer-transfer-write-ops.ts +4 -0
- package/src/db/timer-write-ops.ts +14 -0
- package/src/db/token-db-write-ops.ts +21 -0
- package/src/db/token-standing.ts +15 -11
- package/src/db/user-settings-write-ops.ts +1 -0
- package/src/db/vault-backup.ts +2 -2
- package/src/db/vault-expiry.ts +2 -2
- package/src/db/vault-health.ts +3 -3
- package/src/db/vault-token-ops.ts +8 -8
- package/src/db/web-tool-call-ops.ts +3 -3
- package/src/db/workflow-contact.ts +1 -1
- package/src/db/workflow-task-read-ops.ts +30 -0
- package/src/db/write-ops.ts +15 -1
- package/src/db/writer-fault-seam.ts +5 -2
- package/src/db/writer-gateway.ts +21 -12
- package/src/dispatch-availability.ts +10 -6
- package/src/ephemeral/{landing-ingress-proof-worker.ts → landing-ingress-proof.test-fixture.ts} +4 -2
- package/src/host/disk-alert.ts +121 -0
- package/src/lifecycle-manager.ts +3 -2
- package/src/maintenance/cd-watcher.ts +3 -3
- package/src/maintenance/critical-job-watchdog.ts +16 -17
- package/src/maintenance/index.ts +1 -0
- package/src/maintenance/jobs.ts +3 -2
- package/src/maintenance/scheduler.ts +84 -128
- package/src/maintenance/stalled-delivery-pager.ts +6 -5
- package/src/maintenance/workspaces.ts +1 -1
- package/src/managed-policy.ts +31 -27
- package/src/mcp/index.ts +5 -2
- package/src/mcp/output-descriptions.ts +8 -8
- package/src/mcp/presentation.ts +4 -4
- package/src/mcp/project-profile-instructions.ts +32 -0
- package/src/mcp/tool-groups.ts +1 -1
- package/src/mcp/tools-spawn.ts +4 -3
- package/src/mcp-agent-action.ts +1 -2
- package/src/mcp-host-tools.ts +1 -1
- package/src/mcp-issue-ref-tools.ts +12 -11
- package/src/mcp-maintenance-tools.ts +2 -2
- package/src/mcp-profile-trust-tools.ts +4 -4
- package/src/mcp-provider-catalog.ts +2 -2
- package/src/mcp-task-tools.ts +11 -5
- package/src/mcp-team-template-tools.ts +3 -3
- package/src/mcp-workflow-tools.ts +10 -3
- package/src/notification-types.ts +2 -1
- package/src/operator-alarm.ts +26 -11
- package/src/package-pack.ts +42 -39
- package/src/project-instructions.ts +4 -0
- package/src/project-profile-instruction-text.ts +6 -0
- package/src/project-profile-instructions.ts +20 -0
- package/src/provider-catalog-store.ts +3 -49
- package/src/provider-config-store.ts +12 -11
- package/src/provider-model-seed.ts +1 -18
- package/src/provider-quota-config-store.ts +5 -4
- package/src/provider-secrets.test-fixture.ts +3 -3
- package/src/recipe-runner.ts +2 -2
- package/src/repo-config-source.ts +10 -4
- package/src/routes/agent-profiles.ts +6 -6
- package/src/routes/agents-spawn.ts +1 -1
- package/src/routes/agents.ts +6 -6
- package/src/routes/commands-listing.ts +4 -0
- package/src/routes/commands.ts +2 -2
- package/src/routes/continuation-archives.ts +2 -2
- package/src/routes/host-resource-control.ts +4 -1
- package/src/routes/index.ts +10 -1
- package/src/routes/integrations.ts +24 -50
- package/src/routes/issue-refs.ts +7 -7
- package/src/routes/orchestrator-bootstrap.ts +3 -3
- package/src/routes/orchestrator-host-registration.ts +2 -2
- package/src/routes/orchestrator-host-utilization.ts +3 -1
- package/src/routes/orchestrator-proxy.ts +2 -2
- package/src/routes/orchestrator.ts +4 -4
- package/src/routes/project-profile-instructions.ts +27 -0
- package/src/routes/project-trust.ts +2 -2
- package/src/routes/provider-config.ts +9 -8
- package/src/routes/provider-quota-config.ts +2 -2
- package/src/routes/settings.ts +5 -5
- package/src/routes/stats.ts +6 -6
- package/src/routes/task-http.ts +15 -1
- package/src/routes/task-issues.ts +11 -26
- package/src/routes/tasks.ts +22 -53
- package/src/routes/team-templates.ts +5 -5
- package/src/routes/teams-config.ts +2 -2
- package/src/routes/tokens.ts +12 -5
- package/src/routes/workflows.ts +20 -5
- package/src/routing-policy-normalize.ts +1 -2
- package/src/routing-step-defaults.ts +0 -2
- package/src/runner-token-mint-limit.ts +63 -0
- package/src/runtime-grant.ts +4 -2
- package/src/runtime-tokens.ts +50 -9
- package/src/security.ts +13 -20
- package/src/server-background.ts +1 -1
- package/src/server.ts +5 -2
- package/src/services/host-utilization.ts +23 -0
- package/src/services/idle-epics.ts +5 -6
- package/src/services/issue-lifecycle.ts +57 -61
- package/src/services/issue-refs.ts +9 -13
- package/src/services/kernel-timers-cutover-chaos-worker.ts +12 -7
- package/src/services/kernel-timers-ingress.ts +1 -1
- package/src/services/kernel-timers-request-key-migration-chaos-worker.ts +4 -1
- package/src/services/landed-ancestry.ts +19 -3
- package/src/services/memory-areas.ts +15 -51
- package/src/services/plan-ci-notice.ts +57 -0
- package/src/services/plan-ci-recovery.ts +2 -55
- package/src/services/plan-wake-digest.ts +1 -0
- package/src/services/profile-trust.ts +4 -3
- package/src/services/project-config-read.ts +70 -0
- package/src/services/project-profile-instructions.ts +54 -0
- package/src/services/provisioning-harness/budget-baseline.json +4 -4
- package/src/services/restart-spawn.ts +61 -49
- package/src/services/self-care-state.ts +2 -1
- package/src/services/self-effort.ts +5 -5
- package/src/services/send-message.ts +5 -1
- package/src/services/spawn-agent.ts +13 -2
- package/src/services/spawn-chain-inspector.ts +6 -13
- package/src/services/spawn-policy.ts +68 -0
- package/src/services/spawn-tree-cleanup.ts +2 -2
- package/src/services/task-dispatch.ts +3 -0
- package/src/services/task-state-reconcile.ts +6 -14
- package/src/services/team-templates.ts +9 -8
- package/src/services/transient-agent-reaper.ts +3 -1
- package/src/services/workflow-auto-start.ts +25 -13
- package/src/services/workflow-ci-main.ts +1 -1
- package/src/services/workflow-deferred-cleanup.ts +4 -2
- package/src/services/workflow-effect-driver.ts +2 -0
- package/src/services/workflow-effects.ts +18 -11
- package/src/services/workflow-land-notice.ts +10 -0
- package/src/services/workflow-pause-text.ts +43 -0
- package/src/services/workflow-reconcile.ts +13 -11
- package/src/services/workflow-run-view.ts +2 -0
- package/src/services/workflow-runs.ts +29 -13
- package/src/services/workflow-supervisor-state.ts +5 -0
- package/src/services/workflow-supervisor.ts +7 -3
- package/src/settings/registry.ts +1 -2
- package/src/spawn-command.ts +4 -4
- package/src/spawn-models.ts +10 -12
- package/src/spawn-placement.ts +1 -1
- package/src/spawn-targets.ts +1 -1
- package/src/startup-context-injection.ts +1 -0
- package/src/state-adoption/cd-cutover.ts +53 -7
- package/src/state-adoption/fleet-cutover.ts +35 -0
- package/src/state-adoption/fleet-plan.ts +36 -0
- package/src/state-adoption/index.ts +2 -2
- package/src/state-adoption/linux-plan.ts +2 -2
- package/src/state-adoption/linux.ts +2 -2
- package/src/state-adoption/macos-plan.ts +2 -2
- package/src/state-adoption/macos.ts +1 -1
- package/src/steward.ts +7 -6
- package/src/task-dispatch-live.ts +1 -1
- package/src/task-dispatcher.ts +1 -1
- package/src/teams-config-store.ts +4 -3
- package/src/token-db-write.ts +10 -0
- package/src/workspace-command-settle.ts +2 -1
- package/src/workspace-steward-wake.ts +2 -2
- package/node_modules/@humwick/kernel/LICENSE +0 -167
- package/node_modules/@humwick/kernel/async-entity-persistence.ts +0 -344
- package/node_modules/@humwick/kernel/async-event-log.ts +0 -107
- package/node_modules/@humwick/kernel/automations.pilot.ts +0 -147
- package/node_modules/@humwick/kernel/config.ts +0 -111
- package/node_modules/@humwick/kernel/contract.ts +0 -572
- package/node_modules/@humwick/kernel/control-authz.ts +0 -281
- package/node_modules/@humwick/kernel/control.ts +0 -645
- package/node_modules/@humwick/kernel/effects.ts +0 -493
- package/node_modules/@humwick/kernel/event-log.ts +0 -319
- package/node_modules/@humwick/kernel/http-transport.ts +0 -384
- package/node_modules/@humwick/kernel/identity-grammar.ts +0 -20
- package/node_modules/@humwick/kernel/identity.ts +0 -248
- package/node_modules/@humwick/kernel/index.ts +0 -28
- package/node_modules/@humwick/kernel/kernel-persistence.ts +0 -181
- package/node_modules/@humwick/kernel/landing-incident.ts +0 -306
- package/node_modules/@humwick/kernel/landing.ts +0 -953
- package/node_modules/@humwick/kernel/manifest-validation.ts +0 -460
- package/node_modules/@humwick/kernel/mcp-transport.ts +0 -109
- package/node_modules/@humwick/kernel/message-authority-chaos-worker.ts +0 -200
- package/node_modules/@humwick/kernel/message-authority.ts +0 -281
- package/node_modules/@humwick/kernel/module-host.ts +0 -722
- package/node_modules/@humwick/kernel/openapi-init.ts +0 -14
- package/node_modules/@humwick/kernel/package.json +0 -47
- package/node_modules/@humwick/kernel/persistence-schema.ts +0 -248
- package/node_modules/@humwick/kernel/persistence.ts +0 -510
- package/node_modules/@humwick/kernel/registry.ts +0 -60
- package/node_modules/@humwick/kernel/sqlite-kernel-persistence.ts +0 -499
- package/node_modules/@humwick/kernel/sqlite-module-host.ts +0 -87
- package/node_modules/@humwick/kernel/timers-chaos-worker.ts +0 -186
- package/node_modules/@humwick/kernel/timers.pilot.ts +0 -1411
- package/node_modules/@humwick/kernel/workflow-activation.ts +0 -25
- package/node_modules/@humwick/kernel/workflow-backstop.ts +0 -40
- package/node_modules/@humwick/kernel/workflow-cancel-tree.ts +0 -20
- package/node_modules/@humwick/kernel/workflow-chaos-worker.ts +0 -213
- package/node_modules/@humwick/kernel/workflow-composition.ts +0 -231
- package/node_modules/@humwick/kernel/workflow-container.ts +0 -72
- package/node_modules/@humwick/kernel/workflow-control.test-fixture.ts +0 -42
- package/node_modules/@humwick/kernel/workflow-control.ts +0 -178
- package/node_modules/@humwick/kernel/workflow-definition.ts +0 -507
- package/node_modules/@humwick/kernel/workflow-dispatch.ts +0 -60
- package/node_modules/@humwick/kernel/workflow-foreach.ts +0 -167
- package/node_modules/@humwick/kernel/workflow-journal-codec.ts +0 -339
- package/node_modules/@humwick/kernel/workflow-journal.ts +0 -922
- package/node_modules/@humwick/kernel/workflow-lane.ts +0 -48
- package/node_modules/@humwick/kernel/workflow-lease-owner.ts +0 -60
- package/node_modules/@humwick/kernel/workflow-recovery.test-fixture.ts +0 -19
- package/node_modules/@humwick/kernel/workflow-runtime.ts +0 -938
- package/node_modules/@humwick/kernel/workflow-script.ts +0 -11
- package/node_modules/@humwick/kernel/workflow-state.ts +0 -299
- package/node_modules/@humwick/kernel/workflow-step-commit.ts +0 -195
- package/node_modules/@humwick/kernel/workflow-transitions.ts +0 -317
- package/node_modules/@humwick/kernel/workflow-values.ts +0 -153
- package/node_modules/@humwick/kernel/workflow.pilot.ts +0 -376
- package/node_modules/@humwick/offload/LICENSE +0 -167
- package/node_modules/@humwick/offload/README.md +0 -57
- package/node_modules/@humwick/offload/dist/async-runner.d.ts +0 -18
- package/node_modules/@humwick/offload/dist/async-runner.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/async-runner.js +0 -158
- package/node_modules/@humwick/offload/dist/async-runner.js.map +0 -1
- package/node_modules/@humwick/offload/dist/envelope.d.ts +0 -25
- package/node_modules/@humwick/offload/dist/envelope.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/envelope.js +0 -80
- package/node_modules/@humwick/offload/dist/envelope.js.map +0 -1
- package/node_modules/@humwick/offload/dist/fixtures/worker-task.d.ts +0 -10
- package/node_modules/@humwick/offload/dist/fixtures/worker-task.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/fixtures/worker-task.js +0 -59
- package/node_modules/@humwick/offload/dist/fixtures/worker-task.js.map +0 -1
- package/node_modules/@humwick/offload/dist/index.d.ts +0 -5
- package/node_modules/@humwick/offload/dist/index.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/index.js +0 -5
- package/node_modules/@humwick/offload/dist/index.js.map +0 -1
- package/node_modules/@humwick/offload/dist/subprocess-runner.d.ts +0 -62
- package/node_modules/@humwick/offload/dist/subprocess-runner.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/subprocess-runner.js +0 -295
- package/node_modules/@humwick/offload/dist/subprocess-runner.js.map +0 -1
- package/node_modules/@humwick/offload/dist/types.d.ts +0 -111
- package/node_modules/@humwick/offload/dist/types.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/types.js +0 -176
- package/node_modules/@humwick/offload/dist/types.js.map +0 -1
- package/node_modules/@humwick/offload/dist/worker-pool.d.ts +0 -23
- package/node_modules/@humwick/offload/dist/worker-pool.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/worker-pool.js +0 -435
- package/node_modules/@humwick/offload/dist/worker-pool.js.map +0 -1
- package/node_modules/@humwick/offload/dist/worker-runtime.d.ts +0 -2
- package/node_modules/@humwick/offload/dist/worker-runtime.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/worker-runtime.js +0 -27
- package/node_modules/@humwick/offload/dist/worker-runtime.js.map +0 -1
- package/node_modules/@humwick/offload/dist/worker.d.ts +0 -5
- package/node_modules/@humwick/offload/dist/worker.d.ts.map +0 -1
- package/node_modules/@humwick/offload/dist/worker.js +0 -1
- package/node_modules/@humwick/offload/dist/worker.js.map +0 -1
- package/node_modules/@humwick/offload/package.json +0 -46
- package/node_modules/@humwick/offload/src/async-runner.ts +0 -170
- package/node_modules/@humwick/offload/src/envelope.ts +0 -92
- package/node_modules/@humwick/offload/src/fixtures/worker-task.ts +0 -49
- package/node_modules/@humwick/offload/src/index.ts +0 -4
- package/node_modules/@humwick/offload/src/subprocess-runner.ts +0 -334
- package/node_modules/@humwick/offload/src/types.ts +0 -235
- package/node_modules/@humwick/offload/src/worker-pool.ts +0 -463
- package/node_modules/@humwick/offload/src/worker-runtime.ts +0 -32
- package/node_modules/@humwick/offload/src/worker.ts +0 -5
- package/public/assets/agent-liveness-DyqxfB-t.js +0 -2
- package/public/assets/agent-profiles-BpI_jTxq.js +0 -2
- package/public/assets/fleet-BL4UXS_k.js +0 -2
- package/public/assets/index-B7ZerHLY.css +0 -2
- package/public/assets/index-BK0WV6hF.js +0 -15
- package/public/assets/projects-B_lmcqfO.js +0 -2
- package/public/assets/provider-display-DbLWpP3j.js +0 -2
- package/public/assets/run-card-CO_bguQf.js +0 -2
- package/public/assets/use-project-list-B-T2Bnnn.js +0 -2
- package/public/assets/versioned-prompt-editor-Diu8BIaM.js +0 -8
- package/runner/src/gate-run-lease-probe.ts +0 -38
- package/src/db/claude-catalog-reconcile.ts +0 -27
- package/src/routing-model-aliases.ts +0 -21
|
@@ -24,6 +24,8 @@ const releaseFile = process.env.CUTOVER_CHAOS_RELEASE_FILE;
|
|
|
24
24
|
const resultFile = process.env.CUTOVER_CHAOS_RESULT_FILE;
|
|
25
25
|
const owner = process.env.CUTOVER_CHAOS_OWNER ?? `worker-${process.pid}`;
|
|
26
26
|
const leaseMs = Number(process.env.CUTOVER_CHAOS_LEASE_MS ?? "400");
|
|
27
|
+
let now = Number(required("CUTOVER_CHAOS_NOW"));
|
|
28
|
+
const clock = () => now;
|
|
27
29
|
|
|
28
30
|
function required(name: string): string {
|
|
29
31
|
const value = relayEnv(process.env, name);
|
|
@@ -60,7 +62,7 @@ function timerInput(idSuffix = String(process.pid)): CreateScheduledTimerRowInpu
|
|
|
60
62
|
createdByKind: "system",
|
|
61
63
|
createdByScopes: ["message:send"],
|
|
62
64
|
deliveryFrom: "system",
|
|
63
|
-
nextRunAt:
|
|
65
|
+
nextRunAt: now + 100_000,
|
|
64
66
|
scheduleKind: "once",
|
|
65
67
|
scheduleDef: { afterMs: 100_000 },
|
|
66
68
|
body: `cutover-${idSuffix}`,
|
|
@@ -87,6 +89,7 @@ async function main(): Promise<void> {
|
|
|
87
89
|
};
|
|
88
90
|
const c = new TimerMessageCutoverCoordinator({
|
|
89
91
|
ownerId: owner,
|
|
92
|
+
now: clock,
|
|
90
93
|
leaseMs,
|
|
91
94
|
tickMs: 25,
|
|
92
95
|
quiesceTimeoutMs: 5_000,
|
|
@@ -94,7 +97,7 @@ async function main(): Promise<void> {
|
|
|
94
97
|
faults,
|
|
95
98
|
});
|
|
96
99
|
await c.requestQuiesce();
|
|
97
|
-
if (boundary === "after-renewal")
|
|
100
|
+
if (boundary === "after-renewal") now += Math.ceil(leaseMs * 0.75);
|
|
98
101
|
await c.tick();
|
|
99
102
|
await c.tick();
|
|
100
103
|
await c.tick();
|
|
@@ -112,7 +115,7 @@ async function main(): Promise<void> {
|
|
|
112
115
|
const ticks = phaseTicks[boundary];
|
|
113
116
|
if (ticks === undefined) throw new Error(`unknown deadline-watch phase ${boundary}`);
|
|
114
117
|
const c = new TimerMessageCutoverCoordinator({
|
|
115
|
-
ownerId: owner, leaseMs: 10_000, tickMs: 25, quiesceTimeoutMs: 8_000, worstCaseDrainTransferMs: 10_000,
|
|
118
|
+
ownerId: owner, now: clock, leaseMs: 10_000, tickMs: 25, quiesceTimeoutMs: 8_000, worstCaseDrainTransferMs: 10_000,
|
|
116
119
|
});
|
|
117
120
|
await c.requestQuiesce();
|
|
118
121
|
for (let i = 0; i < ticks; i++) await c.tick();
|
|
@@ -124,14 +127,14 @@ async function main(): Promise<void> {
|
|
|
124
127
|
|
|
125
128
|
if (command === "claim-bound") {
|
|
126
129
|
const c = new TimerMessageCutoverCoordinator({
|
|
127
|
-
ownerId: owner, leaseMs: 1_000, tickMs: 25, quiesceTimeoutMs: 300, worstCaseDrainTransferMs: 1_000,
|
|
130
|
+
ownerId: owner, now: clock, leaseMs: 1_000, tickMs: 25, quiesceTimeoutMs: 300, worstCaseDrainTransferMs: 1_000,
|
|
128
131
|
});
|
|
129
132
|
await c.requestQuiesce();
|
|
130
133
|
await c.tick();
|
|
131
134
|
await c.tick();
|
|
132
135
|
const drain = await c.tick();
|
|
133
136
|
if (boundary === "held") {
|
|
134
|
-
|
|
137
|
+
now += 340;
|
|
135
138
|
await c.tick();
|
|
136
139
|
await c.tick();
|
|
137
140
|
}
|
|
@@ -141,7 +144,7 @@ async function main(): Promise<void> {
|
|
|
141
144
|
|
|
142
145
|
if (command === "recover") {
|
|
143
146
|
const c = new TimerMessageCutoverCoordinator({
|
|
144
|
-
ownerId: owner, leaseMs, tickMs: 25, quiesceTimeoutMs: 5_000, worstCaseDrainTransferMs: 6_000,
|
|
147
|
+
ownerId: owner, now: clock, leaseMs, tickMs: 25, quiesceTimeoutMs: 5_000, worstCaseDrainTransferMs: 6_000,
|
|
145
148
|
});
|
|
146
149
|
const outcomes = [];
|
|
147
150
|
for (let i = 0; i < 5; i++) outcomes.push(await c.tick());
|
|
@@ -152,7 +155,7 @@ async function main(): Promise<void> {
|
|
|
152
155
|
if (command === "close-gate") {
|
|
153
156
|
marker();
|
|
154
157
|
const c = new TimerMessageCutoverCoordinator({
|
|
155
|
-
ownerId: owner, leaseMs: 5_000, tickMs: 25, quiesceTimeoutMs: 5_000, worstCaseDrainTransferMs: 6_000,
|
|
158
|
+
ownerId: owner, now: clock, leaseMs: 5_000, tickMs: 25, quiesceTimeoutMs: 5_000, worstCaseDrainTransferMs: 6_000,
|
|
156
159
|
});
|
|
157
160
|
const acquired = await c.requestQuiesce();
|
|
158
161
|
const outcome = acquired ? await c.tick() : null;
|
|
@@ -207,3 +210,5 @@ async function main(): Promise<void> {
|
|
|
207
210
|
}
|
|
208
211
|
|
|
209
212
|
await main();
|
|
213
|
+
// Off-loop timer ops start the file-DB writer thread, which keeps an idle process alive (#1870 S6a).
|
|
214
|
+
process.exit(0);
|
|
@@ -322,7 +322,7 @@ export class TimerIngressRejectedError extends Error {
|
|
|
322
322
|
}
|
|
323
323
|
|
|
324
324
|
export function createTimerIngress(options: TimerIngressOptions): TimerIngress {
|
|
325
|
-
installTimerIngressSchema();
|
|
325
|
+
installTimerIngressSchema({ ifNotInstalled: true });
|
|
326
326
|
const apply = options.apply;
|
|
327
327
|
const ownerId = options.ownerId ?? `${process.pid}-${randomUUID()}`;
|
|
328
328
|
const now = options.now ?? Date.now;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { writeSync } from "node:fs";
|
|
1
2
|
import { Database } from "bun:sqlite";
|
|
2
3
|
import { applyConnectionPragmas, setDb } from "../db/connection.ts";
|
|
3
4
|
import { createTimerIngress, installTimerIngressSchema } from "./kernel-timers-ingress.ts";
|
|
@@ -66,7 +67,7 @@ if (command === "migrate") {
|
|
|
66
67
|
source: { kind: "snapshot", timer: replayDecoySnapshot() },
|
|
67
68
|
});
|
|
68
69
|
const cancelReplay = await ingress.acceptCancel({ timerId: "timer-cancel", requestKey: rawCancelKey });
|
|
69
|
-
|
|
70
|
+
writeSync(1, JSON.stringify({
|
|
70
71
|
create: { seq: replay.seq, timerId: replay.bound.id, state: replay.state },
|
|
71
72
|
cancel: { seq: cancelReplay.seq, timerId: "timer-cancel", state: cancelReplay.state },
|
|
72
73
|
}));
|
|
@@ -75,3 +76,5 @@ if (command === "migrate") {
|
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
db.close();
|
|
79
|
+
// The off-loop ingress ops start the file-DB writer thread, which keeps an idle process alive (#1870 S6a).
|
|
80
|
+
process.exit(0);
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { repoDefaultBranch } from "@humwick/sdk/land-gates";
|
|
2
|
+
import { withWorkspaceTaskOwnerRoles } from "../task-owner-notices";
|
|
1
3
|
// #1713 — the coordinator's "landed" signal, derived from GIT ANCESTRY rather than a command's
|
|
2
4
|
// reported status. The observed bug: a worker delivers `complete`, the merge executor stalls, and
|
|
3
5
|
// the coordinator is nonetheless told the work is "complete"/"landed" while `origin/main` is
|
|
@@ -113,7 +115,8 @@ const realVerifier: AncestryVerifier = async (repoRoot, sha, targetRef) => {
|
|
|
113
115
|
// source of truth; a fetch failure/timeout is swallowed — the checks below then run against
|
|
114
116
|
// whatever refs are already local, preserving the prior fail-closed behavior exactly.
|
|
115
117
|
const remoteBranch = ref.startsWith("origin/") ? ref.slice("origin/".length) : ref;
|
|
116
|
-
|
|
118
|
+
const fetched = remoteBranch ? await gitStatus(root, ["fetch", "--quiet", "origin", `+refs/heads/${remoteBranch}:refs/remotes/origin/${remoteBranch}`], GIT_FETCH_TIMEOUT_MS) : null;
|
|
119
|
+
if (ref.startsWith("origin/") && fetched !== 0) return "unresolvable";
|
|
117
120
|
// The sha itself must resolve to a commit in this repo, else ancestry is unknowable (not a
|
|
118
121
|
// negative). `--verify --quiet ^{commit}` distinguishes "sha absent" from "sha present but not
|
|
119
122
|
// on the ref".
|
|
@@ -246,6 +249,18 @@ export async function verifyLandedAncestry(repoRoot: string | undefined, sha: st
|
|
|
246
249
|
}
|
|
247
250
|
}
|
|
248
251
|
|
|
252
|
+
/** Notice proof requires the freshly fetched remote; a local-only main never qualifies (#3761). */
|
|
253
|
+
export async function verifyPublishedLand(repoRoot: string | undefined, sha: string | undefined, baseRef?: string): Promise<LandedAncestryVerdict> {
|
|
254
|
+
if (!repoRoot || !sha?.trim()) return "unresolvable";
|
|
255
|
+
try {
|
|
256
|
+
const base = baseRef ?? await repoDefaultBranch(async args => {
|
|
257
|
+
const result = await gitOutput(repoRoot, args);
|
|
258
|
+
return { ok: result?.exitCode === 0, stdout: result?.stdout ?? "", unavailable: !result };
|
|
259
|
+
});
|
|
260
|
+
return base ? await verifyLandedAncestry(repoRoot, sha, base.startsWith("origin/") ? base : `origin/${base}`) : "unresolvable";
|
|
261
|
+
} catch { return "unresolvable"; }
|
|
262
|
+
}
|
|
263
|
+
|
|
249
264
|
// #2718 — the landing host's own fresh-fetch ancestry check (`landVerifiedSha`) is ref-backed proof
|
|
250
265
|
// that works wherever the repo lives; the hub's check needs a checkout of the same remote on its own
|
|
251
266
|
// disk. Without a matching host verdict, fall back to the hub check (an absent path never confirms).
|
|
@@ -366,6 +381,7 @@ export function notifyLandUnconfirmed(input: LandUnconfirmedInput): void {
|
|
|
366
381
|
const { subject, body } = landUnconfirmedText(input);
|
|
367
382
|
|
|
368
383
|
const payload = {
|
|
384
|
+
verified: false,
|
|
369
385
|
kind: "workspace.land-unconfirmed",
|
|
370
386
|
notification: "land-unconfirmed",
|
|
371
387
|
landOutcome: kind,
|
|
@@ -397,7 +413,7 @@ export function notifyLandUnconfirmed(input: LandUnconfirmedInput): void {
|
|
|
397
413
|
metadata: { source: "server", maintenanceJobId: "workspace-land-unconfirmed", workspaceId: workspace.id, mergeCommandId: input.commandId, landOutcome: kind },
|
|
398
414
|
});
|
|
399
415
|
|
|
400
|
-
const recipients =
|
|
416
|
+
const recipients = withWorkspaceTaskOwnerRoles([owner, coordinator, fallback], workspace);
|
|
401
417
|
if (recipients.length === 0) return;
|
|
402
418
|
routeNotification({
|
|
403
419
|
type: "workspace.land_unconfirmed",
|
|
@@ -407,7 +423,7 @@ export function notifyLandUnconfirmed(input: LandUnconfirmedInput): void {
|
|
|
407
423
|
forceDelivery: true,
|
|
408
424
|
message: {
|
|
409
425
|
subject,
|
|
410
|
-
body
|
|
426
|
+
body: `${body} verified: false.`,
|
|
411
427
|
payload,
|
|
412
428
|
idempotencyKey: kind === "merge-failed" ? landFailureEventKey(workspace, input.commandId)
|
|
413
429
|
: `land-unconfirmed:${workspace.id}:${input.commandId ?? "nocmd"}:${kind}`,
|
|
@@ -2,15 +2,13 @@
|
|
|
2
2
|
// (relay_project_write_entry, relay_store_memory, the project-entry REST routes) resolves the
|
|
3
3
|
// vocabulary and the stored value here, so the closed vocabulary cannot drift between them.
|
|
4
4
|
|
|
5
|
-
import { AREAS_FILE, SYSTEM_AREAS, SYSTEM_AREA_DESCRIPTIONS, areasAtCommit, injectsAtStartup, isSystemArea,
|
|
6
|
-
import {
|
|
7
|
-
import { countActivePinnedEntriesInArea, getAgent,
|
|
8
|
-
import { localProjectGit, readHostProjectFiles } from "../project-roles";
|
|
9
|
-
import { hostIsRelayHost } from "../relay-host-locality";
|
|
5
|
+
import { AREAS_FILE, SYSTEM_AREAS, SYSTEM_AREA_DESCRIPTIONS, areasAtCommit, injectsAtStartup, isSystemArea, type AreaDefinition, type AreasConfig } from "@humwick/sdk/areas";
|
|
6
|
+
import { stringValue } from "@humwick/sdk";
|
|
7
|
+
import { countActivePinnedEntriesInArea, getAgent, ValidationError } from "../db";
|
|
10
8
|
import type { Project, ProjectEntry } from "../types";
|
|
11
9
|
import type { AuthContext } from "./auth-context";
|
|
12
10
|
import { callerHost, callerProjectId } from "./memory-identity";
|
|
13
|
-
import {
|
|
11
|
+
import { readProjectConfig, readProjectConfigFirst, type ConfigLookup, type ProjectCheckout } from "./project-config-read";
|
|
14
12
|
|
|
15
13
|
export const MAX_ENTRY_AREAS = 4;
|
|
16
14
|
const PROJECT_WIDE = "project-wide";
|
|
@@ -47,42 +45,15 @@ export function areaCallerFromCtx(ctx: AuthContext): AreaCaller {
|
|
|
47
45
|
}
|
|
48
46
|
|
|
49
47
|
/** A checkout to read areas.json from: a cwd on a host (a hostname or a registered orchestrator). */
|
|
50
|
-
export
|
|
51
|
-
cwd: string;
|
|
52
|
-
host?: string;
|
|
53
|
-
orchestratorId?: string;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export type AreasLookup = AreasAtCommit | { status: "unreadable"; error: string };
|
|
48
|
+
export type AreasCheckout = ProjectCheckout;
|
|
57
49
|
|
|
58
|
-
|
|
50
|
+
export type AreasLookup = ConfigLookup<AreasConfig>;
|
|
59
51
|
|
|
60
|
-
|
|
61
|
-
if (loggedProblems.has(key)) return;
|
|
62
|
-
if (loggedProblems.size >= 1000) loggedProblems.clear();
|
|
63
|
-
loggedProblems.add(key);
|
|
64
|
-
console.warn(`[memory-areas] ${AREAS_FILE} for ${key}: ${problem}; accepting system areas only`);
|
|
65
|
-
}
|
|
52
|
+
const ACCEPTING_SYSTEM_AREAS = "accepting system areas only";
|
|
66
53
|
|
|
67
|
-
/** #3635 — the areas.json at the checkout's default branch
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
export async function readAreasConfig(checkout: AreasCheckout): Promise<AreasLookup> {
|
|
71
|
-
const where = `${checkout.orchestratorId ?? checkout.host ?? "local"}:${checkout.cwd}`;
|
|
72
|
-
try {
|
|
73
|
-
const lookup = !checkout.orchestratorId && hostIsRelayHost(checkout.host)
|
|
74
|
-
? await readAreasAtDefaultBranch(localProjectGit(checkout.cwd))
|
|
75
|
-
: areasAtCommit(await readHostProjectFiles(
|
|
76
|
-
resolveProjectFilesHost({ subject: AREAS_FILE, cwd: checkout.cwd, orchestratorId: checkout.orchestratorId, preferHost: checkout.host }),
|
|
77
|
-
checkout.cwd, { paths: [AREAS_FILE] }, AREAS_FILE,
|
|
78
|
-
));
|
|
79
|
-
if (lookup.status === "invalid") logProblemOnce(`${where}@${lookup.sha}`, `invalid: ${lookup.error}`);
|
|
80
|
-
return lookup;
|
|
81
|
-
} catch (err) {
|
|
82
|
-
const error = errMessage(err);
|
|
83
|
-
logProblemOnce(where, `unreadable: ${error}`);
|
|
84
|
-
return { status: "unreadable", error };
|
|
85
|
-
}
|
|
54
|
+
/** #3635 — the areas.json at the checkout's default branch, through the shared project-config reader. */
|
|
55
|
+
export function readAreasConfig(checkout: AreasCheckout): Promise<AreasLookup> {
|
|
56
|
+
return readProjectConfig(checkout, AREAS_FILE, areasAtCommit, ACCEPTING_SYSTEM_AREAS);
|
|
86
57
|
}
|
|
87
58
|
|
|
88
59
|
function invalidDetail(lookup: Extract<AreasLookup, { status: "invalid" }>): string {
|
|
@@ -94,18 +65,11 @@ function invalidDetail(lookup: Extract<AreasLookup, { status: "invalid" }>): str
|
|
|
94
65
|
* answers. Every other cell (base/provider/host) takes system areas only, optionally. */
|
|
95
66
|
export async function resolveAreaVocabulary(anchor: Project, caller: AreaCaller | undefined): Promise<AreaVocabulary> {
|
|
96
67
|
if (anchor.scope !== "project") return { pathAreas: [], required: false, pathAreasMissing: "not-project" };
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
];
|
|
101
|
-
|
|
102
|
-
for (const checkout of checkouts) {
|
|
103
|
-
const lookup = await readAreasConfig(checkout);
|
|
104
|
-
if (lookup.status === "found") return { pathAreas: lookup.config.areas, required: true };
|
|
105
|
-
if (lookup.status === "absent") return { pathAreas: [], required: false, pathAreasMissing: "undeclared" };
|
|
106
|
-
if (lookup.status === "invalid") return { pathAreas: [], required: false, pathAreasMissing: "invalid", pathAreasDetail: invalidDetail(lookup) };
|
|
107
|
-
failures.push(lookup.error);
|
|
108
|
-
}
|
|
68
|
+
const preferred = caller?.cwd && caller.projectId === anchor.id ? [{ cwd: caller.cwd, host: caller.host }] : [];
|
|
69
|
+
const { lookup, failures } = await readProjectConfigFirst(anchor.id, preferred, AREAS_FILE, areasAtCommit, ACCEPTING_SYSTEM_AREAS);
|
|
70
|
+
if (lookup?.status === "found") return { pathAreas: lookup.config.areas, required: true };
|
|
71
|
+
if (lookup?.status === "absent") return { pathAreas: [], required: false, pathAreasMissing: "undeclared" };
|
|
72
|
+
if (lookup?.status === "invalid") return { pathAreas: [], required: false, pathAreasMissing: "invalid", pathAreasDetail: invalidDetail(lookup) };
|
|
109
73
|
return { pathAreas: [], required: false, pathAreasMissing: "unreadable", pathAreasDetail: failures.join("; ") || "the project has no checkout to read it from" };
|
|
110
74
|
}
|
|
111
75
|
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// #3716 — workflow effects read CI episodes without depending on workflow launch or recovery.
|
|
2
|
+
import { ValidationError } from "../db/connection";
|
|
3
|
+
import { computeCiFailureFingerprint } from "../ci/incidents";
|
|
4
|
+
import type { CiShaObservation } from "../db/ci-incidents";
|
|
5
|
+
import type { StoredPlanCiBlock } from "../db/plan-ci-blocks";
|
|
6
|
+
import { getGateway } from "../db/gateway";
|
|
7
|
+
import { getPlanGraphProjectionAsync } from "../db/plan-graph-async";
|
|
8
|
+
import { planNoticeTarget } from "../db/plan-completion";
|
|
9
|
+
import { notifySystemMessage } from "../notify";
|
|
10
|
+
import type { WorkflowRunRecord } from "@humwick/kernel/workflow.pilot";
|
|
11
|
+
|
|
12
|
+
/** Same watch and landed SHA, or the same failing jobs: an unrelated red main must not quiet this failure. */
|
|
13
|
+
export async function correlatedCiIncidents(rows: Array<{ workflow: string; row?: CiShaObservation }>): Promise<string[]> {
|
|
14
|
+
const ids: string[] = [];
|
|
15
|
+
for (const { workflow, row } of rows) {
|
|
16
|
+
if (!row) return [];
|
|
17
|
+
const incident = await getGateway().read("ciIncidents.activeForWatch", { watchKey: row.watchKey });
|
|
18
|
+
if (!incident) return [];
|
|
19
|
+
if (incident.openedHeadSha !== row.headSha) {
|
|
20
|
+
if (!row.failingJobs?.length) return [];
|
|
21
|
+
const fingerprint = computeCiFailureFingerprint({ workflow, failingRuns: [{ id: "sha", status: "completed", jobs: row.failingJobs.map((job) => ({ id: job.name, name: job.name })) }], conclusion: "failure" });
|
|
22
|
+
if (fingerprint !== incident.fingerprint) return [];
|
|
23
|
+
}
|
|
24
|
+
ids.push(incident.id);
|
|
25
|
+
}
|
|
26
|
+
return [...new Set(ids)].sort();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Return a quiet receipt only after both the blocked attempt and the actionable plan notice are durable. */
|
|
30
|
+
export async function planCiNotice(run: WorkflowRunRecord, payload: Record<string, unknown>, result = false): Promise<Record<string, unknown> | undefined> {
|
|
31
|
+
const owner = await getGateway().read("planDispatch.owner", { taskId: run.taskId });
|
|
32
|
+
if (!owner || owner.audit) return undefined;
|
|
33
|
+
let block = await getGateway().read("planDispatch.ci.block", { runId: run.id });
|
|
34
|
+
const incidentIds = Array.isArray(payload.incidentIds) ? payload.incidentIds.filter((id): id is string => typeof id === "string") : [];
|
|
35
|
+
if (!result && incidentIds.length && typeof payload.landedSha === "string") {
|
|
36
|
+
try {
|
|
37
|
+
block = await getGateway().write("planDispatch.ci.block", { planId: owner.planId, taskId: run.taskId, runId: run.id, sha: payload.landedSha, incidentIds, waitKey: run.escalation?.waitKey ?? "" }) as unknown as StoredPlanCiBlock;
|
|
38
|
+
} catch (error) {
|
|
39
|
+
// A superseded attempt must still send its normal CI notice (#3660).
|
|
40
|
+
if (error instanceof ValidationError && error.message === "CI blocker no longer owns its plan slot") return undefined;
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (!block || (result && (block.retriedAt !== undefined || block.waitKey !== run.escalation?.waitKey)) || (!result && !incidentIds.length)) return undefined;
|
|
45
|
+
const plan = await getPlanGraphProjectionAsync({ planId: owner.planId });
|
|
46
|
+
const to = plan && planNoticeTarget(plan);
|
|
47
|
+
if (!to) return undefined;
|
|
48
|
+
for (const incidentId of block.incidentIds) {
|
|
49
|
+
notifySystemMessage(to, {
|
|
50
|
+
subject: `Plan ${plan.title ?? plan.id}: CI blocked`,
|
|
51
|
+
body: `Plan ${plan.id}: T${run.taskId}, run ${run.id}, landed SHA ${block.sha}, is blocked by CI incident ${incidentId}. Fix the incident; Relay automatically retries each blocked run's exact landed SHA after clearance. Slots and dependents wait. relay_plan_get shows every blocked Task and run.`,
|
|
52
|
+
idempotencyKey: `plan:${plan.id}:ci:${incidentId}`, replyExpected: false,
|
|
53
|
+
payload: { kind: "plan.ci-blocked", planId: plan.id, incidentId, taskId: run.taskId, runId: run.id, landedSha: block.sha },
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return { quiet: "plan-ci-episode", planId: owner.planId, incidentIds: block.incidentIds };
|
|
57
|
+
}
|
|
@@ -1,62 +1,9 @@
|
|
|
1
|
-
// #3660 —
|
|
2
|
-
import {
|
|
3
|
-
import { computeCiFailureFingerprint } from "../ci/incidents";
|
|
4
|
-
import type { CiShaObservation } from "../db/ci-incidents";
|
|
1
|
+
// #3660 — clearance reuses the original run's CI resume path.
|
|
2
|
+
import type { WorkflowRunRecord } from "@humwick/kernel/workflow.pilot";
|
|
5
3
|
import type { StoredPlanCiBlock } from "../db/plan-ci-blocks";
|
|
6
4
|
import { getGateway } from "../db/gateway";
|
|
7
|
-
import { getPlanGraphProjectionAsync } from "../db/plan-graph-async";
|
|
8
|
-
import { planNoticeTarget } from "../db/plan-completion";
|
|
9
|
-
import { notifySystemMessage } from "../notify";
|
|
10
|
-
import type { WorkflowRunRecord } from "@humwick/kernel/workflow.pilot";
|
|
11
5
|
import { resumeWorkflowRun } from "./workflow-runs";
|
|
12
6
|
|
|
13
|
-
/** Same watch and landed SHA, or the same failing jobs: an unrelated red main must not quiet this failure. */
|
|
14
|
-
export async function correlatedCiIncidents(rows: Array<{ workflow: string; row?: CiShaObservation }>): Promise<string[]> {
|
|
15
|
-
const ids: string[] = [];
|
|
16
|
-
for (const { workflow, row } of rows) {
|
|
17
|
-
if (!row) return [];
|
|
18
|
-
const incident = await getGateway().read("ciIncidents.activeForWatch", { watchKey: row.watchKey });
|
|
19
|
-
if (!incident) return [];
|
|
20
|
-
if (incident.openedHeadSha !== row.headSha) {
|
|
21
|
-
if (!row.failingJobs?.length) return [];
|
|
22
|
-
const fingerprint = computeCiFailureFingerprint({ workflow, failingRuns: [{ id: "sha", status: "completed", jobs: row.failingJobs.map((job) => ({ id: job.name, name: job.name })) }], conclusion: "failure" });
|
|
23
|
-
if (fingerprint !== incident.fingerprint) return [];
|
|
24
|
-
}
|
|
25
|
-
ids.push(incident.id);
|
|
26
|
-
}
|
|
27
|
-
return [...new Set(ids)].sort();
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/** Return a quiet receipt only after both the blocked attempt and the actionable plan notice are durable. */
|
|
31
|
-
export async function planCiNotice(run: WorkflowRunRecord, payload: Record<string, unknown>, result = false): Promise<Record<string, unknown> | undefined> {
|
|
32
|
-
const owner = await getGateway().read("planDispatch.owner", { taskId: run.taskId });
|
|
33
|
-
if (!owner || owner.audit) return undefined;
|
|
34
|
-
let block = await getGateway().read("planDispatch.ci.block", { runId: run.id });
|
|
35
|
-
const incidentIds = Array.isArray(payload.incidentIds) ? payload.incidentIds.filter((id): id is string => typeof id === "string") : [];
|
|
36
|
-
if (!result && incidentIds.length && typeof payload.landedSha === "string") {
|
|
37
|
-
try {
|
|
38
|
-
block = await getGateway().write("planDispatch.ci.block", { planId: owner.planId, taskId: run.taskId, runId: run.id, sha: payload.landedSha, incidentIds, waitKey: run.escalation?.waitKey ?? "" }) as unknown as StoredPlanCiBlock;
|
|
39
|
-
} catch (error) {
|
|
40
|
-
// A superseded attempt must still send its normal CI notice (#3660).
|
|
41
|
-
if (error instanceof ValidationError && error.message === "CI blocker no longer owns its plan slot") return undefined;
|
|
42
|
-
throw error;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
if (!block || (result && (block.retriedAt !== undefined || block.waitKey !== run.escalation?.waitKey)) || (!result && !incidentIds.length)) return undefined;
|
|
46
|
-
const plan = await getPlanGraphProjectionAsync({ planId: owner.planId });
|
|
47
|
-
const to = plan && planNoticeTarget(plan);
|
|
48
|
-
if (!to) return undefined;
|
|
49
|
-
for (const incidentId of block.incidentIds) {
|
|
50
|
-
notifySystemMessage(to, {
|
|
51
|
-
subject: `Plan ${plan.title ?? plan.id}: CI blocked`,
|
|
52
|
-
body: `Plan ${plan.id}: T${run.taskId}, run ${run.id}, landed SHA ${block.sha}, is blocked by CI incident ${incidentId}. Fix the incident; Relay automatically retries each blocked run's exact landed SHA after clearance. Slots and dependents wait. relay_plan_get shows every blocked Task and run.`,
|
|
53
|
-
idempotencyKey: `plan:${plan.id}:ci:${incidentId}`, replyExpected: false,
|
|
54
|
-
payload: { kind: "plan.ci-blocked", planId: plan.id, incidentId, taskId: run.taskId, runId: run.id, landedSha: block.sha },
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
return { quiet: "plan-ci-episode", planId: owner.planId, incidentIds: block.incidentIds };
|
|
58
|
-
}
|
|
59
|
-
|
|
60
7
|
/** A kernel resume note is the durable receipt if the process dies before marking the plan's row. */
|
|
61
8
|
export async function recoverPlanCiBlock(run: WorkflowRunRecord): Promise<StoredPlanCiBlock | null> {
|
|
62
9
|
const block = await getGateway().read("planDispatch.ci.block", { runId: run.id });
|
|
@@ -70,6 +70,7 @@ export function renderPlanWakeDigest(agentId: string, now = Date.now()): string
|
|
|
70
70
|
do {
|
|
71
71
|
const page = listPlansByTeam(teamId, { limit: 8, cursor });
|
|
72
72
|
for (const summary of page.plans) {
|
|
73
|
+
if (!summary.nodeCount) continue;
|
|
73
74
|
const plan = getPlan(summary.id);
|
|
74
75
|
if (!plan) continue;
|
|
75
76
|
const line = planLine(plan, now);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// #3636 — project trust edits without the dashboard: set a profile's review mode, acknowledge its
|
|
2
2
|
// project hooks/MCP, approve an observed value. One seam for the HTTP routes and the MCP tool.
|
|
3
3
|
import { ValidationError, type AgentProfile, type ProfileProjectReviewMode } from "@humwick/sdk";
|
|
4
|
-
import { getAgentProfile,
|
|
4
|
+
import { getAgentProfile, validateAgentProfile } from "../config-store";
|
|
5
|
+
import { setAgentProfileAsync } from "../config-store-write";
|
|
5
6
|
import type { ConfigEntry } from "../types";
|
|
6
7
|
import type { ProjectTrustApproval, ProjectTrustCapability } from "../db/project-trust-approvals";
|
|
7
8
|
import { approveProjectCapabilityReview, projectCellsHash } from "../project-trust";
|
|
@@ -38,9 +39,9 @@ export function withProjectTrust(profile: AgentProfile, update: ProjectTrustUpda
|
|
|
38
39
|
return validated;
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
export function
|
|
42
|
+
export async function updateProfileProjectTrustAsync(name: string, update: ProjectTrustUpdate, by: string): Promise<ConfigEntry<AgentProfile>> {
|
|
42
43
|
if (!update.review && !update.ack?.length) throw new ValidationError("nothing to update: pass review and/or ack");
|
|
43
|
-
const saved =
|
|
44
|
+
const saved = await setAgentProfileAsync(withProjectTrust(existingProfile(name).value, update, by), by, { overrideBuiltIn: true });
|
|
44
45
|
emitConfigChanged(saved.namespace, saved.key, saved.version);
|
|
45
46
|
return saved;
|
|
46
47
|
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// #3635/#3736 — one reader for `.agent-relay/*.json` project config (areas.json, spawn-policy.json): the
|
|
2
|
+
// file at the checkout's default branch (origin/<default>, fetched first), read on the checkout's host
|
|
3
|
+
// through the #3043 project-files reader. Never a working tree and never cached, so a stale checkout or
|
|
4
|
+
// an old negative answer cannot hide a file that landed.
|
|
5
|
+
import { readProjectFiles } from "@humwick/sdk";
|
|
6
|
+
import type { ConfigAtCommit } from "@humwick/sdk/repo-config-file";
|
|
7
|
+
import { errMessage } from "@humwick/sdk";
|
|
8
|
+
import { rootsForProject } from "../db";
|
|
9
|
+
import { localProjectGit, readHostProjectFiles } from "../project-roles";
|
|
10
|
+
import { hostIsRelayHost } from "../relay-host-locality";
|
|
11
|
+
import { resolveProjectFilesHost } from "./spawn-project-role";
|
|
12
|
+
|
|
13
|
+
/** A checkout to read from: a cwd on a host (a hostname or a registered orchestrator). */
|
|
14
|
+
export interface ProjectCheckout {
|
|
15
|
+
cwd: string;
|
|
16
|
+
host?: string;
|
|
17
|
+
orchestratorId?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type ConfigLookup<T> = ConfigAtCommit<T> | { status: "unreadable"; error: string };
|
|
21
|
+
|
|
22
|
+
type Snapshot = { ref: string; sha: string; files: Record<string, string | null> };
|
|
23
|
+
|
|
24
|
+
const warned = new Set<string>();
|
|
25
|
+
|
|
26
|
+
/** One warning per distinct problem, so a broken file does not flood the log on every read. */
|
|
27
|
+
export function warnConfigOnce(key: string, message: string): void {
|
|
28
|
+
if (warned.has(key)) return;
|
|
29
|
+
if (warned.size >= 1000) warned.clear();
|
|
30
|
+
warned.add(key);
|
|
31
|
+
console.warn(message);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** `file` at the checkout's default branch; a read failure is `unreadable`, never a throw. Invalid and
|
|
35
|
+
* unreadable outcomes warn once, naming `file`, the checkout and why, with `consequence` appended. */
|
|
36
|
+
export async function readProjectConfig<T>(checkout: ProjectCheckout, file: string, parse: (snapshot: Snapshot) => ConfigAtCommit<T>, consequence: string): Promise<ConfigLookup<T>> {
|
|
37
|
+
const where = `${checkout.orchestratorId ?? checkout.host ?? "local"}:${checkout.cwd}`;
|
|
38
|
+
try {
|
|
39
|
+
const snapshot = !checkout.orchestratorId && hostIsRelayHost(checkout.host)
|
|
40
|
+
? await readProjectFiles({ paths: [file] }, localProjectGit(checkout.cwd))
|
|
41
|
+
: await readHostProjectFiles(
|
|
42
|
+
resolveProjectFilesHost({ subject: file, cwd: checkout.cwd, orchestratorId: checkout.orchestratorId, preferHost: checkout.host }),
|
|
43
|
+
checkout.cwd, { paths: [file] }, file,
|
|
44
|
+
);
|
|
45
|
+
const lookup = parse(snapshot);
|
|
46
|
+
if (lookup.status === "invalid") warnConfigOnce(`${file}@${where}@${lookup.sha}`, `[project-config] ${file} for ${where} at ${lookup.sha.slice(0, 12)} is invalid: ${lookup.error}; ${consequence}`);
|
|
47
|
+
return lookup;
|
|
48
|
+
} catch (err) {
|
|
49
|
+
const error = errMessage(err);
|
|
50
|
+
warnConfigOnce(`${file}@${where}`, `[project-config] ${file} for ${where}: unreadable: ${error}; ${consequence}`);
|
|
51
|
+
return { status: "unreadable", error };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** The first checkout that can be read answers: the preferred ones, then each registered root of the project. */
|
|
56
|
+
export async function readProjectConfigFirst<T>(
|
|
57
|
+
projectId: string | undefined, preferred: readonly ProjectCheckout[], file: string, parse: (snapshot: Snapshot) => ConfigAtCommit<T>, consequence: string,
|
|
58
|
+
): Promise<{ lookup?: ConfigAtCommit<T>; failures: string[] }> {
|
|
59
|
+
const checkouts: ProjectCheckout[] = [
|
|
60
|
+
...preferred,
|
|
61
|
+
...(projectId ? rootsForProject(projectId).map((root) => ({ cwd: root.rootPath, ...(root.orchestratorId ? { orchestratorId: root.orchestratorId } : {}) })) : []),
|
|
62
|
+
];
|
|
63
|
+
const failures: string[] = [];
|
|
64
|
+
for (const checkout of checkouts) {
|
|
65
|
+
const lookup = await readProjectConfig(checkout, file, parse, consequence);
|
|
66
|
+
if (lookup.status !== "unreadable") return { lookup, failures };
|
|
67
|
+
failures.push(lookup.error);
|
|
68
|
+
}
|
|
69
|
+
return { failures };
|
|
70
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { hasScope, isRecord } from "@humwick/sdk";
|
|
3
|
+
import type { ProjectProfileInstructions } from "../types";
|
|
4
|
+
import { listAgentProfiles, resolveAgentProfileKey } from "../config-store";
|
|
5
|
+
import { getGateway } from "../db/gateway";
|
|
6
|
+
import { ValidationError } from "../db/connection";
|
|
7
|
+
import { promptActorFromContext, promptVersionNumber } from "./prompt-version-writer";
|
|
8
|
+
import type { AuthContext } from "./auth-context";
|
|
9
|
+
import { cleanString } from "../validation";
|
|
10
|
+
import { emitConfigChanged } from "../sse";
|
|
11
|
+
|
|
12
|
+
export async function projectProfileInstructionKey(projectId: string, profile: string) {
|
|
13
|
+
const canonical = resolveAgentProfileKey(profile);
|
|
14
|
+
if (!canonical) throw new ValidationError("profile not found");
|
|
15
|
+
if (!await getGateway().read("projects.get", { id: projectId })) throw new ValidationError("project not found");
|
|
16
|
+
return { projectId, profile: canonical };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function readProjectProfileInstructions(projectId: string, profile: string) {
|
|
20
|
+
return getGateway().read("projectProfileInstructions.get", await projectProfileInstructionKey(projectId, profile));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function projectProfileInstructionHistory(projectId: string, profile: string) {
|
|
24
|
+
return { versions: await getGateway().read("projectProfileInstructions.history", await projectProfileInstructionKey(projectId, profile)) };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function listProjectProfileInstructions(filter: { projectId?: string; profile?: string }) {
|
|
28
|
+
const gateway = getGateway();
|
|
29
|
+
const profile = filter.profile ? resolveAgentProfileKey(filter.profile) : undefined;
|
|
30
|
+
if (filter.profile && !profile) throw new ValidationError("profile not found");
|
|
31
|
+
if (filter.projectId && !await gateway.read("projects.get", { id: filter.projectId })) throw new ValidationError("project not found");
|
|
32
|
+
if (!filter.projectId && !profile) throw new ValidationError("projectId or profile required");
|
|
33
|
+
const stored = await gateway.read("projectProfileInstructions.list", { projectId: filter.projectId, profile });
|
|
34
|
+
const byKey = new Map(stored.map(entry => [JSON.stringify([entry.projectId, entry.profile]), entry]));
|
|
35
|
+
const entry = (projectId: string, profile: string): ProjectProfileInstructions => byKey.get(JSON.stringify([projectId, profile])) ?? { projectId, profile, template: "", version: 0, updatedAt: 0 };
|
|
36
|
+
return { instructions: filter.projectId
|
|
37
|
+
? listAgentProfiles().filter(item => !profile || item.key === profile).map(item => entry(filter.projectId!, item.key))
|
|
38
|
+
: (await gateway.read("projects.list", { includeArchived: true })).map(project => ({ ...entry(project.id, profile!), projectTitle: project.title, projectSlug: project.slug })) };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function mutateProjectProfileInstructions(ctx: AuthContext, projectId: string, profile: string, input: unknown, restore = false) {
|
|
42
|
+
if (ctx.actor.kind !== "system" && !hasScope(ctx.scopes, "agent:write")) throw new ValidationError("agent:write scope required");
|
|
43
|
+
const key = await projectProfileInstructionKey(projectId, profile);
|
|
44
|
+
if (!isRecord(input)) throw new ValidationError("JSON object body required");
|
|
45
|
+
if (!Number.isSafeInteger(input.expectedVersion) || (input.expectedVersion as number) < 0) throw new ValidationError("expectedVersion must be a non-negative integer");
|
|
46
|
+
const result = await getGateway().write("projectProfileInstructions.write", {
|
|
47
|
+
...key, expectedVersion: input.expectedVersion as number,
|
|
48
|
+
template: restore ? undefined : input.template as string,
|
|
49
|
+
restoreVersion: restore ? promptVersionNumber(input.version, "version") : undefined,
|
|
50
|
+
actor: promptActorFromContext(ctx), reason: cleanString(input.reason, "reason", { max: 2000 }),
|
|
51
|
+
}, { idempotencyKey: randomUUID() }) as unknown as ProjectProfileInstructions;
|
|
52
|
+
emitConfigChanged("project-profile-instructions", `${key.projectId}:${key.profile}`, result.version);
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"unknown": 0
|
|
20
20
|
},
|
|
21
21
|
"mcp:relay-schemas": {
|
|
22
|
-
"residentKnown":
|
|
22
|
+
"residentKnown": 24574,
|
|
23
23
|
"onDemandAvailable": 0,
|
|
24
24
|
"unknown": 0
|
|
25
25
|
},
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
"unknown": 0
|
|
115
115
|
},
|
|
116
116
|
"mcp:relay-schemas": {
|
|
117
|
-
"residentKnown":
|
|
117
|
+
"residentKnown": 24574,
|
|
118
118
|
"onDemandAvailable": 0,
|
|
119
119
|
"unknown": 0
|
|
120
120
|
},
|
|
@@ -364,11 +364,11 @@
|
|
|
364
364
|
"reviewer:codex": 11221,
|
|
365
365
|
"sec-ops:claude": 11610,
|
|
366
366
|
"sec-ops:codex": 10644,
|
|
367
|
-
"ops-assistant:claude":
|
|
367
|
+
"ops-assistant:claude": 25390,
|
|
368
368
|
"ops-assistant:codex": 25562,
|
|
369
369
|
"sub-coordinator:claude": 20137,
|
|
370
370
|
"sub-coordinator:codex": 20392,
|
|
371
|
-
"coordinator:claude":
|
|
371
|
+
"coordinator:claude": 26584,
|
|
372
372
|
"coordinator:codex": 26622,
|
|
373
373
|
"isolated-research:claude": 2852,
|
|
374
374
|
"isolated-research:codex": 2790,
|