@humwick/server 0.130.1 → 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 +4 -0
- 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-B_rFEbnL.js → fleet-C7jQWbw2.js} +2 -2
- 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 +8 -0
- 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 +3 -3
- 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 +12 -1
- package/src/db/activity.ts +1 -14
- package/src/db/agent-search.ts +1 -15
- package/src/db/agents-async.ts +52 -5
- package/src/db/agents-write-ops.ts +12 -3
- package/src/db/agents.ts +81 -54
- package/src/db/ci-incidents-async.ts +5 -5
- package/src/db/ci-incidents-write-ops.ts +2 -2
- 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/config-store-write-ops.ts +7 -2
- package/src/db/continuation-archives-async.ts +5 -0
- package/src/db/continuation-archives-write-ops.ts +7 -0
- package/src/db/continuations.ts +2 -2
- package/src/db/db-writer-worker.ts +8 -1
- 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.ts +9 -6
- package/src/db/inbox.ts +0 -14
- package/src/db/index.ts +2 -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/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/migrations.ts +7 -15
- package/src/db/orchestrators.ts +4 -72
- package/src/db/pairs.ts +1 -13
- package/src/db/project-profile-instructions.ts +50 -0
- package/src/db/prompt-versions.ts +9 -2
- package/src/db/provider-config-history.ts +8 -3
- package/src/db/provider-model-seed.ts +34 -0
- package/src/db/read-op-marshaling-fixtures.ts +5 -0
- package/src/db/read-ops.ts +4 -0
- package/src/db/routing-policy.ts +2 -3
- package/src/db/schema.ts +9 -3
- package/src/db/spawn-delegation-state.ts +11 -4
- 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/timer-ingress.ts +7 -1
- package/src/db/token-db-write-ops.ts +17 -0
- package/src/db/token-standing.ts +15 -11
- package/src/db/workflow-contact.ts +1 -1
- package/src/db/workflow-task-read-ops.ts +30 -0
- package/src/db/write-ops.ts +6 -0
- package/src/db/writer-gateway.ts +17 -11
- 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-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/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/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 +1 -1
- 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 +2 -2
- package/src/routes/project-profile-instructions.ts +27 -0
- package/src/routes/project-trust.ts +2 -2
- package/src/routes/provider-config.ts +6 -6
- 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 +39 -7
- 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 +10 -7
- package/src/services/kernel-timers-ingress.ts +1 -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 -39
- 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 -42
- 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/index-B7ZerHLY.css +0 -2
- package/public/assets/index-BU3uWo2t.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
package/src/maintenance/jobs.ts
CHANGED
|
@@ -498,8 +498,9 @@ export const definitions: MaintenanceJobDefinition[] = [
|
|
|
498
498
|
// event loop was stalled, a live replacement's heartbeats may still be unprocessed, so the
|
|
499
499
|
// grace widens by the observed lag instead of misreading the backlog as post-mortem silence.
|
|
500
500
|
// #1895 (S3.2 L10, review finding 3) — routed through the async gateway facade.
|
|
501
|
-
|
|
502
|
-
|
|
501
|
+
// #3776 — paged short writes, bounded by the abort signal and a time budget.
|
|
502
|
+
const { revoked, complete } = await revokeOrphanedRuntimeTokensAsync(STALE_TTL_MS, eventLoopLagGraceMs(), { signal: context.signal });
|
|
503
|
+
return { revokedOrphanTokens: revoked, moreWork: !complete };
|
|
503
504
|
},
|
|
504
505
|
},
|
|
505
506
|
{
|
|
@@ -2,8 +2,11 @@ import { timePostBindPhase } from "../post-bind-startup";
|
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { errMessage, isRecord } from "@humwick/sdk";
|
|
4
4
|
import { classifyPidLiveness } from "@humwick/sdk/process-utils";
|
|
5
|
-
import { CRITICAL_MAINTENANCE_PROGRESS_STALE_MS, createActivityEventAsync, getDb,
|
|
5
|
+
import { CRITICAL_MAINTENANCE_PROGRESS_STALE_MS, createActivityEventAsync, getDb, MAINTENANCE_RUN_AUTORESET_GUARD_SQL, recordDrainConvoy, writeTransaction } from "../db";
|
|
6
6
|
import { claimMaintenanceRunAsync, finishMaintenanceRunAsync, heartbeatMaintenanceRunAsync } from "../db/maintenance-job-runs-async";
|
|
7
|
+
import { ensureMaintenanceJobsAsync, reclaimDeadLeasesAsync, rescheduleRunOnStartJobsAsync } from "../db/maintenance-job-runs-async";
|
|
8
|
+
import { reclaimDeadLeases, rescheduleRunOnStartJobs, upsertMaintenanceJobs } from "../db/maintenance-job-reconcile";
|
|
9
|
+
import type { DeadLease, MaintenanceJobSpec } from "../db/maintenance-job-reconcile";
|
|
7
10
|
import type { MaintenanceRunOutcome } from "../db/maintenance-job-runs";
|
|
8
11
|
import { withAbortDeadline } from "../async-deadline";
|
|
9
12
|
import { MAINTENANCE_BREAKER_LAG_MS } from "../config";
|
|
@@ -173,36 +176,13 @@ export function resetOrphanTrackerForTests(): void {
|
|
|
173
176
|
}
|
|
174
177
|
|
|
175
178
|
export function startMaintenanceScheduler(): void {
|
|
176
|
-
//
|
|
177
|
-
//
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
scheduleRunOnStartJobs();
|
|
181
|
-
} catch (error) {
|
|
182
|
-
console.warn(`maintenance job reconcile at startup failed (will retry on next tick): ${errMessage(error)}`);
|
|
183
|
-
void ensureMaintenanceJobsResilient()
|
|
184
|
-
.then(() => {
|
|
185
|
-
scheduleRunOnStartJobs();
|
|
186
|
-
return runDueMaintenanceJobs();
|
|
187
|
-
})
|
|
188
|
-
.catch((retryError) => console.warn(`maintenance startup retry failed (will retry on next tick): ${errMessage(retryError)}`));
|
|
189
|
-
}
|
|
190
|
-
// #1493 — a relay restart orphans the maintenance-job lease its own dying
|
|
191
|
-
// process held moments earlier: lease_owner still names the dead PID and
|
|
192
|
-
// lease_until is still in the future, so the claim guard in runJob/
|
|
193
|
-
// runDueMaintenanceJobs (lease_until IS NULL OR lease_until <= now) blocks
|
|
194
|
-
// the job — e.g. the CD watcher — until the lease's TTL self-expires (up to
|
|
195
|
-
// deadlineMs + 5s). Reclaim any lease whose owner PID is no longer alive so
|
|
196
|
-
// maintenance resumes immediately after a restart instead of stalling.
|
|
197
|
-
try {
|
|
198
|
-
reclaimDeadPidLeases();
|
|
199
|
-
} catch (error) {
|
|
200
|
-
console.warn(`dead-PID maintenance lease reclaim at startup failed (will retry on next tick): ${errMessage(error)}`);
|
|
201
|
-
}
|
|
179
|
+
// #1870 S7: the boot reconcile, run-on-start reschedule and dead-lease reclaim are writer ops. The first
|
|
180
|
+
// tick waits for them, so a due run-on-start job is never claimed before its reschedule lands.
|
|
181
|
+
void bootMaintenanceJobs().then(() => runDueMaintenanceJobs())
|
|
182
|
+
.catch((error) => console.warn(`maintenance tick failed: ${errMessage(error)}`));
|
|
202
183
|
// Floated ticks are wrapped defensively: runDueMaintenanceJobs already catches
|
|
203
184
|
// its own errors (#1481), but a stray rejection from a floated promise must
|
|
204
185
|
// never bubble to an unhandledRejection that exits the relay process.
|
|
205
|
-
void runDueMaintenanceJobs().catch((error) => console.warn(`maintenance tick failed: ${errMessage(error)}`));
|
|
206
186
|
if (timer) return;
|
|
207
187
|
timer = setInterval(
|
|
208
188
|
() => void runDueMaintenanceJobs().catch((error) => console.warn(`maintenance tick failed: ${errMessage(error)}`)),
|
|
@@ -210,6 +190,29 @@ export function startMaintenanceScheduler(): void {
|
|
|
210
190
|
);
|
|
211
191
|
}
|
|
212
192
|
|
|
193
|
+
async function bootMaintenanceJobs(): Promise<void> {
|
|
194
|
+
// A transient SQLITE_BUSY here must not abort startup: every scheduler tick runs the reconcile again.
|
|
195
|
+
try {
|
|
196
|
+
await ensureMaintenanceJobsAsync(jobSpecs());
|
|
197
|
+
} catch (error) {
|
|
198
|
+
console.warn(`maintenance job reconcile at startup failed (will retry on next tick): ${errMessage(error)}`);
|
|
199
|
+
}
|
|
200
|
+
try {
|
|
201
|
+
await rescheduleRunOnStartJobsAsync();
|
|
202
|
+
} catch (error) {
|
|
203
|
+
// The next tick retries it, so run-on-start jobs do not wait a full interval (#1870 S7).
|
|
204
|
+
runOnStartPending = true;
|
|
205
|
+
console.warn(`maintenance run-on-start reschedule failed (will retry on next tick): ${errMessage(error)}`);
|
|
206
|
+
}
|
|
207
|
+
// #1493 — a restart orphans the lease its dying process held: lease_owner still names the dead PID and
|
|
208
|
+
// lease_until is still in the future, which blocks the job (e.g. the CD watcher) until the TTL expires.
|
|
209
|
+
try {
|
|
210
|
+
await reclaimDeadLeasesAsync(findDeadLeases());
|
|
211
|
+
} catch (error) {
|
|
212
|
+
console.warn(`dead-PID maintenance lease reclaim at startup failed (will retry on next tick): ${errMessage(error)}`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
213
216
|
/**
|
|
214
217
|
* `runOnStart` means every Relay process start, not only the first insertion of
|
|
215
218
|
* a durable maintenance_jobs row. Historically ensureMaintenanceJobs used
|
|
@@ -219,29 +222,37 @@ export function startMaintenanceScheduler(): void {
|
|
|
219
222
|
* scheduler tick.
|
|
220
223
|
*/
|
|
221
224
|
export function scheduleRunOnStartJobs(now: number = Date.now()): void {
|
|
222
|
-
|
|
223
|
-
UPDATE maintenance_jobs
|
|
224
|
-
SET next_run_at = ?, updated_at = ?
|
|
225
|
-
WHERE enabled = 1 AND run_on_start = 1
|
|
226
|
-
`).run(now, now);
|
|
225
|
+
rescheduleRunOnStartJobs(now);
|
|
227
226
|
}
|
|
228
227
|
|
|
228
|
+
function readMaintenanceJobs(): MaintenanceJob[] {
|
|
229
|
+
const rows = getDb().query("SELECT * FROM maintenance_jobs ORDER BY title COLLATE NOCASE ASC").all() as MaintenanceJobRow[];
|
|
230
|
+
const now = Date.now();
|
|
231
|
+
const currentIds = new Set(definitions.map((definition) => definition.id));
|
|
232
|
+
return rows.filter((row) => currentIds.has(row.id)).map((row) => rowToJob(row, now));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// A transient lock while reconciling must not fail a listing; return the current rows (a later reconcile catches up). #1481.
|
|
229
236
|
export function listMaintenanceJobs(): MaintenanceJob[] {
|
|
230
237
|
try {
|
|
231
238
|
ensureMaintenanceJobs();
|
|
232
239
|
} catch (error) {
|
|
233
|
-
// A transient lock while reconciling must not fail a read-only listing; fall
|
|
234
|
-
// through and return the current rows (a later reconcile catches up). #1481.
|
|
235
240
|
console.warn(`maintenance job reconcile during list failed: ${errMessage(error)}`);
|
|
236
241
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
242
|
+
return readMaintenanceJobs();
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export async function listMaintenanceJobsAsync(): Promise<MaintenanceJob[]> {
|
|
246
|
+
try {
|
|
247
|
+
await ensureMaintenanceJobsAsync(jobSpecs());
|
|
248
|
+
} catch (error) {
|
|
249
|
+
console.warn(`maintenance job reconcile during list failed: ${errMessage(error)}`);
|
|
250
|
+
}
|
|
251
|
+
return readMaintenanceJobs();
|
|
241
252
|
}
|
|
242
253
|
|
|
243
254
|
export async function runMaintenanceJobNow(id: string, options: { overridePause?: boolean } = {}): Promise<MaintenanceJobRun> {
|
|
244
|
-
await
|
|
255
|
+
await ensureMaintenanceJobsAsync(jobSpecs());
|
|
245
256
|
const definition = definitions.find((job) => job.id === id);
|
|
246
257
|
if (!definition) throw new Error("maintenance job not found");
|
|
247
258
|
return runJob(definition, { force: true, overridePause: options.overridePause });
|
|
@@ -266,6 +277,7 @@ async function runMaintenanceJobsNow(ids: string[]): Promise<MaintenanceJobRun[]
|
|
|
266
277
|
// no-op (the still-open tick, or the one after, picks up whatever came due). Combined with the
|
|
267
278
|
// serialized drain below, this is the "one job at a time from a queue" the design (§5.4) calls for.
|
|
268
279
|
let tickInFlight = false;
|
|
280
|
+
let runOnStartPending = false;
|
|
269
281
|
|
|
270
282
|
export async function runDueMaintenanceJobs(): Promise<void> {
|
|
271
283
|
if (tickInFlight) return;
|
|
@@ -274,7 +286,11 @@ export async function runDueMaintenanceJobs(): Promise<void> {
|
|
|
274
286
|
// is guarded: a transient SQLITE_BUSY (or any other failure) reconciling or
|
|
275
287
|
// querying the job table is logged and the tick is skipped — the next tick retries.
|
|
276
288
|
try {
|
|
277
|
-
await
|
|
289
|
+
await ensureMaintenanceJobsAsync(jobSpecs());
|
|
290
|
+
if (runOnStartPending) {
|
|
291
|
+
await rescheduleRunOnStartJobsAsync();
|
|
292
|
+
runOnStartPending = false;
|
|
293
|
+
}
|
|
278
294
|
const now = Date.now();
|
|
279
295
|
const progressStaleAt = now - CRITICAL_MAINTENANCE_PROGRESS_STALE_MS;
|
|
280
296
|
const rows = getDb().query(`
|
|
@@ -363,84 +379,28 @@ export function drainDueMaintenanceJobsForTests(dueDefinitions: MaintenanceJobDe
|
|
|
363
379
|
* otherwise leave it latched, wedging every later test's tick). Production never calls this. */
|
|
364
380
|
export function resetSchedulerTickGuardForTests(): void {
|
|
365
381
|
tickInFlight = false;
|
|
382
|
+
runOnStartPending = false;
|
|
366
383
|
}
|
|
367
384
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
async function ensureMaintenanceJobsResilient(): Promise<void> {
|
|
380
|
-
for (let attempt = 1; ; attempt++) {
|
|
381
|
-
try {
|
|
382
|
-
ensureMaintenanceJobs();
|
|
383
|
-
return;
|
|
384
|
-
} catch (error) {
|
|
385
|
-
if (attempt < ENSURE_BUSY_RETRY_ATTEMPTS && isSqliteBusyError(error)) {
|
|
386
|
-
const cap = Math.min(ENSURE_BUSY_RETRY_MAX_MS, ENSURE_BUSY_RETRY_BASE_MS * 2 ** (attempt - 1));
|
|
387
|
-
const delay = cap / 2 + Math.random() * (cap / 2);
|
|
388
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
389
|
-
continue;
|
|
390
|
-
}
|
|
391
|
-
throw error;
|
|
392
|
-
}
|
|
393
|
-
}
|
|
385
|
+
function jobSpecs(): MaintenanceJobSpec[] {
|
|
386
|
+
return definitions.map((definition) => ({
|
|
387
|
+
id: definition.id,
|
|
388
|
+
title: definition.title,
|
|
389
|
+
description: definition.description,
|
|
390
|
+
intervalMs: definition.intervalMs,
|
|
391
|
+
deadlineMs: definition.deadlineMs ?? DEFAULT_DEADLINE_MS,
|
|
392
|
+
enabled: !isEphemeralMode() || definition.instanceSafe === true,
|
|
393
|
+
critical: definition.critical === true,
|
|
394
|
+
runOnStart: definition.runOnStart === true,
|
|
395
|
+
}));
|
|
394
396
|
}
|
|
395
397
|
|
|
398
|
+
// Boot/test twin of `ensureMaintenanceJobsAsync`: one BEGIN IMMEDIATE so busy_timeout governs a single lock
|
|
399
|
+
// acquisition (#1481). The listing read path and tests use it; the running scheduler uses the writer op.
|
|
396
400
|
function ensureMaintenanceJobs(): void {
|
|
397
|
-
const
|
|
401
|
+
const specs = jobSpecs();
|
|
398
402
|
const now = Date.now();
|
|
399
|
-
|
|
400
|
-
// CODE-owned and therefore self-healing. Before this it was set only on INSERT and deliberately
|
|
401
|
-
// omitted from the conflict clause, because the one column doubled as the operator's "stop" lever
|
|
402
|
-
// and a redeploy would have clobbered their intent. Operator intent now lives in
|
|
403
|
-
// `maintenance_job_pauses` (holder, reason, expiry), so re-asserting `enabled` overrides nobody —
|
|
404
|
-
// and a hand-written `UPDATE maintenance_jobs SET enabled=0` recovers at the next boot instead of
|
|
405
|
-
// persisting until someone writes more raw SQL (nine hours of deploy blackout, #1645).
|
|
406
|
-
//
|
|
407
|
-
// PRECONDITION: convertManualJobDisablesToPauses() (src/db/migrations.ts) must have run first, or
|
|
408
|
-
// this silently switches back on a job an operator deliberately stopped.
|
|
409
|
-
const stmt = db.query(`
|
|
410
|
-
INSERT INTO maintenance_jobs (id, title, description, interval_ms, timeout_ms, enabled, critical, run_on_start, next_run_at, updated_at)
|
|
411
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
412
|
-
ON CONFLICT(id) DO UPDATE SET
|
|
413
|
-
title = excluded.title,
|
|
414
|
-
description = excluded.description,
|
|
415
|
-
interval_ms = excluded.interval_ms,
|
|
416
|
-
timeout_ms = excluded.timeout_ms,
|
|
417
|
-
run_on_start = excluded.run_on_start,
|
|
418
|
-
enabled = excluded.enabled, -- #1645: code-owned, see the note above this query
|
|
419
|
-
critical = excluded.critical,
|
|
420
|
-
next_run_at = COALESCE(maintenance_jobs.next_run_at, excluded.next_run_at),
|
|
421
|
-
updated_at = excluded.updated_at
|
|
422
|
-
`);
|
|
423
|
-
// Batch the upserts into a single BEGIN IMMEDIATE write so busy_timeout governs
|
|
424
|
-
// ONE lock acquisition (SQLite waits) instead of one per definition, cutting the
|
|
425
|
-
// lock churn that made this loop the crash site under contention (#1481).
|
|
426
|
-
writeTransaction(() => {
|
|
427
|
-
for (const definition of definitions) {
|
|
428
|
-
stmt.run(
|
|
429
|
-
definition.id,
|
|
430
|
-
definition.title,
|
|
431
|
-
definition.description,
|
|
432
|
-
definition.intervalMs,
|
|
433
|
-
// #1728: the DB column stays `timeout_ms` — renaming it is a migration, not asked for here.
|
|
434
|
-
// Code-side this is `deadlineMs`: see MaintenanceJobDefinition for why.
|
|
435
|
-
definition.deadlineMs ?? DEFAULT_DEADLINE_MS,
|
|
436
|
-
!isEphemeralMode() || definition.instanceSafe === true ? 1 : 0,
|
|
437
|
-
definition.critical ? 1 : 0,
|
|
438
|
-
definition.runOnStart ? 1 : 0,
|
|
439
|
-
definition.runOnStart ? now : now + definition.intervalMs,
|
|
440
|
-
now,
|
|
441
|
-
);
|
|
442
|
-
}
|
|
443
|
-
});
|
|
403
|
+
writeTransaction(() => upsertMaintenanceJobs(specs, now));
|
|
444
404
|
}
|
|
445
405
|
|
|
446
406
|
// #1493 — reclaim current-schema maintenance_jobs leases whose owner PID is proven dead.
|
|
@@ -455,23 +415,19 @@ function ensureMaintenanceJobs(): void {
|
|
|
455
415
|
// #3645: the crash is not a job failure — it neither bumps consecutive_failures nor replaces an existing
|
|
456
416
|
// last_error (a non-null one belongs to the live failure streak; success clears it). The page-only guard remains on every
|
|
457
417
|
// deadline-based reclaim/claim path, where that positive evidence is unavailable.
|
|
458
|
-
|
|
459
|
-
const
|
|
460
|
-
const
|
|
461
|
-
const leased = db.query(`
|
|
462
|
-
SELECT id, lease_owner FROM maintenance_jobs
|
|
463
|
-
WHERE lease_owner IS NOT NULL
|
|
464
|
-
`).all() as Array<{ id: string; lease_owner: string }>;
|
|
418
|
+
function findDeadLeases(): DeadLease[] {
|
|
419
|
+
const leased = getDb().query("SELECT id, lease_owner FROM maintenance_jobs WHERE lease_owner IS NOT NULL").all() as Array<{ id: string; lease_owner: string }>;
|
|
420
|
+
const dead: DeadLease[] = [];
|
|
465
421
|
for (const row of leased) {
|
|
466
422
|
const pid = parseLeaseOwnerPid(row.lease_owner);
|
|
467
|
-
if (pid
|
|
468
|
-
db.query(`
|
|
469
|
-
UPDATE maintenance_jobs
|
|
470
|
-
SET lease_owner = NULL, lease_until = NULL, last_status = 'failed',
|
|
471
|
-
last_error = COALESCE(last_error, ?), run_token = NULL, last_progress_at = ?, updated_at = ?
|
|
472
|
-
WHERE id = ? AND lease_owner = ?
|
|
473
|
-
`).run(`reclaimed stale lease at startup: owner pid ${pid} is confirmed dead`, now, now, row.id, row.lease_owner);
|
|
423
|
+
if (pid !== null && classifyPidLiveness(pid) === "dead") dead.push({ id: row.id, owner: row.lease_owner, pid });
|
|
474
424
|
}
|
|
425
|
+
return dead;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
export function reclaimDeadPidLeases(): void {
|
|
429
|
+
const dead = findDeadLeases();
|
|
430
|
+
if (dead.length) writeTransaction(() => reclaimDeadLeases(dead, Date.now()));
|
|
475
431
|
}
|
|
476
432
|
|
|
477
433
|
function parseLeaseOwnerPid(owner: string): number | null {
|
|
@@ -527,7 +483,7 @@ async function runJob(definition: MaintenanceJobDefinition, options: { force?: b
|
|
|
527
483
|
const progressStaleAt = startedAt - CRITICAL_MAINTENANCE_PROGRESS_STALE_MS;
|
|
528
484
|
const claimed = await claimMaintenanceRunAsync({ id: definition.id, owner, runToken, startedAt, leaseUntil, progressStaleAt, force: options.force === true });
|
|
529
485
|
if (!claimed) {
|
|
530
|
-
const state =
|
|
486
|
+
const state = readMaintenanceJobs().find((job) => job.id === definition.id);
|
|
531
487
|
return { id: definition.id, status: "skipped", startedAt, finishedAt: Date.now(), durationMs: 0, result: { reason: "not due or already running", state } };
|
|
532
488
|
}
|
|
533
489
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { errMessage } from "@humwick/sdk";
|
|
2
|
-
import { getConfig
|
|
2
|
+
import { getConfig } from "../config-store";
|
|
3
|
+
import { setConfigAsync } from "../config-store-write";
|
|
3
4
|
import { getStalledDeliverySignalAsync } from "../db/stats-async.ts";
|
|
4
5
|
import { notifyWorkspaceHumanEscalation } from "../workspace-human-escalation";
|
|
5
6
|
import type { MaintenanceJobDefinition } from "./types";
|
|
@@ -56,8 +57,8 @@ export async function runStalledDeliveryPager(now: number = Date.now()): Promise
|
|
|
56
57
|
}
|
|
57
58
|
let alerted = false;
|
|
58
59
|
let recovered = false;
|
|
59
|
-
const persist = () => {
|
|
60
|
-
if (JSON.stringify(state) !== JSON.stringify(prior))
|
|
60
|
+
const persist = async () => {
|
|
61
|
+
if (JSON.stringify(state) !== JSON.stringify(prior)) await setConfigAsync(STATE_NAMESPACE, STATE_KEY, state, "stalled-delivery-pager");
|
|
61
62
|
};
|
|
62
63
|
|
|
63
64
|
const { check } = signal;
|
|
@@ -87,7 +88,7 @@ export async function runStalledDeliveryPager(now: number = Date.now()): Promise
|
|
|
87
88
|
if (!state.stalled && state.failStreak >= STALL_PAGE_CONFIRM_CHECKS) state = { ...state, stalled: true, since: now, alerted: false };
|
|
88
89
|
if (state.stalled && !state.alerted) {
|
|
89
90
|
// Persist the episode first so a crash after the send re-sends under the same key.
|
|
90
|
-
persist();
|
|
91
|
+
await persist();
|
|
91
92
|
const result = notifyWorkspaceHumanEscalation({
|
|
92
93
|
subject: "Message delivery stalled",
|
|
93
94
|
body: [
|
|
@@ -107,7 +108,7 @@ export async function runStalledDeliveryPager(now: number = Date.now()): Promise
|
|
|
107
108
|
state = { ...state, alerted: true };
|
|
108
109
|
}
|
|
109
110
|
}
|
|
110
|
-
persist();
|
|
111
|
+
await persist();
|
|
111
112
|
return { state: state.stalled ? "stalled" : "ok", alerted, recovered };
|
|
112
113
|
}
|
|
113
114
|
|
|
@@ -487,7 +487,7 @@ export async function workspaceGC(context: { signal?: AbortSignal } = {}): Promi
|
|
|
487
487
|
const proof = landedElsewhere === "confirmed" ? "ancestor" : landedElsewhere;
|
|
488
488
|
await reconcileLandedWorkspace(fresh, zeroCommitCloseOut
|
|
489
489
|
? { strategy: preview && !("available" in preview) ? preview.strategy : "rebase-ff", baseRef, ahead: 0, unmergedAhead: 0, dirtyCount: 0 }
|
|
490
|
-
: { strategy: preview && !("available" in preview) ? preview.strategy : "rebase-ff", baseRef, headSha: currentTip, landed: true, landedSha: currentTip });
|
|
490
|
+
: { strategy: preview && !("available" in preview) ? preview.strategy : "rebase-ff", baseRef, headSha: currentTip, landed: true, landedSha: currentTip }, landedElsewhere === "confirmed" ? currentTip : undefined);
|
|
491
491
|
await patchWorkspaceMetadataAsync(fresh.id, {
|
|
492
492
|
landedElsewhere: true,
|
|
493
493
|
landedElsewhereAt: now,
|
package/src/managed-policy.ts
CHANGED
|
@@ -7,14 +7,13 @@ import { getAgent } from "./db/agents";
|
|
|
7
7
|
import { getOrchestratorAsync } from "./db/orchestrators-async";
|
|
8
8
|
import { composeProfileSpawnInstructions, composeProfileSpawnInstructionsAsync } from "./project-instructions";
|
|
9
9
|
import { getSelfEffortCeiling } from "./db/self-effort-permissions";
|
|
10
|
-
import { runnerRuntimeTokenEnv, type RunnerLaunchAuthority } from "./runtime-tokens";
|
|
11
|
-
import { gatewayBackend } from "./db/gateway";
|
|
10
|
+
import { runnerRuntimeTokenEnv, runnerRuntimeTokenEnvAsync, type RunnerLaunchAuthority, type RunnerRuntimeTokenInput } from "./runtime-tokens";
|
|
12
11
|
import { recordedLaunchMode, resolveHostLaunchMode } from "./launch-mode";
|
|
13
12
|
import { buildSpawnCommand, generateSpawnAgentId, resolveSpawnModelParams, resolveSpawnModelParamsAsync } from "./spawn-command";
|
|
14
13
|
import { applyDefaultSharedMcp } from "./shared-mcp";
|
|
15
14
|
import { AGENT_SYSTEM_TAGS, systemTagsForSpawn } from "./agent-system-tags";
|
|
16
15
|
import { buildWhoamiDefaultForAgent } from "./mcp/identity-projection";
|
|
17
|
-
import {
|
|
16
|
+
import { isGenuineStewardPolicy, stewardWorktreeCwdPrefixes } from "./steward-spawn-guard";
|
|
18
17
|
// #2033 — from isolated-workspace-placement.ts, NOT ./services/spawn-identity: that module also
|
|
19
18
|
// exports buildProspectiveSpawnIdentity, which imports mcp/identity.ts → self-resume.ts →
|
|
20
19
|
// restart-spawn.ts → managed-policy.ts, and importing it here would close that into a cycle.
|
|
@@ -97,7 +96,8 @@ export function buildManagedSpawnParams(policy: SpawnPolicy, requestId: string,
|
|
|
97
96
|
const composed = composeProfileSpawnInstructions(managedInstructionInput(policy, agentProfile, agentRelayIdentity, orchestratorHost, alwaysOnAppend));
|
|
98
97
|
|
|
99
98
|
return buildManagedCommand({ policy, requestId, ctx, agentId, prompt, agentProfile, orchestrator, extraCwdPrefixes, composed,
|
|
100
|
-
modelParams: resolveSpawnModelParams(policy.provider, policy.model, policy.effort
|
|
99
|
+
modelParams: resolveSpawnModelParams(policy.provider, policy.model, policy.effort),
|
|
100
|
+
env: runnerRuntimeTokenEnv(managedTokenInput(policy, requestId, ctx, agentId, extraCwdPrefixes)),
|
|
101
101
|
});
|
|
102
102
|
}
|
|
103
103
|
|
|
@@ -147,6 +147,7 @@ function buildManagedCommand(input: {
|
|
|
147
147
|
extraCwdPrefixes: string[];
|
|
148
148
|
composed: ReturnType<typeof composeProfileSpawnInstructions>;
|
|
149
149
|
modelParams: ReturnType<typeof resolveSpawnModelParams>;
|
|
150
|
+
env: Record<string, string>;
|
|
150
151
|
}): Record<string, unknown> {
|
|
151
152
|
const { policy, requestId, ctx, agentId, prompt, agentProfile, orchestrator, extraCwdPrefixes, composed, modelParams } = input;
|
|
152
153
|
return buildSpawnCommand({
|
|
@@ -177,26 +178,31 @@ function buildManagedCommand(input: {
|
|
|
177
178
|
headless: true,
|
|
178
179
|
policyName: policy.name,
|
|
179
180
|
spawnRequestId: requestId,
|
|
180
|
-
env:
|
|
181
|
-
agentId,
|
|
182
|
-
orchestratorId: policy.orchestratorId,
|
|
183
|
-
cwd: policy.cwd,
|
|
184
|
-
provider: policy.provider,
|
|
185
|
-
label: policy.label,
|
|
186
|
-
policyName: policy.name,
|
|
187
|
-
spawnRequestId: requestId,
|
|
188
|
-
createdBy: ctx.createdBy,
|
|
189
|
-
authority: ctx.authority,
|
|
190
|
-
profile: policy.profile || undefined,
|
|
191
|
-
// #2245 — the same posture this launch is given above; stamped into the signed constraints.
|
|
192
|
-
approvalMode: policy.permissionMode,
|
|
193
|
-
...(extraCwdPrefixes.length ? { extraCwdPrefixes } : {}),
|
|
194
|
-
}),
|
|
181
|
+
env: input.env,
|
|
195
182
|
requestedBy: ctx.createdBy,
|
|
196
183
|
requestedAt: ctx.requestedAt ?? Date.now(),
|
|
197
184
|
});
|
|
198
185
|
}
|
|
199
186
|
|
|
187
|
+
/** The runner token a managed launch carries; the sync builder and the writer-op builder mint the same input. */
|
|
188
|
+
function managedTokenInput(policy: SpawnPolicy, requestId: string, ctx: ManagedSpawnContext, agentId: string, extraCwdPrefixes: string[]): RunnerRuntimeTokenInput {
|
|
189
|
+
return {
|
|
190
|
+
agentId,
|
|
191
|
+
orchestratorId: policy.orchestratorId,
|
|
192
|
+
cwd: policy.cwd,
|
|
193
|
+
provider: policy.provider,
|
|
194
|
+
label: policy.label,
|
|
195
|
+
policyName: policy.name,
|
|
196
|
+
spawnRequestId: requestId,
|
|
197
|
+
createdBy: ctx.createdBy,
|
|
198
|
+
authority: ctx.authority,
|
|
199
|
+
profile: policy.profile || undefined,
|
|
200
|
+
// #2245 — the same posture this launch is given above; stamped into the signed constraints.
|
|
201
|
+
approvalMode: policy.permissionMode,
|
|
202
|
+
...(extraCwdPrefixes.length ? { extraCwdPrefixes } : {}),
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
200
206
|
// #2656 — backstop for every managed launch path (policy start/restart routes, agent restart): no
|
|
201
207
|
// new process starts on a disabled provider. The lifecycle manager parks the policy before this.
|
|
202
208
|
function assertManagedProviderEnabled(message: string | undefined): void {
|
|
@@ -205,15 +211,12 @@ function assertManagedProviderEnabled(message: string | undefined): void {
|
|
|
205
211
|
|
|
206
212
|
/** Worker-safe counterpart used by every runtime managed launch path. The synchronous builder
|
|
207
213
|
* remains for in-process unit fixtures; production must never borrow its main-thread connection. */
|
|
208
|
-
export function buildManagedSpawnParamsAsync(policy: SpawnPolicy, requestId: string, ctx: ManagedSpawnContext):
|
|
214
|
+
export async function buildManagedSpawnParamsAsync(policy: SpawnPolicy, requestId: string, ctx: ManagedSpawnContext): Promise<Record<string, unknown>> {
|
|
209
215
|
if (effectiveManagedPolicyWorkspaceMode(policy) === "isolated" && ctx.gitRepoRoot === undefined) {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}));
|
|
216
|
+
const host = await getOrchestratorAsync(policy.orchestratorId);
|
|
217
|
+
ctx = { ...ctx, gitRepoRoot: host ? await resolveSpawnRepoRoot(host, policy.cwd) : null };
|
|
213
218
|
}
|
|
214
|
-
|
|
215
|
-
// backend never enters this synchronous branch, so it cannot reopen the closed-main DB path.
|
|
216
|
-
return gatewayBackend() === "in-process" ? buildManagedSpawnParams(policy, requestId, ctx) : buildManagedSpawnParamsWorkerAsync(policy, requestId, ctx);
|
|
219
|
+
return buildManagedSpawnParamsWorkerAsync(policy, requestId, ctx);
|
|
217
220
|
}
|
|
218
221
|
|
|
219
222
|
async function buildManagedSpawnParamsWorkerAsync(policy: SpawnPolicy, requestId: string, ctx: ManagedSpawnContext): Promise<Record<string, unknown>> {
|
|
@@ -231,6 +234,7 @@ async function buildManagedSpawnParamsWorkerAsync(policy: SpawnPolicy, requestId
|
|
|
231
234
|
const extraCwdPrefixes = managedCwdPrefixes(policy, requestId, ctx, orchestrator);
|
|
232
235
|
const composed = await composeProfileSpawnInstructionsAsync(managedInstructionInput(policy, agentProfile, agentRelayIdentity, orchestratorHost, alwaysOnAppend));
|
|
233
236
|
return buildManagedCommand({ policy, requestId, ctx, agentId, prompt, agentProfile, orchestrator, extraCwdPrefixes, composed,
|
|
234
|
-
modelParams: await resolveSpawnModelParamsAsync(policy.provider, policy.model, policy.effort
|
|
237
|
+
modelParams: await resolveSpawnModelParamsAsync(policy.provider, policy.model, policy.effort),
|
|
238
|
+
env: await runnerRuntimeTokenEnvAsync(managedTokenInput(policy, requestId, ctx, agentId, extraCwdPrefixes)),
|
|
235
239
|
});
|
|
236
240
|
}
|
package/src/mcp/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { normalizeOutputArgs, outputToolDefinition, presentToolOutput } from "./output-levels";
|
|
2
2
|
import { PROMPT_TOOLS, relayPrompt } from "./prompts";
|
|
3
|
+
import { PROJECT_PROFILE_INSTRUCTION_TOOLS, relayProjectProfileInstructions } from "./project-profile-instructions";
|
|
3
4
|
import { PromptVersionConflictError } from "../prompt-version-errors";
|
|
4
5
|
import { memoryRetirementMessage } from "../memory-retirement";
|
|
5
6
|
import { GUIDE_TOOLS, relayGuide } from "./guides";
|
|
@@ -60,6 +61,7 @@ const MCP_PROTOCOL_VERSION = "2024-11-05";
|
|
|
60
61
|
const MCP_BODY_OVERHEAD_BYTES = ARTIFACT_BODY_OVERHEAD_BYTES;
|
|
61
62
|
const TOOLS: ToolDefinition[] = [
|
|
62
63
|
...PROMPT_TOOLS,
|
|
64
|
+
...PROJECT_PROFILE_INSTRUCTION_TOOLS,
|
|
63
65
|
...WEB_TOOL_CALL_TOOLS,
|
|
64
66
|
...MESSAGING_TOOLS,
|
|
65
67
|
...ARTIFACT_TOOLS,
|
|
@@ -215,6 +217,7 @@ async function callTool(auth: VerifiedMcpAuthContext, params: unknown, signal?:
|
|
|
215
217
|
else if (name === "relay_hosts") result = await relayHosts(auth, args);
|
|
216
218
|
else if (name === "relay_host_gate_budget") result = await relayHostGateBudget(auth, args);
|
|
217
219
|
else if (name === "relay_prompt") result = await relayPrompt(auth, args);
|
|
220
|
+
else if (name === "relay_project_profile_instructions") result = await relayProjectProfileInstructions(auth, args);
|
|
218
221
|
else if (name === "relay_service_access") result = await relayServiceAccess(auth, args);
|
|
219
222
|
else if (name.startsWith("relay_secret_")) result = await relaySecretTool(auth, name, args);
|
|
220
223
|
else if (name === "relay_my_agents") result = await relayMyAgents(auth, args);
|
|
@@ -223,7 +226,7 @@ async function callTool(auth: VerifiedMcpAuthContext, params: unknown, signal?:
|
|
|
223
226
|
else if (name === "relay_agent_keep") result = await relayAgentKeep(auth, args);
|
|
224
227
|
else if (name === "relay_codex_reset_consume") result = await relayCodexResetConsume(args);
|
|
225
228
|
else if (name === "relay_claude_api_budget") result = await relayClaudeApiBudget(args);
|
|
226
|
-
else if (name === "relay_profile_trust") result = relayProfileTrust(auth, args);
|
|
229
|
+
else if (name === "relay_profile_trust") result = await relayProfileTrust(auth, args);
|
|
227
230
|
else if (name.startsWith("relay_routing_policy_")) result = await relayRoutingPolicyTool(auth, name, args);
|
|
228
231
|
else if (name === "relay_agent_action") result = await relayAgentAction(auth, args);
|
|
229
232
|
else if (name === "relay_set_effort") result = await relaySetEffort(auth, args);
|
|
@@ -253,7 +256,7 @@ async function callTool(auth: VerifiedMcpAuthContext, params: unknown, signal?:
|
|
|
253
256
|
// #2591 — the journal tools must claim their prefix BEFORE the generic relay_project_
|
|
254
257
|
// dispatcher, or they would fall through to relayProjectTool as "unknown tool".
|
|
255
258
|
else if (name.startsWith("relay_project_journal_")) result = relayProjectJournalTool(auth, name, args);
|
|
256
|
-
else if (name === "relay_project_ingest_claudemd") result = await relayProjectIngestClaudeMd(auth, args); else if (name.startsWith("relay_project_")) result = await relayProjectTool(auth, name, args); else if (name.startsWith("relay_issue_ref_")) result = relayIssueRefTool(auth, name, args); else if (name.startsWith("relay_issue_association_")) result = relayIssueAssociationTool(auth, name, args); else if (name.startsWith("relay_issue_activity_")) result = await relayIssueActivityTool(auth, name, args); else if (name.startsWith("relay_issue_lifecycle_")) result = await relayIssueLifecycleTool(auth, name, args);
|
|
259
|
+
else if (name === "relay_project_ingest_claudemd") result = await relayProjectIngestClaudeMd(auth, args); else if (name.startsWith("relay_project_")) result = await relayProjectTool(auth, name, args); else if (name.startsWith("relay_issue_ref_")) result = await relayIssueRefTool(auth, name, args); else if (name.startsWith("relay_issue_association_")) result = relayIssueAssociationTool(auth, name, args); else if (name.startsWith("relay_issue_activity_")) result = await relayIssueActivityTool(auth, name, args); else if (name.startsWith("relay_issue_lifecycle_")) result = await relayIssueLifecycleTool(auth, name, args);
|
|
257
260
|
else if (name === "relay_subscribe" || name === "relay_unsubscribe" || name === "relay_list_subscriptions" || name === "relay_why_notified" || name === "relay_notification_preview") result = await relayNotificationSubscriptionTool(auth, name, args);
|
|
258
261
|
else if (name === "relay_notify_ntfy") result = relayNotifyNtfy(args);
|
|
259
262
|
else if (name === "relay_delete_channel_binding") result = await relayDeleteChannelBinding(args, auth);
|
|
@@ -27,14 +27,14 @@ export const OUTPUT_DESCRIPTIONS: Record<string, string> = {
|
|
|
27
27
|
relay_kernel_listEntities: "List entities.",
|
|
28
28
|
relay_kernel_listEvents: "List events.",
|
|
29
29
|
relay_kernel_listInstances: "List workflow instances.",
|
|
30
|
-
relay_list_artifacts: "List artifacts
|
|
30
|
+
relay_list_artifacts: "List artifacts (metadata only).",
|
|
31
31
|
relay_list_subscriptions: "Your subscriptions",
|
|
32
32
|
relay_list_timers: "List your scheduled timers.",
|
|
33
33
|
relay_maintenance_jobs: "Job health; jobId reads runbook.",
|
|
34
34
|
relay_maintenance_pauses: "Maintenance pauses.",
|
|
35
35
|
relay_merge_pauses: "Merge holds",
|
|
36
36
|
relay_notification_preview: "Preview notification routing.",
|
|
37
|
-
relay_plan_get: "Read plan; mode:resume
|
|
37
|
+
relay_plan_get: "Read plan; mode:resume reads the frontier.",
|
|
38
38
|
relay_plan_list: "List team plans.",
|
|
39
39
|
relay_project_checkout: "Inspect, bind, unbind (keeps files) or remove (deletes files) a checkout.",
|
|
40
40
|
relay_project_get: "Read project and roots.",
|
|
@@ -44,7 +44,7 @@ export const OUTPUT_DESCRIPTIONS: Record<string, string> = {
|
|
|
44
44
|
relay_project_search: "Search memory; read full bodies by returned ids.",
|
|
45
45
|
relay_prompt: "Read/edit prompts; writes need expectedVersion; restore/adopt append.",
|
|
46
46
|
relay_read_artifact: "Page artifact text; binary returns download command.",
|
|
47
|
-
relay_recall: "Search history; read a returned ref
|
|
47
|
+
relay_recall: "Search history; read a returned ref.",
|
|
48
48
|
relay_release_readiness: "Read release readiness.",
|
|
49
49
|
relay_routing_policy_get: "Read policy",
|
|
50
50
|
relay_run: "Run shell work. Long work: waitMs:0, END YOUR TURN and await completion; never poll.",
|
|
@@ -53,17 +53,17 @@ export const OUTPUT_DESCRIPTIONS: Record<string, string> = {
|
|
|
53
53
|
relay_task_close_bridge: "Read Task close-bridge state.",
|
|
54
54
|
relay_task_get: "Read Task hydration.",
|
|
55
55
|
relay_task_history: "Read Task history.",
|
|
56
|
-
relay_task_list: "
|
|
56
|
+
relay_task_list: "Tasks.",
|
|
57
57
|
relay_task_memory_read: "Read Task continuation.",
|
|
58
58
|
relay_task_needs_validation: "List Tasks awaiting validation.",
|
|
59
59
|
relay_team_get: "Read team and roster summary.",
|
|
60
60
|
relay_team_template_get: "Read saved",
|
|
61
61
|
relay_team_template_list: "Saved",
|
|
62
|
-
relay_web_tool_calls: "Page web calls newest first;
|
|
62
|
+
relay_web_tool_calls: "Page web calls, newest first; epoch ms.",
|
|
63
63
|
relay_why_notified: "Explain delivery.",
|
|
64
|
-
relay_workflow_cancel: "Cancel run
|
|
65
|
-
relay_workflow_get: "Read run status; history
|
|
66
|
-
relay_workflow_resume: "Resume or answer/escalate a decision.",
|
|
64
|
+
relay_workflow_cancel: "Cancel run; reap agents.",
|
|
65
|
+
relay_workflow_get: "Read run status; history opt-in.",
|
|
66
|
+
relay_workflow_resume: "Resume, pause/unpause, or answer/escalate a decision.",
|
|
67
67
|
relay_workflow_start: "Start Task workflow.",
|
|
68
68
|
relay_workspace_deps: "Check or refresh workspace dependencies.",
|
|
69
69
|
relay_workspace_list: "List workspaces.",
|
package/src/mcp/presentation.ts
CHANGED
|
@@ -50,7 +50,7 @@ export type PresentationVerbosity = "brief" | "lean" | "default" | "verbose";
|
|
|
50
50
|
|
|
51
51
|
type TaskGateSummary = Record<TaskGateStatus, number> & { total: number };
|
|
52
52
|
|
|
53
|
-
type TaskDetailLeanPresentation = Pick<TaskDetail, "validationItems" | "staleOwnerGone" | "terminalStage" | "completedAt" | "owner"> & {
|
|
53
|
+
type TaskDetailLeanPresentation = Pick<TaskDetail, "validationItems" | "staleOwnerGone" | "terminalStage" | "completedAt" | "owner" | "onDeploy"> & {
|
|
54
54
|
id: number;
|
|
55
55
|
title: string;
|
|
56
56
|
status: string;
|
|
@@ -328,7 +328,7 @@ export function projectTaskList(
|
|
|
328
328
|
return tasks.map((task) => ({
|
|
329
329
|
id: task.id,
|
|
330
330
|
label: truncateTaskListLabel(task.label),
|
|
331
|
-
status: task.status, ...taskValidationProjection(task),
|
|
331
|
+
status: task.status, ...taskValidationProjection(task), ...(task.onDeploy ? { onDeploy: task.onDeploy } : {}),
|
|
332
332
|
}));
|
|
333
333
|
}
|
|
334
334
|
return tasks.map((task) => {
|
|
@@ -339,7 +339,7 @@ export function projectTaskList(
|
|
|
339
339
|
status: task.status,
|
|
340
340
|
deliverableStage: task.deliverableStage ?? null,
|
|
341
341
|
ownerAgentId: task.ownerAgentId ?? null,
|
|
342
|
-
...taskValidationProjection(task),
|
|
342
|
+
...taskValidationProjection(task), ...(task.onDeploy ? { onDeploy: task.onDeploy } : {}),
|
|
343
343
|
updatedAt: updatedAt.relative,
|
|
344
344
|
...(crossRepo && task.repo ? { repo: task.repo } : {}),
|
|
345
345
|
};
|
|
@@ -368,7 +368,7 @@ export function projectTaskDetail(
|
|
|
368
368
|
deliverableStage: task.deliverableStage ?? null,
|
|
369
369
|
ownerAgentLabel: task.ownerAgentLabel ?? null,
|
|
370
370
|
...(task.owner ? { owner: task.owner } : {}),
|
|
371
|
-
...taskValidationProjection(task),
|
|
371
|
+
...taskValidationProjection(task), ...(task.onDeploy ? { onDeploy: task.onDeploy } : {}),
|
|
372
372
|
issues: task.issues,
|
|
373
373
|
...(task.waitedOnBy?.length ? { waitedOnByPlans: task.waitedOnBy.length } : {}),
|
|
374
374
|
gates: summarizeTaskGates(task.gates),
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ValidationError } from "../db/connection";
|
|
2
|
+
import { authContextFromMcp } from "../services/auth-context";
|
|
3
|
+
import { listProjectProfileInstructions, mutateProjectProfileInstructions, projectProfileInstructionHistory, readProjectProfileInstructions } from "../services/project-profile-instructions";
|
|
4
|
+
import { stringField } from "./validation";
|
|
5
|
+
import type { McpAuthContext, ToolDefinition } from "./types";
|
|
6
|
+
import { hasAnyScope } from "./identity";
|
|
7
|
+
import { McpAuthError } from "../mcp-errors";
|
|
8
|
+
|
|
9
|
+
export const PROJECT_PROFILE_INSTRUCTION_TOOLS: ToolDefinition[] = [{
|
|
10
|
+
name: "relay_project_profile_instructions",
|
|
11
|
+
description: "Project additions to profile instructions. Empty version is 0. Writes need expectedVersion; restore appends history.",
|
|
12
|
+
requiredScopes: ["agent:read"],
|
|
13
|
+
inputSchema: { type: "object", properties: {
|
|
14
|
+
operation: { type: "string", enum: ["list", "get", "set", "history", "restore"] },
|
|
15
|
+
projectId: { type: "string" }, profile: { type: "string" }, template: { type: "string" },
|
|
16
|
+
expectedVersion: { type: "integer", minimum: 0 }, version: { type: "integer", minimum: 1 }, reason: { type: "string" },
|
|
17
|
+
}, required: ["operation"], additionalProperties: false },
|
|
18
|
+
}];
|
|
19
|
+
|
|
20
|
+
export async function relayProjectProfileInstructions(auth: McpAuthContext, args: Record<string, unknown>) {
|
|
21
|
+
if (["set", "restore"].includes(String(args.operation)) && !hasAnyScope(auth, ["agent:write"])) throw new McpAuthError("agent:write scope required");
|
|
22
|
+
const required = args.operation !== "list";
|
|
23
|
+
const projectId = stringField(args.projectId, "projectId", { required, max: 200 });
|
|
24
|
+
const profile = stringField(args.profile, "profile", { required, max: 200 });
|
|
25
|
+
switch (args.operation) {
|
|
26
|
+
case "list": return listProjectProfileInstructions({ projectId: projectId || undefined, profile: profile || undefined });
|
|
27
|
+
case "get": return readProjectProfileInstructions(projectId, profile);
|
|
28
|
+
case "history": return projectProfileInstructionHistory(projectId, profile);
|
|
29
|
+
case "set": case "restore": return mutateProjectProfileInstructions(authContextFromMcp(auth), projectId, profile, args, args.operation === "restore");
|
|
30
|
+
default: throw new ValidationError("unknown project profile instructions operation");
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/mcp/tool-groups.ts
CHANGED
|
@@ -29,7 +29,7 @@ export const RELAY_TOOL_GROUPS: Record<string, readonly string[]> = {
|
|
|
29
29
|
// #3110 — opt-in: only profiles that list it (plus coordinator/ops-assistant by default) get the
|
|
30
30
|
// tools AND the secrets:write scope; see runtime-tokens.ts.
|
|
31
31
|
secrets: ["relay_service_access", "relay_secret_reader_create", "relay_secret_project_create", "relay_secret_set"],
|
|
32
|
-
prompts: ["relay_prompt"],
|
|
32
|
+
prompts: ["relay_prompt", "relay_project_profile_instructions"],
|
|
33
33
|
quota: ["relay_codex_reset_consume", "relay_claude_api_budget"],
|
|
34
34
|
// #3327 — coordinator-only, like decisions: edits the fleet's routing preferences.
|
|
35
35
|
routing: ["relay_routing_policy_get", "relay_routing_policy_set"],
|