@humwick/server 0.130.2 → 0.130.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundled/prompts/manifest.json +1 -0
- package/bundled/prompts/task.hydration.md +1 -1
- package/bundled/roles/builder.md +1 -1
- package/docs/guides/sub-coordinators.md +12 -5
- package/docs/guides/tools-coordination.md +1 -0
- package/docs/guides/workflows.md +16 -0
- package/docs/openapi.json +73 -4
- package/package.json +8 -7
- package/public/assets/{MessageBubble-BWLqMGKG.js → MessageBubble-Bq9kr21N.js} +2 -2
- package/public/assets/{PlanGraphPanel-3TlIQpjk.js → PlanGraphPanel-Dqiroy06.js} +2 -2
- package/public/assets/{TraceView-Wimm0Czv.js → TraceView-DYU962-H.js} +2 -2
- package/public/assets/{VaultDecisionCard-Bau_vNax.js → VaultDecisionCard-DhPfzioE.js} +2 -2
- package/public/assets/{activity-CrSurn-r.js → activity-DzCcQTee.js} +2 -2
- package/public/assets/{agent-lineage-CknAJKcb.js → agent-lineage-SgDt9i3T.js} +2 -2
- package/public/assets/agent-liveness-Bq-s73jW.js +2 -0
- package/public/assets/{agent-name-DKbiIhBo.js → agent-name-CcY3Aijy.js} +2 -2
- package/public/assets/{agent-profiles-DwF6EQ4R.js → agent-profiles-cxtRBn6O.js} +2 -2
- package/public/assets/{agent-project-badge-Diz4Sjjb.js → agent-project-badge-DGYbIhaI.js} +2 -2
- package/public/assets/{agent-tools-CRhpRQza.js → agent-tools-DHbftV0N.js} +2 -2
- package/public/assets/{agent-type-icon-LrUEU3lB.js → agent-type-icon-CY113sp-.js} +2 -2
- package/public/assets/{agents-B-v66C-4.js → agents-BPzY7IlQ.js} +2 -2
- package/public/assets/{analytics-BqCb-LKi.js → analytics-DENFPv_a.js} +2 -2
- package/public/assets/{artifact-viewer-kf4pGuAR.js → artifact-viewer-KRvSc7sl.js} +2 -2
- package/public/assets/{artifacts-DJevXcvg.js → artifacts-C9gSmL3s.js} +2 -2
- package/public/assets/{artifacts-D32zyw4Y.js → artifacts-DT-HkI5O.js} +2 -2
- package/public/assets/{automation-BpRajx_5.js → automation-BsmjQhKD.js} +2 -2
- package/public/assets/{backlog-D_UjTdzX.js → backlog-8NYH_62t.js} +2 -2
- package/public/assets/{busy-health-N3VEh4ao.js → busy-health-D3hwvYb4.js} +2 -2
- package/public/assets/{channels-9jopungq.js → channels-DCJ5WL7O.js} +2 -2
- package/public/assets/{chat-CSXrTB3C.js → chat-DAIl357U.js} +3 -3
- package/public/assets/{code-preview-p1NnmqK0.js → code-preview-CzrZfr9U.js} +2 -2
- package/public/assets/{connectors-CZdqlDNL.js → connectors-xTxtlyou.js} +2 -2
- package/public/assets/{cosmos-BgdsE4dG.js → cosmos-BpebFLK2.js} +2 -2
- package/public/assets/{crate-agent-30aXTKva.js → crate-agent-BHvI5wXC.js} +2 -2
- package/public/assets/{dependency-graph-B_jvw2GE.js → dependency-graph-DGT0o3wJ.js} +2 -2
- package/public/assets/{deploys-DoN6bAb5.js → deploys-hqWOHSHE.js} +2 -2
- package/public/assets/{display-BrKWFGJz.js → display-CDeH4I8T.js} +2 -2
- package/public/assets/{files-D6XEu8Ng.js → files-BTeGVmTg.js} +2 -2
- package/public/assets/{fleet-C7jQWbw2.js → fleet-DgXtNgCa.js} +2 -2
- package/public/assets/{fleet-metrics-BiDs3MLx.js → fleet-metrics-BHEt5yc_.js} +2 -2
- package/public/assets/{formatted-body-CQBwc1fb.js → formatted-body-MLtodyPp.js} +3 -3
- package/public/assets/{formatted-body-impl-DyG0WsQS.js → formatted-body-impl-CAe664Z8.js} +2 -2
- package/public/assets/{host-providers-dialog-DKaW31dN.js → host-providers-dialog-mapF4Fj9.js} +2 -2
- package/public/assets/{index-CRHep3M9.js → index-CqqgOI5R.js} +5 -5
- package/public/assets/{insights-CzrtkpOE.js → insights-DNyf7IzF.js} +2 -2
- package/public/assets/{integrations-aVsUmigt.js → integrations-DlYOHq7T.js} +2 -2
- package/public/assets/{issue-ref-chip-BnSlM_95.js → issue-ref-chip-CeWBNWn7.js} +2 -2
- package/public/assets/{log-viewer-qe7Ee47Z.js → log-viewer-Cib8nqdE.js} +2 -2
- package/public/assets/{maintenance-NfjqFZAk.js → maintenance-DSn1cPm7.js} +2 -2
- package/public/assets/{managed-agents-BiH0HgaE.js → managed-agents-C3CkM81m.js} +2 -2
- package/public/assets/{markdown-preview-BixaT3Xr.js → markdown-preview-W4VQQPAv.js} +3 -3
- package/public/assets/{markdown-preview-impl-DNGlWIS3.js → markdown-preview-impl-CMsTdhjq.js} +2 -2
- package/public/assets/{memory-DNsCl6UX.js → memory-BV17qtO4.js} +2 -2
- package/public/assets/{messages-DopU1L1X.js → messages-Bm_1AqSA.js} +2 -2
- package/public/assets/{orchestrators-Cq7ckLQT.js → orchestrators-Dg-vfP09.js} +2 -2
- package/public/assets/{overview-C1DCTzzV.js → overview-DcYCrvlr.js} +2 -2
- package/public/assets/{pairs-DE_m6rrE.js → pairs-8o9nuG3V.js} +2 -2
- package/public/assets/{profile-icon-B3rfh77F.js → profile-icon-BsRyBMea.js} +2 -2
- package/public/assets/{project-profile-instructions-DQWZE0kA.js → project-profile-instructions-DETKFn_E.js} +2 -2
- package/public/assets/{projects-Br2Bd2h1.js → projects-M_m6AHws.js} +2 -2
- package/public/assets/{prompt-settings-BKvFsvGD.js → prompt-settings-C_mNCgeL.js} +2 -2
- package/public/assets/provider-display-BEeLXC_D.js +2 -0
- package/public/assets/{registry-Cw38_6gW.js → registry-DDHHHE5N.js} +2 -2
- package/public/assets/run-card-Dt4I4jaP.js +2 -0
- package/public/assets/{secrets-DxI-vzSq.js → secrets-BkVwxI5O.js} +2 -2
- package/public/assets/{service-access-settings-D9s_VJbd.js → service-access-settings-Crq-6a-B.js} +2 -2
- package/public/assets/{settings-CKZTVGIx.js → settings-CYcavMFu.js} +2 -2
- package/public/assets/shipping-H6gzEeZB.js +2 -0
- package/public/assets/{stats-DyUBKyWI.js → stats-BJVeWkpV.js} +2 -2
- package/public/assets/{status-dot-DYqlEUhJ.js → status-dot-BcHyw1Vw.js} +2 -2
- package/public/assets/store-DKPDIZBe.js +9 -0
- package/public/assets/{tasks-Bh7Chr4-.js → tasks-qJjlQPpx.js} +2 -2
- package/public/assets/{teams-bowI7qVY.js → teams-Cd5Sk1uX.js} +2 -2
- package/public/assets/{terminal-viewer-BgVLHdsU.js → terminal-viewer-CpY5_m7W.js} +3 -3
- package/public/assets/{terminal-viewer-impl-Dz22UPy6.js → terminal-viewer-impl-DI_2llFR.js} +2 -2
- package/public/assets/{use-agent-terminal-DSQQ-iY7.js → use-agent-terminal-0yJA_buM.js} +2 -2
- package/public/assets/{use-decisions-samMQhQf.js → use-decisions-uJXHf34W.js} +2 -2
- package/public/assets/{use-project-list-59LheVP7.js → use-project-list-CpWl6eHQ.js} +2 -2
- package/public/assets/{use-selectors-BZa25xEB.js → use-selectors-BdxBW8mL.js} +2 -2
- package/public/assets/{user-avatar-DHxlvUvV.js → user-avatar-3IndfpOc.js} +2 -2
- package/public/assets/{versioned-prompt-editor-BmED5DnJ.js → versioned-prompt-editor-CIVg7XB1.js} +2 -2
- package/public/assets/{work-queue-CSvpuNLz.js → work-queue-B9rnLwHZ.js} +2 -2
- package/public/assets/{workflow-marker-DAZcj5qt.js → workflow-marker-X8wxyhzC.js} +2 -2
- package/public/assets/{workflows-DdmxRSyg.js → workflows-o5YyMFW7.js} +2 -2
- package/public/assets/{workspaces-BxeKfVuF.js → workspaces-CzEGoAng.js} +2 -2
- package/public/cosmos.html +4 -4
- package/public/index.html +28 -28
- package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/runner/src/adapters/claude-headless.ts +1 -1
- package/runner/src/adapters/claude-launch-exit.ts +26 -0
- package/runner/src/adapters/claude-session-probe.ts +1 -1
- package/runner/src/adapters/claude-tmux.ts +38 -10
- package/runner/src/adapters/claude.ts +28 -75
- package/runner/src/adapters/provider-quota-harvest.ts +20 -71
- package/runner/src/agent-runner/lifecycle-milestones.ts +2 -2
- package/runner/src/agent-runner/liveness-exchange.ts +104 -0
- package/runner/src/claude-system-prompt.ts +49 -0
- package/runner/src/launch-assembly.ts +14 -10
- package/runner/src/profile-home.ts +1 -1
- package/runner/src/project-instructions.ts +2 -2
- package/runner/src/provider-command.ts +18 -0
- package/runner/src/provider-setup.ts +2 -1
- package/runner/src/runner-core.ts +14 -15
- package/runner/src/runner-helpers.ts +0 -8
- package/runner/src/session-scratch.ts +6 -2
- package/runner/test-support/provisioning-context.ts +4 -2
- package/runner/test-support/runner-harness.ts +2 -1
- package/scripts/steward-policy-cleanup.ts +2 -2
- package/src/agent-authz.ts +2 -2
- package/src/agent-drive-arbiter.ts +3 -8
- package/src/agent-exit-cleanup-async.ts +2 -2
- package/src/agent-exit-cleanup.ts +23 -19
- package/src/agent-lifecycle-events.ts +112 -85
- package/src/agent-ref.ts +3 -2
- package/src/agent-status-events.ts +43 -14
- package/src/auth/password.ts +4 -3
- package/src/auth/session-refresh.ts +1 -1
- package/src/auth/session.test-fixture.ts +2 -2
- package/src/auth/session.ts +7 -7
- package/src/automations/crud.ts +8 -81
- package/src/automations/dispatch.ts +33 -58
- package/src/automations/index.ts +1 -1
- package/src/automations/persistence.ts +65 -0
- package/src/automations/precheck.ts +18 -16
- package/src/automations/read-async.ts +11 -0
- package/src/automations/reconcile.ts +30 -39
- package/src/automations/storage.ts +33 -10
- package/src/automations/write-async.ts +33 -0
- package/src/branch-land-notice.ts +3 -3
- package/src/branch-landed.ts +25 -23
- package/src/bus.ts +21 -19
- package/src/cd-deploy-notify.ts +2 -2
- package/src/ci/notifications.ts +4 -4
- package/src/ci/root-missing.ts +3 -3
- package/src/ci/transition-notify.ts +5 -5
- package/src/claude-app/oauth-http.ts +3 -3
- package/src/cli/workflow.ts +12 -3
- package/src/commands-db.ts +4 -4
- package/src/commands-write.ts +8 -0
- package/src/compaction-watch.ts +10 -8
- package/src/config-store-write.ts +2 -2
- package/src/config.ts +4 -11
- package/src/connectors.ts +10 -8
- package/src/context-advisory.ts +22 -21
- package/src/db/agent-action-write.ts +36 -0
- package/src/db/agent-control-async.ts +9 -0
- package/src/db/agent-control-write.ts +49 -0
- package/src/db/agent-effort-observe.ts +22 -0
- package/src/db/agent-meta.ts +14 -3
- package/src/db/agents-async.ts +8 -5
- package/src/db/agents.ts +15 -12
- package/src/db/auth-identities.ts +2 -2
- package/src/db/automation-read-ops.ts +11 -0
- package/src/db/automation-write-ops.ts +46 -0
- package/src/db/blackboard.ts +2 -2
- package/src/db/browser-session-write.ts +32 -0
- package/src/db/bus-outbox-write-ops.ts +3 -1
- package/src/db/checkout-refresh-schema.ts +47 -0
- package/src/db/checkout-refresh.ts +5 -5
- package/src/db/command-agent-profile-backfill.ts +39 -0
- package/src/db/command-agent-profiles.ts +5 -1
- package/src/db/command-retention.ts +126 -0
- package/src/db/commands-planner-stats.test-fixture.ts +8 -0
- package/src/db/commands-read-ops.ts +16 -0
- package/src/db/connection.ts +1 -182
- package/src/db/context-carry-write.ts +74 -0
- package/src/db/continuations.ts +4 -4
- package/src/db/db-error-registry.ts +12 -0
- package/src/db/gateway.ts +26 -133
- package/src/db/history-retention-ops.ts +13 -0
- package/src/db/inbox-async.ts +2 -2
- package/src/db/inbox-read-ops.ts +1 -1
- package/src/db/inbox.ts +9 -3
- package/src/db/issue-associations.ts +2 -2
- package/src/db/issue-refs-async.ts +4 -1
- package/src/db/issue-refs-read-ops.ts +6 -0
- package/src/db/issue-refs.ts +8 -8
- package/src/db/kernel-event-log-schema.ts +2 -0
- package/src/db/land-boundaries.ts +51 -5
- package/src/db/land-gate-attestations.ts +2 -2
- package/src/db/landing-receipts.ts +2 -2
- package/src/db/lifecycle-context-write.ts +5 -0
- package/src/db/lifecycle-tail-async.ts +76 -0
- package/src/db/lifecycle-tail-write-ops.ts +62 -0
- package/src/db/lifecycle-write-window.ts +2 -1
- package/src/db/lineage-pair-dedupe-sql.ts +3 -3
- package/src/db/main-connection-mode.ts +11 -0
- package/src/db/main-connection-writes.ts +20 -2
- package/src/db/maintenance-job-runs-async.ts +2 -2
- package/src/db/maintenance-job-write-ops.ts +1 -1
- package/src/db/maintenance-pauses.ts +2 -2
- package/src/db/message-send.ts +4 -0
- package/src/db/messages.ts +31 -25
- package/src/db/migrations.ts +18 -1
- package/src/db/notification-write-ops.ts +19 -0
- package/src/db/operator-answer-write.ts +19 -0
- package/src/db/orchestrators.ts +10 -4
- package/src/db/pairs-async.ts +22 -1
- package/src/db/pairs-write-ops.ts +8 -3
- package/src/db/pairs.ts +41 -48
- package/src/db/plan-dispatch-schema.ts +5 -1
- package/src/db/plan-ready-notices.ts +43 -0
- package/src/db/plan-tools-include-migration.ts +36 -0
- package/src/db/plans-write-ops.ts +2 -0
- package/src/db/presence-buffer.ts +5 -0
- package/src/db/presence-loop.ts +18 -23
- package/src/db/presence-write.ts +0 -6
- package/src/db/project-entries-async.ts +14 -0
- package/src/db/project-entries-read-ops.ts +5 -1
- package/src/db/project-entries-write-ops.ts +8 -0
- package/src/db/project-entries.ts +15 -12
- package/src/db/project-journal.ts +2 -2
- package/src/db/project-trust-approvals.ts +2 -2
- package/src/db/prompt-templates.ts +4 -4
- package/src/db/read-op-marshaling-fixtures.ts +18 -0
- package/src/db/read-ops.ts +4 -0
- package/src/db/retention.ts +13 -2
- package/src/db/review-publish-write.ts +8 -0
- package/src/db/runtime-admin-async.ts +19 -0
- package/src/db/runtime-admin-write-ops.ts +36 -0
- package/src/db/runtime-tail-async.ts +288 -0
- package/src/db/runtime-tail-write-ops.ts +97 -0
- package/src/db/schema.ts +4 -1
- package/src/db/spawn-budget.ts +5 -0
- package/src/db/spawn-delegation.ts +1 -11
- package/src/db/sql-audit-indexes.ts +3 -0
- package/src/db/stats-async.ts +5 -3
- package/src/db/task-entity.ts +5 -4
- package/src/db/team-claim-policy.ts +60 -0
- package/src/db/teams-async.ts +4 -0
- package/src/db/teams-write-ops.ts +4 -0
- package/src/db/terminal-events.ts +15 -0
- package/src/db/timer-cutover-bodies.ts +292 -0
- package/src/db/timer-cutover-core.ts +37 -278
- package/src/db/timer-cutover-state.ts +40 -0
- package/src/db/timer-cutover-write-ops.ts +1 -40
- package/src/db/token-db-write-ops.ts +2 -0
- package/src/db/token-standing-async.ts +12 -0
- package/src/db/token-standing.ts +6 -9
- package/src/db/token-usage-retention.ts +33 -0
- package/src/db/token-usage-write-ops.ts +1 -1
- package/src/db/token-usage.ts +4 -1
- package/src/db/user-invites-async.ts +13 -0
- package/src/db/user-invites.ts +3 -3
- package/src/db/users.ts +14 -6
- package/src/db/workspaces.ts +8 -5
- package/src/db/write-ops.ts +31 -12
- package/src/db/writer-fault-seam.ts +2 -9
- package/src/db/writer-fault-test-ops.ts +18 -0
- package/src/db/writer-gateway.ts +42 -46
- package/src/dev.ts +8 -3
- package/src/event-loop-lag.ts +4 -2
- package/src/event-loop-yield.ts +2 -2
- package/src/events.ts +10 -8
- package/src/host-provider-store.ts +5 -4
- package/src/hub-metrics-report.ts +5 -1
- package/src/hub-metrics.ts +38 -18
- package/src/injection-sweep-pacing.ts +6 -8
- package/src/land-gate-notifications.ts +11 -10
- package/src/land-gate-queue-progress.ts +3 -3
- package/src/land-settle-dispatch.ts +52 -92
- package/src/lifecycle-context-pressure.ts +11 -10
- package/src/lifecycle-manager.ts +104 -98
- package/src/maintenance/breaker.ts +18 -16
- package/src/maintenance/checkout-refresh.ts +18 -1
- package/src/maintenance/critical-job-watchdog.ts +3 -3
- package/src/maintenance/events.ts +3 -3
- package/src/maintenance/history-retention-job.ts +30 -0
- package/src/maintenance/jobs.ts +29 -67
- package/src/maintenance/long-running-work.ts +3 -1
- package/src/maintenance/offline-agent-prune-job.ts +28 -0
- package/src/maintenance/orphaned-sessions.ts +5 -4
- package/src/maintenance/pauses.ts +4 -10
- package/src/maintenance/plan-ready-notice-job.ts +71 -0
- package/src/maintenance/scheduler.ts +18 -13
- package/src/maintenance/stalled-delivery-pager.ts +4 -4
- package/src/maintenance/teams.ts +19 -30
- package/src/maintenance/workspaces.ts +17 -27
- package/src/managed-policy-escalation.ts +7 -7
- package/src/managed-policy.ts +3 -3
- package/src/managed-provider-gate.ts +5 -4
- package/src/managed-spawn-reservation.ts +5 -4
- package/src/mcp/index.ts +8 -8
- package/src/mcp/output-descriptions.ts +1 -1
- package/src/mcp/presentation.ts +2 -1
- package/src/mcp/runtime-output.ts +6 -1
- package/src/mcp/self-care-visibility.ts +2 -2
- package/src/mcp/spawn-targets-matrix.ts +10 -1
- package/src/mcp/tool-groups.ts +3 -2
- package/src/mcp/tool-roles.ts +81 -0
- package/src/mcp/tools-workspace.ts +1 -1
- package/src/mcp-blackboard-tools.ts +6 -5
- package/src/mcp-decision-tools.ts +7 -7
- package/src/mcp-egress-tools.ts +10 -9
- package/src/mcp-issue-activity-tools.ts +2 -2
- package/src/mcp-issue-association-tools.ts +4 -4
- package/src/mcp-issue-ref-tools.ts +3 -3
- package/src/mcp-merge-tools.ts +5 -5
- package/src/mcp-notification-subscription-tools.ts +14 -15
- package/src/mcp-output-levels.test-fixture.ts +1 -1
- package/src/mcp-profile-trust-tools.ts +1 -1
- package/src/mcp-project-journal-tools.ts +4 -12
- package/src/mcp-project-tools.ts +17 -18
- package/src/mcp-routing-policy-tools.ts +3 -2
- package/src/mcp-team-tools.ts +6 -6
- package/src/mcp-workflow-tools.ts +5 -3
- package/src/memory-area-injection.ts +13 -12
- package/src/memory-boundary-capture.ts +6 -6
- package/src/memory-injection.ts +45 -43
- package/src/merge-pause-maintenance.ts +5 -4
- package/src/notification-router.ts +44 -17
- package/src/notify.ts +5 -5
- package/src/observed-reset-refresh.ts +4 -4
- package/src/operator-alarm.ts +2 -5
- package/src/project-ingest.ts +6 -6
- package/src/project-instructions.ts +8 -1
- package/src/project-trust.ts +12 -11
- package/src/prompt-resolver.ts +3 -3
- package/src/provider-quota-async.ts +5 -7
- package/src/provider-timeline-event.ts +4 -7
- package/src/quota-band-delivery.ts +2 -2
- package/src/recipe-runner.ts +4 -3
- package/src/retention-config-store.ts +4 -3
- package/src/reviewer-pipeline.ts +11 -10
- package/src/routes/_route.ts +4 -4
- package/src/routes/_shared.ts +13 -12
- package/src/routes/agent-effort.ts +5 -5
- package/src/routes/agent-heartbeat-quota.ts +23 -0
- package/src/routes/agent-keep.ts +1 -1
- package/src/routes/agent-reporting.ts +1 -1
- package/src/routes/agent-self-effort.ts +5 -5
- package/src/routes/agent-sessions.ts +24 -24
- package/src/routes/agents-spawn.ts +1 -1
- package/src/routes/agents.ts +17 -119
- package/src/routes/auth.ts +14 -15
- package/src/routes/authority-audit.ts +2 -2
- package/src/routes/automations.ts +6 -5
- package/src/routes/channel-binding-delete.ts +1 -1
- package/src/routes/channel-output.ts +3 -3
- package/src/routes/claude-api-budget.ts +5 -5
- package/src/routes/commands-listing.ts +1 -1
- package/src/routes/commands.ts +29 -27
- package/src/routes/context-resets.ts +2 -2
- package/src/routes/coordinator-teams.ts +1 -1
- package/src/routes/decisions.ts +2 -2
- package/src/routes/host-secret-files.ts +1 -1
- package/src/routes/hosts.ts +11 -11
- package/src/routes/inbox.ts +3 -2
- package/src/routes/index.ts +2 -1
- package/src/routes/integrations.ts +9 -7
- package/src/routes/issue-activity.ts +2 -2
- package/src/routes/issue-associations.ts +4 -4
- package/src/routes/issue-refs.ts +3 -3
- package/src/routes/kernel-timers-operator.ts +1 -1
- package/src/routes/memory.ts +1 -1
- package/src/routes/merges.ts +11 -11
- package/src/routes/messages.ts +23 -17
- package/src/routes/orchestrator-bootstrap.ts +1 -1
- package/src/routes/orchestrator-host-registration.ts +2 -0
- package/src/routes/orchestrator-host-utilization.ts +2 -2
- package/src/routes/orchestrator-proxy.ts +2 -2
- package/src/routes/orchestrator.ts +27 -18
- package/src/routes/pairs.ts +8 -8
- package/src/routes/project-profile-instructions.ts +1 -1
- package/src/routes/project-trust.ts +1 -1
- package/src/routes/projects.ts +16 -16
- package/src/routes/prompt-templates.ts +12 -12
- package/src/routes/provider-quotas.ts +10 -10
- package/src/routes/run-command.ts +1 -1
- package/src/routes/secrets-backup.ts +2 -2
- package/src/routes/secrets-import.ts +1 -1
- package/src/routes/secrets-vault.ts +7 -7
- package/src/routes/settings.ts +1 -1
- package/src/routes/shipping.ts +9 -1
- package/src/routes/spawn-policy.ts +8 -7
- package/src/routes/sse.ts +2 -2
- package/src/routes/team-templates.ts +7 -7
- package/src/routes/teams.ts +10 -10
- package/src/routes/tokens.ts +18 -17
- package/src/routes/users.ts +24 -24
- package/src/routes/viewer-read.ts +3 -3
- package/src/routes/web-tool-calls.ts +4 -4
- package/src/routes/workflows.ts +2 -2
- package/src/routes/workspaces.ts +17 -16
- package/src/routing-policy-push.ts +6 -6
- package/src/runtime-grant.ts +17 -11
- package/src/runtime-tokens.ts +20 -28
- package/src/secrets-broker/config.ts +1 -1
- package/src/secrets-broker/service.ts +2 -2
- package/src/security.ts +3 -1
- package/src/self-resume-carry.ts +21 -110
- package/src/self-resume-supersede.ts +4 -3
- package/src/self-resume.ts +54 -66
- package/src/server-background.ts +1 -1
- package/src/server.ts +21 -22
- package/src/services/agent-action.ts +12 -31
- package/src/services/agent-effort.ts +7 -32
- package/src/services/agent-hang-watchdog-job.ts +2 -2
- package/src/services/agent-heartbeat.ts +166 -0
- package/src/services/agent-reporting.ts +1 -1
- package/src/services/browser-inventory.ts +3 -3
- package/src/services/browser-session-state.ts +3 -2
- package/src/services/browser-session.ts +21 -27
- package/src/services/bus-command-update.ts +9 -9
- package/src/services/checkout-refresh-state.ts +1 -0
- package/src/services/checkout-refresh.test-fixture.ts +3 -2
- package/src/services/command-host.ts +7 -2
- package/src/services/coordinator-digest.ts +3 -2
- package/src/services/dispatch-command.ts +12 -13
- package/src/services/effective-launch-driver.ts +14 -9
- package/src/services/host-changed-paths.test-fixture.ts +5 -6
- package/src/services/host-project-readiness.ts +2 -2
- package/src/services/host-providers.ts +1 -1
- package/src/services/host-utilization.ts +9 -9
- package/src/services/idle-agent-digest.ts +17 -12
- package/src/services/idle-epics.ts +4 -4
- package/src/services/interrupt-agent.ts +12 -9
- package/src/services/issue-activity.ts +3 -3
- package/src/services/issue-associations.ts +15 -18
- package/src/services/issue-lifecycle-comments.ts +3 -2
- package/src/services/issue-lifecycle.ts +14 -13
- package/src/services/issue-refs.ts +4 -5
- package/src/services/kernel-timers-cutover-chaos-worker.ts +1 -1
- package/src/services/kernel-timers-ingress-chaos-worker.ts +6 -1
- package/src/services/kernel-timers-ingress.ts +3 -3
- package/src/services/kernel-timers-message-authority-chaos-worker.ts +3 -10
- package/src/services/kernel-timers-operator-chaos-worker.ts +3 -2
- package/src/services/kernel-timers-outbox.ts +1 -1
- package/src/services/kernel-timers-stage10-operator.ts +1 -1
- package/src/services/kernel-timers.ts +4 -3
- package/src/services/land-gate-placement.ts +11 -2
- package/src/services/land-issue-relations.ts +5 -4
- package/src/services/land-restart-retry.ts +7 -6
- package/src/services/landed-ancestry.ts +6 -5
- package/src/services/legacy-landing-host.ts +4 -1
- package/src/services/legacy-landing-recovery.ts +8 -6
- package/src/services/managed-running.ts +17 -17
- package/src/services/memory-areas.ts +3 -3
- package/src/services/merge-pause.ts +9 -15
- package/src/services/operator-decisions.ts +37 -41
- package/src/services/orchestrator-registration-recovery.ts +6 -2
- package/src/services/orchestrator-upgrade.ts +14 -12
- package/src/services/permission-decision.ts +4 -3
- package/src/services/plan-ci-notice.ts +1 -1
- package/src/services/plan-conformance.ts +4 -4
- package/src/services/plan-dispatch-policy.ts +10 -3
- package/src/services/plan-dispatch.ts +35 -14
- package/src/services/plan-graph.ts +39 -261
- package/src/services/plan-import.ts +1 -1
- package/src/services/plan-live-bindings.ts +8 -37
- package/src/services/plan-task-create.ts +1 -1
- package/src/services/plan-terminal-refs.ts +2 -12
- package/src/services/plan-wake-digest.ts +8 -3
- package/src/services/profile-trust.ts +2 -2
- package/src/services/project.ts +11 -10
- package/src/services/prompt-templates.ts +6 -5
- package/src/services/provider-hold-nudge.ts +9 -9
- package/src/services/provider-hold-record.ts +13 -12
- package/src/services/provider-state-audit.ts +19 -18
- package/src/services/provisioning-harness/driver.ts +1 -1
- package/src/services/provisioning-harness/fixture-matrix.ts +3 -3
- package/src/services/provisioning-harness/lifecycle.ts +24 -22
- package/src/services/provisioning-harness/prepare.ts +16 -17
- package/src/services/rate-limit-resume.ts +10 -9
- package/src/services/red-main-auto-revert.ts +7 -5
- package/src/services/red-main-controller-deps.ts +20 -3
- package/src/services/red-main-incident.ts +3 -1
- package/src/services/red-main-quarantine.ts +2 -3
- package/src/services/register-agent.ts +22 -22
- package/src/services/restart-project-role.ts +1 -1
- package/src/services/restart-spawn.ts +17 -16
- package/src/services/review-publish.ts +7 -24
- package/src/services/review-ref-reconcile.ts +29 -38
- package/src/services/runtime-dependency-alert.ts +27 -0
- package/src/services/scheduler-command.ts +11 -9
- package/src/services/scheduler.ts +5 -5
- package/src/services/self-effort-grant.ts +5 -25
- package/src/services/send-message.ts +16 -33
- package/src/services/shutdown-agent.ts +17 -14
- package/src/services/spawn-agent.ts +12 -41
- package/src/services/spawn-chain-inspector.ts +1 -1
- package/src/services/spawn-cwd-placement.ts +21 -5
- package/src/services/spawn-issue-activity.ts +8 -6
- package/src/services/spawn-launch.ts +17 -1
- package/src/services/spawn-loss-escalation.ts +1 -1
- package/src/services/spawn-registration-watchdog.ts +2 -2
- package/src/services/spawn-repo-probe.ts +7 -5
- package/src/services/spawn-tree-cleanup.ts +7 -8
- package/src/services/steward-reaper.ts +4 -4
- package/src/services/task-context-hydration.ts +25 -1
- package/src/services/task-delivery.ts +20 -34
- package/src/services/task-entity.ts +40 -203
- package/src/services/task-issue-link.ts +13 -20
- package/src/services/task-issue-weave.ts +12 -11
- package/src/services/task-lifecycle.ts +27 -31
- package/src/services/task-repo-cwd.ts +37 -0
- package/src/services/task-review-hold.ts +4 -17
- package/src/services/task-semantic-events.ts +13 -11
- package/src/services/task-state-reconcile.ts +3 -4
- package/src/services/task-validation.ts +2 -2
- package/src/services/team-templates.ts +5 -5
- package/src/services/team.ts +31 -98
- package/src/services/terminal-provider-exit.ts +6 -5
- package/src/services/timer-message-fire-chaos-worker.ts +7 -3
- package/src/services/timer-message-fire-outbox.ts +1 -1
- package/src/services/transient-agent-reaper.ts +22 -19
- package/src/services/turn-reconcile-audit.ts +9 -9
- package/src/services/user-onboarding.ts +8 -6
- package/src/services/user-profile.ts +6 -9
- package/src/services/work-finality.ts +16 -4
- package/src/services/workflow-auto-start.ts +2 -1
- package/src/services/workflow-caller.ts +61 -4
- package/src/services/workflow-child-proof-worker.ts +1 -1
- package/src/services/workflow-control-proof-worker.ts +1 -1
- package/src/services/workflow-coordinator.ts +8 -2
- package/src/services/workflow-decisions.ts +5 -5
- package/src/services/workflow-deferred-cleanup.ts +1 -1
- package/src/services/workflow-effect-driver.ts +42 -19
- package/src/services/workflow-effects.ts +41 -27
- package/src/services/workflow-pins.ts +7 -4
- package/src/services/workflow-reconcile.ts +1 -0
- package/src/services/workflow-resume-pins.ts +3 -3
- package/src/services/workflow-routing-defaults.ts +18 -0
- package/src/services/workflow-run-view.ts +4 -3
- package/src/services/workflow-run.test-fixture.ts +1 -1
- package/src/services/workflow-runs.ts +34 -22
- package/src/services/workflow-supervisor-state.ts +21 -1
- package/src/services/workflow-supervisor.ts +4 -3
- package/src/services/workspace-recovery.ts +4 -3
- package/src/spawn-admission.ts +10 -2
- package/src/spawn-placement.ts +27 -9
- package/src/spawn-targets.ts +8 -8
- package/src/sse-agent-status.ts +15 -6
- package/src/sse.ts +49 -24
- package/src/startup-context-injection.ts +61 -57
- package/src/steward-handoff.ts +4 -3
- package/src/steward-policy-cleanup.ts +9 -8
- package/src/steward.ts +2 -2
- package/src/task-dispatch-live.ts +5 -2
- package/src/task-dispatcher.ts +2 -2
- package/src/token-db.ts +2 -2
- package/src/token-usage-async.ts +2 -4
- package/src/user-awaiting-reply.ts +5 -5
- package/src/workspace-actions.ts +24 -29
- package/src/workspace-auto-merge.ts +14 -12
- package/src/workspace-changed-paths-client.ts +16 -5
- package/src/workspace-cleanup-stale.ts +30 -7
- package/src/workspace-command-settle.ts +24 -16
- package/src/workspace-config.ts +1 -1
- package/src/workspace-conflict-resolver.ts +9 -7
- package/src/workspace-dead-owner-recovery.ts +5 -4
- package/src/workspace-escalation.ts +12 -11
- package/src/workspace-human-escalation.ts +3 -3
- package/src/workspace-land-gate-watchdog.ts +3 -3
- package/src/workspace-land-refusal.ts +20 -0
- package/src/workspace-land-stopped.ts +1 -0
- package/src/workspace-merge.ts +10 -26
- package/src/workspace-nothing-to-land-notice.ts +2 -2
- package/src/workspace-orphans.ts +18 -37
- package/src/workspace-pr-completion.ts +10 -8
- package/src/workspace-rekey.ts +3 -3
- package/src/workspace-steward-failure.ts +2 -2
- package/src/workspace-steward-wake.ts +2 -2
- package/public/assets/agent-liveness-CaNwK0Zf.js +0 -2
- package/public/assets/provider-display-DsuWMvVL.js +0 -2
- package/public/assets/run-card-CpKqRbhg.js +0 -2
- package/public/assets/shipping-B9YtnwtG.js +0 -2
- package/public/assets/store-DcClXLH0.js +0 -9
- package/runner/src/agent-runner/http-liveness.ts +0 -85
- package/src/db/on-loop-write-transaction.ts +0 -34
|
@@ -4,7 +4,7 @@ import { isHumanAgentId, PROVIDER_SUBMIT_UNCONFIRMED, RELAY_TOKEN_HEADER, SUBMIT
|
|
|
4
4
|
import { authorizeAgentControl } from "../agent-authz";
|
|
5
5
|
import { VALID_AGENT_ACTIONS, agentControlActionIcon, auditEvent, authAuditMetadata, authorizeAgentRoute, authorizeRoute, canViewAgentRoute, cleanAttachmentRefs, dashboardAttribution, emitCommand, error, json, metaString, parseBody, requestUserAddress, requestUserId, spawnRequestId, type AgentControlAction, type Handler } from "./_shared";
|
|
6
6
|
import { arbitrateDrive, currentDriver, drivingMessage, releaseDrive, type DriveArbitration } from "../agent-drive-arbiter";
|
|
7
|
-
import { ValidationError, getAgent, getAgentTimelineAsync, getOrchestrator, markReadyAsync, mergeAgentMetaAsync
|
|
7
|
+
import { sendMessageAsync, ValidationError, getAgent, getAgentTimelineAsync, getOrchestrator, markReadyAsync, mergeAgentMetaAsync } from "../db";
|
|
8
8
|
import { announcePresence, viewersForAgent } from "../dashboard-presence";
|
|
9
9
|
import { cleanString, optionalEnum } from "../validation";
|
|
10
10
|
import { createCommandAsync } from "../commands-write";
|
|
@@ -40,10 +40,10 @@ export const postAgentSelfRestartRecheck: Handler = async (req, params) => {
|
|
|
40
40
|
// orchestrator's own runtime token authorizes this exactly as it authorizes the agent.restart it
|
|
41
41
|
// is executing — no over-refusal that would silently abort every self-restart.
|
|
42
42
|
const orchestrator = agent ? managedControlOrchestrator(agent) : null;
|
|
43
|
-
const denied = authorizeRoute(req, {
|
|
43
|
+
const denied = (await authorizeRoute(req, {
|
|
44
44
|
scope: "command:write",
|
|
45
45
|
resource: { agentId: params.id!, orchestratorId: orchestrator?.id, policyName: metaString(agent?.meta, "policyName"), spawnRequestId: metaString(agent?.meta, "spawnRequestId") },
|
|
46
|
-
});
|
|
46
|
+
}));
|
|
47
47
|
if (denied) return denied;
|
|
48
48
|
if (!agent) return json({ ok: false, reason: `self-restart safe-point re-check: agent ${params.id} not found` });
|
|
49
49
|
const violation = await evaluateSelfRestartSafePoint(agent);
|
|
@@ -161,11 +161,11 @@ export const postAgentAction: Handler = async (req, params) => {
|
|
|
161
161
|
if (!agent) return error("agent not found", 404);
|
|
162
162
|
// #392: the acting user must be allowed to drive this agent (fail-open single-user). Gated
|
|
163
163
|
// before the capability probe so an unauthorized user learns nothing about what the agent supports.
|
|
164
|
-
const driveDenied = authorizeAgentRoute(req, agent.id, "drive", action);
|
|
164
|
+
const driveDenied = (await authorizeAgentRoute(req, agent.id, "drive", action));
|
|
165
165
|
if (driveDenied) return driveDenied;
|
|
166
166
|
// #3567 — the one lifecycle-control check (the services re-run it for bus/MCP); self excepted.
|
|
167
167
|
const actionCtx = authContextFromRequest(req);
|
|
168
|
-
const controlDenied = actionCtx.callerAgentId === agent.id ? null : authorizeAgentControl(actionCtx, agent, action);
|
|
168
|
+
const controlDenied = actionCtx.callerAgentId === agent.id ? null : (await authorizeAgentControl(actionCtx, agent, action));
|
|
169
169
|
if (controlDenied) return error(controlDenied.message, 403);
|
|
170
170
|
// #2664 — releasing a hold that is already gone (recovered, or a double click) is an idempotent no-op.
|
|
171
171
|
if (action === "releaseSubmitHold" && agentIsControlEligible(agent) && !agentSubmitHoldActive(agent)) return json({ ok: true, action, released: false }, 200);
|
|
@@ -181,10 +181,10 @@ export const postAgentAction: Handler = async (req, params) => {
|
|
|
181
181
|
if (action === "interrupt") {
|
|
182
182
|
const arb = await arbitrateDrive(requestUserId(req), agent.id);
|
|
183
183
|
if (!arb.allowed) return driveBlockedResponse(arb);
|
|
184
|
-
const denied = authorizeRoute(req, {
|
|
184
|
+
const denied = (await authorizeRoute(req, {
|
|
185
185
|
scope: "agent:write",
|
|
186
186
|
resource: controlResource,
|
|
187
|
-
});
|
|
187
|
+
}));
|
|
188
188
|
if (denied) return denied;
|
|
189
189
|
try {
|
|
190
190
|
const result = await dispatchInterrupt({
|
|
@@ -208,10 +208,10 @@ export const postAgentAction: Handler = async (req, params) => {
|
|
|
208
208
|
// bus + MCP surfaces. The route still enforces the coarse `agent:write` scope +
|
|
209
209
|
// constraint-list; the service adds the fine parent→child gate.
|
|
210
210
|
if (action === "shutdown") {
|
|
211
|
-
const denied = authorizeRoute(req, {
|
|
211
|
+
const denied = (await authorizeRoute(req, {
|
|
212
212
|
scope: "agent:write",
|
|
213
213
|
resource: controlResource,
|
|
214
|
-
});
|
|
214
|
+
}));
|
|
215
215
|
if (denied) return denied;
|
|
216
216
|
try {
|
|
217
217
|
if (parsed.body.hard !== undefined && typeof parsed.body.hard !== "boolean") throw new ValidationError("hard must be a boolean");
|
|
@@ -243,10 +243,10 @@ export const postAgentAction: Handler = async (req, params) => {
|
|
|
243
243
|
const resumeId = action === "resume" ? await latestResumeIdForAgent(agent) : undefined;
|
|
244
244
|
if (action === "resume" && !orchestrator) return error("no online orchestrator available to resume agent", 409);
|
|
245
245
|
if (action === "resume" && !resumeId) return error("no resume id recorded for agent", 422);
|
|
246
|
-
const denied = authorizeRoute(req, {
|
|
246
|
+
const denied = (await authorizeRoute(req, {
|
|
247
247
|
scope: "agent:write",
|
|
248
248
|
resource: { ...controlResource, orchestratorId: orchestrator?.id },
|
|
249
|
-
});
|
|
249
|
+
}));
|
|
250
250
|
if (denied) return denied;
|
|
251
251
|
// #1953 RC3 — a dashboard restart/resume of a disabled steward is refused (see helper).
|
|
252
252
|
// #1955 (#1953-review nit 2) — AFTER authorizeRoute: run pre-auth, the 409 disclosed to an
|
|
@@ -320,7 +320,7 @@ export const postAgentPrompt: Handler = async (req, params) => {
|
|
|
320
320
|
const agent = getAgent(params.id!);
|
|
321
321
|
if (!agent) return error("agent not found", 404);
|
|
322
322
|
// #392: only a user authorized to drive this agent may prompt it (fail-open single-user).
|
|
323
|
-
const driveDenied = authorizeAgentRoute(req, agent.id, "drive");
|
|
323
|
+
const driveDenied = (await authorizeAgentRoute(req, agent.id, "drive"));
|
|
324
324
|
if (driveDenied) return driveDenied;
|
|
325
325
|
// #394: submitting a prompt takes/refreshes the drive lease. A second human driving the same
|
|
326
326
|
// agent is blocked here (single-user is a no-op), so two operators can't double-submit into one
|
|
@@ -328,10 +328,10 @@ export const postAgentPrompt: Handler = async (req, params) => {
|
|
|
328
328
|
const arb = await arbitrateDrive(requestUserId(req), agent.id);
|
|
329
329
|
if (!arb.allowed) return driveBlockedResponse(arb);
|
|
330
330
|
if (agent.status === "offline") return error("agent is offline", 422);
|
|
331
|
-
const denied = authorizeRoute(req, {
|
|
331
|
+
const denied = (await authorizeRoute(req, {
|
|
332
332
|
scope: "message:send",
|
|
333
333
|
resource: { target: agent.id, agentId: requestUserAddress(req) },
|
|
334
|
-
});
|
|
334
|
+
}));
|
|
335
335
|
if (denied) return denied;
|
|
336
336
|
// #2664 — the runner holds exactly one unconfirmed prompt for the operator's resend; a new one
|
|
337
337
|
// must not queue behind (or replace) it. Refused before any message row exists.
|
|
@@ -341,13 +341,13 @@ export const postAgentPrompt: Handler = async (req, params) => {
|
|
|
341
341
|
// canonical `user` sink, so today's single operator routes/renders exactly as before; once
|
|
342
342
|
// auth (#389) sets the real session user, the bubble carries the real `user:<id>`.
|
|
343
343
|
const fromUser = requestUserAddress(req);
|
|
344
|
-
const message =
|
|
344
|
+
const message = (await sendMessageAsync({
|
|
345
345
|
from: fromUser,
|
|
346
346
|
to: agent.id,
|
|
347
347
|
kind: "session",
|
|
348
348
|
body,
|
|
349
349
|
...(attachments ? { payload: { attachments } } : {}),
|
|
350
|
-
});
|
|
350
|
+
}));
|
|
351
351
|
const command = await createCommandAsync({
|
|
352
352
|
type: "prompt.inject",
|
|
353
353
|
source: "dashboard",
|
|
@@ -397,7 +397,7 @@ export const postAgentPermissionDecision: Handler = async (req, params) => {
|
|
|
397
397
|
const agent = getAgent(params.id!);
|
|
398
398
|
if (!agent) return error("agent not found", 404);
|
|
399
399
|
// #392: approving/denying a permission is driving the agent (fail-open single-user).
|
|
400
|
-
const driveDenied = authorizeAgentRoute(req, agent.id, "drive");
|
|
400
|
+
const driveDenied = (await authorizeAgentRoute(req, agent.id, "drive"));
|
|
401
401
|
if (driveDenied) return driveDenied;
|
|
402
402
|
// #394: a plan-approve / AskUserQuestion answer drives the turn — only the lease holder (or an
|
|
403
403
|
// admin override) may answer, so two humans can't race competing decisions into one prompt.
|
|
@@ -405,10 +405,10 @@ export const postAgentPermissionDecision: Handler = async (req, params) => {
|
|
|
405
405
|
if (!arb.allowed) return driveBlockedResponse(arb);
|
|
406
406
|
const metaPolicyName = typeof agent.meta?.policyName === "string" ? agent.meta.policyName : undefined;
|
|
407
407
|
const metaSpawnRequestId = typeof agent.meta?.spawnRequestId === "string" ? agent.meta.spawnRequestId : undefined;
|
|
408
|
-
const denied = authorizeRoute(req, {
|
|
408
|
+
const denied = (await authorizeRoute(req, {
|
|
409
409
|
scope: "agent:write",
|
|
410
410
|
resource: { agentId: agent.id, policyName: metaPolicyName, spawnRequestId: metaSpawnRequestId },
|
|
411
|
-
});
|
|
411
|
+
}));
|
|
412
412
|
if (denied) return denied;
|
|
413
413
|
const result = await dispatchAgentPermissionDecision({
|
|
414
414
|
agentId: agent.id,
|
|
@@ -487,7 +487,7 @@ export const postAgentDrive: Handler = async (req, params) => {
|
|
|
487
487
|
const agent = getAgent(params.id!);
|
|
488
488
|
if (!agent) return error("agent not found", 404);
|
|
489
489
|
// Driving the wheel requires the same #392 drive authorization the prompt/interrupt routes use.
|
|
490
|
-
const driveDenied = authorizeAgentRoute(req, agent.id, "drive");
|
|
490
|
+
const driveDenied = (await authorizeAgentRoute(req, agent.id, "drive"));
|
|
491
491
|
if (driveDenied) return driveDenied;
|
|
492
492
|
const userId = requestUserId(req);
|
|
493
493
|
|
|
@@ -545,10 +545,10 @@ export const postAgentTerminalSession: Handler = async (req, params) => {
|
|
|
545
545
|
const metaTmuxSession = typeof agent.meta?.tmuxSession === "string" ? agent.meta.tmuxSession : undefined;
|
|
546
546
|
const metaPolicyName = typeof agent.meta?.policyName === "string" ? agent.meta.policyName : undefined;
|
|
547
547
|
const metaSpawnRequestId = typeof agent.meta?.spawnRequestId === "string" ? agent.meta.spawnRequestId : undefined;
|
|
548
|
-
const denied = authorizeRoute(req, {
|
|
548
|
+
const denied = (await authorizeRoute(req, {
|
|
549
549
|
scope: "terminal:attach",
|
|
550
550
|
resource: { agentId: agent.id, orchestratorId: orchestrator.id, policyName: metaPolicyName, spawnRequestId: metaSpawnRequestId, terminalAttach: true },
|
|
551
|
-
});
|
|
551
|
+
}));
|
|
552
552
|
if (denied) return denied;
|
|
553
553
|
// Provider-owned attach surfaces are more specific than the runner's tmux pane.
|
|
554
554
|
// Codex runs its real interactive TUI as a guest attached to the app-server thread;
|
|
@@ -588,7 +588,7 @@ export const deleteAgentTerminalSession: Handler = async (req, params) => {
|
|
|
588
588
|
if (!agent) return error("agent not found", 404);
|
|
589
589
|
const orchestrator = managedControlOrchestrator(agent);
|
|
590
590
|
if (!orchestrator) return error("agent does not have an online orchestrator", 422);
|
|
591
|
-
const denied = authorizeRoute(req, {
|
|
591
|
+
const denied = (await authorizeRoute(req, {
|
|
592
592
|
scope: "terminal:attach",
|
|
593
593
|
resource: {
|
|
594
594
|
agentId: agent.id,
|
|
@@ -597,7 +597,7 @@ export const deleteAgentTerminalSession: Handler = async (req, params) => {
|
|
|
597
597
|
spawnRequestId: typeof agent.meta?.spawnRequestId === "string" ? agent.meta.spawnRequestId : undefined,
|
|
598
598
|
terminalAttach: true,
|
|
599
599
|
},
|
|
600
|
-
});
|
|
600
|
+
}));
|
|
601
601
|
if (denied) return denied;
|
|
602
602
|
const session = params.session!;
|
|
603
603
|
return proxyOrchestratorDelete(req, orchestrator.id, `/api/terminal-guests/${encodeURIComponent(session)}`);
|
|
@@ -85,7 +85,7 @@ export const postAgent: Handler = async (req) => {
|
|
|
85
85
|
// timeline note, parent-wake, audit) live in src/services/register-agent.ts so the HTTP,
|
|
86
86
|
// bus, and orchestrator-report paths cannot drift (epic #342).
|
|
87
87
|
const input = normalizeAgentInput(parsed.body);
|
|
88
|
-
const agent = registerAgent(input, authContextFromRequest(req));
|
|
88
|
+
const agent = await registerAgent(input, authContextFromRequest(req));
|
|
89
89
|
return json(agent, 201);
|
|
90
90
|
} catch (e) {
|
|
91
91
|
if (e instanceof ValidationError) return error(e.message, 400);
|
package/src/routes/agents.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Auto-split from routes.ts (#299). Domain: agents.
|
|
2
2
|
import { messageViewerForRequest, visibleMessages } from "./message-viewer";
|
|
3
3
|
import { attachPrincipal, attachPrincipals } from "../agent-principal";
|
|
4
|
-
import {
|
|
5
|
-
import { ValidationError, deleteAgentAsync, getAgent, getAgentAsync, getAgentIdentityAsync, getAgentQuotaSnapshotsAsync, getAgentTimelineAsync, getCrashReport,
|
|
4
|
+
import { agentSessionStatus, auditEvent, authorizeRoute, canViewAgentRoute, error, json, normalizeAgentSessionGuard, parseBody, parseId, parseQueryInt, visibleAgentIdsForRequest, type Handler } from "./_shared";
|
|
5
|
+
import { ValidationError, deleteAgentAsync, getAgent, getAgentAsync, getAgentIdentityAsync, getAgentQuotaSnapshotsAsync, getAgentTimelineAsync, getCrashReport, listAgentChatHistoryAsync, listAgents, listAgentsAsync, listArtifactsForEntityAsync, listContextSnapshotsAsync, listPendingReplyObligationsAsync, searchAgentsAsync, setLabelAsync, setTagsAsync, validateAgentSession } from "../db";
|
|
6
6
|
import { attachBranchState, attachBranchStates } from "../agent-branch-state";
|
|
7
7
|
import { redactAgentCardLaunchShape } from "../agent-launch-shape-redaction";
|
|
8
8
|
import { attachChannelBinding, attachChannelBindings } from "../agent-channel-binding";
|
|
@@ -10,47 +10,16 @@ import { attachCommandActivities, attachCommandActivity } from "../agent-command
|
|
|
10
10
|
import { attachIssueRepo, attachIssueRepos } from "../agent-issue-repo";
|
|
11
11
|
import { cleanStringArray } from "../validation";
|
|
12
12
|
import { emitAgentRemoved, emitAgentStatus } from "../sse";
|
|
13
|
-
import { announceSpawnedAgentReady } from "../services/register-agent";
|
|
14
|
-
import { flushQueuedDirectMessages } from "../services/managed-running";
|
|
15
|
-
import { acquireTeamOnReady } from "../services/team";
|
|
16
13
|
import { artifactReadScopeForRequest } from "../services/artifact-access";
|
|
17
|
-
import { isAgentUnavailable } from "../db/agent-predicates";
|
|
18
14
|
import { getCompactionWatch } from "../compaction-watch";
|
|
19
15
|
import { isRecord } from "@humwick/sdk";
|
|
20
16
|
import { getComponentAuth, hasComponentScope } from "../security";
|
|
21
|
-
import { type AgentCard
|
|
17
|
+
import { type AgentCard } from "../types";
|
|
22
18
|
import { attachToolchain } from "../project-toolchain";
|
|
23
19
|
import { listEffortChangesAsync } from "../commands-write";
|
|
24
20
|
import { markAgentWorkFinal } from "../services/work-finality";
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
key: "available" | "busy" | "not-ready" | "offline";
|
|
28
|
-
title: string;
|
|
29
|
-
icon: string;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
function agentActivityState(agent?: Pick<AgentCard, "ready" | "status"> | null): AgentActivityState | null {
|
|
33
|
-
if (!agent) return null;
|
|
34
|
-
if (agent.status === "offline") return { key: "offline", title: "Agent offline", icon: "ti-plug-off" };
|
|
35
|
-
if (!agent.ready) return { key: "not-ready", title: "Agent not ready", icon: "ti-loader" };
|
|
36
|
-
if (agent.status === "busy") return { key: "busy", title: "Agent busy", icon: "ti-activity" };
|
|
37
|
-
return { key: "available", title: "Agent available", icon: "ti-circle-check" };
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async function auditAgentStateTransition(agentId: string, before: AgentCard | null | undefined, after: AgentCard | null | undefined): Promise<void> {
|
|
41
|
-
const previous = agentActivityState(before);
|
|
42
|
-
const next = agentActivityState(after);
|
|
43
|
-
if (!next || previous?.key === next.key) return;
|
|
44
|
-
await auditEvent({
|
|
45
|
-
clientId: "server-agent-" + agentId + "-state-" + next.key + "-" + Date.now(),
|
|
46
|
-
kind: "state",
|
|
47
|
-
title: next.title,
|
|
48
|
-
meta: agentId,
|
|
49
|
-
icon: next.icon,
|
|
50
|
-
view: "agents",
|
|
51
|
-
agentId,
|
|
52
|
-
});
|
|
53
|
-
}
|
|
21
|
+
import { authorizeProviderQuotaWrite } from "./provider-quotas";
|
|
22
|
+
import { applyAgentHeartbeat, applyAgentReady, applyAgentStatus, cleanAgentStatus, cleanHeartbeatReport, noteUnchangedLifecycle } from "../services/agent-heartbeat";
|
|
54
23
|
|
|
55
24
|
// #1797 round 2 (F1b) — the agent roster carried `meta.policyName` + `meta.spawnRequestId` in the
|
|
56
25
|
// clear to any `agent:read` holder. Those two strings are the ADDRESSING for every managed-agent
|
|
@@ -248,56 +217,6 @@ export const getTaskArtifactsRoute: Handler = async (req, params) => {
|
|
|
248
217
|
return json(await listArtifactsForEntityAsync("task", id, artifactReadScopeForRequest(req)));
|
|
249
218
|
};
|
|
250
219
|
|
|
251
|
-
type AgentStatusValue = (typeof VALID_AGENT_STATUSES)[number];
|
|
252
|
-
|
|
253
|
-
function cleanAgentStatus(value: unknown): AgentStatusValue {
|
|
254
|
-
if (!VALID_AGENT_STATUSES.includes(value as AgentStatusValue)) throw new ValidationError(`status must be one of: ${VALID_AGENT_STATUSES.join(", ")}`);
|
|
255
|
-
return value as AgentStatusValue;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// #3606 — a reported status/readiness equal to the live card is a liveness tick, not a transition: no
|
|
259
|
-
// row write, no agent.status event. Presence still advances through the coalescing heartbeat buffer,
|
|
260
|
-
// except for a terminal row, which a beat would resurrect.
|
|
261
|
-
async function noteUnchangedLifecycle(agentId: string, current: AgentCard, guard: AgentSessionGuard | undefined): Promise<void> {
|
|
262
|
-
if (current.status !== "offline" && current.status !== "stale") await recordHeartbeatAsync(agentId, { guard });
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
async function applyAgentStatus(agentId: string, status: AgentStatusValue, guard: AgentSessionGuard | undefined, before: AgentCard | null): Promise<boolean> {
|
|
266
|
-
// #1895 (S3.2 L10, review finding 3) — routed through the async gateway facade.
|
|
267
|
-
if (!(await setStatusAsync(agentId, status, guard))) return false;
|
|
268
|
-
await auditAgentStateTransition(agentId, before, getAgent(agentId));
|
|
269
|
-
await emitAgentStatus(agentId);
|
|
270
|
-
return true;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
async function applyAgentReady(agentId: string, ready: boolean, guard: AgentSessionGuard | undefined, before: AgentCard | null): Promise<boolean> {
|
|
274
|
-
// #1895 (S3.2 L10, review finding 3) — routed through the async gateway facade.
|
|
275
|
-
if (!(await markReadyAsync(agentId, ready, guard))) return false;
|
|
276
|
-
await auditAgentStateTransition(agentId, before, getAgent(agentId));
|
|
277
|
-
// #308 — a spawned child flipping false→true ready is "genuinely ready" (past onboarding):
|
|
278
|
-
// wake its parent so it can stop block-polling and stamp the durable readiness. #2432 —
|
|
279
|
-
// announceSpawnedAgentReady is the shared seam; it no-ops for non-spawned agents and dedups
|
|
280
|
-
// repeat readies (e.g. a later bus status frame or PATCH for an already-announced child).
|
|
281
|
-
// #1927 review round 2 (HIGH 1) — the audit above now awaits the async gateway, widening the
|
|
282
|
-
// window for a concurrent same-instance PATCH to flip ready back to false before we get here.
|
|
283
|
-
// Re-read the LIVE card (and re-check the session guard) right before firing these effects
|
|
284
|
-
// instead of trusting the pre-audit snapshot, so a superseded transition never fires a false
|
|
285
|
-
// durable agent.ready.
|
|
286
|
-
if (ready && !before?.ready) {
|
|
287
|
-
const live = getAgent(agentId);
|
|
288
|
-
const stillCurrentInstance = validateAgentSession(agentId, guard).ok;
|
|
289
|
-
if (live?.ready === true && stillCurrentInstance) {
|
|
290
|
-
announceSpawnedAgentReady(agentId, "agent-ready");
|
|
291
|
-
acquireTeamOnReady(agentId); // #3107 — before the flush, so seat mail reaches a new holder
|
|
292
|
-
// #416 — also flush queued direct messages: a not-ready agent that becomes ready
|
|
293
|
-
// may have queued messages waiting (e.g. starting up, clearing context).
|
|
294
|
-
flushQueuedDirectMessages(agentId);
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
await emitAgentStatus(agentId);
|
|
298
|
-
return true;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
220
|
export const patchAgentStatus: Handler = async (req, params) => {
|
|
302
221
|
const parsed = await parseBody<{ status: string }>(req);
|
|
303
222
|
if (!parsed.ok) return error(parsed.error, parsed.status);
|
|
@@ -322,40 +241,19 @@ export const postHeartbeat: Handler = async (req, params) => {
|
|
|
322
241
|
const parsed = await parseBody<unknown>(req);
|
|
323
242
|
if (!parsed.ok) return error(parsed.error, parsed.status);
|
|
324
243
|
try {
|
|
325
|
-
// #3606 — the runner's liveness tick reports status and readiness on this one call
|
|
326
|
-
//
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
const ready = body.ready as boolean | undefined;
|
|
332
|
-
const guard = normalizeAgentSessionGuard(req, parsed.body);
|
|
333
|
-
const session = validateAgentSession(params.id!, guard);
|
|
334
|
-
if (!session.ok) return error(session.error!, agentSessionStatus(session.error));
|
|
335
|
-
// #416 — capture unavailability before heartbeat to detect the transition.
|
|
336
|
-
const beforeHeartbeat = getAgent(params.id!);
|
|
337
|
-
if (beforeHeartbeat?.kind === "channel" && !beforeHeartbeat.ready) {
|
|
338
|
-
return error("channel agent not ready; re-register required", 409);
|
|
339
|
-
}
|
|
340
|
-
const wasUnavailable = beforeHeartbeat ? isAgentUnavailable(beforeHeartbeat) : false;
|
|
341
|
-
let current = beforeHeartbeat;
|
|
342
|
-
if (status !== undefined && status !== current?.status) {
|
|
343
|
-
if (!(await applyAgentStatus(params.id!, status, guard, current))) return error("agent not found", 404);
|
|
344
|
-
current = getAgent(params.id!);
|
|
345
|
-
}
|
|
346
|
-
if (ready !== undefined && ready !== current?.ready) {
|
|
347
|
-
if (!(await applyAgentReady(params.id!, ready, guard, current))) return error("agent not found", 404);
|
|
348
|
-
}
|
|
349
|
-
// A reported terminal status must not be undone by the beat's offline/stale resurrection.
|
|
350
|
-
if (status !== "offline" && status !== "stale") {
|
|
351
|
-
// #1468 — route the API heartbeat through the same coalescing buffer as the bus path.
|
|
352
|
-
if (!(await recordHeartbeatAsync(params.id!, { guard }))) return error("agent not found", 404);
|
|
244
|
+
// #3606 — the runner's liveness tick reports status and readiness on this one call; the bus
|
|
245
|
+
// `agent.heartbeat` frame (#3618) is the same exchange through the same service.
|
|
246
|
+
const report = cleanHeartbeatReport(params.id!, parsed.body);
|
|
247
|
+
if (report.unread) {
|
|
248
|
+
const denied = await authorizeRoute(req, { scope: "message:read", resource: { agentId: params.id!, target: params.id! } });
|
|
249
|
+
if (denied) return denied;
|
|
353
250
|
}
|
|
354
|
-
if (
|
|
355
|
-
const
|
|
356
|
-
if (
|
|
251
|
+
if (report.providerQuota) {
|
|
252
|
+
const denied = (await authorizeRoute(req, { scope: "quota:write", resource: { agentId: params.id! } })) ?? await authorizeProviderQuotaWrite(req, report.providerQuota);
|
|
253
|
+
if (denied) return denied;
|
|
357
254
|
}
|
|
358
|
-
|
|
255
|
+
const outcome = await applyAgentHeartbeat(params.id!, report, normalizeAgentSessionGuard(req, parsed.body), messageViewerForRequest(req));
|
|
256
|
+
return outcome.ok ? json(outcome.result) : error(outcome.error, outcome.status);
|
|
359
257
|
} catch (e) {
|
|
360
258
|
if (e instanceof ValidationError) return error(e.message, 400);
|
|
361
259
|
throw e;
|
|
@@ -439,7 +337,7 @@ export const patchAgentReady: Handler = async (req, params) => {
|
|
|
439
337
|
};
|
|
440
338
|
|
|
441
339
|
export const deleteAgentById: Handler = async (_req, params) => {
|
|
442
|
-
markAgentWorkFinal(params.id!, "edge-fallback");
|
|
340
|
+
(await markAgentWorkFinal(params.id!, "edge-fallback"));
|
|
443
341
|
// #1895 (S3.2 L10, review finding 3) — routed through the async gateway facade.
|
|
444
342
|
const result = await deleteAgentAsync(params.id!);
|
|
445
343
|
if (!result.ok) {
|
package/src/routes/auth.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { authorityDenied, recordUserManagement } from "../authority-audit";
|
|
|
6
6
|
import { isRecord, type User } from "@humwick/sdk";
|
|
7
7
|
import { error, json, parseBody, type Handler } from "./_shared";
|
|
8
8
|
import { getAuthProvider, listAuthProviders, mintUserSession, type MintedSession } from "../auth";
|
|
9
|
-
import { getAuthIdentity, getUser } from "../db";
|
|
9
|
+
import { getAuthIdentity, getUser, InviteUnavailableError, ValidationError } from "../db";
|
|
10
10
|
import { revokeTokenAsync } from "../token-db-write.ts";
|
|
11
11
|
import { refreshUserSession } from "../auth/session-refresh";
|
|
12
12
|
import { revokeUserSession } from "../auth/session";
|
|
@@ -14,7 +14,6 @@ import { readSessionCookie, sessionOriginAllowed, withSessionCookie } from "../a
|
|
|
14
14
|
import { getComponentAuth } from "../security";
|
|
15
15
|
import { acceptInvite, userInvitesEnabled } from "../services/user-onboarding";
|
|
16
16
|
import { invitesDisabled } from "./users";
|
|
17
|
-
import { InviteUnavailableError, ValidationError } from "../db";
|
|
18
17
|
|
|
19
18
|
/** Public projection of a user for login/refresh responses — never leaks internal-only columns. */
|
|
20
19
|
function userView(user: User): Record<string, unknown> {
|
|
@@ -60,7 +59,7 @@ export const getAuthProvidersRoute: Handler = () =>
|
|
|
60
59
|
* session. Every failure returns the same generic 401 (no provider/user/credential enumeration).
|
|
61
60
|
*/
|
|
62
61
|
export const postAuthLoginRoute: Handler = async (req) => {
|
|
63
|
-
if (!sessionOriginAllowed(req, false)) return authorityDenied(req, "origin not allowed", 403);
|
|
62
|
+
if (!sessionOriginAllowed(req, false)) return (await authorityDenied(req, "origin not allowed", 403));
|
|
64
63
|
const parsed = await parseBody<unknown>(req);
|
|
65
64
|
if (!parsed.ok) return error(parsed.error, parsed.status);
|
|
66
65
|
if (!isRecord(parsed.body)) return error("login body required", 400);
|
|
@@ -68,33 +67,33 @@ export const postAuthLoginRoute: Handler = async (req) => {
|
|
|
68
67
|
if (typeof providerId !== "string" || !providerId) return error("provider required", 400);
|
|
69
68
|
|
|
70
69
|
const provider = getAuthProvider(providerId);
|
|
71
|
-
if (!provider) return authorityDenied(req, "invalid credentials", 401);
|
|
70
|
+
if (!provider) return (await authorityDenied(req, "invalid credentials", 401));
|
|
72
71
|
|
|
73
72
|
const result = await provider.verify(parsed.body);
|
|
74
|
-
if (!result) return authorityDenied(req, "invalid credentials", 401);
|
|
73
|
+
if (!result) return (await authorityDenied(req, "invalid credentials", 401));
|
|
75
74
|
|
|
76
75
|
const identity = getAuthIdentity(provider.id, result.providerSubject);
|
|
77
|
-
if (!identity) return authorityDenied(req, "invalid credentials", 401);
|
|
76
|
+
if (!identity) return (await authorityDenied(req, "invalid credentials", 401));
|
|
78
77
|
|
|
79
78
|
const user = getUser(identity.userId);
|
|
80
|
-
if (!user || user.status !== "active") return authorityDenied(req, "invalid credentials", 401);
|
|
79
|
+
if (!user || user.status !== "active") return (await authorityDenied(req, "invalid credentials", 401));
|
|
81
80
|
|
|
82
|
-
return sessionResponse(req, user, mintUserSession(user));
|
|
81
|
+
return sessionResponse(req, user, (await mintUserSession(user)));
|
|
83
82
|
};
|
|
84
83
|
|
|
85
84
|
/** POST /api/auth/refresh — cookie only, same-origin, with a short concurrent-request grace. */
|
|
86
85
|
export const postAuthRefreshRoute: Handler = async (req) => {
|
|
87
|
-
if (!sessionOriginAllowed(req)) return authorityDenied(req, "origin not allowed", 403);
|
|
86
|
+
if (!sessionOriginAllowed(req)) return (await authorityDenied(req, "origin not allowed", 403));
|
|
88
87
|
const refreshToken = readSessionCookie(req);
|
|
89
88
|
const result = refreshToken ? await refreshUserSession(refreshToken) : null;
|
|
90
89
|
// A stale response must never delete the cookie just rotated by another tab (#3567).
|
|
91
|
-
if (!result) return authorityDenied(req, "invalid refresh token", 401);
|
|
90
|
+
if (!result) return (await authorityDenied(req, "invalid refresh token", 401));
|
|
92
91
|
return sessionResponse(req, result.user, result.session);
|
|
93
92
|
};
|
|
94
93
|
|
|
95
94
|
/** POST /api/auth/logout — bearer authenticates the session; the refresh-only cookie is cleared. */
|
|
96
95
|
export const postAuthLogoutRoute: Handler = async (req) => {
|
|
97
|
-
if (!sessionOriginAllowed(req, false)) return authorityDenied(req, "origin not allowed", 403);
|
|
96
|
+
if (!sessionOriginAllowed(req, false)) return (await authorityDenied(req, "origin not allowed", 403));
|
|
98
97
|
const component = getComponentAuth(req);
|
|
99
98
|
if (component?.jti && !(await revokeUserSession(component))) await revokeTokenAsync(component.jti, "admin");
|
|
100
99
|
return withSessionCookie(req, new Response(null, { status: 204 }));
|
|
@@ -105,7 +104,7 @@ export const postAuthLogoutRoute: Handler = async (req) => {
|
|
|
105
104
|
* the single-use code is the credential. Creates the user and returns their first session.
|
|
106
105
|
*/
|
|
107
106
|
export const postAcceptInviteRoute: Handler = async (req) => {
|
|
108
|
-
if (!sessionOriginAllowed(req, false)) return authorityDenied(req, "origin not allowed", 403);
|
|
107
|
+
if (!sessionOriginAllowed(req, false)) return (await authorityDenied(req, "origin not allowed", 403));
|
|
109
108
|
if (!userInvitesEnabled()) return invitesDisabled();
|
|
110
109
|
const parsed = await parseBody<unknown>(req);
|
|
111
110
|
if (!parsed.ok) return error(parsed.error, parsed.status);
|
|
@@ -113,10 +112,10 @@ export const postAcceptInviteRoute: Handler = async (req) => {
|
|
|
113
112
|
const body = parsed.body;
|
|
114
113
|
try {
|
|
115
114
|
const user = await acceptInvite({ code: body.code, handle: body.handle, password: body.password, displayName: typeof body.displayName === "string" ? body.displayName.trim().slice(0, 120) || undefined : undefined });
|
|
116
|
-
recordUserManagement(req, user.id, "accept invite", { role: user.role }, user.id);
|
|
117
|
-
return sessionResponse(req, user, mintUserSession(user), 201);
|
|
115
|
+
(await recordUserManagement(req, user.id, "accept invite", { role: user.role }, user.id));
|
|
116
|
+
return sessionResponse(req, user, (await mintUserSession(user)), 201);
|
|
118
117
|
} catch (e) {
|
|
119
|
-
if (e instanceof InviteUnavailableError) return authorityDenied(req, e.message, 410);
|
|
118
|
+
if (e instanceof InviteUnavailableError) return (await authorityDenied(req, e.message, 410));
|
|
120
119
|
if (e instanceof ValidationError) return error(e.message, 400);
|
|
121
120
|
throw e;
|
|
122
121
|
}
|
|
@@ -5,9 +5,9 @@ import { isAdminRequest } from "./users";
|
|
|
5
5
|
|
|
6
6
|
/** GET /api/users/authority-denials — private, bounded admin audit list. */
|
|
7
7
|
export const getAuthorityDenials: Handler = async (req) => {
|
|
8
|
-
const denied = authorizeRoute(req, { scope: "system:admin" });
|
|
8
|
+
const denied = (await authorizeRoute(req, { scope: "system:admin" }));
|
|
9
9
|
if (denied) return denied;
|
|
10
|
-
if (!isAdminRequest(req)) return authorityDenied(req, "only admins can read authority denials");
|
|
10
|
+
if (!isAdminRequest(req)) return (await authorityDenied(req, "only admins can read authority denials"));
|
|
11
11
|
const limit = parseQueryInt(new URL(req.url).searchParams.get("limit"), { min: 1, max: 500 });
|
|
12
12
|
if (Number.isNaN(limit)) return error("limit must be an integer between 1 and 500");
|
|
13
13
|
return json(await listActivityEventsAsync({ title: AUTHORITY_DENIED, limit: limit ?? 100 }));
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Auto-split from routes.ts (#299). Domain: automations.
|
|
2
2
|
import { ValidationError } from "../db";
|
|
3
|
-
import {
|
|
3
|
+
import { listAutomationRunsAsync } from "../automations/read-async";
|
|
4
|
+
import { createAutomationAsync, deleteAutomationAsync, getAutomation, listAutomations, runAutomationNowAsync, updateAutomationAsync, type AutomationDispatchResult } from "../automations";
|
|
4
5
|
import { emitCommand, error, json, parseBody, parseQueryInt, type Handler } from "./_shared";
|
|
5
6
|
import { emitNewMessage, emitTaskChanged } from "../sse";
|
|
6
7
|
import { emitRelayEvent } from "../events";
|
|
@@ -53,8 +54,8 @@ export const patchAutomation: Handler = async (req, params) => {
|
|
|
53
54
|
}
|
|
54
55
|
};
|
|
55
56
|
|
|
56
|
-
export const deleteAutomationById: Handler = (_req, params) => {
|
|
57
|
-
if (!
|
|
57
|
+
export const deleteAutomationById: Handler = async (_req, params) => {
|
|
58
|
+
if (!(await deleteAutomationAsync(params.id!))) return error("automation not found", 404);
|
|
58
59
|
emitRelayEvent({ type: "automation.deleted", source: "server", subject: params.id!, data: { automationId: params.id! } });
|
|
59
60
|
return json({ ok: true });
|
|
60
61
|
};
|
|
@@ -71,11 +72,11 @@ export const postAutomationRun: Handler = async (_req, params) => {
|
|
|
71
72
|
}
|
|
72
73
|
};
|
|
73
74
|
|
|
74
|
-
export const getAutomationRuns: Handler = (req) => {
|
|
75
|
+
export const getAutomationRuns: Handler = async (req) => {
|
|
75
76
|
const url = new URL(req.url);
|
|
76
77
|
const limitRaw = parseQueryInt(url.searchParams.get("limit"), { min: 1, max: 500 });
|
|
77
78
|
if (Number.isNaN(limitRaw)) return error("limit must be an integer between 1 and 500");
|
|
78
|
-
return json(
|
|
79
|
+
return json(await listAutomationRunsAsync({
|
|
79
80
|
automationId: url.searchParams.get("automationId") ?? undefined,
|
|
80
81
|
status: url.searchParams.get("status") ?? undefined,
|
|
81
82
|
limit: limitRaw ?? 100,
|
|
@@ -5,7 +5,7 @@ import { ValidationError } from "../db/connection";
|
|
|
5
5
|
import { authorizeRoute, error, json, normalizeAgentSessionGuard, parseBody, type Handler } from "./_shared";
|
|
6
6
|
|
|
7
7
|
export const deleteChannelBinding: Handler = async (req, params) => {
|
|
8
|
-
const denied = authorizeRoute(req, { scope: "channel:write" });
|
|
8
|
+
const denied = (await authorizeRoute(req, { scope: "channel:write" }));
|
|
9
9
|
if (denied) return denied;
|
|
10
10
|
const parsed = await parseBody<unknown>(req);
|
|
11
11
|
if (!parsed.ok) return error(parsed.error, parsed.status);
|
|
@@ -3,12 +3,12 @@ import { authContextFromRequest } from "../services/auth-context";
|
|
|
3
3
|
import { ValidationError } from "../db/connection";
|
|
4
4
|
import { authorizeRoute, error, json, parseBody, type Handler } from "./_shared";
|
|
5
5
|
|
|
6
|
-
export const getChannelOutput: Handler = (req, params) => {
|
|
7
|
-
const denied = authorizeRoute(req, { scope: "channel:read", resource: { channel: params.id } });
|
|
6
|
+
export const getChannelOutput: Handler = async (req, params) => {
|
|
7
|
+
const denied = (await authorizeRoute(req, { scope: "channel:read", resource: { channel: params.id } }));
|
|
8
8
|
return denied ?? json(channelOutputControl(params.id!));
|
|
9
9
|
};
|
|
10
10
|
export const postChannelOutput: Handler = async (req, params) => {
|
|
11
|
-
const denied = authorizeRoute(req, { scope: "channel:egress", resource: { channel: params.id } });
|
|
11
|
+
const denied = (await authorizeRoute(req, { scope: "channel:egress", resource: { channel: params.id } }));
|
|
12
12
|
if (denied) return denied;
|
|
13
13
|
const parsed = await parseBody<unknown>(req);
|
|
14
14
|
if (!parsed.ok) return error(parsed.error, parsed.status);
|
|
@@ -5,7 +5,7 @@ import { authorizeRoute, error, json, parseBody, type Handler } from "./_shared"
|
|
|
5
5
|
import { apiBudgetId, isRecord } from "@humwick/sdk";
|
|
6
6
|
|
|
7
7
|
export const apiBudgetStatusRoute: Handler = async (req, params) => {
|
|
8
|
-
const denied = authorizeRoute(req, { scope: "system:admin" });
|
|
8
|
+
const denied = (await authorizeRoute(req, { scope: "system:admin" }));
|
|
9
9
|
if (denied) return denied;
|
|
10
10
|
try { return json(await apiBudgetStatusAsync(apiBudgetId(params.budgetId))); }
|
|
11
11
|
catch { return error("Unknown API budget", 404); }
|
|
@@ -16,7 +16,7 @@ export const apiBudgetStatusRoute: Handler = async (req, params) => {
|
|
|
16
16
|
* it returns the status snapshot only and exposes no mutation.
|
|
17
17
|
*/
|
|
18
18
|
export const apiBudgetSummaryRoute: Handler = async (req, params) => {
|
|
19
|
-
const denied = authorizeRoute(req, { scope: "agent:read" });
|
|
19
|
+
const denied = (await authorizeRoute(req, { scope: "agent:read" }));
|
|
20
20
|
if (denied) return denied;
|
|
21
21
|
try {
|
|
22
22
|
const summary: ApiBudgetSummary = await apiBudgetStatusAsync(apiBudgetId(params.budgetId));
|
|
@@ -24,7 +24,7 @@ export const apiBudgetSummaryRoute: Handler = async (req, params) => {
|
|
|
24
24
|
} catch { return error("Unknown API budget", 404); }
|
|
25
25
|
};
|
|
26
26
|
export const apiBudgetConfigureRoute: Handler = async (req, params) => {
|
|
27
|
-
const denied = authorizeRoute(req, { scope: "system:admin" });
|
|
27
|
+
const denied = (await authorizeRoute(req, { scope: "system:admin" }));
|
|
28
28
|
if (denied) return denied;
|
|
29
29
|
const body = await parseBody<unknown>(req);
|
|
30
30
|
if (!body.ok) return error(body.error, body.status);
|
|
@@ -37,7 +37,7 @@ export const apiBudgetConfigureRoute: Handler = async (req, params) => {
|
|
|
37
37
|
export const apiBudgetSessionRoute: Handler = async (req, params) => {
|
|
38
38
|
if (params.action !== "reserve" && params.action !== "report") return error("Unknown budget operation", 404);
|
|
39
39
|
const agentId = params.id!;
|
|
40
|
-
const denied = authorizeRoute(req, { scope: "agent:write", resource: { agentId } });
|
|
40
|
+
const denied = (await authorizeRoute(req, { scope: "agent:write", resource: { agentId } }));
|
|
41
41
|
if (denied) return denied;
|
|
42
42
|
const body = await parseBody<unknown>(req);
|
|
43
43
|
if (!body.ok) return error(body.error, body.status);
|
|
@@ -55,7 +55,7 @@ export const apiBudgetSessionRoute: Handler = async (req, params) => {
|
|
|
55
55
|
};
|
|
56
56
|
|
|
57
57
|
export const apiBudgetAgentRoute: Handler = async (req, params) => {
|
|
58
|
-
const denied = authorizeRoute(req, { scope: "agent:read", resource: { agentId: params.id! } });
|
|
58
|
+
const denied = (await authorizeRoute(req, { scope: "agent:read", resource: { agentId: params.id! } }));
|
|
59
59
|
if (denied) return denied;
|
|
60
60
|
try {
|
|
61
61
|
const budgetId = apiBudgetId(new URL(req.url).searchParams.get("budgetId") ?? "openai-api");
|
|
@@ -42,7 +42,7 @@ export const getCommands: Handler = async (req) => {
|
|
|
42
42
|
|
|
43
43
|
// The host only needs existence, not historical command params/results (#3063).
|
|
44
44
|
export const getActiveCleanup: Handler = async (req) => {
|
|
45
|
-
const denied = authorizeRoute(req, { scope: "command:read" });
|
|
45
|
+
const denied = (await authorizeRoute(req, { scope: "command:read" }));
|
|
46
46
|
if (denied) return denied;
|
|
47
47
|
try {
|
|
48
48
|
const target = cleanString(new URL(req.url).searchParams.get("target"), "target", { required: true, max: 240 });
|