@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/runtime-tokens.ts
CHANGED
|
@@ -2,8 +2,9 @@ import { registryToolName } from "@humwick/sdk";
|
|
|
2
2
|
import { writeChildEnv } from "@humwick/sdk/env";
|
|
3
3
|
import { deriveRunnerGrant, successorGrant } from "./runtime-grant";
|
|
4
4
|
export { runnerSpawnGrantForAgent } from "./runtime-grant";
|
|
5
|
-
import { TOKEN_RECOVERY_WINDOW_SECONDS, createToken, getToken, getTokenProfile, type CreateTokenInput } from "./token-db";
|
|
6
|
-
import {
|
|
5
|
+
import { TOKEN_RECOVERY_WINDOW_SECONDS, createToken, getToken, getTokenProfile, type CreateTokenInput, type HandoffTokenInput, type TokenHandoff } from "./token-db";
|
|
6
|
+
import { runnerTokenMintLimit } from "./runner-token-mint-limit";
|
|
7
|
+
import { createRunnerTokenAsync, createTokenAsync, handoffTokenAsync } from "./token-db-write";
|
|
7
8
|
import { FLEET_SCOPE, verifyComponentTokenAllowExpired } from "./security";
|
|
8
9
|
import { getAgentProfile, hostPinForProfile, spawnGrantForProfile, toolGroupsForProfile } from "./config-store";
|
|
9
10
|
import { toolAllowlistForRunner } from "./mcp-tool-allowlist";
|
|
@@ -38,6 +39,22 @@ interface RuntimeTokenResult {
|
|
|
38
39
|
record: TokenRecord;
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
/** A refused successor mint; `retryAfterSeconds` marks a per-subject rate limit (HTTP 429). */
|
|
43
|
+
export type RunnerTokenMintRefusal = { error: string; retryAfterSeconds?: number };
|
|
44
|
+
|
|
45
|
+
/** #3775 — the one way a runner token's successor is minted: within the subject's mint limit, charged per new token. */
|
|
46
|
+
export async function handoffRunnerTokenAsync(sub: string, input: HandoffTokenInput): Promise<TokenHandoff | null | { retryAfterSeconds: number }> {
|
|
47
|
+
const retryAfterSeconds = runnerTokenMintLimit.refusal(sub);
|
|
48
|
+
if (retryAfterSeconds !== undefined) return { retryAfterSeconds };
|
|
49
|
+
const handoff = await handoffTokenAsync(input);
|
|
50
|
+
if (handoff && !handoff.replayed) runnerTokenMintLimit.charge(sub);
|
|
51
|
+
return handoff;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function mintRateLimited(handoff: object | null): handoff is { retryAfterSeconds: number } {
|
|
55
|
+
return handoff !== null && "retryAfterSeconds" in handoff;
|
|
56
|
+
}
|
|
57
|
+
|
|
41
58
|
function runnerTokenOwnedByOrchestratorHost(
|
|
42
59
|
constraints: NonNullable<ReturnType<typeof verifyComponentTokenAllowExpired>>["constraints"],
|
|
43
60
|
orchestratorId: string,
|
|
@@ -68,7 +85,7 @@ export async function reissueRunnerRuntimeToken(input: {
|
|
|
68
85
|
expiredToken: string;
|
|
69
86
|
orchestratorId: string;
|
|
70
87
|
createdBy?: string;
|
|
71
|
-
}): Promise<RuntimeTokenResult |
|
|
88
|
+
}): Promise<RuntimeTokenResult | RunnerTokenMintRefusal> {
|
|
72
89
|
const payload = verifyComponentTokenAllowExpired(input.expiredToken, { allowStaleRevoked: true });
|
|
73
90
|
// #3133 — recovery hands off from the token's record, which holds its standing; a token without one recovers nothing.
|
|
74
91
|
if (!payload?.jti) return { error: "invalid or revoked runner token" };
|
|
@@ -95,7 +112,8 @@ export async function reissueRunnerRuntimeToken(input: {
|
|
|
95
112
|
constraints: payload.constraints,
|
|
96
113
|
createdBy: input.createdBy ?? `remint:${payload.jti}`,
|
|
97
114
|
};
|
|
98
|
-
const handoff = await
|
|
115
|
+
const handoff = await handoffRunnerTokenAsync(payload.sub, { jti: payload.jti, successor, recoverExpiredWithinSeconds: TOKEN_RECOVERY_WINDOW_SECONDS, createdBy: successor.createdBy });
|
|
116
|
+
if (mintRateLimited(handoff)) return { error: "runner token mint rate limited", retryAfterSeconds: handoff.retryAfterSeconds };
|
|
99
117
|
return handoff ?? { error: "invalid or revoked runner token" };
|
|
100
118
|
}
|
|
101
119
|
|
|
@@ -122,7 +140,7 @@ function runnerGrantKey(scope: readonly string[] | undefined, constraints: Token
|
|
|
122
140
|
* (current profile, lineage ceiling) and the presented token's launch identity. An identical grant is left
|
|
123
141
|
* alone; otherwise the token is handed off like a renewal, so a lost response is recovered by a retry.
|
|
124
142
|
*/
|
|
125
|
-
export async function remintRunnerRuntimeToken(auth: ComponentToken, createdBy: string): Promise<RunnerTokenRemint | { unchanged: true; jti: string } |
|
|
143
|
+
export async function remintRunnerRuntimeToken(auth: ComponentToken, createdBy: string): Promise<RunnerTokenRemint | { unchanged: true; jti: string } | RunnerTokenMintRefusal> {
|
|
126
144
|
const previous = auth.jti ? getToken(auth.jti) : null;
|
|
127
145
|
const c = auth.constraints ?? {};
|
|
128
146
|
const agentId = c.agents?.length === 1 ? c.agents[0] : undefined;
|
|
@@ -149,7 +167,8 @@ export async function remintRunnerRuntimeToken(auth: ComponentToken, createdBy:
|
|
|
149
167
|
});
|
|
150
168
|
if (token.constraints?.orchestrators?.includes("")) return { error: "the agent's host is unknown, so its host pin cannot be kept" };
|
|
151
169
|
if (runnerGrantKey(previous.scope, previous.constraints) === runnerGrantKey(token.scope, token.constraints)) return { unchanged: true, jti: previous.jti };
|
|
152
|
-
const handoff = await
|
|
170
|
+
const handoff = await handoffRunnerTokenAsync(auth.sub, { jti: previous.jti, allowedProfileIds: ["provider-agent"], successor: token, createdBy });
|
|
171
|
+
if (mintRateLimited(handoff)) return { error: "runner token mint rate limited", retryAfterSeconds: handoff.retryAfterSeconds };
|
|
153
172
|
if (!handoff) return { error: "the presented token was already rotated" };
|
|
154
173
|
const { record } = handoff;
|
|
155
174
|
return {
|
|
@@ -171,7 +190,16 @@ export function issueOrchestratorRuntimeToken(input: {
|
|
|
171
190
|
baseDir: string;
|
|
172
191
|
createdBy?: string;
|
|
173
192
|
}): RuntimeTokenResult {
|
|
174
|
-
return createToken(
|
|
193
|
+
return createToken(orchestratorTokenSpec(input));
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** #1870 S6c — the mint on the writer thread, for the async routes. */
|
|
197
|
+
export function issueOrchestratorRuntimeTokenAsync(input: Parameters<typeof issueOrchestratorRuntimeToken>[0]): Promise<RuntimeTokenResult> {
|
|
198
|
+
return createTokenAsync(orchestratorTokenSpec(input));
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function orchestratorTokenSpec(input: Parameters<typeof issueOrchestratorRuntimeToken>[0]): CreateTokenInput {
|
|
202
|
+
return {
|
|
175
203
|
profileId: "orchestrator-host",
|
|
176
204
|
sub: `orchestrator:${input.orchestratorId}`,
|
|
177
205
|
role: "orchestrator",
|
|
@@ -184,7 +212,7 @@ export function issueOrchestratorRuntimeToken(input: {
|
|
|
184
212
|
terminalWrite: true,
|
|
185
213
|
},
|
|
186
214
|
createdBy: input.createdBy ?? "runtime",
|
|
187
|
-
}
|
|
215
|
+
};
|
|
188
216
|
}
|
|
189
217
|
|
|
190
218
|
export interface RunnerRuntimeTokenInput {
|
|
@@ -297,6 +325,7 @@ function runnerTokenSpec(input: RunnerRuntimeTokenInput): { token: CreateTokenIn
|
|
|
297
325
|
: { kind: "root", ...(authority.kind === "delegated" ? { launcherScopes: authority.callerScopes } : {}), ...(profile ? { profile } : {}) } };
|
|
298
326
|
}
|
|
299
327
|
|
|
328
|
+
/** Sync twin for in-process tests; every runtime launch path mints through the writer op below. */
|
|
300
329
|
export function issueRunnerRuntimeToken(input: RunnerRuntimeTokenInput): RuntimeTokenResult {
|
|
301
330
|
const spec = runnerTokenSpec(input);
|
|
302
331
|
const issued = createToken(spec.token);
|
|
@@ -305,6 +334,11 @@ export function issueRunnerRuntimeToken(input: RunnerRuntimeTokenInput): Runtime
|
|
|
305
334
|
return issued;
|
|
306
335
|
}
|
|
307
336
|
|
|
337
|
+
export function issueRunnerRuntimeTokenAsync(input: RunnerRuntimeTokenInput): Promise<RuntimeTokenResult> {
|
|
338
|
+
const spec = runnerTokenSpec(input);
|
|
339
|
+
return createRunnerTokenAsync(spec.token, spec.launchFact ? { agentId: input.agentId, fact: spec.launchFact } : undefined);
|
|
340
|
+
}
|
|
341
|
+
|
|
308
342
|
export function issueInteractiveRunnerRuntimeToken(input: {
|
|
309
343
|
provider: string;
|
|
310
344
|
cwd: string;
|
|
@@ -384,7 +418,14 @@ export function issueIntegrationRuntimeToken(input: {
|
|
|
384
418
|
}
|
|
385
419
|
|
|
386
420
|
export function runnerRuntimeTokenEnv(input: RunnerRuntimeTokenInput): Record<string, string> {
|
|
387
|
-
|
|
421
|
+
return runnerTokenEnv(input, issueRunnerRuntimeToken(input));
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
export async function runnerRuntimeTokenEnvAsync(input: RunnerRuntimeTokenInput): Promise<Record<string, string>> {
|
|
425
|
+
return runnerTokenEnv(input, await issueRunnerRuntimeTokenAsync(input));
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function runnerTokenEnv(input: RunnerRuntimeTokenInput, issued: RuntimeTokenResult): Record<string, string> {
|
|
388
429
|
return writeChildEnv({}, {
|
|
389
430
|
AGENT_RELAY_TOKEN: issued.token,
|
|
390
431
|
AGENT_RELAY_TOKEN_JTI: issued.record.jti,
|
package/src/security.ts
CHANGED
|
@@ -233,6 +233,8 @@ export function isOrchestratorRegistrationAuthorized(req: Request): boolean {
|
|
|
233
233
|
}
|
|
234
234
|
|
|
235
235
|
const SHARED_UI_ROUTE_SCOPES: Record<string, string> = {
|
|
236
|
+
"/api/orchestrators/bootstrap": "token:write",
|
|
237
|
+
"/api/orchestrators/bootstrap/exchange": "token:write",
|
|
236
238
|
"/api/claude-api-budget/summary": "agent:read",
|
|
237
239
|
"/api/claude-api-budget": "system:admin",
|
|
238
240
|
"/api/stats": "stats:read",
|
|
@@ -252,6 +254,15 @@ function sharedUiRouteScope(pathname: string, method: string): string | undefine
|
|
|
252
254
|
if (/^\/api\/api-budgets\/[^/]+\/summary$/.test(pathname)) return "agent:read";
|
|
253
255
|
if (/^\/api\/api-budgets\/[^/]+$/.test(pathname)) return "system:admin";
|
|
254
256
|
if (/^\/api\/shipping\/incidents\/[^/]+\/abandon$/.test(pathname)) return "command:write";
|
|
257
|
+
if (pathname === "/api/teams/config") return method === "GET" ? "teams:read" : "settings:write:teams";
|
|
258
|
+
if (pathname.startsWith("/api/teams")) return method === "GET" ? "teams:read" : "teams:write";
|
|
259
|
+
if (pathname === "/api/decisions" && method === "POST") return "teams:write"; // #2914: filing = the relay_decision scope
|
|
260
|
+
if (pathname.startsWith("/api/decisions")) return method === "GET" ? "teams:read" : "message:send"; // #2885: answering = messaging the coordinator
|
|
261
|
+
if (pathname.startsWith("/api/blackboard")) return method === "GET" ? "blackboard:read" : "blackboard:write";
|
|
262
|
+
if (pathname.startsWith("/api/issue-associations")) return method === "GET" ? "issue:read" : "issue:write";
|
|
263
|
+
if (pathname.startsWith("/api/issue-activity")) return method === "GET" ? "issue:read" : "issue:write";
|
|
264
|
+
if (pathname.startsWith("/api/issue-lifecycle")) return method === "GET" ? "issue:read" : "issue:write";
|
|
265
|
+
if (pathname.startsWith("/api/issue-refs")) return method === "GET" ? "issue:read" : "issue:write";
|
|
255
266
|
return Object.hasOwn(SHARED_UI_ROUTE_SCOPES, pathname) ? SHARED_UI_ROUTE_SCOPES[pathname] : undefined;
|
|
256
267
|
}
|
|
257
268
|
|
|
@@ -266,8 +277,6 @@ export function requiredScopeFor(method: string, pathname: string): string | nul
|
|
|
266
277
|
if (pathname === "/api/route") return "agent:read";
|
|
267
278
|
if (pathname === "/api/notifications/preview") return "notifications:read";
|
|
268
279
|
if (pathname === "/api/mcp") return "mcp:use";
|
|
269
|
-
if (pathname === "/api/orchestrators/bootstrap/exchange") return "token:write";
|
|
270
|
-
if (pathname === "/api/orchestrators/bootstrap") return "token:write";
|
|
271
280
|
if (pathname.match(/^\/api\/orchestrators\/[^/]+\/logs\//)) return "logs:read";
|
|
272
281
|
if (pathname.match(/^\/api\/orchestrators\/[^/]+\/terminal\/[^/]+\/input$/)) return "terminal:write";
|
|
273
282
|
if (pathname.match(/^\/api\/orchestrators\/[^/]+\/terminal\//)) return "terminal:read";
|
|
@@ -323,16 +332,8 @@ export function requiredScopeFor(method: string, pathname: string): string | nul
|
|
|
323
332
|
if (pathname.match(/^\/api\/provider-quotas\/[^/]+\/reset-fired$/)) return "quota:write";
|
|
324
333
|
if (pathname === "/api/provider-quotas") return method === "GET" ? "agent:read" : "quota:write";
|
|
325
334
|
if (pathname === "/api/routing-policy") return method === "GET" ? "agent:read" : "settings:write:routing";
|
|
326
|
-
if (pathname === "/api/teams/config") return method === "GET" ? "teams:read" : "settings:write:teams";
|
|
327
335
|
if (pathname === "/api/settings/prompts" || pathname.startsWith("/api/settings/prompts/")) return method === "GET" ? "agent:read" : "system:admin";
|
|
328
|
-
if (
|
|
329
|
-
if (pathname === "/api/decisions" && method === "POST") return "teams:write"; // #2914: filing = the relay_decision scope
|
|
330
|
-
if (pathname.startsWith("/api/decisions")) return method === "GET" ? "teams:read" : "message:send"; // #2885: answering = messaging the coordinator
|
|
331
|
-
if (pathname.startsWith("/api/blackboard")) return method === "GET" ? "blackboard:read" : "blackboard:write";
|
|
332
|
-
if (pathname.startsWith("/api/issue-associations")) return method === "GET" ? "issue:read" : "issue:write";
|
|
333
|
-
if (pathname.startsWith("/api/issue-activity")) return method === "GET" ? "issue:read" : "issue:write";
|
|
334
|
-
if (pathname.startsWith("/api/issue-lifecycle")) return method === "GET" ? "issue:read" : "issue:write";
|
|
335
|
-
if (pathname.startsWith("/api/issue-refs")) return method === "GET" ? "issue:read" : "issue:write";
|
|
336
|
+
if (/^\/api\/projects\/[^/]+\/profile-instructions(?:\/|$)/.test(pathname)) return method === "GET" ? "agent:read" : "agent:write";
|
|
336
337
|
if (pathname.startsWith("/api/projects")) return method === "GET" ? "project:read" : "project:write";
|
|
337
338
|
// Insights config (the feature toggle) stays admin-only via the default; only the
|
|
338
339
|
// mechanical observation feed is writable by lower-privilege callers.
|
|
@@ -466,7 +467,6 @@ export function requiredComponentScopeFor(method: string, pathname: string): str
|
|
|
466
467
|
if (pathname === "/api/routing-policy") {
|
|
467
468
|
return method === "GET" ? "agent:read" : "settings:write:routing";
|
|
468
469
|
}
|
|
469
|
-
if (pathname === "/api/teams/config") return method === "GET" ? "teams:read" : "settings:write:teams";
|
|
470
470
|
if (pathname === "/api/settings/prompts" || pathname.startsWith("/api/settings/prompts/")) {
|
|
471
471
|
return method === "GET" ? "agent:read" : versionedPromptsEnabled() ? "agent:write" : "system:admin";
|
|
472
472
|
}
|
|
@@ -477,16 +477,9 @@ export function requiredComponentScopeFor(method: string, pathname: string): str
|
|
|
477
477
|
if (pathname.startsWith("/api/workflows")) return method === "GET" ? "task:read" : "task:write";
|
|
478
478
|
if (pathname.startsWith("/api/orchestrators")) return method === "GET" ? "agent:read" : "command:write";
|
|
479
479
|
if (pathname.startsWith("/api/plans")) return method === "GET" ? "plans:read" : "plans:write";
|
|
480
|
-
if (pathname.startsWith("/api/teams")) return method === "GET" ? "teams:read" : "teams:write";
|
|
481
|
-
if (pathname === "/api/decisions" && method === "POST") return "teams:write"; // #2914: filing = the relay_decision scope
|
|
482
|
-
if (pathname.startsWith("/api/decisions")) return method === "GET" ? "teams:read" : "message:send"; // #2885: answering = messaging the coordinator
|
|
483
|
-
if (pathname.startsWith("/api/blackboard")) return method === "GET" ? "blackboard:read" : "blackboard:write";
|
|
484
|
-
if (pathname.startsWith("/api/issue-associations")) return method === "GET" ? "issue:read" : "issue:write";
|
|
485
|
-
if (pathname.startsWith("/api/issue-activity")) return method === "GET" ? "issue:read" : "issue:write";
|
|
486
|
-
if (pathname.startsWith("/api/issue-lifecycle")) return method === "GET" ? "issue:read" : "issue:write";
|
|
487
|
-
if (pathname.startsWith("/api/issue-refs")) return method === "GET" ? "issue:read" : "issue:write";
|
|
488
480
|
// Fail-closed default returns system:admin, so this case is load-bearing: without it
|
|
489
481
|
// every /api/projects call from a scoped component token would 403-blackhole (#405).
|
|
482
|
+
if (/^\/api\/projects\/[^/]+\/profile-instructions(?:\/|$)/.test(pathname)) return method === "GET" ? "agent:read" : "agent:write";
|
|
490
483
|
if (pathname.startsWith("/api/projects")) return method === "GET" ? "project:read" : "project:write";
|
|
491
484
|
// The Runner posts the #184 context-gathering signal here (source:"server") via its
|
|
492
485
|
// provider token. Without this case the path fell through to the system:admin default
|
package/src/server-background.ts
CHANGED
|
@@ -19,6 +19,6 @@ export function startServerBackgroundJobs(): void {
|
|
|
19
19
|
{ name: "connectors", start: startConnectorStatusPoller },
|
|
20
20
|
{ name: "maintenance", start: startMaintenanceScheduler },
|
|
21
21
|
{ name: "steward-rekey-notices", start: async () => (await import("./workspace-rekey")).drainStewardRekeyNotices() },
|
|
22
|
-
{ name: "deploy-notice", start: () => { if (!isEphemeralMode()) notifyCdDeployLive(); } },
|
|
22
|
+
{ name: "deploy-notice", start: async () => { if (!isEphemeralMode()) await notifyCdDeployLive(); } },
|
|
23
23
|
]);
|
|
24
24
|
}
|
package/src/server.ts
CHANGED
|
@@ -7,6 +7,8 @@ import { inviteLinkRedirect } from "./invite-link";
|
|
|
7
7
|
import { claudeAppBoundary, CLAUDE_APP_PATH, isClaudeAppPath } from "./claude-app-auth";
|
|
8
8
|
import { handleClaudeApp } from "./claude-app-gateway";
|
|
9
9
|
import { handleClaudeAppOAuth } from "./claude-app/oauth-http";
|
|
10
|
+
import { installTimerIngressSchema } from "./db/timer-ingress";
|
|
11
|
+
import { beginMainWriteBootPhase, endMainWriteBootPhase } from "./db/main-connection-writes";
|
|
10
12
|
import { markServerBound, timePostBindPhase } from "./post-bind-startup";
|
|
11
13
|
import type { ServerWebSocket } from "bun";
|
|
12
14
|
import { beginStartupPhase, enableStartupTiming, timeStartupPhase } from "./startup-timing";
|
|
@@ -24,7 +26,6 @@ import { reconcileAutomationRuns, runDueAutomationsAsync, type AutomationDispatc
|
|
|
24
26
|
import { getLifecycleManager } from "./lifecycle-manager";
|
|
25
27
|
import { reconcileStewardPolicyModels } from "./steward-policy-model-reconcile";
|
|
26
28
|
import { getConfig, setConfig } from "./config-store";
|
|
27
|
-
import { reconcileProviderModelSeed } from "./provider-model-seed";
|
|
28
29
|
export { reconcileProviderModelSeed } from "./provider-model-seed";
|
|
29
30
|
import { ensureEscalationChannelRegistered } from "./connectors";
|
|
30
31
|
import { installNotificationSubscriptionRule } from "./notification-subscription-rules";
|
|
@@ -111,6 +112,7 @@ export async function startServer(): Promise<void> {
|
|
|
111
112
|
startArtifactCheckoutLease();
|
|
112
113
|
|
|
113
114
|
assertSafeNetworkConfig(HOST);
|
|
115
|
+
beginMainWriteBootPhase();
|
|
114
116
|
timeStartupPhase("server.database", () => initDb(DB_PATH));
|
|
115
117
|
// #1960 T2 — stand the gateway up EXPLICITLY here rather than leaving it to the lazy self-init
|
|
116
118
|
// (`getGateway()` on first call, gateway.ts), which would race the rest of this boot sequence.
|
|
@@ -136,7 +138,6 @@ export async function startServer(): Promise<void> {
|
|
|
136
138
|
installNotificationSubscriptionRule();
|
|
137
139
|
startPlanLiveBindings();
|
|
138
140
|
reconcileRelayVersion();
|
|
139
|
-
if (!isEphemeralMode()) reconcileProviderModelSeed();
|
|
140
141
|
// Register the ntfy escalation channel row from persisted connector config BEFORE the
|
|
141
142
|
// warning check, so a correctly-configured relay doesn't spuriously warn while the async
|
|
142
143
|
// channels-host connect is still in flight (#1510).
|
|
@@ -174,7 +175,9 @@ export async function startServer(): Promise<void> {
|
|
|
174
175
|
}, AUTOMATION_INTERVAL_MS);
|
|
175
176
|
}
|
|
176
177
|
|
|
178
|
+
installTimerIngressSchema();
|
|
177
179
|
finishServices();
|
|
180
|
+
endMainWriteBootPhase();
|
|
178
181
|
timeStartupPhase("server.bind", () => Bun.serve<RelaySocketData>({
|
|
179
182
|
port: PORT,
|
|
180
183
|
hostname: HOST,
|
|
@@ -11,10 +11,14 @@
|
|
|
11
11
|
// 2. the §4.4 in-memory last-hour series, ONLY once the durable write landed, so the Monitor
|
|
12
12
|
// chart can never show a point the Fleet card cannot corroborate;
|
|
13
13
|
// 3. #2822 — the one operator page a gate-capacity transition owes, decided by that same write.
|
|
14
|
+
// 4. #3762 — the one low-disk page (ntfy + coordinator) a disk level change owes, same write.
|
|
14
15
|
import type { HostDaemonSample, HostMachineSample } from "@humwick/sdk";
|
|
15
16
|
import { recordHostUtilizationAsync } from "../db";
|
|
16
17
|
import { recordHostUtilizationSample } from "../host/utilization-buffer";
|
|
17
18
|
import { composeGateCapacityNotice, type GateCapacityAlert } from "../host/gate-capacity-alert";
|
|
19
|
+
import { composeDiskNotice, type DiskAlert } from "../host/disk-alert";
|
|
20
|
+
import { stewardFallbackTarget } from "../config";
|
|
21
|
+
import { notifyTarget } from "../maintenance/events";
|
|
18
22
|
import { notifyWorkspaceHumanEscalation } from "../workspace-human-escalation";
|
|
19
23
|
|
|
20
24
|
export interface RecordHostUtilizationInput {
|
|
@@ -52,9 +56,28 @@ export async function recordHostUtilization(input: RecordHostUtilizationInput):
|
|
|
52
56
|
}
|
|
53
57
|
recordHostUtilizationSample(input.hostId, input.machine, input.now ?? Date.now());
|
|
54
58
|
if (recorded?.gateAlert) notifyGateCapacity(input.hostId, recorded.hostLabel ?? input.hostId, recorded.gateAlert, input.now ?? Date.now());
|
|
59
|
+
if (recorded?.diskAlert) notifyDisk(input.hostId, recorded.hostLabel ?? input.hostId, recorded.diskAlert);
|
|
55
60
|
return true;
|
|
56
61
|
}
|
|
57
62
|
|
|
63
|
+
/** Operator ntfy plus the steward/coordinator target — the same two recipients as the critical-job watchdog. */
|
|
64
|
+
function notifyDisk(hostId: string, label: string, alert: DiskAlert): void {
|
|
65
|
+
const notice = composeDiskNotice(hostId, label, alert);
|
|
66
|
+
const payload = { ...alert, kind: `host-disk.${alert.kind}`, hostId };
|
|
67
|
+
const human = notifyWorkspaceHumanEscalation({
|
|
68
|
+
subject: notice.subject,
|
|
69
|
+
body: notice.body,
|
|
70
|
+
priority: notice.priority,
|
|
71
|
+
tags: ["agent-relay", "host-disk"],
|
|
72
|
+
source: "host-disk",
|
|
73
|
+
payload,
|
|
74
|
+
idempotencyKey: notice.idempotencyKey,
|
|
75
|
+
});
|
|
76
|
+
const target = stewardFallbackTarget();
|
|
77
|
+
const routed = target ? notifyTarget("maintenance.degraded", {}, target, notice.subject, notice.body, payload, notice.idempotencyKey) : null;
|
|
78
|
+
console.warn(`[relay] ${notice.subject}${human.accepted || routed ? "" : " — nobody was notified (no ntfy channel or steward target)"}`);
|
|
79
|
+
}
|
|
80
|
+
|
|
58
81
|
function notifyGateCapacity(hostId: string, label: string, alert: GateCapacityAlert, now: number): void {
|
|
59
82
|
const notice = composeGateCapacityNotice(hostId, label, alert, now);
|
|
60
83
|
const result = notifyWorkspaceHumanEscalation({
|
|
@@ -3,8 +3,8 @@ import { isTerminalTaskStatus } from "./task-entity";
|
|
|
3
3
|
import { errMessage } from "@humwick/sdk";
|
|
4
4
|
import { coordinatedTeamIds } from "../db/coordinator-seat";
|
|
5
5
|
import { teamEpicWork } from "../db/team-epics";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { issueTrackerNotFoundCount, getIssueHierarchy } from "../db/long-running-work";
|
|
7
|
+
import { putIssueHierarchyAsync, recordIssueRefLiveStatusAsync, recordIssueTrackerNotFoundAsync } from "../db/issue-refs-async";
|
|
8
8
|
import { resolveIssueTrackerAdapter } from "../issue-tracker";
|
|
9
9
|
import { GitHubApiError } from "../issue-tracker/github";
|
|
10
10
|
import { routeNotification } from "../notification-router";
|
|
@@ -41,14 +41,13 @@ export async function refreshTeamEpicHierarchy(teamId: string, signal?: AbortSig
|
|
|
41
41
|
const live = await adapter.getStatus(issue);
|
|
42
42
|
if (signal?.aborted) break;
|
|
43
43
|
statusLoaded = true;
|
|
44
|
-
|
|
45
|
-
updateIssueRef(issue.id, { status: live.status, title: live.title, closeReason: live.closeReason, statusSyncedAt: Date.now() });
|
|
44
|
+
await recordIssueRefLiveStatusAsync(issue.id, { status: live.status, title: live.title, closeReason: live.closeReason, statusSyncedAt: Date.now() });
|
|
46
45
|
if (live.status !== "open") continue;
|
|
47
46
|
const children = adapter.listSubIssues ? await adapter.listSubIssues(issue) : await adapter.listOpenSubIssues?.(issue);
|
|
48
|
-
if (children)
|
|
47
|
+
if (children) await putIssueHierarchyAsync(issue.id, children);
|
|
49
48
|
} catch (error) {
|
|
50
49
|
if (!statusLoaded && error instanceof GitHubApiError && error.statusCode === 404
|
|
51
|
-
&&
|
|
50
|
+
&& await recordIssueTrackerNotFoundAsync(issue.id) >= 2) {
|
|
52
51
|
reportUnresolvable(teamId, issue);
|
|
53
52
|
continue;
|
|
54
53
|
}
|
|
@@ -8,9 +8,9 @@ import { listEntitiesForIssue, listIssuesForEntity } from "../db/issue-associati
|
|
|
8
8
|
import {
|
|
9
9
|
getSuccessfulIssueLifecycleAction,
|
|
10
10
|
listSuccessfulCloseActionsForCommit,
|
|
11
|
-
upsertIssueLifecycleAction,
|
|
12
11
|
} from "../db/issue-lifecycle";
|
|
13
|
-
import { getIssueRef
|
|
12
|
+
import { getIssueRef } from "../db/issue-refs";
|
|
13
|
+
import { resolveIssueRefByExternalKeyAsync, resolveIssueRefFromStringAsync, updateIssueRefAsync, upsertIssueLifecycleActionAsync } from "../db/issue-refs-async";
|
|
14
14
|
import { associateIssue } from "../db/issue-associations";
|
|
15
15
|
import { resolveIssueTrackerAdapter } from "../issue-tracker";
|
|
16
16
|
import { defaultIssueRepo } from "../config";
|
|
@@ -141,42 +141,53 @@ const ISSUE_REF_AT_START_RE = /^(https:\/\/github\.com\/[A-Za-z0-9_.-]+\/[A-Za-z
|
|
|
141
141
|
const REF_SEPARATOR_RE = /^(?:\s*,\s*|\s+(?:and|&)\s+)/i;
|
|
142
142
|
|
|
143
143
|
// A run of issue refs ("#1, #2 and owner/repo#3") starting at the head of `segment`.
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
function collectRefRun(segment: string, fallbackDefaultRepo: string | undefined, into: IssueRef[]): void {
|
|
144
|
+
function refRun(segment: string): string[] {
|
|
145
|
+
const refs: string[] = [];
|
|
147
146
|
let rest = segment;
|
|
148
147
|
while (rest.trim()) {
|
|
149
148
|
rest = rest.trimStart();
|
|
150
149
|
const refMatch = rest.match(ISSUE_REF_AT_START_RE);
|
|
151
150
|
if (!refMatch) break;
|
|
152
|
-
|
|
153
|
-
into.push(resolveIssueRefFromString({ ref: refMatch[1]!, defaultRepo: fallbackDefaultRepo }));
|
|
154
|
-
} catch {
|
|
155
|
-
// Invalid or bare-without-default refs are ignored; the land itself must never fail here.
|
|
156
|
-
}
|
|
151
|
+
refs.push(refMatch[1]!);
|
|
157
152
|
rest = rest.slice(refMatch[0].length);
|
|
158
153
|
const separator = rest.match(REF_SEPARATOR_RE);
|
|
159
154
|
if (!separator) break;
|
|
160
155
|
rest = rest.slice(separator[0].length);
|
|
161
156
|
}
|
|
157
|
+
return refs;
|
|
162
158
|
}
|
|
163
159
|
|
|
164
|
-
|
|
165
|
-
|
|
160
|
+
// Bare refs resolve against `fallbackDefaultRepo`; unresolvable ones are skipped so a land never fails here.
|
|
161
|
+
async function resolveRefRuns(segments: Iterable<string>, fallbackDefaultRepo: string | undefined): Promise<IssueRef[]> {
|
|
166
162
|
const refs: IssueRef[] = [];
|
|
167
|
-
for (const
|
|
163
|
+
for (const segment of segments) {
|
|
164
|
+
for (const ref of refRun(segment)) {
|
|
165
|
+
try {
|
|
166
|
+
refs.push(await resolveIssueRefFromStringAsync({ ref, defaultRepo: fallbackDefaultRepo }));
|
|
167
|
+
} catch {
|
|
168
|
+
// Invalid or bare-without-default refs are ignored; the land itself must never fail here.
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
168
172
|
return issueRefMap(refs);
|
|
169
173
|
}
|
|
170
174
|
|
|
175
|
+
export function parseClosingIssueRefsAsync(text: string | undefined, fallbackDefaultRepo = defaultIssueRepo()): Promise<IssueRef[]> {
|
|
176
|
+
return resolveRefRuns(text ? [...text.matchAll(CLOSING_KEYWORD_RE)].map((match) => match[1] ?? "") : [], fallbackDefaultRepo);
|
|
177
|
+
}
|
|
178
|
+
|
|
171
179
|
// Parenthesized backlink groups: the trailing `(#NNN)` convention agents (and a squash merge)
|
|
172
180
|
// stamp on a subject without a closing keyword. Non-issue parens (conventional-commit scopes
|
|
173
181
|
// like `fix(tokens)`, notes like `(see also)`) carry no `#` and fall out.
|
|
174
182
|
const BACKLINK_GROUP_RE = /\(([^()]*#[0-9]+[^()]*)\)/g;
|
|
175
183
|
|
|
176
|
-
export function
|
|
177
|
-
|
|
184
|
+
export function parseBacklinkIssueRefsAsync(text: string | undefined, fallbackDefaultRepo = defaultIssueRepo()): Promise<IssueRef[]> {
|
|
185
|
+
return resolveRefRuns(text ? [...text.matchAll(BACKLINK_GROUP_RE)].map((group) => group[1] ?? "") : [], fallbackDefaultRepo);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async function eachSubject(subjects: string[], parse: (text: string) => Promise<IssueRef[]>): Promise<IssueRef[]> {
|
|
178
189
|
const refs: IssueRef[] = [];
|
|
179
|
-
for (const
|
|
190
|
+
for (const subject of subjects) refs.push(...await parse(subject));
|
|
180
191
|
return issueRefMap(refs);
|
|
181
192
|
}
|
|
182
193
|
|
|
@@ -190,13 +201,13 @@ async function resolveIssueRefsForLand(input: LandedIssueLifecycleInput, links:
|
|
|
190
201
|
const associated = issueRefMap(links.flatMap((link) => link.issues.filter((issue) => issue.relation === "delivers").map((issue) => issue.ref)));
|
|
191
202
|
const fallbackDefaultRepo = await landDefaultRepo(input.workspace);
|
|
192
203
|
const subjects = landedSubjectTexts(input);
|
|
193
|
-
const keyworded =
|
|
204
|
+
const keyworded = await eachSubject(subjects, (subject) => parseClosingIssueRefsAsync(subject, fallbackDefaultRepo));
|
|
194
205
|
// A commit declaration is authored at the change boundary, unlike a Task association
|
|
195
206
|
// which can legitimately be historical or span several work items. It is the sole close
|
|
196
207
|
// target when present (#1694).
|
|
197
208
|
if (keyworded.length > 0) return { source: "commit", refs: keyworded, declared: keyworded };
|
|
198
209
|
if (!input.prLand && links.some((link) => link.buildLike)) {
|
|
199
|
-
const backlinks =
|
|
210
|
+
const backlinks = (await eachSubject(subjects, (subject) => parseBacklinkIssueRefsAsync(subject, fallbackDefaultRepo)))
|
|
200
211
|
.filter((ref) => canCloseBacklink(ref, links));
|
|
201
212
|
if (backlinks.length) return { source: "backlink", refs: backlinks, declared: backlinks };
|
|
202
213
|
}
|
|
@@ -348,7 +359,7 @@ async function reconcileClosedIssueRefBeforeSkip(issueRef: IssueRef, opts: Issue
|
|
|
348
359
|
// LOW-4 (#2520 review) — thread the live closeReason through: a completed close observed
|
|
349
360
|
// here was dropped, so the cached ref stayed reason-less and could never count as a
|
|
350
361
|
// completed close. updateIssueRef preserves the cached reason when live carries none.
|
|
351
|
-
issueRef:
|
|
362
|
+
issueRef: await updateIssueRefAsync(issueRef.id, { status, title: live.title, closeReason: live.closeReason, statusSyncedAt: Date.now() }) ?? issueRef,
|
|
352
363
|
liveStatus: live.status,
|
|
353
364
|
};
|
|
354
365
|
} catch (error) {
|
|
@@ -406,7 +417,7 @@ export async function closeIssueRefForLand(
|
|
|
406
417
|
logFailure(opts, `issue lifecycle epic-guard title lookup returned no title for ${latest.id} on ${key}`);
|
|
407
418
|
return { issueRef: latest, action: "skipped", reason: "title-unavailable", landKey: key };
|
|
408
419
|
}
|
|
409
|
-
latest =
|
|
420
|
+
latest = await updateIssueRefAsync(latest.id, { title, statusSyncedAt: Date.now() }) ?? latest;
|
|
410
421
|
} catch (error) {
|
|
411
422
|
logFailure(opts, `issue lifecycle epic-guard title lookup failed for ${latest.id} on ${key}: ${errMessage(error)}`);
|
|
412
423
|
return { issueRef: latest, action: "skipped", reason: "title-unavailable", landKey: key };
|
|
@@ -423,22 +434,25 @@ export async function closeIssueRefForLand(
|
|
|
423
434
|
return { issueRef: latest, action: "skipped", reason: "epic-issue", landKey: key };
|
|
424
435
|
}
|
|
425
436
|
|
|
437
|
+
const closeAction = {
|
|
438
|
+
issueRefId: latest.id,
|
|
439
|
+
action: "close" as const,
|
|
440
|
+
landKey: key,
|
|
441
|
+
workspaceId: input.workspace.id,
|
|
442
|
+
branch: input.workspace.branch,
|
|
443
|
+
baseRef: input.workspace.baseRef ?? "main",
|
|
444
|
+
commitSha: input.mergedSha,
|
|
445
|
+
commitSubject: input.subject,
|
|
446
|
+
agentId: input.workspace.ownerAgentId,
|
|
447
|
+
source,
|
|
448
|
+
};
|
|
426
449
|
let openSubIssues: IssueTrackerIssueSummary[];
|
|
427
450
|
try {
|
|
428
451
|
openSubIssues = await listOpenSubIssuesForLand(latest, opts);
|
|
429
452
|
} catch (error) {
|
|
430
453
|
const message = `sub-issue lookup failed: ${errMessage(error)}`;
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
action: "close",
|
|
434
|
-
landKey: key,
|
|
435
|
-
workspaceId: input.workspace.id,
|
|
436
|
-
branch: input.workspace.branch,
|
|
437
|
-
baseRef: input.workspace.baseRef ?? "main",
|
|
438
|
-
commitSha: input.mergedSha,
|
|
439
|
-
commitSubject: input.subject,
|
|
440
|
-
agentId: input.workspace.ownerAgentId,
|
|
441
|
-
source,
|
|
454
|
+
await upsertIssueLifecycleActionAsync({
|
|
455
|
+
...closeAction,
|
|
442
456
|
status: "failed",
|
|
443
457
|
error: message,
|
|
444
458
|
});
|
|
@@ -458,18 +472,9 @@ export async function closeIssueRefForLand(
|
|
|
458
472
|
const comment = closeAuditComment(input);
|
|
459
473
|
try {
|
|
460
474
|
await adapterFor(latest, opts).close(latest, { comment, labels: ["fixed"], closeReason: "completed" });
|
|
461
|
-
const closed =
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
action: "close",
|
|
465
|
-
landKey: key,
|
|
466
|
-
workspaceId: input.workspace.id,
|
|
467
|
-
branch: input.workspace.branch,
|
|
468
|
-
baseRef: input.workspace.baseRef ?? "main",
|
|
469
|
-
commitSha: input.mergedSha,
|
|
470
|
-
commitSubject: input.subject,
|
|
471
|
-
agentId: input.workspace.ownerAgentId,
|
|
472
|
-
source,
|
|
475
|
+
const closed = await updateIssueRefAsync(latest.id, { status: "closed", closeReason: "completed", statusSyncedAt: Date.now() }) ?? latest;
|
|
476
|
+
await upsertIssueLifecycleActionAsync({
|
|
477
|
+
...closeAction,
|
|
473
478
|
comment,
|
|
474
479
|
status: "succeeded",
|
|
475
480
|
});
|
|
@@ -494,17 +499,8 @@ export async function closeIssueRefForLand(
|
|
|
494
499
|
return { issueRef: closed, action: "closed", source, landKey: key };
|
|
495
500
|
} catch (error) {
|
|
496
501
|
const message = errMessage(error);
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
action: "close",
|
|
500
|
-
landKey: key,
|
|
501
|
-
workspaceId: input.workspace.id,
|
|
502
|
-
branch: input.workspace.branch,
|
|
503
|
-
baseRef: input.workspace.baseRef ?? "main",
|
|
504
|
-
commitSha: input.mergedSha,
|
|
505
|
-
commitSubject: input.subject,
|
|
506
|
-
agentId: input.workspace.ownerAgentId,
|
|
507
|
-
source,
|
|
502
|
+
await upsertIssueLifecycleActionAsync({
|
|
503
|
+
...closeAction,
|
|
508
504
|
comment,
|
|
509
505
|
status: "failed",
|
|
510
506
|
error: message,
|
|
@@ -564,15 +560,15 @@ export async function handleLandedIssueLifecycle(input: LandedIssueLifecycleInpu
|
|
|
564
560
|
return { source: resolved.source, closed, reopened, skipped: [], mismatches };
|
|
565
561
|
}
|
|
566
562
|
|
|
567
|
-
function resolveReopenRef(input: ReopenIssueLifecycleInput): IssueRef {
|
|
563
|
+
async function resolveReopenRef(input: ReopenIssueLifecycleInput): Promise<IssueRef> {
|
|
568
564
|
if (input.issueRefId) {
|
|
569
565
|
const issueRef = getIssueRef(input.issueRefId);
|
|
570
566
|
if (!issueRef) throw new Error(`issue ref ${input.issueRefId} not found`);
|
|
571
567
|
return issueRef;
|
|
572
568
|
}
|
|
573
|
-
if (input.ref) return
|
|
569
|
+
if (input.ref) return resolveIssueRefFromStringAsync({ ref: input.ref, defaultRepo: input.defaultRepo ?? input.repo, provider: input.provider, title: input.title });
|
|
574
570
|
if (input.provider && input.repo && input.externalKey) {
|
|
575
|
-
return
|
|
571
|
+
return resolveIssueRefByExternalKeyAsync({
|
|
576
572
|
provider: input.provider,
|
|
577
573
|
repo: input.repo,
|
|
578
574
|
externalKey: input.externalKey,
|
|
@@ -584,14 +580,14 @@ function resolveReopenRef(input: ReopenIssueLifecycleInput): IssueRef {
|
|
|
584
580
|
}
|
|
585
581
|
|
|
586
582
|
export async function reopenIssueLifecycle(input: ReopenIssueLifecycleInput, opts: IssueLifecycleOptions = {}): Promise<ReopenIssueLifecycleResult> {
|
|
587
|
-
const issueRef = resolveReopenRef(input);
|
|
583
|
+
const issueRef = await resolveReopenRef(input);
|
|
588
584
|
const key = manualReopenKey(issueRef);
|
|
589
585
|
if (issueRef.status === "open") return { issueRef, action: "skipped", reason: "already-open", landKey: key };
|
|
590
586
|
const comment = input.comment ?? "Reopened by Humwick issue lifecycle.";
|
|
591
587
|
try {
|
|
592
588
|
await adapterFor(issueRef, opts).reopen(issueRef, { comment });
|
|
593
|
-
const opened =
|
|
594
|
-
|
|
589
|
+
const opened = await updateIssueRefAsync(issueRef.id, { status: "open", statusSyncedAt: Date.now() }) ?? issueRef;
|
|
590
|
+
await upsertIssueLifecycleActionAsync({
|
|
595
591
|
issueRefId: issueRef.id,
|
|
596
592
|
action: "reopen",
|
|
597
593
|
landKey: key,
|
|
@@ -604,7 +600,7 @@ export async function reopenIssueLifecycle(input: ReopenIssueLifecycleInput, opt
|
|
|
604
600
|
return { issueRef: opened, action: "reopened", landKey: key };
|
|
605
601
|
} catch (error) {
|
|
606
602
|
const message = errMessage(error);
|
|
607
|
-
|
|
603
|
+
await upsertIssueLifecycleActionAsync({
|
|
608
604
|
issueRefId: issueRef.id,
|
|
609
605
|
action: "reopen",
|
|
610
606
|
landKey: key,
|
|
@@ -1,11 +1,7 @@
|
|
|
1
|
+
import { deleteIssueRefAsync, resolveIssueRefByExternalKeyAsync, resolveIssueRefFromStringAsync, updateIssueRefAsync, upsertIssueRefAsync } from "../db/issue-refs-async";
|
|
1
2
|
import {
|
|
2
|
-
deleteIssueRef,
|
|
3
3
|
getIssueRef,
|
|
4
4
|
listIssueRefs,
|
|
5
|
-
resolveIssueRefByExternalKey,
|
|
6
|
-
resolveIssueRefFromString,
|
|
7
|
-
updateIssueRef,
|
|
8
|
-
upsertIssueRef,
|
|
9
5
|
type ListIssueRefsOptions,
|
|
10
6
|
type ResolveIssueRefByExternalKeyInput,
|
|
11
7
|
type ResolveIssueRefFromStringInput,
|
|
@@ -15,20 +11,20 @@ import {
|
|
|
15
11
|
|
|
16
12
|
export { getIssueRef, listIssueRefs, type ListIssueRefsOptions };
|
|
17
13
|
|
|
18
|
-
export function
|
|
19
|
-
return
|
|
14
|
+
export function upsertIssueRefMappingAsync(input: UpsertIssueRefInput) {
|
|
15
|
+
return upsertIssueRefAsync(input);
|
|
20
16
|
}
|
|
21
17
|
|
|
22
|
-
export function
|
|
23
|
-
return "ref" in input ?
|
|
18
|
+
export function resolveIssueRefMappingAsync(input: ResolveIssueRefByExternalKeyInput | ResolveIssueRefFromStringInput) {
|
|
19
|
+
return "ref" in input ? resolveIssueRefFromStringAsync(input) : resolveIssueRefByExternalKeyAsync(input);
|
|
24
20
|
}
|
|
25
21
|
|
|
26
|
-
export function
|
|
27
|
-
return
|
|
22
|
+
export function patchIssueRefMappingAsync(id: string, input: UpdateIssueRefInput) {
|
|
23
|
+
return updateIssueRefAsync(id, input);
|
|
28
24
|
}
|
|
29
25
|
|
|
30
|
-
export function
|
|
31
|
-
return
|
|
26
|
+
export function deleteIssueRefMappingAsync(id: string) {
|
|
27
|
+
return deleteIssueRefAsync(id);
|
|
32
28
|
}
|
|
33
29
|
|
|
34
30
|
// --- Caller-supplied issue-ref normalization (#2214/#1930) ---------------------------------
|