@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
package/src/routes/stats.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { listPauses, pauseJob, resumeJobAsync } from "../maintenance/pauses";
|
|
|
7
7
|
import { defaultIssueRepo } from "../config";
|
|
8
8
|
import { listSpawnPolicies } from "../config-store";
|
|
9
9
|
import { disabledBackingPolicyNames } from "../channel-policy-health";
|
|
10
|
-
import { cancelMaintenanceRun,
|
|
10
|
+
import { cancelMaintenanceRun, listMaintenanceJobsAsync, listRecentMaintenanceRuns, listRunningMaintenanceRuns, runLegacyMaintenanceReaper, runMaintenanceJobNow } from "../maintenance";
|
|
11
11
|
import { claudeAppOAuthHealth } from "../claude-app/oauth-http";
|
|
12
12
|
import { getConnector } from "../connectors";
|
|
13
13
|
import { channelsHostEnsureHealthCheck } from "../connector-boot-health";
|
|
@@ -66,7 +66,7 @@ export const getHealthRoute: Handler = async () => {
|
|
|
66
66
|
// #3606 — rolling 60 s load breakdown: callers by rate and CPU, emits, reader-pool ops.
|
|
67
67
|
export const getHubMetricsRoute: Handler = () => json(hubMetricsReport());
|
|
68
68
|
|
|
69
|
-
export const getMaintenanceJobs: Handler = () => json(
|
|
69
|
+
export const getMaintenanceJobs: Handler = async () => json(await listMaintenanceJobsAsync());
|
|
70
70
|
|
|
71
71
|
// #1728 stage 0 — a POST-HOC diagnostic surface, not a live incident one: it reads the in-memory
|
|
72
72
|
// run registry the scheduler maintains (see src/maintenance/scheduler.ts). That registry can only
|
|
@@ -87,7 +87,7 @@ export const postMaintenanceJobRun: Handler = async (req, params) => {
|
|
|
87
87
|
const parsed = await parseBody<{ overridePause?: unknown }>(req);
|
|
88
88
|
const overridePause = parsed.ok && parsed.body?.overridePause === true;
|
|
89
89
|
const run = await runMaintenanceJobNow(params.id!, { overridePause });
|
|
90
|
-
return json({ run, jobs:
|
|
90
|
+
return json({ run, jobs: await listMaintenanceJobsAsync() }, run.status === "failed" ? 500 : 202);
|
|
91
91
|
} catch (e) {
|
|
92
92
|
if (e instanceof Error && e.message.includes("not found")) return error(e.message, 404);
|
|
93
93
|
throw e;
|
|
@@ -110,7 +110,7 @@ export const postMaintenanceJobCancel: Handler = async (req, params) => {
|
|
|
110
110
|
if (cancelledRunIds.length === 0) {
|
|
111
111
|
return json({ ok: false, error: "maintenance job is not running", cancelledRunIds: [] }, 404);
|
|
112
112
|
}
|
|
113
|
-
return json({ ok: true, cancelledRunIds, requester, jobs:
|
|
113
|
+
return json({ ok: true, cancelledRunIds, requester, jobs: await listMaintenanceJobsAsync() });
|
|
114
114
|
};
|
|
115
115
|
|
|
116
116
|
export const getMaintenancePauses: Handler = () => {
|
|
@@ -136,7 +136,7 @@ export const postMaintenanceJobPause: Handler = async (req, params) => {
|
|
|
136
136
|
if (!result.ok) {
|
|
137
137
|
return json({ ok: false, error: "maintenance job already paused", pause: result.pause }, 409);
|
|
138
138
|
}
|
|
139
|
-
return json({ ok: true, pause: result.pause, jobs:
|
|
139
|
+
return json({ ok: true, pause: result.pause, jobs: await listMaintenanceJobsAsync() });
|
|
140
140
|
} catch (e) {
|
|
141
141
|
if (e instanceof ValidationError) return error(e.message, 400);
|
|
142
142
|
throw e;
|
|
@@ -147,7 +147,7 @@ export const postMaintenanceJobResume: Handler = async (_req, params) => {
|
|
|
147
147
|
try {
|
|
148
148
|
const result = await resumeJobAsync({ jobId: params.id! });
|
|
149
149
|
if (!result.resumed) return error("maintenance job is not paused", 404);
|
|
150
|
-
return json({ ok: true, ...result, jobs:
|
|
150
|
+
return json({ ok: true, ...result, jobs: await listMaintenanceJobsAsync() });
|
|
151
151
|
} catch (e) {
|
|
152
152
|
if (e instanceof ValidationError) return error(e.message, 400);
|
|
153
153
|
throw e;
|
package/src/routes/task-http.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isRecord } from "@humwick/sdk";
|
|
2
|
+
import { resolveActingUser, requestUserAddress, resolveActorLabel, error, parseBody, parseId } from "./_shared";
|
|
2
3
|
import { ValidationError } from "../db/connection";
|
|
3
4
|
import { authContextFromRequest } from "../services/auth-context";
|
|
4
5
|
import { isRequestAuthorizedFor } from "../security";
|
|
@@ -25,3 +26,16 @@ export function taskEntityError(e: unknown): Response {
|
|
|
25
26
|
}
|
|
26
27
|
throw e;
|
|
27
28
|
}
|
|
29
|
+
|
|
30
|
+
export async function parseTaskMutation(req: Request, rawId: string | undefined): Promise<
|
|
31
|
+
{ ok: true; id: number; body: Record<string, unknown> } | { ok: false; response: Response }
|
|
32
|
+
> {
|
|
33
|
+
const id = parseId(rawId);
|
|
34
|
+
if (id === null) return { ok: false, response: error("invalid task id") };
|
|
35
|
+
const forbidden = await requireTaskMutationAuth(req, id);
|
|
36
|
+
if (forbidden) return { ok: false, response: forbidden };
|
|
37
|
+
const parsed = await parseBody<Record<string, unknown>>(req);
|
|
38
|
+
if (!parsed.ok) return { ok: false, response: error(parsed.error, parsed.status) };
|
|
39
|
+
if (!isRecord(parsed.body)) return { ok: false, response: error("JSON object body required") };
|
|
40
|
+
return { ok: true, id, body: parsed.body };
|
|
41
|
+
}
|
|
@@ -6,18 +6,13 @@ import { cleanIssueRefRelation } from "../db/task-issue-relation";
|
|
|
6
6
|
import { issueRefsFromArgs } from "../services/issue-refs";
|
|
7
7
|
import { cleanString } from "../validation";
|
|
8
8
|
import { isRecord } from "@humwick/sdk";
|
|
9
|
-
import { error, json,
|
|
10
|
-
import { taskHttpActor as actorOf,
|
|
9
|
+
import { error, json, type Handler } from "./_shared";
|
|
10
|
+
import { taskHttpActor as actorOf, parseTaskMutation, taskEntityError as entityError } from "./task-http";
|
|
11
11
|
|
|
12
12
|
export const postTaskRef: Handler = async (req, params) => {
|
|
13
|
-
const
|
|
14
|
-
if (
|
|
15
|
-
const
|
|
16
|
-
if (forbidden) return forbidden;
|
|
17
|
-
const parsed = await parseBody<Record<string, unknown>>(req);
|
|
18
|
-
if (!parsed.ok) return error(parsed.error, parsed.status);
|
|
19
|
-
const body = parsed.body;
|
|
20
|
-
if (!isRecord(body)) return error("JSON object body required");
|
|
13
|
+
const parsed = await parseTaskMutation(req, params.id);
|
|
14
|
+
if (!parsed.ok) return parsed.response;
|
|
15
|
+
const { id, body } = parsed;
|
|
21
16
|
try {
|
|
22
17
|
// Same alias tolerance + attach-nothing rejection as relay_task_add_ref (#2214), one body.
|
|
23
18
|
return json(await attachTaskRefs(id, {
|
|
@@ -33,14 +28,9 @@ export const postTaskRef: Handler = async (req, params) => {
|
|
|
33
28
|
}
|
|
34
29
|
};
|
|
35
30
|
export const deleteTaskRef: Handler = async (req, params) => {
|
|
36
|
-
const
|
|
37
|
-
if (
|
|
38
|
-
const
|
|
39
|
-
if (forbidden) return forbidden;
|
|
40
|
-
const parsed = await parseBody<Record<string, unknown>>(req);
|
|
41
|
-
if (!parsed.ok) return error(parsed.error, parsed.status);
|
|
42
|
-
const body = parsed.body;
|
|
43
|
-
if (!isRecord(body)) return error("JSON object body required");
|
|
31
|
+
const parsed = await parseTaskMutation(req, params.id);
|
|
32
|
+
if (!parsed.ok) return parsed.response;
|
|
33
|
+
const { id, body } = parsed;
|
|
44
34
|
try {
|
|
45
35
|
const detail = await removeTaskEntityRefAsync(id, {
|
|
46
36
|
issueRefId: cleanString(body.issueRefId, "issueRefId", { max: 120 }),
|
|
@@ -57,14 +47,9 @@ export const deleteTaskRef: Handler = async (req, params) => {
|
|
|
57
47
|
// #833 — comment-on-Task writes THROUGH to the linked GitHub issue (discussion lives in
|
|
58
48
|
// GitHub, never a relay table). Execution telemetry never reaches this path.
|
|
59
49
|
export const postTaskComment: Handler = async (req, params) => {
|
|
60
|
-
const
|
|
61
|
-
if (
|
|
62
|
-
const
|
|
63
|
-
if (forbidden) return forbidden;
|
|
64
|
-
const parsed = await parseBody<Record<string, unknown>>(req);
|
|
65
|
-
if (!parsed.ok) return error(parsed.error, parsed.status);
|
|
66
|
-
const body = parsed.body;
|
|
67
|
-
if (!isRecord(body)) return error("JSON object body required");
|
|
50
|
+
const parsed = await parseTaskMutation(req, params.id);
|
|
51
|
+
if (!parsed.ok) return parsed.response;
|
|
52
|
+
const { id, body } = parsed;
|
|
68
53
|
try {
|
|
69
54
|
const result = await commentOnTaskThrough({
|
|
70
55
|
taskId: id,
|
package/src/routes/tasks.ts
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
export { postTaskRef, deleteTaskRef, postTaskComment } from "./task-issues";
|
|
3
3
|
import { createPlanTaskAsync, isPlanTaskCreate } from "../services/plan-task-create";
|
|
4
4
|
import { prepareIssueTaskCreate, refreshTaskIssueUpdateAsync } from "../services/task-from-issue";
|
|
5
|
-
import { taskHttpActor as actorOf, requireTaskMutationAuth, taskEntityError as entityError } from "./task-http";
|
|
5
|
+
import { taskHttpActor as actorOf, requireTaskMutationAuth, parseTaskMutation, taskEntityError as entityError } from "./task-http";
|
|
6
6
|
import { MAX_BODY_BYTES } from "../config";
|
|
7
|
-
import { VALID_TASK_STATUSES, agentSessionStatus, auditEvent, dispatchTaskCallbacksInBackground,
|
|
7
|
+
import { VALID_TASK_STATUSES, agentSessionStatus, auditEvent, dispatchTaskCallbacksInBackground, error, json, normalizeAgentSessionGuard, parseBody, parseId, type Handler } from "./_shared";
|
|
8
8
|
import { isRequestAuthorizedFor } from "../security";
|
|
9
9
|
import { authContextFromRequest } from "../services/auth-context";
|
|
10
10
|
import { hasTaskReadAdminScope } from "../task-authz";
|
|
@@ -25,7 +25,7 @@ import { cleanIssueRefRelation } from "../db/task-issue-relation";
|
|
|
25
25
|
import { issueRefsFromArgs } from "../services/issue-refs";
|
|
26
26
|
import { cleanEpoch, cleanMeta, cleanString, dependencyRefField, optionalEnum, taskIdField } from "../validation";
|
|
27
27
|
import { emitMessageClaimed, emitTaskChanged } from "../sse";
|
|
28
|
-
import {
|
|
28
|
+
import { isRecord, TASK_TERMINAL_STAGES, TASK_DELIVERABLE_STAGES, TASK_DISPATCH_COMPLEXITIES, type TaskBlocker, type TaskDispatch, type TaskGateStatus, type TaskPriority } from "@humwick/sdk";
|
|
29
29
|
import { type AgentSessionGuard, type TaskStatus, type TaskStatusInput } from "../types";
|
|
30
30
|
|
|
31
31
|
async function taskDetailOr404(id: number) {
|
|
@@ -100,6 +100,7 @@ export const postTaskCreate: Handler = async (req) => {
|
|
|
100
100
|
complexity: optionalEnum(body.complexity, "complexity", TASK_DISPATCH_COMPLEXITIES),
|
|
101
101
|
context: cleanString(body.context, "context", { max: MAX_BODY_BYTES }),
|
|
102
102
|
terminalStage: optionalEnum(body.terminalStage, "terminalStage", TASK_TERMINAL_STAGES),
|
|
103
|
+
onDeploy: cleanString(body.onDeploy, "onDeploy", { max: 500 }),
|
|
103
104
|
doneWhen: cleanString(body.doneWhen, "doneWhen", { max: MAX_BODY_BYTES }),
|
|
104
105
|
teamId: cleanString(body.teamId, "teamId", { max: 200 }),
|
|
105
106
|
projectId: cleanString(body.projectId, "projectId", { max: 120 }),
|
|
@@ -109,6 +110,7 @@ export const postTaskCreate: Handler = async (req) => {
|
|
|
109
110
|
// Raw passthrough (same convention as `dispatch` above) — validated inside
|
|
110
111
|
// createDeliverableTask (cleanTags / cleanTaskPriority), not at the route layer.
|
|
111
112
|
tags: body.tags as string[] | undefined,
|
|
113
|
+
paths: body.paths as string[] | undefined,
|
|
112
114
|
priority: body.priority as TaskPriority | undefined,
|
|
113
115
|
// Same alias-tolerant normalization the MCP surface uses (#2214) — one home, both transports.
|
|
114
116
|
issueRefs: issueRefsFromArgs(body),
|
|
@@ -148,21 +150,10 @@ export const postTaskCreate: Handler = async (req) => {
|
|
|
148
150
|
}
|
|
149
151
|
};
|
|
150
152
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
153
|
export const patchTaskEntity: Handler = async (req, params) => {
|
|
158
|
-
const
|
|
159
|
-
if (
|
|
160
|
-
const
|
|
161
|
-
if (forbidden) return forbidden;
|
|
162
|
-
const parsed = await parseBody<Record<string, unknown>>(req);
|
|
163
|
-
if (!parsed.ok) return error(parsed.error, parsed.status);
|
|
164
|
-
const body = parsed.body;
|
|
165
|
-
if (!isRecord(body)) return error("JSON object body required");
|
|
154
|
+
const parsed = await parseTaskMutation(req, params.id);
|
|
155
|
+
if (!parsed.ok) return parsed.response;
|
|
156
|
+
const { id, body } = parsed;
|
|
166
157
|
try {
|
|
167
158
|
if (body.refreshIssue !== undefined) return json(await refreshTaskIssueUpdateAsync(id, body, actorOf(req)));
|
|
168
159
|
const detail = await updateTaskEntityAsync(id, {
|
|
@@ -171,6 +162,7 @@ export const patchTaskEntity: Handler = async (req, params) => {
|
|
|
171
162
|
...(body.complexity !== undefined ? { complexity: optionalEnum(body.complexity, "complexity", TASK_DISPATCH_COMPLEXITIES) } : {}),
|
|
172
163
|
...(body.context !== undefined ? { context: cleanString(body.context, "context", { max: MAX_BODY_BYTES }) ?? "" } : {}),
|
|
173
164
|
...(body.latestSteer !== undefined ? { latestSteer: cleanString(body.latestSteer, "latestSteer", { max: MAX_BODY_BYTES }) ?? "" } : {}),
|
|
165
|
+
...(body.onDeploy !== undefined ? { onDeploy: cleanString(body.onDeploy, "onDeploy", { max: 500 }) ?? "" } : {}),
|
|
174
166
|
...(body.doneWhen !== undefined ? { doneWhen: cleanString(body.doneWhen, "doneWhen", { max: MAX_BODY_BYTES }) ?? "" } : {}),
|
|
175
167
|
...(body.dispatch !== undefined ? { dispatch: body.dispatch as TaskDispatch } : {}),
|
|
176
168
|
...(body.teamId !== undefined ? { teamId: cleanString(body.teamId, "teamId", { max: 200 }) ?? "" } : {}),
|
|
@@ -180,6 +172,7 @@ export const patchTaskEntity: Handler = async (req, params) => {
|
|
|
180
172
|
// survive into updateDeliverableTask, which cleanString's `?? ""` collapse would erase.
|
|
181
173
|
...(body.terminalStage !== undefined ? { terminalStage: body.terminalStage === null ? null : optionalEnum(body.terminalStage, "terminalStage", TASK_TERMINAL_STAGES) } : {}),
|
|
182
174
|
...(body.tags !== undefined ? { tags: body.tags as string[] | null } : {}),
|
|
175
|
+
...(body.paths !== undefined ? { paths: body.paths as string[] | null } : {}),
|
|
183
176
|
...(body.priority !== undefined ? { priority: body.priority as TaskPriority | null } : {}),
|
|
184
177
|
actor: actorOf(req),
|
|
185
178
|
});
|
|
@@ -190,14 +183,9 @@ export const patchTaskEntity: Handler = async (req, params) => {
|
|
|
190
183
|
};
|
|
191
184
|
|
|
192
185
|
export const postTaskEntityStatus: Handler = async (req, params) => {
|
|
193
|
-
const
|
|
194
|
-
if (
|
|
195
|
-
const
|
|
196
|
-
if (forbidden) return forbidden;
|
|
197
|
-
const parsed = await parseBody<Record<string, unknown>>(req);
|
|
198
|
-
if (!parsed.ok) return error(parsed.error, parsed.status);
|
|
199
|
-
const body = parsed.body;
|
|
200
|
-
if (!isRecord(body)) return error("JSON object body required");
|
|
186
|
+
const parsed = await parseTaskMutation(req, params.id);
|
|
187
|
+
if (!parsed.ok) return parsed.response;
|
|
188
|
+
const { id, body } = parsed;
|
|
201
189
|
try {
|
|
202
190
|
const detail = await setTaskStatusControl(id, {
|
|
203
191
|
status: body.status as TaskStatus,
|
|
@@ -213,14 +201,9 @@ export const postTaskEntityStatus: Handler = async (req, params) => {
|
|
|
213
201
|
};
|
|
214
202
|
|
|
215
203
|
export const postTaskEntityStage: Handler = async (req, params) => {
|
|
216
|
-
const
|
|
217
|
-
if (
|
|
218
|
-
const
|
|
219
|
-
if (forbidden) return forbidden;
|
|
220
|
-
const parsed = await parseBody<Record<string, unknown>>(req);
|
|
221
|
-
if (!parsed.ok) return error(parsed.error, parsed.status);
|
|
222
|
-
const body = parsed.body;
|
|
223
|
-
if (!isRecord(body)) return error("JSON object body required");
|
|
204
|
+
const parsed = await parseTaskMutation(req, params.id);
|
|
205
|
+
if (!parsed.ok) return parsed.response;
|
|
206
|
+
const { id, body } = parsed;
|
|
224
207
|
try {
|
|
225
208
|
const detail = await setTaskEntityStageAsync(id, {
|
|
226
209
|
deliverableStage: optionalEnum(body.deliverableStage, "deliverableStage", TASK_DELIVERABLE_STAGES) as any,
|
|
@@ -235,14 +218,9 @@ export const postTaskEntityStage: Handler = async (req, params) => {
|
|
|
235
218
|
};
|
|
236
219
|
|
|
237
220
|
export const postTaskEntityGate: Handler = async (req, params) => {
|
|
238
|
-
const
|
|
239
|
-
if (
|
|
240
|
-
const
|
|
241
|
-
if (forbidden) return forbidden;
|
|
242
|
-
const parsed = await parseBody<Record<string, unknown>>(req);
|
|
243
|
-
if (!parsed.ok) return error(parsed.error, parsed.status);
|
|
244
|
-
const body = parsed.body;
|
|
245
|
-
if (!isRecord(body)) return error("JSON object body required");
|
|
221
|
+
const parsed = await parseTaskMutation(req, params.id);
|
|
222
|
+
if (!parsed.ok) return parsed.response;
|
|
223
|
+
const { id, body } = parsed;
|
|
246
224
|
try {
|
|
247
225
|
return json(await recordTaskEntityGateAsync(id, {
|
|
248
226
|
gate: cleanString(body.gate, "gate", { required: true, max: 120 })!,
|
|
@@ -345,14 +323,9 @@ export const deleteTaskRelated: Handler = async (req, params) => {
|
|
|
345
323
|
};
|
|
346
324
|
|
|
347
325
|
export const postTaskAssign: Handler = async (req, params) => {
|
|
348
|
-
const
|
|
349
|
-
if (
|
|
350
|
-
const
|
|
351
|
-
if (forbidden) return forbidden;
|
|
352
|
-
const parsed = await parseBody<Record<string, unknown>>(req);
|
|
353
|
-
if (!parsed.ok) return error(parsed.error, parsed.status);
|
|
354
|
-
const body = parsed.body;
|
|
355
|
-
if (!isRecord(body)) return error("JSON object body required");
|
|
326
|
+
const parsed = await parseTaskMutation(req, params.id);
|
|
327
|
+
if (!parsed.ok) return parsed.response;
|
|
328
|
+
const { id, body } = parsed;
|
|
356
329
|
try {
|
|
357
330
|
const detail = await assignTaskEntityAgentAsync(id, cleanString(body.agentId, "agentId", { required: true, max: 200 })!, cleanString(body.role, "role", { max: 120 }), actorOf(req));
|
|
358
331
|
return json(detail);
|
|
@@ -444,10 +417,6 @@ export const postWorkspaceWithdrawLand: Handler = async (req) => {
|
|
|
444
417
|
}
|
|
445
418
|
};
|
|
446
419
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
420
|
export const postClaimTask: Handler = async (req, params) => {
|
|
452
421
|
const id = parseId(params.id);
|
|
453
422
|
if (id === null) return error("invalid task id");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { authContextFromRequest } from "../services/auth-context";
|
|
2
|
-
import {
|
|
2
|
+
import { deleteTeamTemplateAsync, getTeamTemplate, listTeamTemplates, saveTeamTemplateAsync } from "../services/team-templates";
|
|
3
3
|
import { cleanString } from "../validation";
|
|
4
4
|
import { authorizeRoute, error, json, parseBody, type Handler } from "./_shared";
|
|
5
5
|
|
|
@@ -39,7 +39,7 @@ export const postTeamTemplateRoute: Handler = async (req) => {
|
|
|
39
39
|
const parsed = await parseBody<Record<string, unknown>>(req);
|
|
40
40
|
if (!parsed.ok) return error(parsed.error, parsed.status);
|
|
41
41
|
try {
|
|
42
|
-
return json(
|
|
42
|
+
return json(await saveTeamTemplateAsync(parsed.body ?? {}, updatedBy(req)), 201);
|
|
43
43
|
} catch (err) {
|
|
44
44
|
return routeError(err);
|
|
45
45
|
}
|
|
@@ -51,15 +51,15 @@ export const putTeamTemplateRoute: Handler = async (req, params) => {
|
|
|
51
51
|
const parsed = await parseBody<Record<string, unknown>>(req);
|
|
52
52
|
if (!parsed.ok) return error(parsed.error, parsed.status);
|
|
53
53
|
try {
|
|
54
|
-
return json(
|
|
54
|
+
return json(await saveTeamTemplateAsync({ ...(parsed.body ?? {}), slug: slugParam(params) }, updatedBy(req)));
|
|
55
55
|
} catch (err) {
|
|
56
56
|
return routeError(err);
|
|
57
57
|
}
|
|
58
58
|
};
|
|
59
59
|
|
|
60
|
-
export const deleteTeamTemplateRoute: Handler = (req, params) => {
|
|
60
|
+
export const deleteTeamTemplateRoute: Handler = async (req, params) => {
|
|
61
61
|
const denied = authorizeRoute(req, { scope: "system:admin" });
|
|
62
62
|
if (denied) return denied;
|
|
63
63
|
const slug = slugParam(params);
|
|
64
|
-
return json({ slug, deleted:
|
|
64
|
+
return json({ slug, deleted: await deleteTeamTemplateAsync(slug, updatedBy(req)) });
|
|
65
65
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getTeamsConfig,
|
|
1
|
+
import { getTeamsConfig, setTeamsConfigAsync } from "../teams-config-store";
|
|
2
2
|
import { cleanString } from "../validation";
|
|
3
3
|
import { error, json, parseBody, type Handler } from "./_shared";
|
|
4
4
|
import { isRecord } from "@humwick/sdk";
|
|
@@ -11,5 +11,5 @@ export const putTeamsConfigRoute: Handler = async (req) => {
|
|
|
11
11
|
const parsed = await parseBody<unknown>(req);
|
|
12
12
|
if (!parsed.ok) return error(parsed.error, parsed.status);
|
|
13
13
|
const updatedBy = isRecord(parsed.body) ? cleanString(parsed.body.updatedBy, "updatedBy", { max: 200 }) : undefined;
|
|
14
|
-
return json(
|
|
14
|
+
return json(await setTeamsConfigAsync(parsed.body, { updatedBy }));
|
|
15
15
|
};
|
package/src/routes/tokens.ts
CHANGED
|
@@ -6,13 +6,19 @@ import { ValidationError, getOrchestrator, upsertIntegrationRegistry } from "../
|
|
|
6
6
|
import { auditEvent, authAuditMetadata, authorizeRoute, error, isRootCredentialRequest, json, parseBody, resolveActingUser, type Handler } from "./_shared";
|
|
7
7
|
import { cleanString, cleanStringArray, cleanTokenConstraints, optionalEnum } from "../validation";
|
|
8
8
|
import { deleteTokenProfile, getToken, getTokenProfile, listTokenProfiles, listTokens, updateTokenProfile, upsertTokenProfile } from "../token-db";
|
|
9
|
-
import { createTokenAsync,
|
|
9
|
+
import { createTokenAsync, revokeTokenAsync } from "../token-db-write";
|
|
10
10
|
import { getComponentAuth, getIntegrationAuth, isAuthorized } from "../security";
|
|
11
|
-
import { issueIntegrationRuntimeToken, issueInteractiveRunnerRuntimeToken, issueMcpRuntimeToken, reissueRunnerRuntimeToken, remintRunnerRuntimeToken } from "../runtime-tokens";
|
|
11
|
+
import { handoffRunnerTokenAsync, issueIntegrationRuntimeToken, issueInteractiveRunnerRuntimeToken, issueMcpRuntimeToken, reissueRunnerRuntimeToken, remintRunnerRuntimeToken, type RunnerTokenMintRefusal } from "../runtime-tokens";
|
|
12
12
|
import { type SpawnProvider } from "../types";
|
|
13
13
|
|
|
14
14
|
export const getTokens: Handler = () => json(listTokens());
|
|
15
15
|
|
|
16
|
+
// #3775 — a rate-limited runner token mint is a 429 with Retry-After; any other refusal stays 403.
|
|
17
|
+
function mintRefused(refusal: RunnerTokenMintRefusal): Response {
|
|
18
|
+
if (refusal.retryAfterSeconds === undefined) return error(refusal.error, 403);
|
|
19
|
+
return Response.json({ error: refusal.error, retryAfterSeconds: refusal.retryAfterSeconds }, { status: 429, headers: { "Retry-After": String(refusal.retryAfterSeconds) } });
|
|
20
|
+
}
|
|
21
|
+
|
|
16
22
|
export const getTokenMe: Handler = (req) => {
|
|
17
23
|
const component = getComponentAuth(req);
|
|
18
24
|
if (component) {
|
|
@@ -103,11 +109,12 @@ export const postRuntimeTokenRenew: Handler = async (req) => {
|
|
|
103
109
|
const auth = getComponentAuth(req);
|
|
104
110
|
if (!auth?.jti) return error("scoped runtime token required", 403);
|
|
105
111
|
if (auth.role !== "provider") return error("provider runtime token required", 403);
|
|
106
|
-
const renewed = await
|
|
112
|
+
const renewed = await handoffRunnerTokenAsync(auth.sub, {
|
|
107
113
|
jti: auth.jti,
|
|
108
114
|
allowedProfileIds: ["provider-agent", "provider-interactive"],
|
|
109
115
|
createdBy: `renew:${auth.jti}`,
|
|
110
116
|
});
|
|
117
|
+
if (renewed && "retryAfterSeconds" in renewed) return mintRefused({ error: "runtime token mint rate limited", ...renewed });
|
|
111
118
|
if (!renewed) return error("runtime token cannot be renewed", 403);
|
|
112
119
|
await auditEvent({
|
|
113
120
|
// Keyed by the successor, so a replayed handoff (#1245) records one renewal.
|
|
@@ -151,7 +158,7 @@ export const postRuntimeTokenRemint: Handler = async (req) => {
|
|
|
151
158
|
throw e;
|
|
152
159
|
}
|
|
153
160
|
const result = await remintRunnerRuntimeToken(auth, `remint:${auth.jti}`);
|
|
154
|
-
if ("error" in result) return
|
|
161
|
+
if ("error" in result) return mintRefused(result);
|
|
155
162
|
if ("unchanged" in result) return json(result);
|
|
156
163
|
await auditEvent({
|
|
157
164
|
clientId: "server-runtime-token-remint-" + result.record.jti,
|
|
@@ -180,7 +187,7 @@ export const postOrchestratorRunnerToken: Handler = async (req, params) => {
|
|
|
180
187
|
orchestratorId: orch.id,
|
|
181
188
|
createdBy: `orchestrator-remint:${orch.id}`,
|
|
182
189
|
});
|
|
183
|
-
if ("error" in result) return
|
|
190
|
+
if ("error" in result) return mintRefused(result);
|
|
184
191
|
await auditEvent({
|
|
185
192
|
clientId: "server-runner-token-remint-" + result.record.jti,
|
|
186
193
|
kind: "state",
|
package/src/routes/workflows.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { parseWorkflowDecisionResponse } from "../services/workflow-decisions";
|
|
2
2
|
// #3076 — the dashboard's workflow run reads: the run list (by status and project), one run, the stats
|
|
3
3
|
// window and the workflows that ran, all from the workflow run records relay_workflow_get reads, with its visibility rule.
|
|
4
|
-
// #3120 — the catalog. #3166 — cancel a run, as relay_workflow_cancel does.
|
|
4
|
+
// #3120 — the catalog. #3166 — cancel a run, as relay_workflow_cancel does. #3757 — pause and unpause one.
|
|
5
5
|
import { OpError } from "@humwick/kernel";
|
|
6
6
|
import { isRecord, WORKFLOW_STATS_WINDOWS, type WorkflowStatsWindow } from "@humwick/sdk";
|
|
7
7
|
import { resolveProjectAsync, ValidationError } from "../db";
|
|
@@ -10,7 +10,7 @@ import { authContextFromRequest } from "../services/auth-context";
|
|
|
10
10
|
import { ServiceAuthError } from "../services/errors";
|
|
11
11
|
import { readWorkflowCatalog } from "../services/workflow-catalog";
|
|
12
12
|
import { requireSpawnBudget } from "../spawn-budget";
|
|
13
|
-
import { amendWorkflowRunBudget, cancelWorkflowRun, getWorkflowRun, readWorkflowRunBudget, listWorkflowRuns, resumeWorkflowRun, startWorkflowRun } from "../services/workflow-runs";
|
|
13
|
+
import { amendWorkflowRunBudget, cancelWorkflowRun, getWorkflowRun, pauseWorkflowRun, readWorkflowRunBudget, listWorkflowRuns, resumeWorkflowRun, startWorkflowRun } from "../services/workflow-runs";
|
|
14
14
|
import { workflowDefinitions, workflowStats } from "../services/workflow-stats";
|
|
15
15
|
import { cleanString, taskIdField } from "../validation";
|
|
16
16
|
import { error, json, parseBody, type Handler } from "./_shared";
|
|
@@ -83,7 +83,22 @@ export const postWorkflowRunCancelRoute: Handler = async (req, params) => {
|
|
|
83
83
|
}
|
|
84
84
|
};
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
/** #3757 — pause (a `reason` is required) or unpause a run, as relay_workflow_resume's `pause` does; the dashboard's buttons use these. */
|
|
87
|
+
function workflowRunPauseRoute(pause: boolean): Handler {
|
|
88
|
+
return async (req, params) => {
|
|
89
|
+
const parsed = await parseBody<unknown>(req);
|
|
90
|
+
if (!parsed.ok) return error(parsed.error, parsed.status);
|
|
91
|
+
if (parsed.body !== null && !isRecord(parsed.body)) return error("body must be a JSON object", 400);
|
|
92
|
+
try {
|
|
93
|
+
const reason = cleanString(parsed.body?.reason, "reason", { max: 2000 });
|
|
94
|
+
return json(await pauseWorkflowRun({ runId: params.runId!, pause, ...(reason ? { reason } : {}) }, authContextFromRequest(req)));
|
|
95
|
+
} catch (e) { return runRouteError(e); }
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
export const postWorkflowRunPauseRoute = workflowRunPauseRoute(true);
|
|
99
|
+
export const postWorkflowRunUnpauseRoute = workflowRunPauseRoute(false);
|
|
100
|
+
|
|
101
|
+
function runRouteError(e: unknown): Response {
|
|
87
102
|
if (e instanceof McpNotFoundError) return error(e.message, 404);
|
|
88
103
|
if (e instanceof ServiceAuthError) return error(e.message, 403);
|
|
89
104
|
if (e instanceof ValidationError) return error(e.message, 400);
|
|
@@ -91,7 +106,7 @@ function budgetRouteError(e: unknown): Response {
|
|
|
91
106
|
}
|
|
92
107
|
|
|
93
108
|
export const getWorkflowRunBudgetRoute: Handler = async (req, params) => {
|
|
94
|
-
try { return json(await readWorkflowRunBudget(params.runId!, authContextFromRequest(req))); } catch (e) { return
|
|
109
|
+
try { return json(await readWorkflowRunBudget(params.runId!, authContextFromRequest(req))); } catch (e) { return runRouteError(e); }
|
|
95
110
|
};
|
|
96
111
|
|
|
97
112
|
export const patchWorkflowRunBudgetRoute: Handler = async (req, params) => {
|
|
@@ -100,7 +115,7 @@ export const patchWorkflowRunBudgetRoute: Handler = async (req, params) => {
|
|
|
100
115
|
try {
|
|
101
116
|
const { tokenCap } = requireSpawnBudget(parsed.body);
|
|
102
117
|
return json(await amendWorkflowRunBudget({ runId: params.runId!, tokenCap }, authContextFromRequest(req)));
|
|
103
|
-
} catch (e) { return
|
|
118
|
+
} catch (e) { return runRouteError(e); }
|
|
104
119
|
};
|
|
105
120
|
|
|
106
121
|
export const postWorkflowRunResumeRoute: Handler = async (req, params) => {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ValidationError } from "@humwick/sdk";
|
|
2
|
-
import { currentDispatchModels } from "./routing-model-aliases";
|
|
3
2
|
import { normalizeStepRoutingDefaults } from "./routing-step-defaults";
|
|
4
3
|
import {
|
|
5
4
|
isRecord,
|
|
@@ -199,7 +198,7 @@ export function normalizeDispatchPolicy(raw: unknown): DispatchPolicy {
|
|
|
199
198
|
*/
|
|
200
199
|
/** Pure projection used by reader-gateway callers that already captured the policy snapshot. */
|
|
201
200
|
export function dispatchPolicyFor(policy: RoutingPolicy): DispatchPolicy {
|
|
202
|
-
return policy.dispatch
|
|
201
|
+
return policy.dispatch ?? DEFAULT_DISPATCH_POLICY;
|
|
203
202
|
}
|
|
204
203
|
|
|
205
204
|
const MAX_RULES = 100;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { cleanString, isRecord, isSpawnProvider, ValidationError, type StepRoutingDefaults, type StepRoutingTarget } from "@humwick/sdk";
|
|
2
|
-
import { VALID_EFFORTS } from "@humwick/sdk/provider-catalog";
|
|
3
2
|
import { WORKFLOW_PIN_FIELDS } from "@humwick/kernel/workflow-definition";
|
|
4
3
|
|
|
5
4
|
export function normalizeStepRoutingDefaults(raw: unknown): StepRoutingDefaults {
|
|
@@ -25,7 +24,6 @@ function normalizeRoles(raw: unknown, where: string): Record<string, StepRouting
|
|
|
25
24
|
for (const key of Object.keys(target)) if (!(WORKFLOW_PIN_FIELDS as readonly string[]).includes(key)) throw new ValidationError(`${where}.${role}.${key} is not a routing field`);
|
|
26
25
|
const fields = Object.fromEntries(Object.entries(target).map(([key, value]) => [key, cleanString(value, `${where}.${role}.${key}`, { required: true, max: 200 })]));
|
|
27
26
|
if (fields.provider !== undefined && !isSpawnProvider(fields.provider)) throw new ValidationError(`${where}.${role}.provider is not a provider`);
|
|
28
|
-
if (fields.effort !== undefined && !(VALID_EFFORTS as readonly string[]).includes(fields.effort)) throw new ValidationError(`${where}.${role}.effort must be ${VALID_EFFORTS.join(", ")}`);
|
|
29
27
|
return [role, fields as StepRoutingTarget];
|
|
30
28
|
}));
|
|
31
29
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// #3775 — per-subject cap on runner token successor mints (renew, orchestrator re-mint, boundary
|
|
2
|
+
// re-derive). A runner stuck re-minting after 401 minted 700-1,400 tokens/min and overloaded the
|
|
3
|
+
// hub writer; this refuses the excess before any token row is written. A spawn subject
|
|
4
|
+
// (`runner:spawn:<id>`) keeps one subject across its whole token lineage, so it is the key. Only a
|
|
5
|
+
// real mint is charged: a replayed handoff (retry or concurrent caller) writes nothing.
|
|
6
|
+
|
|
7
|
+
/** At most this many new tokens per subject in any rolling window. */
|
|
8
|
+
export const RUNNER_TOKEN_MINTS_PER_WINDOW = 3;
|
|
9
|
+
export const RUNNER_TOKEN_MINT_WINDOW_MS = 60_000;
|
|
10
|
+
const MAX_TRACKED_SUBJECTS = 10_000;
|
|
11
|
+
|
|
12
|
+
export class RunnerTokenMintLimit {
|
|
13
|
+
/** Recent mint times per subject, oldest first, at most RUNNER_TOKEN_MINTS_PER_WINDOW long. */
|
|
14
|
+
private readonly mints = new Map<string, number[]>();
|
|
15
|
+
/** Subjects currently refused, so a trip logs once rather than once per refused mint. */
|
|
16
|
+
private readonly tripped = new Set<string>();
|
|
17
|
+
private readonly now: () => number;
|
|
18
|
+
private readonly log: (message: string) => void;
|
|
19
|
+
|
|
20
|
+
constructor(options: { now?: () => number; log?: (message: string) => void } = {}) {
|
|
21
|
+
this.now = options.now ?? Date.now;
|
|
22
|
+
this.log = options.log ?? console.warn;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Seconds `sub` must wait before its next mint, or undefined when it may mint now. Charges nothing. */
|
|
26
|
+
refusal(sub: string): number | undefined {
|
|
27
|
+
const recent = this.recent(sub);
|
|
28
|
+
if (recent.length < RUNNER_TOKEN_MINTS_PER_WINDOW) {
|
|
29
|
+
this.tripped.delete(sub);
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
if (!this.tripped.has(sub)) {
|
|
33
|
+
if (this.tripped.size >= MAX_TRACKED_SUBJECTS) this.tripped.clear();
|
|
34
|
+
this.tripped.add(sub);
|
|
35
|
+
this.log(`[token-mint] rate limit tripped for ${sub}: ${RUNNER_TOKEN_MINTS_PER_WINDOW} tokens minted in the last ${RUNNER_TOKEN_MINT_WINDOW_MS / 1000}s; refusing until the window frees`);
|
|
36
|
+
}
|
|
37
|
+
return Math.max(1, Math.ceil((recent[0]! + RUNNER_TOKEN_MINT_WINDOW_MS - this.now()) / 1000));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Record one new token minted for `sub`. */
|
|
41
|
+
charge(sub: string): void {
|
|
42
|
+
const recent = this.recent(sub);
|
|
43
|
+
if (!this.mints.has(sub) && this.mints.size >= MAX_TRACKED_SUBJECTS) this.mints.delete(this.mints.keys().next().value!);
|
|
44
|
+
recent.push(this.now());
|
|
45
|
+
if (recent.length > RUNNER_TOKEN_MINTS_PER_WINDOW) recent.shift();
|
|
46
|
+
this.mints.set(sub, recent);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
reset(): void {
|
|
50
|
+
this.mints.clear();
|
|
51
|
+
this.tripped.clear();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private recent(sub: string): number[] {
|
|
55
|
+
const since = this.now() - RUNNER_TOKEN_MINT_WINDOW_MS;
|
|
56
|
+
const recent = (this.mints.get(sub) ?? []).filter((at) => at > since);
|
|
57
|
+
if (recent.length) this.mints.set(sub, recent);
|
|
58
|
+
else this.mints.delete(sub);
|
|
59
|
+
return recent;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const runnerTokenMintLimit = new RunnerTokenMintLimit();
|
package/src/runtime-grant.ts
CHANGED
|
@@ -24,7 +24,9 @@ const SPAWN_SCOPES = ["command:spawn", "command:shutdown"] as const;
|
|
|
24
24
|
const FLEET_OPS_PROFILES = new Set(["coordinator", "ops-assistant"]);
|
|
25
25
|
// #1925 H10 — fleet visibility is coordinator-facing. Keep this a fixed trusted-profile allowlist,
|
|
26
26
|
// like command:fleet, rather than a user-editable profile setting that could widen worker tokens.
|
|
27
|
-
const
|
|
27
|
+
const HOST_WRITE_PROFILES = new Set(["coordinator"]);
|
|
28
|
+
// #3772 — the reboot runbook reads resource_control held/waiter counts from /api/hosts; ops profiles read, never write.
|
|
29
|
+
const HOST_READ_PROFILES = new Set([...HOST_WRITE_PROFILES, "ops-assistant", "sec-ops"]);
|
|
28
30
|
// #3073 — coordinator-only groups (not in WORKER_TOOL_GROUPS) a coordinator always gets, even when its stored override predates them.
|
|
29
31
|
const COORDINATOR_ONLY_TOOL_GROUPS = ["decisions", "workflows", "project", "routing"] as const;
|
|
30
32
|
// #3327 — the coordinator edits routing preferences, like Edin in the dashboard.
|
|
@@ -109,7 +111,7 @@ export function deriveRunnerGrant(profile: string | undefined, authority: Deriva
|
|
|
109
111
|
...nonTrustedBaseline(agentInitiated),
|
|
110
112
|
...(fleetOps ? [FLEET_SCOPE] : []),
|
|
111
113
|
...(hostsRead ? ["hosts:read"] : []),
|
|
112
|
-
...(!agentInitiated && Boolean(profile &&
|
|
114
|
+
...(!agentInitiated && Boolean(profile && HOST_WRITE_PROFILES.has(profile)) && callerMayMintHostsScope(callerScopes, "hosts:write") ? ["hosts:write"] : []),
|
|
113
115
|
...(grantsSecretsScope(profile, toolGroups, callerScopes) ? [SECRETS_WRITE_SCOPE] : []),
|
|
114
116
|
...(!agentInitiated && (COORDINATOR_PROFILE_NAMES as readonly string[]).includes(profile ?? "") && (callerScopes === undefined || hasScope(callerScopes, ROUTING_WRITE_SCOPE)) ? [ROUTING_WRITE_SCOPE] : []),
|
|
115
117
|
...(grant.canSpawn ? SPAWN_SCOPES.filter(scope => !agentInitiated || callerScopes === undefined || hasScope(callerScopes, scope)) : []),
|