@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
package/src/services/team.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
import { TeamAuthError, TeamNotFoundError, type TeamClaimMode } from "../db/team-claim-policy";
|
|
2
|
+
import type { claimAuthorizedTeam } from "../db/team-claim-policy";
|
|
3
|
+
import { getGateway } from "../db/gateway";
|
|
4
|
+
import { ensureTeamAsync, setTeamBriefAsync } from "../db/teams-async";
|
|
1
5
|
import { hasScope } from "@humwick/sdk/types";
|
|
2
6
|
import { errMessage } from "@humwick/sdk";
|
|
3
7
|
import { MAX_TEAM_ROSTER_ENTRIES, STALE_TTL_MS } from "../config";
|
|
4
|
-
import { claimTeamWithAdoption, countLiveTeamMembers,
|
|
8
|
+
import { claimTeamWithAdoption, countLiveTeamMembers, dissolveTeamAsync, ensureTeam, getAgent, getPlanByTeam, getTaskDetail, getTeam, getTeamBusyState, getTeamMemberStats, getTeamRoster, isClaimableTeam, setTeamBrief } from "../db";
|
|
5
9
|
import { emitTaskChanged } from "../sse";
|
|
6
10
|
import { emitRelayEvent } from "../events";
|
|
7
11
|
import { listIssueRefsForEntity } from "./issue-associations";
|
|
@@ -12,8 +16,7 @@ import { flushQueuedDirectMessages } from "./managed-running";
|
|
|
12
16
|
import { projectIdForAgent, seatTeamIdForProject } from "../db/team-project";
|
|
13
17
|
import { AGENT_SYSTEM_TAGS, hasAgentSystemTag } from "../agent-system-tags";
|
|
14
18
|
|
|
15
|
-
export
|
|
16
|
-
export class TeamNotFoundError extends Error {}
|
|
19
|
+
export { TeamAuthError, TeamNotFoundError } from "../db/team-claim-policy";
|
|
17
20
|
|
|
18
21
|
/** #1304 — what a bounded team payload actually carried vs. how many members the team has, so a
|
|
19
22
|
* truncated roster is visible (not a silent "these are all the members") to the caller. */
|
|
@@ -112,71 +115,19 @@ function payloadForAgent(agentId: string, channel?: string): TeamPayload {
|
|
|
112
115
|
};
|
|
113
116
|
}
|
|
114
117
|
|
|
115
|
-
function lineageRootForAgent(agentId: string): string | undefined {
|
|
116
|
-
let current = getAgent(agentId);
|
|
117
|
-
const seen = new Set<string>();
|
|
118
|
-
while (current) {
|
|
119
|
-
if (seen.has(current.id)) return undefined;
|
|
120
|
-
seen.add(current.id);
|
|
121
|
-
if (!current.spawnedBy) return current.id;
|
|
122
|
-
current = getAgent(current.spawnedBy);
|
|
123
|
-
}
|
|
124
|
-
return undefined;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function coordinatorIsLive(team: Team): boolean {
|
|
128
|
-
if (!team.coordinatorId) return false;
|
|
129
|
-
const coordinator = getAgent(team.coordinatorId);
|
|
130
|
-
if (!coordinator || coordinator.status === "offline") return false;
|
|
131
|
-
if (coordinator.teamId === team.id) return countLiveTeamMembers(team.id, Date.now() - STALE_TTL_MS) > 0;
|
|
132
|
-
return coordinator.lastSeen > Date.now() - STALE_TTL_MS;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/** How a claim was authorized — carried into the audit trail so an orphan takeover is
|
|
136
|
-
* distinguishable from a routine same-lineage reclaim. */
|
|
137
|
-
export type TeamClaimMode = "admin" | "same-lineage" | "orphan-takeover";
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Authorize a team claim and classify HOW it is authorized. The security boundary is
|
|
141
|
-
* `coordinatorIsLive`: a LIVE owner's team is NEVER claimable by a peer — only the team's own
|
|
142
|
-
* lineage (or an admin) may reclaim it. A cross-lineage caller may take over ONLY when the owning
|
|
143
|
-
* coordinator is offline/stale past STALE_TTL_MS (#1454 orphan takeover, recovering a coordinator
|
|
144
|
-
* lost to a host crash / OOM / process death). The `teams:write` scope gate on `relay_team_claim`
|
|
145
|
-
* already restricts every path here to coordinator-class tokens (worker tokens have `teams:write`
|
|
146
|
-
* stripped at mint), so no extra role check is needed.
|
|
147
|
-
*/
|
|
148
|
-
function assertCanClaimTeam(admin: boolean, team: Team, callerAgentId: string): TeamClaimMode {
|
|
149
|
-
const caller = getAgent(callerAgentId);
|
|
150
|
-
const callerTeam = caller?.teamId ? getTeam(caller.teamId) : null;
|
|
151
|
-
if (!admin && callerTeam?.parentTeamId && team.id !== callerTeam.id) {
|
|
152
|
-
throw new TeamAuthError("a sub-coordinator may claim only its own private team");
|
|
153
|
-
}
|
|
154
|
-
const callerRoot = lineageRootForAgent(callerAgentId);
|
|
155
|
-
const teamRoot = team.rootCoordinatorId ?? team.coordinatorId;
|
|
156
|
-
// Same-lineage reclaim is the routine fast path (a replacement spawned under the original
|
|
157
|
-
// founder) and stays allowed regardless of owner liveness.
|
|
158
|
-
if (callerRoot && teamRoot && callerRoot === teamRoot) return "same-lineage";
|
|
159
|
-
if (admin) return "admin";
|
|
160
|
-
// #1454 orphan takeover: a replacement coordinator of a DIFFERENT lineage may adopt the team only
|
|
161
|
-
// when its owning coordinator is not live (offline, or stale past the heartbeat TTL). This is the
|
|
162
|
-
// sole security boundary for the cross-lineage path — a healthy owner is never displaced.
|
|
163
|
-
if (!coordinatorIsLive(team)) return "orphan-takeover";
|
|
164
|
-
throw new TeamAuthError(`caller ${callerAgentId} cannot claim team ${team.id}`);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
118
|
export function getCallerTeam(ctx: AuthContext, channel?: string): TeamPayload {
|
|
168
119
|
return payloadForAgent(requireCallerAgentId(ctx), channel);
|
|
169
120
|
}
|
|
170
121
|
|
|
171
|
-
export function setCallerTeamBrief(ctx: AuthContext, input: TeamBriefInput): TeamPayload {
|
|
122
|
+
export async function setCallerTeamBrief(ctx: AuthContext, input: TeamBriefInput): Promise<TeamPayload> {
|
|
172
123
|
const agentId = requireCallerAgentId(ctx);
|
|
173
|
-
const team = acquireCoordinatorTeam(agentId);
|
|
174
|
-
|
|
124
|
+
const team = (await acquireCoordinatorTeam(agentId));
|
|
125
|
+
await setTeamBriefAsync({ teamId: team.id, ...input });
|
|
175
126
|
return payloadForAgent(agentId);
|
|
176
127
|
}
|
|
177
128
|
|
|
178
|
-
export function claimTeamForCaller(ctx: AuthContext, teamId: string, reason?: string): TeamPayload {
|
|
179
|
-
const claimed = claimTeamAs(requireCallerAgentId(ctx), teamId, hasScope(ctx.scopes, "system:admin"), reason);
|
|
129
|
+
export async function claimTeamForCaller(ctx: AuthContext, teamId: string, reason?: string): Promise<TeamPayload> {
|
|
130
|
+
const claimed = (await claimTeamAs(requireCallerAgentId(ctx), teamId, hasScope(ctx.scopes, "system:admin"), reason));
|
|
180
131
|
return {
|
|
181
132
|
team: claimed,
|
|
182
133
|
...boundedTeamRoster(claimed.id, Date.now() - STALE_TTL_MS),
|
|
@@ -187,34 +138,16 @@ export function claimTeamForCaller(ctx: AuthContext, teamId: string, reason?: st
|
|
|
187
138
|
};
|
|
188
139
|
}
|
|
189
140
|
|
|
190
|
-
function claimTeamAs(agentId: string, teamId: string, admin: boolean, reason?: string): Team {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
// the claim re-activates it. An explicitly dissolved team does not.
|
|
195
|
-
const pre = getTeam(teamId);
|
|
196
|
-
if (!isClaimableTeam(pre)) throw new TeamNotFoundError(`active team ${teamId} not found`);
|
|
197
|
-
// #1454 — authorize against the team as re-read UNDER claimTeamWithAdoption's write lock, so a racing claim
|
|
198
|
-
// that already committed is visible to assertCanClaimTeam (a healthy new owner denies the loser).
|
|
199
|
-
// The mode + prior coordinator captured here come from that same locked snapshot, so the audit
|
|
200
|
-
// trail matches exactly what the compare-and-swap acted on.
|
|
201
|
-
let captured: { mode: TeamClaimMode; priorCoordinatorId: string | null } | undefined;
|
|
202
|
-
const { team: claimed, revived, adoptedTaskIds } = claimTeamWithAdoption(teamId, agentId, (lockedTeam) => {
|
|
203
|
-
captured = {
|
|
204
|
-
mode: assertCanClaimTeam(admin, lockedTeam, agentId),
|
|
205
|
-
priorCoordinatorId: lockedTeam.coordinatorId ?? null,
|
|
206
|
-
};
|
|
207
|
-
});
|
|
208
|
-
// claimTeamWithAdoption always invokes the callback before committing (or throws before returning), so this is
|
|
209
|
-
// populated on every success.
|
|
210
|
-
afterTeamClaimed(agentId, claimed, { ...captured!, revived, adoptedTaskIds, reason });
|
|
211
|
-
return claimed;
|
|
141
|
+
async function claimTeamAs(agentId: string, teamId: string, admin: boolean, reason?: string): Promise<Team> {
|
|
142
|
+
const result = await getGateway().write("teams.claimAuthorized", { teamId, agentId, admin }) as unknown as ReturnType<typeof claimAuthorizedTeam>;
|
|
143
|
+
await afterTeamClaimed(agentId, result.team, { ...result, reason });
|
|
144
|
+
return result.team;
|
|
212
145
|
}
|
|
213
146
|
|
|
214
147
|
/** The one post-claim seam for relay_team_claim and seat acquisition: unread mail, audit, Task feeds. */
|
|
215
|
-
function afterTeamClaimed(agentId: string, claimed: Team, claim: { mode: TeamClaimMode; priorCoordinatorId: string | null; revived: boolean; adoptedTaskIds: number[]; reason?: string }): void {
|
|
148
|
+
async function afterTeamClaimed(agentId: string, claimed: Team, claim: { mode: TeamClaimMode; priorCoordinatorId: string | null; revived: boolean; adoptedTaskIds: number[]; reason?: string }): Promise<void> {
|
|
216
149
|
const { mode, priorCoordinatorId, revived, adoptedTaskIds, reason } = claim;
|
|
217
|
-
flushQueuedDirectMessages(agentId);
|
|
150
|
+
(await flushQueuedDirectMessages(agentId));
|
|
218
151
|
// Audit every ownership CHANGE (#1454) — an orphan takeover is the failover-recovery signal, but a
|
|
219
152
|
// cross-lineage admin claim is equally worth a trail. A no-op reclaim (same owner) emits nothing.
|
|
220
153
|
if (priorCoordinatorId !== agentId) {
|
|
@@ -245,48 +178,48 @@ function afterTeamClaimed(agentId: string, claimed: Team, claim: { mode: TeamCla
|
|
|
245
178
|
/** #3107 — a coordinator-profile agent in project P founds P's stable seat team, or claims it under the
|
|
246
179
|
* #1454 rule (same lineage, or the holder is offline). A refused claim yields a side team discovery never
|
|
247
180
|
* lists; it takes the seat only by an explicit relay_team_claim. Sync, on the main thread. */
|
|
248
|
-
export function acquireCoordinatorTeam(agentId: string): Team {
|
|
181
|
+
export async function acquireCoordinatorTeam(agentId: string): Promise<Team> {
|
|
249
182
|
const agent = getAgent(agentId);
|
|
250
183
|
const current = agent?.teamId ? getTeam(agent.teamId) : null;
|
|
251
184
|
if (current?.status === "active") return current;
|
|
252
185
|
const projectId = agent && hasAgentSystemTag(agent, AGENT_SYSTEM_TAGS.coordinator) ? projectIdForAgent(agent) : null;
|
|
253
|
-
if (!projectId) return
|
|
186
|
+
if (!projectId) return await ensureTeamAsync(agentId);
|
|
254
187
|
const seatId = seatTeamIdForProject(projectId);
|
|
255
|
-
if (seatId) return claimSeatOrSideTeam(agentId, seatId);
|
|
188
|
+
if (seatId) return (await claimSeatOrSideTeam(agentId, seatId));
|
|
256
189
|
try {
|
|
257
|
-
return
|
|
190
|
+
return await ensureTeamAsync(agentId, { projectId });
|
|
258
191
|
} catch (e) {
|
|
259
192
|
// A racing founder took the seat between the read and the insert: claim behind it, once.
|
|
260
193
|
const winner = e instanceof Error && e.message.includes("teams.project_id") ? seatTeamIdForProject(projectId) : null;
|
|
261
194
|
if (!winner) throw e;
|
|
262
|
-
return claimSeatOrSideTeam(agentId, winner);
|
|
195
|
+
return (await claimSeatOrSideTeam(agentId, winner));
|
|
263
196
|
}
|
|
264
197
|
}
|
|
265
198
|
|
|
266
|
-
function claimSeatOrSideTeam(agentId: string, seatId: string): Team {
|
|
199
|
+
async function claimSeatOrSideTeam(agentId: string, seatId: string): Promise<Team> {
|
|
267
200
|
try {
|
|
268
|
-
return claimTeamAs(agentId, seatId, false, "coordinator seat acquisition");
|
|
201
|
+
return (await claimTeamAs(agentId, seatId, false, "coordinator seat acquisition"));
|
|
269
202
|
} catch (e) {
|
|
270
203
|
if (!(e instanceof TeamAuthError)) throw e;
|
|
271
|
-
return
|
|
204
|
+
return await ensureTeamAsync(agentId);
|
|
272
205
|
}
|
|
273
206
|
}
|
|
274
207
|
|
|
275
208
|
/** #3107 — a coordinator takes its project's seat at its ready transition, before queued mail flushes,
|
|
276
209
|
* so a fresh replacement owns the topic before its first spawn. A restart keeps its team: no-op. */
|
|
277
|
-
export function acquireTeamOnReady(agentId: string): void {
|
|
210
|
+
export async function acquireTeamOnReady(agentId: string): Promise<void> {
|
|
278
211
|
const agent = getAgent(agentId);
|
|
279
212
|
if (!agent || !hasAgentSystemTag(agent, AGENT_SYSTEM_TAGS.coordinator)) return;
|
|
280
213
|
if (agent.teamId && getTeam(agent.teamId)?.status === "active") return;
|
|
281
214
|
try {
|
|
282
|
-
acquireCoordinatorTeam(agentId);
|
|
215
|
+
(await acquireCoordinatorTeam(agentId));
|
|
283
216
|
} catch (e) {
|
|
284
217
|
console.warn(`[teams] seat acquisition for ${agentId} failed: ${errMessage(e)}`);
|
|
285
218
|
}
|
|
286
219
|
}
|
|
287
220
|
|
|
288
|
-
export function dissolveTeamById(teamId: string, input: TeamDissolveServiceInput = {}): Team {
|
|
289
|
-
const { team, unpromotedBlackboard } =
|
|
221
|
+
export async function dissolveTeamById(teamId: string, input: TeamDissolveServiceInput = {}): Promise<Team> {
|
|
222
|
+
const { team, unpromotedBlackboard } = await dissolveTeamAsync(teamId, input);
|
|
290
223
|
emitRelayEvent({
|
|
291
224
|
type: "team.dissolved",
|
|
292
225
|
source: "system",
|
|
@@ -304,11 +237,11 @@ export function dissolveTeamById(teamId: string, input: TeamDissolveServiceInput
|
|
|
304
237
|
return team;
|
|
305
238
|
}
|
|
306
239
|
|
|
307
|
-
export function dissolveCallerTeam(ctx: AuthContext, input: TeamDissolveServiceInput): TeamPayload {
|
|
240
|
+
export async function dissolveCallerTeam(ctx: AuthContext, input: TeamDissolveServiceInput): Promise<TeamPayload> {
|
|
308
241
|
const agentId = requireCallerAgentId(ctx);
|
|
309
242
|
const agent = getAgent(agentId);
|
|
310
243
|
if (!agent?.teamId) throw new TeamNotFoundError("caller has no team");
|
|
311
|
-
const team = dissolveTeamById(agent.teamId, input);
|
|
244
|
+
const team = await dissolveTeamById(agent.teamId, input);
|
|
312
245
|
return {
|
|
313
246
|
team,
|
|
314
247
|
...boundedTeamRoster(team.id, Date.now() - STALE_TTL_MS),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { clearAgentMetaKeysAsync } from "../db/agents-async";
|
|
1
2
|
import { agentPrincipalSink } from "../db/agent-ownership";
|
|
2
3
|
// #633 — owning service for the silent clean-exit terminal event. A runner whose provider
|
|
3
4
|
// terminally exits stays alive (manual-resume hold), so neither the orchestrator tmux-gone path nor
|
|
@@ -6,7 +7,7 @@ import { agentPrincipalSink } from "../db/agent-ownership";
|
|
|
6
7
|
// clears the hold when the provider genuinely resumes. Kept out of the bus transport so the
|
|
7
8
|
// transport stays thin (transport-thinness ratchet, #348).
|
|
8
9
|
import { isRecord } from "@humwick/sdk";
|
|
9
|
-
|
|
10
|
+
|
|
10
11
|
import { notifyAgentOffline } from "../agent-lifecycle-events";
|
|
11
12
|
import { diagnoseAgentDeath } from "../agent-death-diagnosis";
|
|
12
13
|
import { routeNotification } from "../notification-router";
|
|
@@ -20,9 +21,9 @@ import type { AgentCard } from "../types";
|
|
|
20
21
|
* a death diagnosis (also persisted as a crash report inside notifyAgentOffline). No-op for
|
|
21
22
|
* non-spawned agents (notifyAgentOffline guards on lineage) and graceful shutdowns (no marker).
|
|
22
23
|
*/
|
|
23
|
-
export function reconcileTerminalProviderExit(agentId: string, before: AgentCard | null | undefined, after: AgentCard | null | undefined): void {
|
|
24
|
+
export async function reconcileTerminalProviderExit(agentId: string, before: AgentCard | null | undefined, after: AgentCard | null | undefined): Promise<void> {
|
|
24
25
|
if (after && after.status !== "offline" && before?.status === "offline" && isRecord(after.meta?.terminalProviderExit)) {
|
|
25
|
-
|
|
26
|
+
(await clearAgentMetaKeysAsync(agentId, ["terminalProviderExit"]));
|
|
26
27
|
return;
|
|
27
28
|
}
|
|
28
29
|
if (!after || after.status !== "offline") return;
|
|
@@ -32,9 +33,9 @@ export function reconcileTerminalProviderExit(agentId: string, before: AgentCard
|
|
|
32
33
|
const reasonText = typeof marker.reason === "string" ? marker.reason : "provider exited";
|
|
33
34
|
const reason = `provider exited (${reasonText})`;
|
|
34
35
|
const diagnosis = diagnoseAgentDeath({ agent: after, reason });
|
|
35
|
-
notifyAgentOffline(agentId, reason, diagnosis);
|
|
36
|
+
(await notifyAgentOffline(agentId, reason, diagnosis));
|
|
36
37
|
if (!after.spawnedBy && reasonText.startsWith("self-resume-")) {
|
|
37
|
-
routeNotification({
|
|
38
|
+
await routeNotification({
|
|
38
39
|
type: "agent.exited",
|
|
39
40
|
scope: { agentId },
|
|
40
41
|
recipients: [agentPrincipalSink(agentId)],
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { initDb } from "../db";
|
|
2
|
+
import { getGateway } from "../db/gateway";
|
|
2
3
|
import { runDueScheduledTimers, type RunDueScheduledTimersFaults } from "./scheduler";
|
|
3
4
|
|
|
4
5
|
const [dbPath, boundary, rawNow] = process.argv.slice(2);
|
|
@@ -17,9 +18,12 @@ const faults: RunDueScheduledTimersFaults = {};
|
|
|
17
18
|
if (boundary === "after-claim") faults.afterClaim = (timer) => {
|
|
18
19
|
if (!timer.command) kill();
|
|
19
20
|
};
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
const writerBoundary = ({
|
|
22
|
+
"after-authority": "afterAuthorityCasAndFireInsert",
|
|
23
|
+
"after-projection": "afterLegacyProjectionUpdate",
|
|
24
|
+
"before-commit": "beforeCommit",
|
|
25
|
+
} as Record<string, string>)[boundary];
|
|
26
|
+
if (writerBoundary) await getGateway().write("seam.armFault", { name: `timers.decideFire.${writerBoundary}`, action: "kill" });
|
|
23
27
|
if (boundary === "after-commit") faults.afterCommit = kill;
|
|
24
28
|
if (boundary === "after-send") faults.afterSendBeforeSent = kill;
|
|
25
29
|
|
|
@@ -147,7 +147,7 @@ export async function drainTimerMessageFireOutbox(
|
|
|
147
147
|
// Durable-timer fire-send proceeds on the FROZEN captured identity even when the original
|
|
148
148
|
// sender agent has deregistered — the outbox row already carries that identity. The relaxation
|
|
149
149
|
// is scoped to THIS path only; normal live sends keep the registration guard (#362 boundary).
|
|
150
|
-
const sent = send(payload.input, authContextOf(payload), { allowUnregisteredSender: true });
|
|
150
|
+
const sent = await send(payload.input, authContextOf(payload), { allowUnregisteredSender: true });
|
|
151
151
|
options.afterSendBeforeSent?.(row, sent.message.id);
|
|
152
152
|
await markTimerMessageFireSentAsync(row.timerId, row.run, sent.message.id);
|
|
153
153
|
await clearTimerMessageFireSendFailureAsync(row.timerId, row.run);
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { recordTerminalEventAsync } from "../db/lifecycle-tail-async";
|
|
2
|
+
import { createActivityEventAsync } from "../db/activity-async";
|
|
3
|
+
import { createCommandAsync } from "../commands-write";
|
|
1
4
|
import { relayEnv } from "@humwick/sdk/env";
|
|
2
5
|
import { notifyAgentExit } from "../agent-lifecycle-events";
|
|
3
6
|
import { errMessage } from "@humwick/sdk";
|
|
4
|
-
|
|
7
|
+
|
|
5
8
|
import { emitRelayEvent } from "../events";
|
|
6
|
-
import { agentsWithPendingOwnRelayRun,
|
|
9
|
+
import { agentsWithPendingOwnRelayRun, getAgent, hasArtifactCreatedBy, listAgents, listFinalResponseCaptureMessages, listWorkspaces, onBeforeAgentStatusTransition, workflowRunIdOf } from "../db";
|
|
7
10
|
import { routeNotification } from "../notification-router";
|
|
8
11
|
import { isProviderBlocked } from "../provider-state";
|
|
9
12
|
import { TERMINAL_WORKSPACE_STATUSES } from "../workspace-phase";
|
|
@@ -37,25 +40,25 @@ export interface TransientShutdownOptions {
|
|
|
37
40
|
now?: number;
|
|
38
41
|
}
|
|
39
42
|
|
|
40
|
-
export function requestTransientAgentShutdown(agent: AgentCard, opts: TransientShutdownOptions): Command {
|
|
43
|
+
export async function requestTransientAgentShutdown(agent: AgentCard, opts: TransientShutdownOptions): Promise<Command> {
|
|
41
44
|
const now = opts.now ?? Date.now();
|
|
42
|
-
const command =
|
|
45
|
+
const command = (await createCommandAsync({
|
|
43
46
|
type: "agent.shutdown",
|
|
44
47
|
source: "system",
|
|
45
48
|
target: agent.id,
|
|
46
49
|
params: { action: "shutdown", agentId: agent.id, agentGeneration: agentGenerationKey(agent), graceful: true, timeoutMs: 10_000, reason: opts.reason, requestedBy: opts.requestedBy, requestedAt: now },
|
|
47
|
-
});
|
|
50
|
+
}));
|
|
48
51
|
emitCommandEvent(command, "command.requested");
|
|
49
52
|
// #1084 — reaping a transient agent always follows a verified-complete land/idle-grace check
|
|
50
53
|
// (the caller only reaches here once `land.complete`/gates-passed holds), so it is a genuine
|
|
51
54
|
// teardown completion signal: close its work window, forwarding only the held final group.
|
|
52
|
-
markAgentWorkFinal(agent.id, "edge-fallback");
|
|
55
|
+
(await markAgentWorkFinal(agent.id, "edge-fallback"));
|
|
53
56
|
// #645 — persist a clean terminal marker so the #634 heartbeat-gap watchdog reads this benign
|
|
54
57
|
// completion as a terminus (not a silent death) once the reaped worker stops heartbeating.
|
|
55
|
-
|
|
58
|
+
(await recordTerminalEventAsync({ agentId: agent.id, reason: opts.reason, kind: "agent.exited", epoch: agent.epoch, createdAt: now }));
|
|
56
59
|
// #1912 §4 retained: callers need the created command synchronously to preserve teardown
|
|
57
60
|
// ordering; an async activity-only hop here would sever that command/audit contract.
|
|
58
|
-
|
|
61
|
+
(await createActivityEventAsync({
|
|
59
62
|
clientId: `transient-agent-reaped-${agent.id}-${now}`,
|
|
60
63
|
kind: "state",
|
|
61
64
|
title: opts.activityTitle,
|
|
@@ -65,8 +68,8 @@ export function requestTransientAgentShutdown(agent: AgentCard, opts: TransientS
|
|
|
65
68
|
view: "agents",
|
|
66
69
|
agentId: agent.id,
|
|
67
70
|
metadata: { source: "server", maintenanceJobId: opts.requestedBy, commandId: command.id, reason: opts.reason, workspaceId: opts.workspaceId, landReason: opts.landReason },
|
|
68
|
-
});
|
|
69
|
-
notifyAgentExit(agent, opts.reason, undefined, { workspaceId: opts.workspaceId, workspaceStatus: opts.workspaceStatus });
|
|
71
|
+
}));
|
|
72
|
+
(await notifyAgentExit(agent, opts.reason, undefined, { workspaceId: opts.workspaceId, workspaceStatus: opts.workspaceStatus }));
|
|
70
73
|
return command;
|
|
71
74
|
}
|
|
72
75
|
|
|
@@ -95,9 +98,9 @@ const persistentIdleTracker = new Map<string, { firstIdleAt: number; lastFlushed
|
|
|
95
98
|
// #1333 — the reaper owns the observed-idle settle clock, but its next poll is too late to close
|
|
96
99
|
// the old window: capture B from the new turn can already displace final A in the held ring. Flush
|
|
97
100
|
// synchronously inside the actual idle→busy mutation, before that write returns and B is accepted.
|
|
98
|
-
onBeforeAgentStatusTransition((agent, nextStatus) => {
|
|
101
|
+
onBeforeAgentStatusTransition(async (agent, nextStatus) => {
|
|
99
102
|
if (agent.status !== "idle" || nextStatus !== "busy" || !persistentIdleTracker.has(agent.id)) return;
|
|
100
|
-
markAgentWorkFinal(agent.id, "edge-fallback");
|
|
103
|
+
(await markAgentWorkFinal(agent.id, "edge-fallback"));
|
|
101
104
|
persistentIdleTracker.delete(agent.id);
|
|
102
105
|
});
|
|
103
106
|
|
|
@@ -246,7 +249,7 @@ async function notifyTransientHeld(agentId: string, workspaceId: string | undefi
|
|
|
246
249
|
// the live card, never from the pre-await snapshot used only to describe this hold event.
|
|
247
250
|
const parent = getAgent(agentId)?.spawnedBy;
|
|
248
251
|
if (parent) {
|
|
249
|
-
routeNotification({
|
|
252
|
+
await routeNotification({
|
|
250
253
|
type: "agent.transient_land_hold",
|
|
251
254
|
scope: { agentId, parent, workspaceId },
|
|
252
255
|
recipients: [parent],
|
|
@@ -316,7 +319,7 @@ export async function reapTransientAgents(context: { signal?: AbortSignal } = {}
|
|
|
316
319
|
const persistentIdleForMs = now - persistentEntry.firstIdleAt;
|
|
317
320
|
if (persistentIdleForMs < persistentGrace || persistentEntry.lastFlushedAt) continue;
|
|
318
321
|
persistentEntry.lastFlushedAt = now;
|
|
319
|
-
markAgentWorkFinal(agent.id, "edge-fallback");
|
|
322
|
+
(await markAgentWorkFinal(agent.id, "edge-fallback"));
|
|
320
323
|
persistentFinalsFlushed.push(agent.id);
|
|
321
324
|
continue;
|
|
322
325
|
}
|
|
@@ -340,7 +343,7 @@ export async function reapTransientAgents(context: { signal?: AbortSignal } = {}
|
|
|
340
343
|
// A graceful runner shutdown flushes its session outbox immediately before it reports
|
|
341
344
|
// offline. That final capture can therefore arrive after the initial reap closed the
|
|
342
345
|
// work window; close it once more at the terminal edge before dropping the tracker (#1139).
|
|
343
|
-
markAgentWorkFinal(agent.id, "edge-fallback");
|
|
346
|
+
(await markAgentWorkFinal(agent.id, "edge-fallback"));
|
|
344
347
|
if (priorEntry.workspaceId) {
|
|
345
348
|
const reconciled = await reconcileReapedWorkspace(priorEntry.workspaceId);
|
|
346
349
|
if (!reconciled) continue; // retain tracker → retry next scan
|
|
@@ -379,7 +382,7 @@ export async function reapTransientAgents(context: { signal?: AbortSignal } = {}
|
|
|
379
382
|
// #2124 — a worker stuck at held-pending-land (wedged workspace) hits none of the other
|
|
380
383
|
// finality anchors (workspace-ready, task->done, teardown-reap). Close its work window here,
|
|
381
384
|
// once, so its turn-final report isn't stranded in the ring for the duration of the hold.
|
|
382
|
-
markAgentWorkFinal(agent.id, "edge-fallback");
|
|
385
|
+
(await markAgentWorkFinal(agent.id, "edge-fallback"));
|
|
383
386
|
}
|
|
384
387
|
} else waiting.push(agent.id);
|
|
385
388
|
continue;
|
|
@@ -387,7 +390,7 @@ export async function reapTransientAgents(context: { signal?: AbortSignal } = {}
|
|
|
387
390
|
if (entry.lastReapAt) continue;
|
|
388
391
|
entry.lastReapAt = now;
|
|
389
392
|
entry.workspaceId = land.workspaceId;
|
|
390
|
-
requestTransientAgentShutdown(agent, {
|
|
393
|
+
(await requestTransientAgentShutdown(agent, {
|
|
391
394
|
reason: "transient-complete",
|
|
392
395
|
requestedBy: "transient-agent-reaper",
|
|
393
396
|
workspaceId: land.workspaceId,
|
|
@@ -396,11 +399,11 @@ export async function reapTransientAgents(context: { signal?: AbortSignal } = {}
|
|
|
396
399
|
activityBody: "Agent idle grace elapsed and transient work is complete; shutdown requested",
|
|
397
400
|
landReason: land.reason,
|
|
398
401
|
now,
|
|
399
|
-
});
|
|
402
|
+
}));
|
|
400
403
|
// #889 — finalize any bound task whose doneWhen is "agent-exit" (auto-scaffolded ephemeral
|
|
401
404
|
// tasks that produce no deliverable and would otherwise hang in_progress forever).
|
|
402
405
|
const spawnRequestId = typeof agent.meta?.spawnRequestId === "string" ? agent.meta.spawnRequestId : undefined;
|
|
403
|
-
finalizeEphemeralTasksOnReap(agent.id, { spawnRequestId, reason: "transient-complete", producedWork: land.producedWork, evidence: land.reason });
|
|
406
|
+
await finalizeEphemeralTasksOnReap(agent.id, { spawnRequestId, reason: "transient-complete", producedWork: land.producedWork, evidence: land.reason });
|
|
404
407
|
reaped.push(agent.id);
|
|
405
408
|
}
|
|
406
409
|
// #1819 — only sweep on a COMPLETE pass. An abort truncates `seen`/`seenPersistent`, and dropping a
|
|
@@ -38,11 +38,11 @@ function describeAge(ageMs: number | undefined): string {
|
|
|
38
38
|
* ("idle at its prompt now") is precisely what live work contradicts. Operator-reported false
|
|
39
39
|
* positive, 2026-09-27 (`dogfood-first-ship`), cost: a coordinator re-sending the work.
|
|
40
40
|
*/
|
|
41
|
-
export function auditReconciledProviderTurn(
|
|
41
|
+
export async function auditReconciledProviderTurn(
|
|
42
42
|
agentId: string,
|
|
43
43
|
after: AgentCard | null | undefined,
|
|
44
44
|
timelineEvent: ProviderTimelineEvent | null,
|
|
45
|
-
): void {
|
|
45
|
+
): Promise<void> {
|
|
46
46
|
if (!timelineEvent || timelineEvent.status !== RECONCILED_TURN_TIMELINE_STATUS) return;
|
|
47
47
|
const metadata = timelineEvent.metadata ?? {};
|
|
48
48
|
if (metadata.source !== "runner") return;
|
|
@@ -51,7 +51,7 @@ export function auditReconciledProviderTurn(
|
|
|
51
51
|
const reason = stringValue(metadata.reconcileReason);
|
|
52
52
|
const label = after?.label ?? after?.name ?? agentId;
|
|
53
53
|
try {
|
|
54
|
-
routeNotification({
|
|
54
|
+
await routeNotification({
|
|
55
55
|
type: "agent.turn_interrupted",
|
|
56
56
|
scope: { agentId, parent: after?.spawnedBy, teamId: after?.teamId },
|
|
57
57
|
message: {
|
|
@@ -86,12 +86,12 @@ export function auditReconciledProviderTurn(
|
|
|
86
86
|
* No liveness verdict means there is no stall evidence; the activity row still records the hold.
|
|
87
87
|
* Transport heartbeats alone do not count as provider progress.
|
|
88
88
|
*/
|
|
89
|
-
export function auditProviderTurnWatchdog(
|
|
89
|
+
export async function auditProviderTurnWatchdog(
|
|
90
90
|
agentId: string,
|
|
91
91
|
after: AgentCard | null | undefined,
|
|
92
92
|
timelineEvent: ProviderTimelineEvent | null,
|
|
93
93
|
now?: number,
|
|
94
|
-
): void {
|
|
94
|
+
): Promise<void> {
|
|
95
95
|
if (!timelineEvent || timelineEvent.status !== PROVIDER_TURN_WATCHDOG_TIMELINE_STATUS) return;
|
|
96
96
|
const metadata = timelineEvent.metadata ?? {};
|
|
97
97
|
if (metadata.source !== "runner") return;
|
|
@@ -114,7 +114,7 @@ export function auditProviderTurnWatchdog(
|
|
|
114
114
|
const reason = stringValue(metadata.watchdogReason);
|
|
115
115
|
const label = after?.label ?? after?.name ?? agentId;
|
|
116
116
|
try {
|
|
117
|
-
routeNotification({
|
|
117
|
+
await routeNotification({
|
|
118
118
|
type: "agent.turn_watchdog",
|
|
119
119
|
scope: { agentId, parent: after?.spawnedBy, teamId: after?.teamId },
|
|
120
120
|
message: {
|
|
@@ -175,11 +175,11 @@ export function auditProviderTurnWatchdog(
|
|
|
175
175
|
* one here, and only `EmptyTurnLedger.escalateOnTimeout` deferring to "retry" on tracked live work can
|
|
176
176
|
* tell them apart — see `answers()`.
|
|
177
177
|
*/
|
|
178
|
-
export function auditProviderTurnEmpty(
|
|
178
|
+
export async function auditProviderTurnEmpty(
|
|
179
179
|
agentId: string,
|
|
180
180
|
after: AgentCard | null | undefined,
|
|
181
181
|
timelineEvent: ProviderTimelineEvent | null,
|
|
182
|
-
): void {
|
|
182
|
+
): Promise<void> {
|
|
183
183
|
if (!timelineEvent || timelineEvent.status !== PROVIDER_TURN_EMPTY_TIMELINE_STATUS) return;
|
|
184
184
|
const metadata = timelineEvent.metadata ?? {};
|
|
185
185
|
if (metadata.source !== "runner") return;
|
|
@@ -199,7 +199,7 @@ export function auditProviderTurnEmpty(
|
|
|
199
199
|
const stalled = stringValue(metadata.emptyKind) === "input-stalled";
|
|
200
200
|
const label = after?.label ?? after?.name ?? agentId;
|
|
201
201
|
try {
|
|
202
|
-
routeNotification({
|
|
202
|
+
await routeNotification({
|
|
203
203
|
type: "agent.turn_empty",
|
|
204
204
|
scope: { agentId, parent: after?.spawnedBy, teamId: after?.teamId },
|
|
205
205
|
message: {
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { createUserInviteAsync, acceptUserInviteAsync } from "../db/user-invites-async";
|
|
2
|
+
import { updateUserAccessAsync, setUserSecretAsync } from "../db/runtime-tail-async";
|
|
1
3
|
// User onboarding (#3567 D2): the invites flag, invite acceptance and password setup. The admin
|
|
2
4
|
// sets their own password with the admin token, then invites users with single-use links.
|
|
3
5
|
import { DEFAULT_USER_ID, userSinkId, type User, type UserRole, type UserStatus } from "@humwick/sdk";
|
|
4
|
-
import { ValidationError,
|
|
6
|
+
import { ValidationError, reassignAgentOwnershipAsync, getAgent, getUser, listAuthIdentitiesForUser, type UserInvite } from "../db";
|
|
5
7
|
import { liveTokenJtisForSub } from "../token-db";
|
|
6
8
|
import { revokeTokenAsync } from "../token-db-write";
|
|
7
9
|
import { getConfig } from "../config-store";
|
|
@@ -32,8 +34,8 @@ function cleanPassword(raw: unknown): string {
|
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
/** Create a single-use invite; the plaintext code is returned once and never stored. */
|
|
35
|
-
export function createInvite(input: { role: UserRole; displayName?: string; createdBy: string }): { invite: UserInvite; code: string } {
|
|
36
|
-
return
|
|
37
|
+
export async function createInvite(input: { role: UserRole; displayName?: string; createdBy: string }): Promise<{ invite: UserInvite; code: string }> {
|
|
38
|
+
return (await createUserInviteAsync(input));
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
/** Accept an invite: hash the password, then create the user and their login in one write. */
|
|
@@ -41,7 +43,7 @@ export async function acceptInvite(input: { code: unknown; handle: unknown; pass
|
|
|
41
43
|
if (typeof input.code !== "string" || !input.code) throw new ValidationError("code required");
|
|
42
44
|
const handle = cleanHandle(input.handle);
|
|
43
45
|
const secretHash = await hashPassword(cleanPassword(input.password));
|
|
44
|
-
return
|
|
46
|
+
return (await acceptUserInviteAsync({ code: input.code, userId: handle, handle, displayName: input.displayName, provider: PASSWORD_PROVIDER_ID, secretHash }));
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
/** True once the user has a password login (the dashboard offers "Set your password" until then). */
|
|
@@ -62,7 +64,7 @@ export async function setPassword(input: { userId: string; password: unknown; cu
|
|
|
62
64
|
const current = typeof input.currentPassword === "string" ? input.currentPassword : "";
|
|
63
65
|
if (!(await verifyPassword(current, existing.secretHash))) throw new ValidationError("current password is wrong");
|
|
64
66
|
}
|
|
65
|
-
|
|
67
|
+
(await setUserSecretAsync({ userId: user.id, provider: PASSWORD_PROVIDER_ID, providerSubject: user.handle, secretHash: await hashPassword(password) }));
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
/**
|
|
@@ -74,7 +76,7 @@ export async function changeUserAccess(input: { actorUserId: string; userId: str
|
|
|
74
76
|
if (input.userId === input.actorUserId) throw new ValidationError("you cannot change your own role or status; ask another admin");
|
|
75
77
|
const before = getUser(input.userId);
|
|
76
78
|
if (!before) throw new ValidationError("user not found");
|
|
77
|
-
const user =
|
|
79
|
+
const user = (await updateUserAccessAsync({ id: input.userId, role: input.role, status: input.status }));
|
|
78
80
|
if (user.role !== before.role || user.status !== before.status) await revokeUserSessions(user.id);
|
|
79
81
|
return user;
|
|
80
82
|
}
|
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
linkArtifactAsync,
|
|
4
|
-
updateUserProfile,
|
|
5
|
-
} from "../db";
|
|
1
|
+
import { updateUserProfileAsync } from "../db/runtime-tail-async";
|
|
2
|
+
import { deleteArtifactLinksForEntityAsync, linkArtifactAsync } from "../db";
|
|
6
3
|
import { normalizeDigest } from "../artifact-storage";
|
|
7
4
|
import { storeArtifactWithReference } from "./artifact-write";
|
|
8
5
|
import { bytesToStream } from "../http-body";
|
|
9
6
|
import { emitRelayEvent } from "../events";
|
|
10
7
|
import type { Artifact, User } from "../types";
|
|
11
8
|
|
|
12
|
-
export function updateUserDisplayName(input: { userId: string; displayName: string }): User {
|
|
13
|
-
const user =
|
|
9
|
+
export async function updateUserDisplayName(input: { userId: string; displayName: string }): Promise<User> {
|
|
10
|
+
const user = (await updateUserProfileAsync({ id: input.userId, displayName: input.displayName }));
|
|
14
11
|
emitRelayEvent({ type: "user.updated", source: "server", subject: user.id, data: { user } });
|
|
15
12
|
return user;
|
|
16
13
|
}
|
|
@@ -44,14 +41,14 @@ export async function storeUserAvatar(input: {
|
|
|
44
41
|
title: input.filename ?? "avatar",
|
|
45
42
|
createdBy: input.actor,
|
|
46
43
|
});
|
|
47
|
-
const user =
|
|
44
|
+
const user = (await updateUserProfileAsync({ id: input.userId, avatarArtifactId: artifact.id }));
|
|
48
45
|
emitRelayEvent({ type: "user.avatar.updated", source: "server", subject: user.id, data: { user, artifact } });
|
|
49
46
|
return { user, artifact };
|
|
50
47
|
}
|
|
51
48
|
|
|
52
49
|
export async function removeUserAvatar(input: { userId: string }): Promise<User> {
|
|
53
50
|
await deleteArtifactLinksForEntityAsync("user", input.userId);
|
|
54
|
-
const user =
|
|
51
|
+
const user = (await updateUserProfileAsync({ id: input.userId, avatarArtifactId: null }));
|
|
55
52
|
emitRelayEvent({ type: "user.avatar.removed", source: "server", subject: user.id, data: { user } });
|
|
56
53
|
return user;
|
|
57
54
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { getGateway } from "../db/gateway";
|
|
2
|
+
import { takeDeferredLineageReports, restoreDeferredLineageReports } from "../db/deferred-lineage-reports";
|
|
1
3
|
// #1084 (Part D of #1079) — the work-window finality service. A "work-final" is a RELAY-LEVEL
|
|
2
4
|
// mark anchored to explicit completion signals the platform already owns: `relay_workspace_ready`
|
|
3
5
|
// (src/mcp/tools-workspace.ts), a task's execution status reaching `done` (src/services/task-entity.ts),
|
|
@@ -12,22 +14,32 @@
|
|
|
12
14
|
// the original already-rendered session captures as `message.finalized` (so a bubble the dashboard
|
|
13
15
|
// showed as ordinary mid-turn narration can be retroactively upgraded once its window closes).
|
|
14
16
|
import type { MessageWorkFinalitySource } from "@humwick/sdk";
|
|
15
|
-
import {
|
|
17
|
+
import { applyLineageReportEffects, type WorkFinalFlushResult } from "../db/messages.ts";
|
|
16
18
|
import { emitMessageFinalized, emitNewMessages } from "../sse.ts";
|
|
17
19
|
|
|
18
|
-
export function markAgentWorkFinal(
|
|
20
|
+
export async function markAgentWorkFinal(
|
|
19
21
|
agentId: string | undefined | null,
|
|
20
22
|
source: MessageWorkFinalitySource,
|
|
21
23
|
options: { includeStaleCompletedFinals?: boolean } = {},
|
|
22
|
-
): WorkFinalFlushResult {
|
|
24
|
+
): Promise<WorkFinalFlushResult> {
|
|
23
25
|
if (!agentId) return { forwarded: [], finalizedOriginals: [] };
|
|
24
|
-
const
|
|
26
|
+
const queues = takeDeferredLineageReports([agentId]);
|
|
27
|
+
const queue = queues[agentId];
|
|
28
|
+
if (!queue?.length) return { forwarded: [], finalizedOriginals: [] };
|
|
29
|
+
let result: WorkFinalFlushResult;
|
|
30
|
+
try {
|
|
31
|
+
result = await getGateway().write("messages.flushWorkFinal", { agentId, queue, source, options }) as unknown as WorkFinalFlushResult;
|
|
32
|
+
} catch (error) {
|
|
33
|
+
restoreDeferredLineageReports(queues);
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
25
36
|
emitWorkFinalityResult(result);
|
|
26
37
|
return result;
|
|
27
38
|
}
|
|
28
39
|
|
|
29
40
|
/** Publish finality only after its synchronous or gateway-routed DB mutation commits. */
|
|
30
41
|
export function emitWorkFinalityResult(result: WorkFinalFlushResult): void {
|
|
42
|
+
for (const report of result.forwarded) applyLineageReportEffects(report);
|
|
31
43
|
const created = result.forwarded.filter((entry) => entry.created).map((entry) => entry.message);
|
|
32
44
|
if (created.length > 0) emitNewMessages(created);
|
|
33
45
|
for (const original of result.finalizedOriginals) emitMessageFinalized(original);
|
|
@@ -118,9 +118,10 @@ async function receiptFor(prepared: PreparedWorkflowRun, started: WorkflowRunVie
|
|
|
118
118
|
const agent = run.agents.filter((entry) => entry.step === step.name).at(-1);
|
|
119
119
|
const profile = step.pin?.profile ?? read.workflow.roles.get(step.role)?.extends;
|
|
120
120
|
const label = `workflow ${run.workflow} (${run.source ?? "project"}) run ${run.runId}`;
|
|
121
|
+
const progress = run.awaiting?.slot ? `is waiting for a slot on ${run.awaiting.slot.host} before spawning` : agent ? "spawned" : "is spawning";
|
|
121
122
|
const note = started.existing
|
|
122
123
|
? `T${task.id} already runs ${label}, which owns its builder: no builder was spawned. relay_workflow_get shows its agents${cause.policyRule ? "" : "; pass workflow:false to spawn beside it"}.`
|
|
123
|
-
: `${cause.why}, so Relay started ${label}; its run ${
|
|
124
|
+
: `${cause.why}, so Relay started ${label}; its run ${progress} the builder (role ${step.role} on profile ${profile}). You hear back on a step spawn failure, an escalation or the result.${cause.policyRule ? "" : " Pass workflow:false to spawn by hand."}`;
|
|
124
125
|
const warnings = [...(started.warnings ?? []), ...(ignored.length ? [`the run sets these for its own agents, so ${ignored.join(", ")} went unused`] : [])];
|
|
125
126
|
return {
|
|
126
127
|
ok: true,
|