@sema-agent/server 1.323.0 → 2.0.1
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/dist/approval-hmac.d.ts +40 -0
- package/dist/approval-hmac.js +67 -0
- package/dist/approval.d.ts +31 -0
- package/dist/approval.js +72 -4
- package/dist/audit.d.ts +26 -0
- package/dist/audit.js +40 -1
- package/dist/auth-bridge.d.ts +10 -0
- package/dist/auth-bridge.js +47 -4
- package/dist/auth-keys.d.ts +19 -0
- package/dist/auth-keys.js +13 -0
- package/dist/bake-runner/main.d.ts +5 -0
- package/dist/bake-runner/main.js +43 -4
- package/dist/bake-runner/protocol.d.ts +57 -0
- package/dist/bake-runner/protocol.js +58 -3
- package/dist/bake-runner/runner.d.ts +54 -0
- package/dist/bake-runner/runner.js +87 -5
- package/dist/bench/l8/artifact.d.ts +63 -0
- package/dist/bench/l8/artifact.js +61 -2
- package/dist/bench/l8/escape.d.ts +36 -0
- package/dist/bench/l8/escape.js +25 -0
- package/dist/bench/l8/index.d.ts +14 -0
- package/dist/bench/l8/index.js +14 -0
- package/dist/bench/l8/probes.d.ts +74 -0
- package/dist/bench/l8/probes.js +60 -0
- package/dist/bench/l8/run-probes.d.ts +43 -0
- package/dist/bench/l8/run-probes.js +45 -2
- package/dist/bench/s1/arms.d.ts +126 -0
- package/dist/bench/s1/arms.js +188 -13
- package/dist/bench/s1/live-deps.d.ts +66 -0
- package/dist/bench/s1/live-deps.js +249 -11
- package/dist/bench/s1/oracle.d.ts +49 -0
- package/dist/bench/s1/oracle.js +36 -1
- package/dist/bench/s1/repair-oracle-adapter.d.ts +33 -0
- package/dist/bench/s1/repair-oracle-adapter.js +30 -0
- package/dist/bench/s1/reviewer.d.ts +38 -0
- package/dist/bench/s1/reviewer.js +34 -0
- package/dist/bench/s1/row.d.ts +77 -0
- package/dist/bench/s1/row.js +62 -0
- package/dist/bench/s1/run-firm.d.ts +47 -0
- package/dist/bench/s1/run-firm.js +70 -4
- package/dist/bench/s1/runner-ctx.d.ts +71 -0
- package/dist/bench/s1/runner-ctx.js +36 -0
- package/dist/bench/s1/tasks.d.ts +108 -0
- package/dist/bench/s1/tasks.js +115 -0
- package/dist/boot/budget-tracing.d.ts +48 -0
- package/dist/boot/budget-tracing.js +86 -0
- package/dist/boot/config-center.d.ts +62 -0
- package/dist/boot/config-center.js +995 -0
- package/dist/boot/coordinators.d.ts +33 -0
- package/dist/boot/coordinators.js +97 -0
- package/dist/boot/execution-env.d.ts +26 -0
- package/dist/boot/execution-env.js +370 -0
- package/dist/boot/leader.d.ts +27 -0
- package/dist/boot/leader.js +81 -0
- package/dist/boot/reapers.d.ts +53 -0
- package/dist/boot/reapers.js +252 -0
- package/dist/boot/resolve-spec.d.ts +70 -0
- package/dist/boot/resolve-spec.js +1072 -0
- package/dist/boot/runner-deps.d.ts +101 -0
- package/dist/boot/runner-deps.js +343 -0
- package/dist/boot/runtime-caps.d.ts +21 -0
- package/dist/boot/runtime-caps.js +62 -0
- package/dist/boot/session-faces.d.ts +57 -0
- package/dist/boot/session-faces.js +157 -0
- package/dist/boot/shutdown.d.ts +50 -0
- package/dist/boot/shutdown.js +129 -0
- package/dist/boot/stores.d.ts +32 -0
- package/dist/boot/stores.js +361 -0
- package/dist/boot/workflow-orchestration.d.ts +46 -0
- package/dist/boot/workflow-orchestration.js +150 -0
- package/dist/boot-reclaim.d.ts +19 -0
- package/dist/boot-reclaim.js +20 -0
- package/dist/brain.d.ts +25 -0
- package/dist/brain.js +61 -1
- package/dist/budget.d.ts +66 -0
- package/dist/budget.js +128 -4
- package/dist/capabilities/builtin-tools.d.ts +3 -0
- package/dist/capabilities/builtin-tools.js +3 -0
- package/dist/capabilities/center-plugins.d.ts +17 -1
- package/dist/capabilities/center-plugins.js +45 -4
- package/dist/capabilities/center-prompts.d.ts +46 -0
- package/dist/capabilities/center-prompts.js +57 -3
- package/dist/capabilities/code-review-council.d.ts +28 -0
- package/dist/capabilities/code-review-council.js +40 -7
- package/dist/capabilities/collab-wire.d.ts +2 -0
- package/dist/capabilities/collab-workflows.d.ts +7 -0
- package/dist/capabilities/collab-workflows.js +44 -1
- package/dist/capabilities/oa-tools.d.ts +6 -0
- package/dist/capabilities/oa-tools.js +10 -0
- package/dist/capabilities/prompt.d.ts +14 -0
- package/dist/capabilities/prompt.js +14 -0
- package/dist/capabilities/prompts/code-review.d.ts +14 -0
- package/dist/capabilities/prompts/code-review.js +14 -0
- package/dist/capabilities/prompts/identity.d.ts +6 -0
- package/dist/capabilities/prompts/identity.js +6 -0
- package/dist/capabilities/prompts/team.d.ts +4 -0
- package/dist/capabilities/prompts/team.js +4 -0
- package/dist/capabilities/repo-tools.d.ts +11 -0
- package/dist/capabilities/repo-tools.js +8 -0
- package/dist/capabilities/sandbox-file-send.d.ts +95 -0
- package/dist/capabilities/sandbox-file-send.js +91 -3
- package/dist/capabilities/scenarios.d.ts +130 -0
- package/dist/capabilities/scenarios.js +180 -7
- package/dist/capabilities/select-environment-tool.d.ts +7 -0
- package/dist/capabilities/select-environment-tool.js +30 -0
- package/dist/capabilities/send-user-file-tool.d.ts +33 -0
- package/dist/capabilities/send-user-file-tool.js +65 -1
- package/dist/capabilities/skills.d.ts +29 -0
- package/dist/capabilities/skills.js +13 -2
- package/dist/capabilities/team.d.ts +22 -0
- package/dist/capabilities/team.js +22 -2
- package/dist/capabilities/tool-defer.d.ts +26 -0
- package/dist/capabilities/tool-defer.js +3 -0
- package/dist/config-center/apply-effective.d.ts +65 -0
- package/dist/config-center/apply-effective.js +204 -12
- package/dist/config-center/http-client.d.ts +34 -0
- package/dist/config-center/http-client.js +68 -0
- package/dist/config-center/restart-signal.d.ts +22 -0
- package/dist/config-center/restart-signal.js +39 -0
- package/dist/config-center/skills-mcp.d.ts +20 -1
- package/dist/config-center/skills-mcp.js +42 -7
- package/dist/config-center/types.d.ts +92 -1
- package/dist/config-lkg.d.ts +12 -0
- package/dist/config-lkg.js +41 -0
- package/dist/config-provider.d.ts +78 -0
- package/dist/config-provider.js +165 -2
- package/dist/config-types.d.ts +585 -1
- package/dist/config.d.ts +78 -2
- package/dist/config.js +874 -301
- package/dist/degenerate-instrument.d.ts +38 -0
- package/dist/degenerate-instrument.js +55 -3
- package/dist/elicitation.d.ts +39 -0
- package/dist/elicitation.js +87 -3
- package/dist/env-facts.d.ts +61 -0
- package/dist/env-facts.js +73 -7
- package/dist/fleet/fleet-bus.d.ts +185 -0
- package/dist/fleet/fleet-bus.js +333 -27
- package/dist/fleet/subagent-tail-bus.d.ts +11 -0
- package/dist/fleet/subagent-tail-bus.js +33 -2
- package/dist/fleet-client.d.ts +38 -0
- package/dist/fleet-client.js +44 -9
- package/dist/fleet-lease.d.ts +46 -0
- package/dist/fleet-lease.js +73 -5
- package/dist/hooks/branch-transcript.d.ts +8 -0
- package/dist/hooks/branch-transcript.js +33 -0
- package/dist/hooks/cc-stop-prompt.d.ts +42 -0
- package/dist/hooks/cc-stop-prompt.js +43 -1
- package/dist/hooks/hook-llm.d.ts +15 -0
- package/dist/hooks/hook-llm.js +47 -0
- package/dist/hooks/hook-runner.d.ts +89 -0
- package/dist/hooks/hook-runner.js +327 -18
- package/dist/http/idempotency.d.ts +31 -0
- package/dist/http/idempotency.js +34 -0
- package/dist/http/principal-gate.d.ts +17 -0
- package/dist/http/principal-gate.js +35 -3
- package/dist/http/route-ctx.d.ts +36 -0
- package/dist/http/routes/approvals-assistant.d.ts +25 -0
- package/dist/http/routes/approvals-assistant.js +253 -23
- package/dist/http/routes/attachments.js +22 -4
- package/dist/http/routes/capabilities.d.ts +7 -0
- package/dist/http/routes/capabilities.js +202 -1
- package/dist/http/routes/fleet.d.ts +6 -0
- package/dist/http/routes/fleet.js +94 -5
- package/dist/http/routes/images.d.ts +17 -0
- package/dist/http/routes/images.js +222 -14
- package/dist/http/routes/leader.d.ts +7 -0
- package/dist/http/routes/leader.js +10 -0
- package/dist/http/routes/memory-policy.d.ts +6 -0
- package/dist/http/routes/memory-policy.js +38 -6
- package/dist/http/routes/notify-wake.d.ts +10 -0
- package/dist/http/routes/notify-wake.js +19 -3
- package/dist/http/routes/observability.d.ts +6 -0
- package/dist/http/routes/observability.js +19 -2
- package/dist/http/routes/runs.d.ts +15 -0
- package/dist/http/routes/runs.js +464 -41
- package/dist/http/routes/session-sync.d.ts +15 -0
- package/dist/http/routes/session-sync.js +229 -19
- package/dist/http/routes/sessions-list.d.ts +6 -0
- package/dist/http/routes/sessions-list.js +35 -4
- package/dist/http/routes/sessions.d.ts +14 -0
- package/dist/http/routes/sessions.js +176 -31
- package/dist/http/routes/side-query.d.ts +8 -0
- package/dist/http/routes/side-query.js +26 -1
- package/dist/http/routes/tasks.d.ts +18 -0
- package/dist/http/routes/tasks.js +501 -38
- package/dist/http/routes/trace-usage.d.ts +10 -0
- package/dist/http/routes/trace-usage.js +66 -14
- package/dist/http/routes/workflows.d.ts +12 -0
- package/dist/http/routes/workflows.js +180 -19
- package/dist/http/run-meta.d.ts +11 -0
- package/dist/http/run-meta.js +6 -0
- package/dist/http/send.d.ts +19 -0
- package/dist/http/send.js +11 -0
- package/dist/http/server.d.ts +255 -0
- package/dist/http/server.js +921 -75
- package/dist/http/sse-log.d.ts +15 -0
- package/dist/http/sse-log.js +16 -0
- package/dist/http/tar.d.ts +9 -0
- package/dist/http/tar.js +21 -5
- package/dist/http/wire-gate.d.ts +9 -0
- package/dist/http/wire-gate.js +9 -0
- package/dist/http/wire-types.d.ts +187 -0
- package/dist/http/workspace-content.d.ts +8 -0
- package/dist/http/workspace-content.js +10 -0
- package/dist/images/bake-validate.d.ts +63 -0
- package/dist/images/bake-validate.js +71 -1
- package/dist/images/manifest.d.ts +19 -0
- package/dist/images/manifest.js +6 -1
- package/dist/index.d.ts +1 -5
- package/dist/index.js +10 -5
- package/dist/key-resolver.d.ts +23 -0
- package/dist/key-resolver.js +27 -2
- package/dist/leader/diffout.d.ts +23 -0
- package/dist/leader/diffout.js +8 -1
- package/dist/leader/diffup.d.ts +32 -0
- package/dist/leader/diffup.js +43 -0
- package/dist/leader/endpoint.d.ts +20 -0
- package/dist/leader/endpoint.js +25 -2
- package/dist/leader/fanout.d.ts +60 -0
- package/dist/leader/fanout.js +29 -3
- package/dist/leader/grader-env-factory.d.ts +74 -0
- package/dist/leader/grader-env-factory.js +62 -3
- package/dist/leader/leader.d.ts +97 -0
- package/dist/leader/leader.js +151 -4
- package/dist/leader/merge.d.ts +94 -0
- package/dist/leader/merge.js +112 -9
- package/dist/leader/planner.d.ts +52 -0
- package/dist/leader/planner.js +47 -3
- package/dist/leader/repair-oracle.d.ts +61 -0
- package/dist/leader/repair-oracle.js +59 -1
- package/dist/leader/repair-wire.d.ts +78 -0
- package/dist/leader/repair-wire.js +79 -2
- package/dist/leader/wire.d.ts +79 -0
- package/dist/leader/wire.js +255 -8
- package/dist/lsp/e2b-bridge.d.ts +16 -0
- package/dist/lsp/e2b-bridge.js +32 -4
- package/dist/lsp/e2b-manager.d.ts +10 -0
- package/dist/lsp/e2b-manager.js +18 -2
- package/dist/lsp/lsp-frames.d.ts +12 -0
- package/dist/lsp/lsp-frames.js +12 -0
- package/dist/lsp/manager.d.ts +12 -0
- package/dist/lsp/manager.js +35 -2
- package/dist/lsp/types.d.ts +10 -0
- package/dist/lsp/ws-transport.d.ts +12 -0
- package/dist/lsp/ws-transport.js +53 -5
- package/dist/lsp-evict.d.ts +14 -0
- package/dist/lsp-evict.js +15 -1
- package/dist/main.js +530 -2129
- package/dist/memory-export.d.ts +1 -0
- package/dist/memory-export.js +4 -0
- package/dist/memory-scope.d.ts +49 -0
- package/dist/memory-scope.js +87 -4
- package/dist/memory-sync-client.d.ts +24 -0
- package/dist/memory-sync-client.js +41 -2
- package/dist/memory-sync.d.ts +23 -0
- package/dist/memory-sync.js +70 -1
- package/dist/model-select.d.ts +39 -0
- package/dist/model-select.js +42 -1
- package/dist/observability/cost-quota.d.ts +20 -0
- package/dist/observability/cost-quota.js +4 -0
- package/dist/observability/cost-taxonomy.d.ts +45 -0
- package/dist/observability/cost-taxonomy.js +34 -0
- package/dist/observability/logger.d.ts +1 -0
- package/dist/observability/logger.js +6 -0
- package/dist/observability/metrics.d.ts +13 -0
- package/dist/observability/metrics.js +63 -0
- package/dist/observability/otel-exporter.d.ts +9 -0
- package/dist/observability/otel-exporter.js +11 -3
- package/dist/observability/principal-context.d.ts +9 -0
- package/dist/observability/principal-context.js +9 -0
- package/dist/observability/prompt-manifest.d.ts +37 -0
- package/dist/observability/prompt-manifest.js +35 -1
- package/dist/observability/rate-limit.d.ts +10 -0
- package/dist/observability/rate-limit.js +1 -0
- package/dist/observability/tool-trace.d.ts +24 -0
- package/dist/observability/tool-trace.js +41 -1
- package/dist/orchestration/hardened-vm-runner.d.ts +107 -0
- package/dist/orchestration/hardened-vm-runner.js +119 -4
- package/dist/orchestration/hardened-vm-worker-runner.d.ts +14 -0
- package/dist/orchestration/hardened-vm-worker-runner.js +26 -1
- package/dist/orchestration/hardened-vm-worker.js +27 -0
- package/dist/orchestration/subagent-steer.d.ts +44 -0
- package/dist/orchestration/subagent-steer.js +45 -1
- package/dist/orchestration/workflow-agent-steer.d.ts +54 -0
- package/dist/orchestration/workflow-agent-steer.js +80 -1
- package/dist/orchestration/workflow-completion-inbox.d.ts +189 -2
- package/dist/orchestration/workflow-completion-inbox.js +283 -32
- package/dist/orchestration/workflow-notify-journal.d.ts +132 -1
- package/dist/orchestration/workflow-notify-journal.js +187 -13
- package/dist/parked-decide.d.ts +61 -0
- package/dist/parked-decide.js +58 -1
- package/dist/per-task-image.d.ts +54 -0
- package/dist/per-task-image.js +41 -0
- package/dist/plan-cache-probe.d.ts +35 -0
- package/dist/plan-cache-probe.js +18 -3
- package/dist/plugins/approval-exemption-store.d.ts +7 -0
- package/dist/plugins/approval-exemption-store.js +28 -2
- package/dist/plugins/approval-store-sql.d.ts +64 -0
- package/dist/plugins/approval-store-sql.js +32 -0
- package/dist/plugins/background-agent-store-sql.d.ts +55 -0
- package/dist/plugins/background-agent-store-sql.js +91 -4
- package/dist/plugins/background-shell-support.d.ts +98 -0
- package/dist/plugins/background-shell-support.js +117 -13
- package/dist/plugins/blob-backend.d.ts +104 -0
- package/dist/plugins/blob-backend.js +163 -6
- package/dist/plugins/breaker-state-sql.d.ts +65 -1
- package/dist/plugins/breaker-state-sql.js +46 -8
- package/dist/plugins/caching-session-store.d.ts +42 -0
- package/dist/plugins/caching-session-store.js +63 -3
- package/dist/plugins/checkpoint-store-sql.d.ts +179 -0
- package/dist/plugins/checkpoint-store-sql.js +239 -11
- package/dist/plugins/file-outcome-sink.d.ts +4 -0
- package/dist/plugins/file-outcome-sink.js +9 -0
- package/dist/plugins/file-resume-anchor-store.d.ts +20 -0
- package/dist/plugins/file-resume-anchor-store.js +43 -4
- package/dist/plugins/file-run-store.d.ts +90 -0
- package/dist/plugins/file-run-store.js +145 -13
- package/dist/plugins/file-snapshot-store-sql.d.ts +133 -1
- package/dist/plugins/file-snapshot-store-sql.js +178 -10
- package/dist/plugins/file-workflow-journal-store.d.ts +10 -0
- package/dist/plugins/file-workflow-journal-store.js +10 -0
- package/dist/plugins/fork-routing-session-store.d.ts +47 -0
- package/dist/plugins/fork-routing-session-store.js +51 -3
- package/dist/plugins/host-platform.d.ts +69 -0
- package/dist/plugins/host-platform.js +81 -2
- package/dist/plugins/image-bake-store-sql.d.ts +136 -1
- package/dist/plugins/image-bake-store-sql.js +241 -7
- package/dist/plugins/image-index-sql.d.ts +46 -0
- package/dist/plugins/image-index-sql.js +113 -4
- package/dist/plugins/k8s-bg-scripts.d.ts +34 -0
- package/dist/plugins/k8s-bg-scripts.js +88 -4
- package/dist/plugins/k8s-exec-protocol.d.ts +33 -0
- package/dist/plugins/k8s-exec-protocol.js +41 -0
- package/dist/plugins/local-checkpoint-store.d.ts +40 -0
- package/dist/plugins/local-checkpoint-store.js +86 -7
- package/dist/plugins/local-session-store.d.ts +157 -0
- package/dist/plugins/local-session-store.js +289 -34
- package/dist/plugins/local-task-attachment-store.js +14 -2
- package/dist/plugins/mailbox-store-sql.d.ts +38 -0
- package/dist/plugins/mailbox-store-sql.js +39 -6
- package/dist/plugins/memory-engine-pg.d.ts +30 -0
- package/dist/plugins/memory-engine-pg.js +122 -4
- package/dist/plugins/memory-engine-tidb.d.ts +26 -0
- package/dist/plugins/memory-engine-tidb.js +115 -5
- package/dist/plugins/memory-engine-vector-util.d.ts +5 -0
- package/dist/plugins/memory-engine-vector-util.js +5 -0
- package/dist/plugins/memory-resume-anchor-store.d.ts +8 -0
- package/dist/plugins/memory-resume-anchor-store.js +17 -0
- package/dist/plugins/memory-run-store.d.ts +63 -0
- package/dist/plugins/memory-run-store.js +73 -8
- package/dist/plugins/memory-session-policy-store.d.ts +17 -0
- package/dist/plugins/memory-session-policy-store.js +17 -0
- package/dist/plugins/memory-sync-store-pg.d.ts +42 -0
- package/dist/plugins/memory-sync-store-pg.js +45 -2
- package/dist/plugins/memory-sync-store-tidb.d.ts +9 -0
- package/dist/plugins/memory-sync-store-tidb.js +31 -1
- package/dist/plugins/outcome-ledger-sql.d.ts +63 -0
- package/dist/plugins/outcome-ledger-sql.js +97 -3
- package/dist/plugins/pg-approval-store.d.ts +7 -0
- package/dist/plugins/pg-approval-store.js +7 -0
- package/dist/plugins/pg-breaker-state.d.ts +6 -0
- package/dist/plugins/pg-breaker-state.js +6 -0
- package/dist/plugins/pg-checkpoint-store.d.ts +8 -0
- package/dist/plugins/pg-checkpoint-store.js +8 -0
- package/dist/plugins/pg-cost-quota.d.ts +17 -0
- package/dist/plugins/pg-cost-quota.js +6 -0
- package/dist/plugins/pg-file-snapshot-store.d.ts +6 -0
- package/dist/plugins/pg-file-snapshot-store.js +6 -0
- package/dist/plugins/pg-image-bake.d.ts +9 -0
- package/dist/plugins/pg-image-bake.js +9 -0
- package/dist/plugins/pg-image-index.d.ts +9 -0
- package/dist/plugins/pg-image-index.js +9 -0
- package/dist/plugins/pg-outcome-ledger.d.ts +9 -0
- package/dist/plugins/pg-outcome-ledger.js +9 -0
- package/dist/plugins/pg-pool.d.ts +50 -0
- package/dist/plugins/pg-pool.js +78 -0
- package/dist/plugins/pg-query.d.ts +11 -0
- package/dist/plugins/pg-rate-limiter.d.ts +23 -0
- package/dist/plugins/pg-rate-limiter.js +12 -2
- package/dist/plugins/pg-resume-anchor-store.d.ts +5 -0
- package/dist/plugins/pg-resume-anchor-store.js +5 -0
- package/dist/plugins/pg-run-store.d.ts +7 -0
- package/dist/plugins/pg-run-store.js +7 -0
- package/dist/plugins/pg-safe-json.d.ts +13 -0
- package/dist/plugins/pg-safe-json.js +40 -4
- package/dist/plugins/pg-session-policy-store.d.ts +5 -0
- package/dist/plugins/pg-session-policy-store.js +5 -0
- package/dist/plugins/pg-session-storage.d.ts +72 -0
- package/dist/plugins/pg-session-storage.js +152 -20
- package/dist/plugins/pg-session-store.d.ts +10 -0
- package/dist/plugins/pg-session-store.js +10 -0
- package/dist/plugins/pg-tool-result-store.d.ts +7 -0
- package/dist/plugins/pg-tool-result-store.js +7 -0
- package/dist/plugins/pg-workflow-journal-store.d.ts +7 -0
- package/dist/plugins/pg-workflow-journal-store.js +7 -0
- package/dist/plugins/pg-workflow-run-store.d.ts +7 -0
- package/dist/plugins/pg-workflow-run-store.js +7 -0
- package/dist/plugins/posix-shell-fs.d.ts +3 -0
- package/dist/plugins/posix-shell-fs.js +31 -1
- package/dist/plugins/remote-env-adb.d.ts +25 -0
- package/dist/plugins/remote-env-adb.js +95 -12
- package/dist/plugins/remote-env-e2b.d.ts +146 -0
- package/dist/plugins/remote-env-e2b.js +371 -40
- package/dist/plugins/remote-env-file-error.d.ts +34 -0
- package/dist/plugins/remote-env-file-error.js +37 -0
- package/dist/plugins/remote-env-host.d.ts +151 -0
- package/dist/plugins/remote-env-host.js +468 -70
- package/dist/plugins/remote-env-k8s.d.ts +135 -0
- package/dist/plugins/remote-env-k8s.js +318 -32
- package/dist/plugins/remote-env-local-docker.d.ts +65 -0
- package/dist/plugins/remote-env-local-docker.js +149 -24
- package/dist/plugins/remote-env-ssh.d.ts +35 -0
- package/dist/plugins/remote-env-ssh.js +117 -20
- package/dist/plugins/remote-scratchpad.d.ts +38 -0
- package/dist/plugins/remote-scratchpad.js +32 -2
- package/dist/plugins/remote-shell.d.ts +22 -0
- package/dist/plugins/remote-shell.js +27 -1
- package/dist/plugins/resume-anchor-store-sql.d.ts +28 -0
- package/dist/plugins/resume-anchor-store-sql.js +11 -0
- package/dist/plugins/roster-store-sql.d.ts +27 -0
- package/dist/plugins/roster-store-sql.js +54 -0
- package/dist/plugins/run-store-sql.d.ts +194 -0
- package/dist/plugins/run-store-sql.js +210 -9
- package/dist/plugins/s3-presign.d.ts +20 -0
- package/dist/plugins/s3-presign.js +49 -2
- package/dist/plugins/scheduler-support.d.ts +12 -0
- package/dist/plugins/scheduler-support.js +75 -3
- package/dist/plugins/send-file-ledger.d.ts +15 -0
- package/dist/plugins/send-file-ledger.js +53 -4
- package/dist/plugins/send-user-file.d.ts +77 -0
- package/dist/plugins/send-user-file.js +92 -4
- package/dist/plugins/session-policy-store-sql.d.ts +15 -0
- package/dist/plugins/session-policy-store-sql.js +75 -3
- package/dist/plugins/session-store.d.ts +45 -0
- package/dist/plugins/session-store.js +51 -0
- package/dist/plugins/sql-driver.d.ts +77 -0
- package/dist/plugins/sql-driver.js +4 -0
- package/dist/plugins/sql-escape.d.ts +8 -0
- package/dist/plugins/sql-escape.js +8 -0
- package/dist/plugins/sql-row-helpers.d.ts +25 -0
- package/dist/plugins/sql-row-helpers.js +25 -0
- package/dist/plugins/store-backend.d.ts +103 -0
- package/dist/plugins/store-backend.js +120 -27
- package/dist/plugins/store-contracts.d.ts +62 -0
- package/dist/plugins/store-contracts.js +3 -0
- package/dist/plugins/task-attachment-store.d.ts +60 -0
- package/dist/plugins/task-attachment-store.js +49 -3
- package/dist/plugins/task-list-store-sql.d.ts +25 -0
- package/dist/plugins/task-list-store-sql.js +21 -0
- package/dist/plugins/tidb-approval-store.d.ts +6 -0
- package/dist/plugins/tidb-approval-store.js +6 -0
- package/dist/plugins/tidb-breaker-state.d.ts +5 -0
- package/dist/plugins/tidb-breaker-state.js +5 -0
- package/dist/plugins/tidb-checkpoint-store.d.ts +7 -0
- package/dist/plugins/tidb-checkpoint-store.js +7 -0
- package/dist/plugins/tidb-cost-quota.d.ts +6 -0
- package/dist/plugins/tidb-cost-quota.js +1 -0
- package/dist/plugins/tidb-file-snapshot-store.d.ts +6 -0
- package/dist/plugins/tidb-file-snapshot-store.js +6 -0
- package/dist/plugins/tidb-image-bake.d.ts +8 -0
- package/dist/plugins/tidb-image-bake.js +8 -0
- package/dist/plugins/tidb-image-index.d.ts +8 -0
- package/dist/plugins/tidb-image-index.js +8 -0
- package/dist/plugins/tidb-outcome-ledger.d.ts +10 -0
- package/dist/plugins/tidb-outcome-ledger.js +10 -0
- package/dist/plugins/tidb-pool.d.ts +56 -0
- package/dist/plugins/tidb-pool.js +181 -1
- package/dist/plugins/tidb-rate-limiter.d.ts +11 -0
- package/dist/plugins/tidb-rate-limiter.js +5 -2
- package/dist/plugins/tidb-resume-anchor-store.d.ts +5 -0
- package/dist/plugins/tidb-resume-anchor-store.js +5 -0
- package/dist/plugins/tidb-run-store.d.ts +7 -0
- package/dist/plugins/tidb-run-store.js +7 -0
- package/dist/plugins/tidb-session-policy-store.d.ts +5 -0
- package/dist/plugins/tidb-session-policy-store.js +5 -0
- package/dist/plugins/tidb-session-storage.d.ts +43 -0
- package/dist/plugins/tidb-session-storage.js +62 -3
- package/dist/plugins/tidb-session-store.d.ts +142 -0
- package/dist/plugins/tidb-session-store.js +264 -16
- package/dist/plugins/tidb-tool-result-store.d.ts +5 -0
- package/dist/plugins/tidb-tool-result-store.js +7 -0
- package/dist/plugins/tidb-workflow-journal-store.d.ts +7 -0
- package/dist/plugins/tidb-workflow-journal-store.js +7 -0
- package/dist/plugins/tidb-workflow-run-store.d.ts +8 -0
- package/dist/plugins/tidb-workflow-run-store.js +8 -0
- package/dist/plugins/tool-result-store-sql.d.ts +58 -0
- package/dist/plugins/tool-result-store-sql.js +38 -2
- package/dist/plugins/web-search.d.ts +35 -0
- package/dist/plugins/web-search.js +45 -9
- package/dist/plugins/workflow-journal-limits.d.ts +10 -0
- package/dist/plugins/workflow-journal-limits.js +10 -0
- package/dist/plugins/workflow-journal-store-sql.d.ts +78 -0
- package/dist/plugins/workflow-journal-store-sql.js +58 -2
- package/dist/plugins/workflow-run-store-sql.d.ts +84 -0
- package/dist/plugins/workflow-run-store-sql.js +84 -6
- package/dist/plugins/worktree-isolation.d.ts +45 -0
- package/dist/plugins/worktree-isolation.js +105 -6
- package/dist/plugins/write-behind-counter.d.ts +56 -1
- package/dist/plugins/write-behind-counter.js +53 -14
- package/dist/principal-jwt.d.ts +24 -0
- package/dist/principal-jwt.js +60 -5
- package/dist/project-memory.d.ts +22 -0
- package/dist/project-memory.js +144 -15
- package/dist/prompts-domain-validate.d.ts +28 -0
- package/dist/prompts-domain-validate.js +42 -1
- package/dist/question.d.ts +33 -0
- package/dist/question.js +69 -2
- package/dist/resource-suspend.d.ts +21 -0
- package/dist/resource-suspend.js +16 -0
- package/dist/router/route-orchestration.d.ts +78 -0
- package/dist/router/route-orchestration.js +77 -0
- package/dist/run-local.d.ts +32 -0
- package/dist/run-local.js +172 -8
- package/dist/runs.d.ts +189 -4
- package/dist/runs.js +465 -24
- package/dist/runtime-caps-resolver.d.ts +106 -0
- package/dist/runtime-caps-resolver.js +89 -4
- package/dist/runtime-governance.d.ts +84 -0
- package/dist/runtime-governance.js +127 -1
- package/dist/sandbox-pkg-source.d.ts +23 -0
- package/dist/sandbox-pkg-source.js +37 -0
- package/dist/sealed-key.d.ts +54 -0
- package/dist/sealed-key.js +68 -3
- package/dist/security.d.ts +108 -0
- package/dist/security.js +145 -6
- package/dist/sema-registry.d.ts +21 -0
- package/dist/sema-registry.js +21 -0
- package/dist/session-leaf-bus.d.ts +31 -0
- package/dist/session-leaf-bus.js +32 -0
- package/dist/session-sync-content.d.ts +46 -0
- package/dist/session-sync-content.js +47 -1
- package/dist/session-sync-kernel.d.ts +89 -0
- package/dist/session-sync-kernel.js +48 -3
- package/dist/session-sync.d.ts +125 -0
- package/dist/session-sync.js +181 -8
- package/dist/session-titler.d.ts +26 -0
- package/dist/session-titler.js +41 -7
- package/dist/session-watch.d.ts +53 -1
- package/dist/session-watch.js +73 -9
- package/dist/sighup-idle.d.ts +30 -0
- package/dist/sighup-idle.js +12 -1
- package/dist/spec-fields.d.ts +91 -3
- package/dist/spec-fields.js +116 -4
- package/dist/task-cwd.d.ts +50 -0
- package/dist/task-cwd.js +68 -1
- package/dist/task-mcp.d.ts +47 -0
- package/dist/task-mcp.js +29 -0
- package/dist/task-settings.d.ts +175 -0
- package/dist/task-settings.js +226 -11
- package/dist/task-workflow.d.ts +67 -0
- package/dist/task-workflow.js +75 -8
- package/dist/tool-approval.d.ts +91 -0
- package/dist/tool-approval.js +215 -0
- package/dist/trace/artifacts.d.ts +9 -0
- package/dist/trace/artifacts.js +30 -5
- package/dist/trace/core-keyset-guard.d.ts +18 -0
- package/dist/trace/ledger-sink.d.ts +43 -0
- package/dist/trace/ledger-sink.js +64 -5
- package/dist/trace/project.d.ts +133 -0
- package/dist/trace/project.js +189 -2
- package/dist/trace/redact.d.ts +19 -0
- package/dist/trace/redact.js +66 -9
- package/dist/usage-analytics.d.ts +26 -0
- package/dist/usage-analytics.js +26 -3
- package/package.json +1 -1
|
@@ -1,24 +1,51 @@
|
|
|
1
1
|
import type { WorkflowRun, WorkflowRunStore, WorkflowRunStatus, WorkflowRunSummary } from "@sema-agent/core";
|
|
2
2
|
import type { FleetEventBus } from "../fleet/fleet-bus.js";
|
|
3
|
+
/** A workflow completion notification's terminal payload — the bounded, redacted shape core's notifier seam
|
|
4
|
+
* carries, re-derivable from a durable {@link import("@sema-agent/core").WorkflowRun} on the recovery path. */
|
|
3
5
|
export interface WorkflowCompletionPayload {
|
|
4
6
|
runId: string;
|
|
5
7
|
status: "completed" | "failed";
|
|
8
|
+
/** Redacted + length-bounded — never host-internal paths/tokens (parity with core's `boundedSummary`). */
|
|
6
9
|
summary: string;
|
|
7
10
|
sourceTaskId?: string;
|
|
8
11
|
principal?: string;
|
|
12
|
+
/** core 1.208: the ORIGINATING session id, closed over by the Runner at RunWorkflow mount time —
|
|
13
|
+
* the lookup-free routing key for the completion inbox. Present on the LIVE notify path; ABSENT on the
|
|
14
|
+
* crash-recovery path (the durable `WorkflowRun` does not carry it — relayed to core to thread it there too,
|
|
15
|
+
* parity with `sourceTaskId`'s 1.187 story), where the delivery falls back to the `sourceTaskId` run-row lookup. */
|
|
9
16
|
originatingSessionId?: string;
|
|
10
17
|
}
|
|
18
|
+
/** The downstream delivery the journal guards (the deployment's real completion route — log+meter today, a full
|
|
19
|
+
* re-invoke into the originator's stream/inbox tomorrow). Idempotent on `runId` by contract; the journal still
|
|
20
|
+
* de-dups so it is invoked AT MOST once per run in steady state and at most one extra time across a crash. */
|
|
11
21
|
export type WorkflowCompletionDelivery = (p: WorkflowCompletionPayload) => Promise<void> | void;
|
|
22
|
+
/** One journal entry: the durable to-do record for a single run's completion notify. `acked` flips true ONLY
|
|
23
|
+
* after delivery succeeds (deliver-then-ack — a crash mid-delivery re-delivers on recovery). */
|
|
12
24
|
export interface WorkflowNotifyJournalEntry {
|
|
13
25
|
runId: string;
|
|
26
|
+
/** The run's scope (= creating principal) — needed to re-fetch / owner-attribute on recovery. */
|
|
14
27
|
scope: string;
|
|
28
|
+
/** Whether the completion has been DELIVERED to the downstream sink (and may be GC'd). */
|
|
15
29
|
acked: boolean;
|
|
30
|
+
/** The originating task id, threaded to the delivery (parity with core's notifier input). */
|
|
16
31
|
sourceTaskId?: string;
|
|
32
|
+
/** The originating principal, threaded to the delivery. */
|
|
17
33
|
principal?: string;
|
|
34
|
+
/** When the run was journaled (epoch ms) — for retention + observability. */
|
|
18
35
|
createdAt: number;
|
|
36
|
+
/** When the entry was acked (epoch ms) — set with `acked`. */
|
|
19
37
|
ackedAt?: number;
|
|
20
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* The pluggable durable seam for the notify journal. Intentionally tiny: record a started run, mark it acked,
|
|
41
|
+
* and enumerate the un-acked backlog the recovery sweep walks. `listPending` is the one method the
|
|
42
|
+
* `WorkflowRunStore` cannot provide (it has no cross-scope enumeration) and is the whole reason the journal is a
|
|
43
|
+
* separate store. A backend (the File default, or a follow-on TiDB/PG) implements this; the gate logic in
|
|
44
|
+
* {@link WorkflowNotifyGate} is backend-agnostic.
|
|
45
|
+
*/
|
|
21
46
|
export interface WorkflowNotifyJournalStore {
|
|
47
|
+
/** Record a STARTED run as pending-notify. Idempotent on `runId`: a second `record` for the same run is a
|
|
48
|
+
* no-op (it must NOT reset an already-`acked` entry back to pending — that would re-deliver forever). */
|
|
22
49
|
record(entry: {
|
|
23
50
|
runId: string;
|
|
24
51
|
scope: string;
|
|
@@ -26,11 +53,27 @@ export interface WorkflowNotifyJournalStore {
|
|
|
26
53
|
principal?: string;
|
|
27
54
|
createdAt: number;
|
|
28
55
|
}): Promise<void>;
|
|
56
|
+
/** Mark a run's completion DELIVERED. Idempotent: acking an already-acked or missing run is a no-op. */
|
|
29
57
|
ack(runId: string, ackedAt: number): Promise<void>;
|
|
58
|
+
/** Every NOT-`acked` entry — the recovery backlog. Order is unspecified (the sweep handles each independently). */
|
|
30
59
|
listPending(): Promise<WorkflowNotifyJournalEntry[]>;
|
|
60
|
+
/** Read one entry (`null` when absent) — lets the gate check `acked` before a live delivery (the dedup). */
|
|
31
61
|
get(runId: string): Promise<WorkflowNotifyJournalEntry | null>;
|
|
32
62
|
}
|
|
33
63
|
export declare function recoverySummary(status: WorkflowRunStatus, error?: string): string;
|
|
64
|
+
/**
|
|
65
|
+
* The AT-LEAST-ONCE completion-notify gate. Wrap the deployment's real delivery once at boot, then:
|
|
66
|
+
* - {@link buildNotifier} hands core a {@link import("@sema-agent/core").WorkflowCompletionNotifier} whose `notify`
|
|
67
|
+
* runs the LIVE deliver-then-ack path (the in-process at-most-once notify, now journaled into at-least-once).
|
|
68
|
+
* - {@link onWorkflowStart} journals a run the moment it starts (so a crash BEFORE its terminal notify still
|
|
69
|
+
* leaves a pending entry the recovery sweep finds). Call it from wherever the runId becomes known.
|
|
70
|
+
* - {@link recover} runs ONCE at boot: re-derives every pending run's terminal state from the
|
|
71
|
+
* {@link WorkflowRunStore} and re-delivers + acks the ones that are terminal.
|
|
72
|
+
*
|
|
73
|
+
* The gate is the SINGLE writer of `acked`, and dedups every delivery against the journal, so the wrapped
|
|
74
|
+
* delivery sink is invoked at most once per run in steady state (and at most one extra time across a crash —
|
|
75
|
+
* the at-least-once / idempotent contract).
|
|
76
|
+
*/
|
|
34
77
|
export declare class WorkflowNotifyGate {
|
|
35
78
|
private readonly journal;
|
|
36
79
|
private readonly runStore;
|
|
@@ -38,21 +81,60 @@ export declare class WorkflowNotifyGate {
|
|
|
38
81
|
private readonly opts;
|
|
39
82
|
constructor(journal: WorkflowNotifyJournalStore, runStore: WorkflowRunStore, deliver: WorkflowCompletionDelivery, opts?: {
|
|
40
83
|
now?: () => number;
|
|
84
|
+
/** Surfaced on a failure (so a transient downstream/journal error is observable, NOT swallowed silently —
|
|
85
|
+
* it stays pending + retries on the next recovery sweep). Never throws back into core's notify. `record` =
|
|
86
|
+
* the start-time journal write failed; `deliver` = the downstream delivery threw; `recover` = a per-entry
|
|
87
|
+
* recovery-sweep error. */
|
|
41
88
|
onError?: (stage: "record" | "deliver" | "recover", runId: string, err: unknown) => void;
|
|
42
89
|
});
|
|
43
90
|
private now;
|
|
91
|
+
/**
|
|
92
|
+
* Journal a STARTED run as pending-notify. Call this with the synchronous `runId` from `startWorkflow` /
|
|
93
|
+
* `run_workflow` — BEFORE the workflow can reach terminal — so a crash mid-run still leaves a recoverable
|
|
94
|
+
* to-do. Best-effort + idempotent: a journal throw is reported, never propagated (it must not break the
|
|
95
|
+
* workflow start; worst case the recovery sweep won't know about this run — degraded, not corrupting).
|
|
96
|
+
*/
|
|
44
97
|
onWorkflowStart(input: {
|
|
45
98
|
runId: string;
|
|
46
99
|
scope: string;
|
|
47
100
|
sourceTaskId?: string;
|
|
48
101
|
principal?: string;
|
|
49
102
|
}): Promise<void>;
|
|
103
|
+
/**
|
|
104
|
+
* The core-facing notifier. core calls this exactly once per run on terminal (its own at-most-once, runId-dedup
|
|
105
|
+
* `fire`). We turn it into at-least-once: ensure the run is journaled (the start hook may have been skipped),
|
|
106
|
+
* then deliver-then-ack. Idempotent: if the run is ALREADY acked (a recovery sweep beat us, or a duplicate
|
|
107
|
+
* core fire), skip — at most one delivery.
|
|
108
|
+
*/
|
|
50
109
|
buildNotifier(): {
|
|
51
110
|
notify: (input: WorkflowCompletionPayload & {
|
|
52
111
|
scope?: string;
|
|
53
112
|
}) => Promise<void>;
|
|
54
113
|
};
|
|
114
|
+
/**
|
|
115
|
+
* Deliver a run's completion exactly once (idempotent on the journal). Ensures the entry exists (record is a
|
|
116
|
+
* no-op if it already does), SKIPS if already acked, otherwise delivers THEN acks (a crash between deliver and
|
|
117
|
+
* ack re-delivers on recovery — at-least-once). A delivery throw is reported and re-thrown to the LIVE caller
|
|
118
|
+
* only as a swallowed report (core's `fire` already `.catch`es), leaving the entry pending for the next sweep.
|
|
119
|
+
*/
|
|
55
120
|
private deliverOnce;
|
|
121
|
+
/**
|
|
122
|
+
* RECOVERY sweep — run at boot AND PERIODICALLY (wired into the service reaper), BEFORE/while serving traffic.
|
|
123
|
+
* For every pending (not-acked) journal entry, re-fetch the run from the durable {@link WorkflowRunStore}:
|
|
124
|
+
* - TERMINAL (completed/failed): its in-process notify was lost to a crash (or it finished while we were
|
|
125
|
+
* down) → re-deliver + ack (idempotent on the receiver).
|
|
126
|
+
* - `running` but STALE (run.startedAt older than `orphanGraceMs`): a crash-orphaned run — core never resumes
|
|
127
|
+
* a prior `running` row (resume mints a NEW runId) and never reaps a `running` row, so NOTHING will ever
|
|
128
|
+
* flip it terminal. Finalize-as-ABANDONED: deliver a `failed` completion (so the originator learns the
|
|
129
|
+
* workflow died) + ack, instead of leaking the entry forever. This closes the exact crash topology SVC-1
|
|
130
|
+
* exists for (a replica SIGKILLed mid-run).
|
|
131
|
+
* - `running` and FRESH (within the grace window): genuinely in flight → leave pending (the owning process
|
|
132
|
+
* delivers its terminal notify, or the next sweep catches it once it goes terminal or stale).
|
|
133
|
+
* - MISSING (reaped / never persisted): ack-as-abandoned so the journal doesn't chase a ghost forever.
|
|
134
|
+
* `orphanGraceMs` MUST exceed the max expected workflow runtime (the run store has no cross-replica liveness
|
|
135
|
+
* signal, so age is the only orphan proxy). Returns a tally. A per-entry throw is isolated so one bad entry
|
|
136
|
+
* can't abort the sweep.
|
|
137
|
+
*/
|
|
56
138
|
recover(opts?: {
|
|
57
139
|
orphanGraceMs?: number;
|
|
58
140
|
}): Promise<{
|
|
@@ -62,13 +144,28 @@ export declare class WorkflowNotifyGate {
|
|
|
62
144
|
abandoned: number;
|
|
63
145
|
}>;
|
|
64
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Default zero-dependency, crash-safe {@link WorkflowNotifyJournalStore}. An append-only JSONL ledger replayed
|
|
149
|
+
* at boot (last-writer-wins per `runId`), the same durable posture as core's `FileWorkflowRunStore` — fsync'd
|
|
150
|
+
* appends, atomic single-line records. Single-instance / TOC-local: a cross-replica TiDB/PG port is a follow-on
|
|
151
|
+
* (the recovery sweep + gate are backend-agnostic).
|
|
152
|
+
*
|
|
153
|
+
* The ledger only ever grows by one line per `record`/`ack`; an acked entry's line is not deleted (the replay
|
|
154
|
+
* folds it), so retention is a future compaction concern — the backlog `listPending` walks is bounded by
|
|
155
|
+
* IN-FLIGHT + un-swept runs, not history.
|
|
156
|
+
*/
|
|
65
157
|
export declare class FileWorkflowNotifyJournalStore implements WorkflowNotifyJournalStore {
|
|
66
158
|
private readonly fsyncEnabled;
|
|
67
159
|
private readonly ledgerPath;
|
|
68
160
|
private readonly entries;
|
|
69
161
|
private fd;
|
|
70
162
|
constructor(root: string, fsyncEnabled?: boolean);
|
|
163
|
+
/** Replay the ledger into the authoritative map (last-writer-wins by line order). Tolerates a torn final line
|
|
164
|
+
* (a crash mid-append) by dropping an unparsable trailing record — the same crash-safety FileWorkflowRunStore
|
|
165
|
+
* relies on (a partial append is simply not yet committed state). */
|
|
71
166
|
private replay;
|
|
167
|
+
/** Commit one entry: append the JSON line (fsync) THEN flip the in-memory map (crash-safe ordering — the
|
|
168
|
+
* durable record lands before the in-memory state the caller observes). */
|
|
72
169
|
private commit;
|
|
73
170
|
record(input: {
|
|
74
171
|
runId: string;
|
|
@@ -80,9 +177,15 @@ export declare class FileWorkflowNotifyJournalStore implements WorkflowNotifyJou
|
|
|
80
177
|
ack(runId: string, ackedAt: number): Promise<void>;
|
|
81
178
|
listPending(): Promise<WorkflowNotifyJournalEntry[]>;
|
|
82
179
|
get(runId: string): Promise<WorkflowNotifyJournalEntry | null>;
|
|
180
|
+
/** Release the append handle (best-effort) — called on shutdown. */
|
|
83
181
|
close(): void;
|
|
182
|
+
/** Test/inspection: total entries (acked + pending). */
|
|
84
183
|
get size(): number;
|
|
85
184
|
}
|
|
185
|
+
/** In-process {@link WorkflowNotifyJournalStore} — tests / single-instance ephemeral. Does NOT survive a
|
|
186
|
+
* restart (so it provides NO crash-recovery on its own); use {@link FileWorkflowNotifyJournalStore} or a
|
|
187
|
+
* durable backend for the real at-least-once guarantee. Useful to unit-test the gate's live + idempotency
|
|
188
|
+
* paths without touching disk, and to simulate a crash by handing a FRESH store to a new gate. */
|
|
86
189
|
export declare class InMemoryWorkflowNotifyJournalStore implements WorkflowNotifyJournalStore {
|
|
87
190
|
private readonly entries;
|
|
88
191
|
record(input: {
|
|
@@ -97,11 +200,39 @@ export declare class InMemoryWorkflowNotifyJournalStore implements WorkflowNotif
|
|
|
97
200
|
get(runId: string): Promise<WorkflowNotifyJournalEntry | null>;
|
|
98
201
|
get size(): number;
|
|
99
202
|
}
|
|
203
|
+
/**
|
|
204
|
+
* A {@link WorkflowRunStore} DECORATOR that journals a run the instant core persists it at START. core's
|
|
205
|
+
* `startWorkflow` `put`s the run row (`status: "running"`) synchronously before the body runs, carrying both the
|
|
206
|
+
* `runId` and the `scope` — the one synchronous start-time hook the service gets WITHOUT a new core seam (core
|
|
207
|
+
* exposes no `onWorkflowStart`). Wrapping the run store here observes that `put`, journals the started run as
|
|
208
|
+
* pending-notify, then delegates to the wrapped store. Every other method is a pure pass-through (the run store
|
|
209
|
+
* stays core's source of truth for `/workflows`). This is the [[core-service-boundary]]-clean wiring: reuse the
|
|
210
|
+
* core store seam, observe writes through it, never fork a parallel run model.
|
|
211
|
+
*
|
|
212
|
+
* Journaling on `put` (not on notify) is what makes recovery cover a crash BEFORE terminal: the entry exists the
|
|
213
|
+
* moment the run starts, so the boot sweep finds it even if the process died mid-run.
|
|
214
|
+
*/
|
|
100
215
|
export declare class JournalingWorkflowRunStore implements WorkflowRunStore {
|
|
101
216
|
private readonly inner;
|
|
217
|
+
/** [1262] optional since the fleet decoupling: undefined ⇒ no start-time journal (ephemeral memory
|
|
218
|
+
* backend has nothing to recover), the FLEET half still publishes — the panel is replica-local UI
|
|
219
|
+
* state and must not be gated on the durable-notify machinery (the regression that emptied clay's
|
|
220
|
+
* workflow panel on memory-backend deployments). */
|
|
102
221
|
private readonly gate?;
|
|
222
|
+
/** MF-Fleet (data contract): publish the workflow's fleet row on every put/update (the same write-observation
|
|
223
|
+
* point as the notify journal). Optional — undefined ⇒ no fleet publish. */
|
|
103
224
|
private readonly fleetBus?;
|
|
104
|
-
constructor(inner: WorkflowRunStore,
|
|
225
|
+
constructor(inner: WorkflowRunStore,
|
|
226
|
+
/** [1262] optional since the fleet decoupling: undefined ⇒ no start-time journal (ephemeral memory
|
|
227
|
+
* backend has nothing to recover), the FLEET half still publishes — the panel is replica-local UI
|
|
228
|
+
* state and must not be gated on the durable-notify machinery (the regression that emptied clay's
|
|
229
|
+
* workflow panel on memory-backend deployments). */
|
|
230
|
+
gate?: Pick<WorkflowNotifyGate, "onWorkflowStart"> | undefined,
|
|
231
|
+
/** MF-Fleet (data contract): publish the workflow's fleet row on every put/update (the same write-observation
|
|
232
|
+
* point as the notify journal). Optional — undefined ⇒ no fleet publish. */
|
|
233
|
+
fleetBus?: FleetEventBus | undefined);
|
|
234
|
+
/** Derive + publish the MF-Fleet workflow row from a WorkflowRun (doneCount/totalCount from agents, tokens from
|
|
235
|
+
* stats); a terminal workflow LEAVES the fleet (publish terminal then remove), a running one stays/updates. */
|
|
105
236
|
private publishFleet;
|
|
106
237
|
put(id: string, run: WorkflowRun): Promise<void>;
|
|
107
238
|
update(id: string, scope: string, run: WorkflowRun, expect?: {
|
|
@@ -1,7 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SVC-1 (core contract) — the AT-LEAST-ONCE workflow completion-notify TRUST GATE.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this exists
|
|
5
|
+
* core's `run_workflow` tool already does the BACKGROUND-RUN half of SVC-1: `startWorkflow` returns a `runId`
|
|
6
|
+
* synchronously (the caller never blocks), the workflow runs to terminal in the background, and on terminal the
|
|
7
|
+
* tool fires a single in-process completion notify through the {@link import("@sema-agent/core").WorkflowCompletionNotifier}
|
|
8
|
+
* seam (see core `run-workflow-tool.js`). That notify is **at-MOST-once**: it lives only in the process that
|
|
9
|
+
* started the run. If the replica CRASHES between the workflow reaching terminal and the receiver acking the
|
|
10
|
+
* notification — or if it crashes before the run even finishes — the notification is LOST. Nothing re-derives it.
|
|
11
|
+
*
|
|
12
|
+
* SVC-1 makes the completion notify **at-LEAST-once**, the half that is service's (design boundary
|
|
13
|
+
* [[core-service-boundary]] — the durable wiring + the deployment notify route is TOB, not core):
|
|
14
|
+
* 1. **terminal → notify** (steady state): every started run is journaled as `pending`; when its in-process
|
|
15
|
+
* notify fires, we record the terminal status durably, DELIVER to the wrapped notifier, and mark the journal
|
|
16
|
+
* entry `acked`. Delivery before ack ⇒ a crash mid-delivery re-delivers on recovery (never a silent drop).
|
|
17
|
+
* 2. **crash-recovery → re-derive + notify** (startup): on boot we read the journal for every NOT-`acked` entry
|
|
18
|
+
* and RE-FETCH the run from the durable {@link import("@sema-agent/core").WorkflowRunStore}. A run that is now
|
|
19
|
+
* TERMINAL (its in-process notify was lost to the crash, or it finished while we were down) is RE-DELIVERED
|
|
20
|
+
* and acked. A run still `running` is left pending — the live process owns its terminal notify, and the next
|
|
21
|
+
* recovery sweep (or that process's own delivery) will catch it. A run MISSING from the store (reaped /
|
|
22
|
+
* never persisted) is acked-as-abandoned so the journal does not grow unbounded chasing a ghost.
|
|
23
|
+
* 3. **idempotent**: the journal de-dups by `runId` (a delivered+acked run is never re-delivered), so the
|
|
24
|
+
* wrapped notifier — and the downstream receiver — may safely be invoked twice for the same run across a
|
|
25
|
+
* crash boundary (the SVC-1 contract: the receiver is idempotent on `runId`). We dedup on OUR side too, so a
|
|
26
|
+
* double recovery sweep, or a recovery racing the live notify, delivers at most one extra time, not N.
|
|
27
|
+
*
|
|
28
|
+
* ## Durability model
|
|
29
|
+
* The journal is itself a {@link WorkflowNotifyJournalStore} — a tiny append-only `(runId → entry)` durable map,
|
|
30
|
+
* deliberately SEPARATE from the `WorkflowRunStore`: the run store is core-owned (its schema/CAS is core's), and
|
|
31
|
+
* critically it has **no cross-scope enumeration** (`listByScope` needs a scope; there is no "list all runs" /
|
|
32
|
+
* "list all scopes"), so it cannot by itself answer "which runs might still owe a notify?" after a restart. The
|
|
33
|
+
* journal records `(runId, scope)` for exactly that: a bounded, service-owned to-do list of notifications, scoped
|
|
34
|
+
* to the deployment, that the recovery sweep walks. It is the SINGLE source of truth for "has this run's
|
|
35
|
+
* completion been delivered?".
|
|
36
|
+
*
|
|
37
|
+
* The default {@link FileWorkflowNotifyJournalStore} is zero-dependency + crash-safe (atomic-append ledger, same
|
|
38
|
+
* posture as core's `FileWorkflowRunStore`) so the no-DB / TOC-local path is durable out of the box. A durable
|
|
39
|
+
* cross-replica backend (TiDB/PG) is a thin port of the same interface (a follow-on; the recovery sweep + the
|
|
40
|
+
* gate logic here are backend-agnostic).
|
|
41
|
+
*/
|
|
1
42
|
import { mkdirSync, readFileSync, existsSync, openSync, writeSync, fsyncSync, closeSync } from "node:fs";
|
|
2
43
|
import { join } from "node:path";
|
|
3
44
|
import { deriveAgentDisplayStatus } from "@sema-agent/core";
|
|
4
45
|
import { redactSecrets } from "../trace/redact.js";
|
|
46
|
+
/** Build a bounded + redacted completion summary from a durable run's terminal state (the recovery path — the
|
|
47
|
+
* live path uses core's own `boundedSummary`). We never have the raw result here (the run store keeps the run,
|
|
48
|
+
* not the originator's payload), so we synthesize a conservative one: status + the run's already-bounded error.
|
|
49
|
+
* Mirrors core `failureSummary`/`redactSummary` posture — strip URLs/tokens/paths defensively. */
|
|
5
50
|
const SUMMARY_MAX = 500;
|
|
6
51
|
export function recoverySummary(status, error) {
|
|
7
52
|
if (status === "completed")
|
|
@@ -12,9 +57,24 @@ export function recoverySummary(status, error) {
|
|
|
12
57
|
.replace(/\b(?:Bearer|Basic)\s+[A-Za-z0-9._~+/=-]+/gi, "[redacted-token]")
|
|
13
58
|
.replace(/\b(?:api[_-]?key|secret|token|password|authorization)\b\s*[:=]\s*"?[^\s"',}]+/gi, "[redacted-secret]")
|
|
14
59
|
.replace(/(?:\/[A-Za-z0-9_.-]+){2,}/g, "[redacted-path]")
|
|
60
|
+
// Windows/UNC paths too (SVC-1 review): `C:\dir\file` + `\\host\share\…` would otherwise surface VERBATIM — a
|
|
61
|
+
// host-path/identity leak the forward-slash rule above misses. `\\{1,2}` covers the UNC double-leading-backslash.
|
|
15
62
|
.replace(/(?:[A-Za-z]:)?(?:\\{1,2}[A-Za-z0-9_.$-]+){2,}/g, "[redacted-path]");
|
|
16
63
|
return redacted.length > SUMMARY_MAX ? `${redacted.slice(0, SUMMARY_MAX)}…[+${redacted.length - SUMMARY_MAX} chars]` : redacted;
|
|
17
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* The AT-LEAST-ONCE completion-notify gate. Wrap the deployment's real delivery once at boot, then:
|
|
67
|
+
* - {@link buildNotifier} hands core a {@link import("@sema-agent/core").WorkflowCompletionNotifier} whose `notify`
|
|
68
|
+
* runs the LIVE deliver-then-ack path (the in-process at-most-once notify, now journaled into at-least-once).
|
|
69
|
+
* - {@link onWorkflowStart} journals a run the moment it starts (so a crash BEFORE its terminal notify still
|
|
70
|
+
* leaves a pending entry the recovery sweep finds). Call it from wherever the runId becomes known.
|
|
71
|
+
* - {@link recover} runs ONCE at boot: re-derives every pending run's terminal state from the
|
|
72
|
+
* {@link WorkflowRunStore} and re-delivers + acks the ones that are terminal.
|
|
73
|
+
*
|
|
74
|
+
* The gate is the SINGLE writer of `acked`, and dedups every delivery against the journal, so the wrapped
|
|
75
|
+
* delivery sink is invoked at most once per run in steady state (and at most one extra time across a crash —
|
|
76
|
+
* the at-least-once / idempotent contract).
|
|
77
|
+
*/
|
|
18
78
|
export class WorkflowNotifyGate {
|
|
19
79
|
journal;
|
|
20
80
|
runStore;
|
|
@@ -29,6 +89,12 @@ export class WorkflowNotifyGate {
|
|
|
29
89
|
now() {
|
|
30
90
|
return this.opts.now ? this.opts.now() : Date.now();
|
|
31
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* Journal a STARTED run as pending-notify. Call this with the synchronous `runId` from `startWorkflow` /
|
|
94
|
+
* `run_workflow` — BEFORE the workflow can reach terminal — so a crash mid-run still leaves a recoverable
|
|
95
|
+
* to-do. Best-effort + idempotent: a journal throw is reported, never propagated (it must not break the
|
|
96
|
+
* workflow start; worst case the recovery sweep won't know about this run — degraded, not corrupting).
|
|
97
|
+
*/
|
|
32
98
|
async onWorkflowStart(input) {
|
|
33
99
|
try {
|
|
34
100
|
await this.journal.record({
|
|
@@ -40,9 +106,15 @@ export class WorkflowNotifyGate {
|
|
|
40
106
|
});
|
|
41
107
|
}
|
|
42
108
|
catch (err) {
|
|
43
|
-
this.opts.onError?.("record", input.runId, err);
|
|
109
|
+
this.opts.onError?.("record", input.runId, err); // start-time JOURNAL write failed (not a delivery)
|
|
44
110
|
}
|
|
45
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* The core-facing notifier. core calls this exactly once per run on terminal (its own at-most-once, runId-dedup
|
|
114
|
+
* `fire`). We turn it into at-least-once: ensure the run is journaled (the start hook may have been skipped),
|
|
115
|
+
* then deliver-then-ack. Idempotent: if the run is ALREADY acked (a recovery sweep beat us, or a duplicate
|
|
116
|
+
* core fire), skip — at most one delivery.
|
|
117
|
+
*/
|
|
46
118
|
buildNotifier() {
|
|
47
119
|
return {
|
|
48
120
|
notify: async (input) => {
|
|
@@ -52,16 +124,24 @@ export class WorkflowNotifyGate {
|
|
|
52
124
|
summary: input.summary,
|
|
53
125
|
...(input.sourceTaskId ? { sourceTaskId: input.sourceTaskId } : {}),
|
|
54
126
|
...(input.principal ? { principal: input.principal } : {}),
|
|
55
|
-
...(input.originatingSessionId ? { originatingSessionId: input.originatingSessionId } : {}),
|
|
127
|
+
...(input.originatingSessionId ? { originatingSessionId: input.originatingSessionId } : {}), // core 1.208: lookup-free session routing
|
|
56
128
|
}, input.scope ?? input.principal ?? "default");
|
|
57
129
|
},
|
|
58
130
|
};
|
|
59
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* Deliver a run's completion exactly once (idempotent on the journal). Ensures the entry exists (record is a
|
|
134
|
+
* no-op if it already does), SKIPS if already acked, otherwise delivers THEN acks (a crash between deliver and
|
|
135
|
+
* ack re-delivers on recovery — at-least-once). A delivery throw is reported and re-thrown to the LIVE caller
|
|
136
|
+
* only as a swallowed report (core's `fire` already `.catch`es), leaving the entry pending for the next sweep.
|
|
137
|
+
*/
|
|
60
138
|
async deliverOnce(p, scope) {
|
|
61
139
|
const existing = await this.journal.get(p.runId);
|
|
62
140
|
if (existing?.acked)
|
|
63
|
-
return;
|
|
141
|
+
return; // already delivered — idempotent skip
|
|
64
142
|
if (!existing) {
|
|
143
|
+
// The start hook didn't journal it (e.g. a run started before this gate existed, or the hook was skipped).
|
|
144
|
+
// Record it now so the deliver-then-ack ordering still holds and a crash mid-delivery is recoverable.
|
|
65
145
|
await this.journal.record({
|
|
66
146
|
runId: p.runId,
|
|
67
147
|
scope,
|
|
@@ -74,13 +154,31 @@ export class WorkflowNotifyGate {
|
|
|
74
154
|
await this.deliver(p);
|
|
75
155
|
}
|
|
76
156
|
catch (err) {
|
|
157
|
+
// Stays PENDING (not acked) → the next recovery sweep re-delivers. Report, never throw back into core.
|
|
77
158
|
this.opts.onError?.("deliver", p.runId, err);
|
|
78
159
|
return;
|
|
79
160
|
}
|
|
80
161
|
await this.journal.ack(p.runId, this.now());
|
|
81
162
|
}
|
|
163
|
+
/**
|
|
164
|
+
* RECOVERY sweep — run at boot AND PERIODICALLY (wired into the service reaper), BEFORE/while serving traffic.
|
|
165
|
+
* For every pending (not-acked) journal entry, re-fetch the run from the durable {@link WorkflowRunStore}:
|
|
166
|
+
* - TERMINAL (completed/failed): its in-process notify was lost to a crash (or it finished while we were
|
|
167
|
+
* down) → re-deliver + ack (idempotent on the receiver).
|
|
168
|
+
* - `running` but STALE (run.startedAt older than `orphanGraceMs`): a crash-orphaned run — core never resumes
|
|
169
|
+
* a prior `running` row (resume mints a NEW runId) and never reaps a `running` row, so NOTHING will ever
|
|
170
|
+
* flip it terminal. Finalize-as-ABANDONED: deliver a `failed` completion (so the originator learns the
|
|
171
|
+
* workflow died) + ack, instead of leaking the entry forever. This closes the exact crash topology SVC-1
|
|
172
|
+
* exists for (a replica SIGKILLed mid-run).
|
|
173
|
+
* - `running` and FRESH (within the grace window): genuinely in flight → leave pending (the owning process
|
|
174
|
+
* delivers its terminal notify, or the next sweep catches it once it goes terminal or stale).
|
|
175
|
+
* - MISSING (reaped / never persisted): ack-as-abandoned so the journal doesn't chase a ghost forever.
|
|
176
|
+
* `orphanGraceMs` MUST exceed the max expected workflow runtime (the run store has no cross-replica liveness
|
|
177
|
+
* signal, so age is the only orphan proxy). Returns a tally. A per-entry throw is isolated so one bad entry
|
|
178
|
+
* can't abort the sweep.
|
|
179
|
+
*/
|
|
82
180
|
async recover(opts = {}) {
|
|
83
|
-
const orphanGraceMs = opts.orphanGraceMs ?? 24 * 60 * 60 * 1000;
|
|
181
|
+
const orphanGraceMs = opts.orphanGraceMs ?? 24 * 60 * 60 * 1000; // 24h default — well beyond any normal workflow
|
|
84
182
|
const pending = await this.journal.listPending();
|
|
85
183
|
let redelivered = 0;
|
|
86
184
|
let stillRunning = 0;
|
|
@@ -89,19 +187,31 @@ export class WorkflowNotifyGate {
|
|
|
89
187
|
try {
|
|
90
188
|
const run = await this.runStore.get(entry.runId);
|
|
91
189
|
if (!run) {
|
|
190
|
+
// No durable run — reaped or never persisted. Don't keep it pending forever.
|
|
92
191
|
await this.journal.ack(entry.runId, this.now());
|
|
93
192
|
abandoned++;
|
|
94
193
|
continue;
|
|
95
194
|
}
|
|
195
|
+
// principal fallback: the start-hook (JournalingWorkflowRunStore.put) records only runId+scope — scope IS
|
|
196
|
+
// the creating principal, so it's the sound owner attribution when entry.principal is absent (M6: a
|
|
197
|
+
// recovered notify must still carry an owner).
|
|
96
198
|
const principal = entry.principal ?? entry.scope;
|
|
199
|
+
// #3 G (core 1.187): sourceTaskId used to be "genuinely unrecoverable" here (the durable WorkflowRun didn't
|
|
200
|
+
// carry it). core now threads it onto WorkflowRun.sourceTaskId (startWorkflow stores it / RunWorkflow passes the
|
|
201
|
+
// host taskId), so fall back to run's copy when the journal entry lost it — the recovered notify can finally
|
|
202
|
+
// attribute the originating host task.
|
|
97
203
|
const sourceTaskId = entry.sourceTaskId ?? run.sourceTaskId;
|
|
204
|
+
// core 1.210: the durable WorkflowRun now records `originatingSessionId` (same story as
|
|
205
|
+
// sourceTaskId's 1.187) — thread it onto the RECOVERED payload so crash-recovery routes lookup-free,
|
|
206
|
+
// symmetric with the live notify (resolveCompletionRoute reads it first).
|
|
98
207
|
const originatingSessionId = run.originatingSessionId;
|
|
99
208
|
if (run.status === "running") {
|
|
100
209
|
const startedAt = run.startedAt || entry.createdAt;
|
|
101
210
|
if (this.now() - startedAt <= orphanGraceMs) {
|
|
102
211
|
stillRunning++;
|
|
103
|
-
continue;
|
|
212
|
+
continue; // fresh → genuinely in flight; the owner (or a later sweep) delivers terminal
|
|
104
213
|
}
|
|
214
|
+
// Stale `running` ⇒ crash-orphan (nothing will ever flip it terminal). Finalize as abandoned/failed.
|
|
105
215
|
await this.deliverOnce({
|
|
106
216
|
runId: entry.runId,
|
|
107
217
|
status: "failed",
|
|
@@ -114,6 +224,7 @@ export class WorkflowNotifyGate {
|
|
|
114
224
|
abandoned++;
|
|
115
225
|
continue;
|
|
116
226
|
}
|
|
227
|
+
// Terminal but not acked ⇒ the crash dropped its notify. Re-derive the bounded summary + re-deliver.
|
|
117
228
|
await this.deliverOnce({
|
|
118
229
|
runId: entry.runId,
|
|
119
230
|
status: run.status,
|
|
@@ -122,6 +233,7 @@ export class WorkflowNotifyGate {
|
|
|
122
233
|
...(principal ? { principal } : {}),
|
|
123
234
|
...(originatingSessionId ? { originatingSessionId } : {}),
|
|
124
235
|
}, entry.scope);
|
|
236
|
+
// deliverOnce only acks on a clean delivery; count a redelivery whether or not it stuck (it logs on fail).
|
|
125
237
|
const after = await this.journal.get(entry.runId);
|
|
126
238
|
if (after?.acked)
|
|
127
239
|
redelivered++;
|
|
@@ -133,6 +245,16 @@ export class WorkflowNotifyGate {
|
|
|
133
245
|
return { scanned: pending.length, redelivered, stillRunning, abandoned };
|
|
134
246
|
}
|
|
135
247
|
}
|
|
248
|
+
/**
|
|
249
|
+
* Default zero-dependency, crash-safe {@link WorkflowNotifyJournalStore}. An append-only JSONL ledger replayed
|
|
250
|
+
* at boot (last-writer-wins per `runId`), the same durable posture as core's `FileWorkflowRunStore` — fsync'd
|
|
251
|
+
* appends, atomic single-line records. Single-instance / TOC-local: a cross-replica TiDB/PG port is a follow-on
|
|
252
|
+
* (the recovery sweep + gate are backend-agnostic).
|
|
253
|
+
*
|
|
254
|
+
* The ledger only ever grows by one line per `record`/`ack`; an acked entry's line is not deleted (the replay
|
|
255
|
+
* folds it), so retention is a future compaction concern — the backlog `listPending` walks is bounded by
|
|
256
|
+
* IN-FLIGHT + un-swept runs, not history.
|
|
257
|
+
*/
|
|
136
258
|
export class FileWorkflowNotifyJournalStore {
|
|
137
259
|
fsyncEnabled;
|
|
138
260
|
ledgerPath;
|
|
@@ -141,10 +263,15 @@ export class FileWorkflowNotifyJournalStore {
|
|
|
141
263
|
constructor(root, fsyncEnabled = true) {
|
|
142
264
|
this.fsyncEnabled = fsyncEnabled;
|
|
143
265
|
const dir = join(root, "workflow-notify");
|
|
266
|
+
// 0o700 dir + 0o600 ledger: the journal persists tenant identity (scope/principal/sourceTaskId) — it must not
|
|
267
|
+
// be group/world-readable (parity with the durable-secret posture; a default-umask file would leak identity).
|
|
144
268
|
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
145
269
|
this.ledgerPath = join(dir, "journal.jsonl");
|
|
146
270
|
this.replay();
|
|
147
271
|
}
|
|
272
|
+
/** Replay the ledger into the authoritative map (last-writer-wins by line order). Tolerates a torn final line
|
|
273
|
+
* (a crash mid-append) by dropping an unparsable trailing record — the same crash-safety FileWorkflowRunStore
|
|
274
|
+
* relies on (a partial append is simply not yet committed state). */
|
|
148
275
|
replay() {
|
|
149
276
|
if (!existsSync(this.ledgerPath))
|
|
150
277
|
return;
|
|
@@ -157,15 +284,17 @@ export class FileWorkflowNotifyJournalStore {
|
|
|
157
284
|
ev = JSON.parse(line);
|
|
158
285
|
}
|
|
159
286
|
catch {
|
|
160
|
-
continue;
|
|
287
|
+
continue; // torn/partial line — skip (uncommitted)
|
|
161
288
|
}
|
|
162
289
|
if (ev && typeof ev.runId === "string")
|
|
163
290
|
this.entries.set(ev.runId, ev);
|
|
164
291
|
}
|
|
165
292
|
}
|
|
293
|
+
/** Commit one entry: append the JSON line (fsync) THEN flip the in-memory map (crash-safe ordering — the
|
|
294
|
+
* durable record lands before the in-memory state the caller observes). */
|
|
166
295
|
commit(entry) {
|
|
167
296
|
if (this.fd === undefined)
|
|
168
|
-
this.fd = openSync(this.ledgerPath, "a", 0o600);
|
|
297
|
+
this.fd = openSync(this.ledgerPath, "a", 0o600); // owner-only (identity data)
|
|
169
298
|
writeSync(this.fd, `${JSON.stringify(entry)}\n`);
|
|
170
299
|
if (this.fsyncEnabled)
|
|
171
300
|
fsyncSync(this.fd);
|
|
@@ -173,6 +302,7 @@ export class FileWorkflowNotifyJournalStore {
|
|
|
173
302
|
}
|
|
174
303
|
async record(input) {
|
|
175
304
|
const existing = this.entries.get(input.runId);
|
|
305
|
+
// Idempotent: never reset an existing entry (esp. an acked one → it would re-deliver forever).
|
|
176
306
|
if (existing)
|
|
177
307
|
return;
|
|
178
308
|
this.commit({
|
|
@@ -187,7 +317,7 @@ export class FileWorkflowNotifyJournalStore {
|
|
|
187
317
|
async ack(runId, ackedAt) {
|
|
188
318
|
const existing = this.entries.get(runId);
|
|
189
319
|
if (!existing || existing.acked)
|
|
190
|
-
return;
|
|
320
|
+
return; // idempotent
|
|
191
321
|
this.commit({ ...existing, acked: true, ackedAt });
|
|
192
322
|
}
|
|
193
323
|
async listPending() {
|
|
@@ -196,25 +326,32 @@ export class FileWorkflowNotifyJournalStore {
|
|
|
196
326
|
async get(runId) {
|
|
197
327
|
return this.entries.get(runId) ?? null;
|
|
198
328
|
}
|
|
329
|
+
/** Release the append handle (best-effort) — called on shutdown. */
|
|
199
330
|
close() {
|
|
200
331
|
if (this.fd !== undefined) {
|
|
201
332
|
try {
|
|
202
333
|
closeSync(this.fd);
|
|
203
334
|
}
|
|
204
335
|
catch {
|
|
336
|
+
/* best-effort */
|
|
205
337
|
}
|
|
206
338
|
this.fd = undefined;
|
|
207
339
|
}
|
|
208
340
|
}
|
|
341
|
+
/** Test/inspection: total entries (acked + pending). */
|
|
209
342
|
get size() {
|
|
210
343
|
return this.entries.size;
|
|
211
344
|
}
|
|
212
345
|
}
|
|
346
|
+
/** In-process {@link WorkflowNotifyJournalStore} — tests / single-instance ephemeral. Does NOT survive a
|
|
347
|
+
* restart (so it provides NO crash-recovery on its own); use {@link FileWorkflowNotifyJournalStore} or a
|
|
348
|
+
* durable backend for the real at-least-once guarantee. Useful to unit-test the gate's live + idempotency
|
|
349
|
+
* paths without touching disk, and to simulate a crash by handing a FRESH store to a new gate. */
|
|
213
350
|
export class InMemoryWorkflowNotifyJournalStore {
|
|
214
351
|
entries = new Map();
|
|
215
352
|
async record(input) {
|
|
216
353
|
if (this.entries.has(input.runId))
|
|
217
|
-
return;
|
|
354
|
+
return; // idempotent
|
|
218
355
|
this.entries.set(input.runId, {
|
|
219
356
|
runId: input.runId,
|
|
220
357
|
scope: input.scope,
|
|
@@ -240,28 +377,58 @@ export class InMemoryWorkflowNotifyJournalStore {
|
|
|
240
377
|
return this.entries.size;
|
|
241
378
|
}
|
|
242
379
|
}
|
|
380
|
+
/**
|
|
381
|
+
* A {@link WorkflowRunStore} DECORATOR that journals a run the instant core persists it at START. core's
|
|
382
|
+
* `startWorkflow` `put`s the run row (`status: "running"`) synchronously before the body runs, carrying both the
|
|
383
|
+
* `runId` and the `scope` — the one synchronous start-time hook the service gets WITHOUT a new core seam (core
|
|
384
|
+
* exposes no `onWorkflowStart`). Wrapping the run store here observes that `put`, journals the started run as
|
|
385
|
+
* pending-notify, then delegates to the wrapped store. Every other method is a pure pass-through (the run store
|
|
386
|
+
* stays core's source of truth for `/workflows`). This is the [[core-service-boundary]]-clean wiring: reuse the
|
|
387
|
+
* core store seam, observe writes through it, never fork a parallel run model.
|
|
388
|
+
*
|
|
389
|
+
* Journaling on `put` (not on notify) is what makes recovery cover a crash BEFORE terminal: the entry exists the
|
|
390
|
+
* moment the run starts, so the boot sweep finds it even if the process died mid-run.
|
|
391
|
+
*/
|
|
243
392
|
export class JournalingWorkflowRunStore {
|
|
244
393
|
inner;
|
|
245
394
|
gate;
|
|
246
395
|
fleetBus;
|
|
247
|
-
constructor(inner,
|
|
396
|
+
constructor(inner,
|
|
397
|
+
/** [1262] optional since the fleet decoupling: undefined ⇒ no start-time journal (ephemeral memory
|
|
398
|
+
* backend has nothing to recover), the FLEET half still publishes — the panel is replica-local UI
|
|
399
|
+
* state and must not be gated on the durable-notify machinery (the regression that emptied clay's
|
|
400
|
+
* workflow panel on memory-backend deployments). */
|
|
401
|
+
gate,
|
|
402
|
+
/** MF-Fleet (data contract): publish the workflow's fleet row on every put/update (the same write-observation
|
|
403
|
+
* point as the notify journal). Optional — undefined ⇒ no fleet publish. */
|
|
404
|
+
fleetBus) {
|
|
248
405
|
this.inner = inner;
|
|
249
406
|
this.gate = gate;
|
|
250
407
|
this.fleetBus = fleetBus;
|
|
251
408
|
}
|
|
409
|
+
/** Derive + publish the MF-Fleet workflow row from a WorkflowRun (doneCount/totalCount from agents, tokens from
|
|
410
|
+
* stats); a terminal workflow LEAVES the fleet (publish terminal then remove), a running one stays/updates. */
|
|
252
411
|
publishFleet(id, run) {
|
|
253
412
|
if (!this.fleetBus)
|
|
254
413
|
return;
|
|
255
414
|
const agents = run.agents ?? [];
|
|
256
415
|
const done = agents.filter((a) => a.status === "completed" || a.status === "failed").length;
|
|
257
416
|
const failed = agents.filter((a) => a.status === "failed").length;
|
|
417
|
+
// cli [1726] 二①:CC 规模告警的分母是 **started**(已启动),而 `totalCount`(= agents.length)是**计划总数**
|
|
418
|
+
// (含排队中)。判别口径走 core 导出的 `deriveAgentDisplayStatus` —— 一个 agent 已在 `run.agents` 里但
|
|
419
|
+
// `startedAt` 未置就是排队中。**不自己写 `startedAt !== undefined`**:那是 core 的显示契约,两端各写一遍
|
|
420
|
+
// 必然漂移(本仓刚在 coarse 门镜像上吃过同款),用它的函数则 core 一改、两端同时跟随。
|
|
258
421
|
const started = agents.filter((a) => deriveAgentDisplayStatus(a, run.status) !== "queued").length;
|
|
259
422
|
const terminal = run.status === "completed" || run.status === "failed";
|
|
260
423
|
this.fleetBus.publishWorkflow({
|
|
261
424
|
id,
|
|
425
|
+
// [WF2-A parity] redact the workflow label surfaces for parity with the run + subagent-child names (fleet-bus.ts):
|
|
426
|
+
// a tool-launched (LLM-authored) workflow's meta.name/description is task-controlled and could carry a secret shape.
|
|
262
427
|
name: redactSecrets(run.name ?? "Dynamic workflow"),
|
|
263
428
|
...(run.description ? { description: redactSecrets(run.description) } : {}),
|
|
264
429
|
scope: run.scope,
|
|
430
|
+
// codex-6 F2:sessionId 必须随行——streamFleet 对无 sessionId 的行按「同 principal 全会话可见」
|
|
431
|
+
// 兜底,漏发=A 会话的 workflow 名/进度/token 泄进 B 会话的 ?session= 过滤流。
|
|
265
432
|
...(run.originatingSessionId ? { sessionId: run.originatingSessionId } : {}),
|
|
266
433
|
status: run.status,
|
|
267
434
|
doneCount: done,
|
|
@@ -269,23 +436,30 @@ export class JournalingWorkflowRunStore {
|
|
|
269
436
|
failedCount: failed,
|
|
270
437
|
startedCount: started,
|
|
271
438
|
tokens: (run.stats?.tokens ?? 0) + (run.stats?.nested?.tokens ?? 0),
|
|
439
|
+
// [1294]:跑动中也带时长(1.232 只在 endedAt 后带——clay 验收轮实锚面板恒显 0s)。终态用
|
|
440
|
+
// endedAt 定格,活跑用 now-startedAt(每次 put/update 观察点刷新,壳侧读帧即当前时长)。
|
|
272
441
|
elapsedMs: (run.endedAt ?? Date.now()) - run.startedAt,
|
|
273
442
|
});
|
|
274
443
|
if (terminal)
|
|
275
|
-
this.fleetBus.removeWorkflow(id);
|
|
444
|
+
this.fleetBus.removeWorkflow(id); // terminal workflow leaves the active fleet (the shell saw the final frame)
|
|
276
445
|
}
|
|
277
446
|
async put(id, run) {
|
|
447
|
+
// Journal FIRST (best-effort, swallowed inside onWorkflowStart) so a started run is always recoverable, THEN
|
|
448
|
+
// persist. Order matters: if persist throws, the journal entry still lets recovery re-check the (absent) run
|
|
449
|
+
// and ack-as-abandoned — no leak, no lost notify owed.
|
|
278
450
|
await this.gate?.onWorkflowStart({
|
|
279
451
|
runId: id,
|
|
280
452
|
scope: run.scope,
|
|
453
|
+
// core threads sourceTaskId/principal to the NOTIFIER, not onto the run row, so they're unavailable here;
|
|
454
|
+
// the notifier path (deliverOnce) fills them in when it records/delivers. Recovery still has runId+scope.
|
|
281
455
|
});
|
|
282
456
|
await this.inner.put(id, run);
|
|
283
|
-
this.publishFleet(id, run);
|
|
457
|
+
this.publishFleet(id, run); // MF-Fleet: the run appeared (running) / its row state changed
|
|
284
458
|
}
|
|
285
459
|
async update(id, scope, run, expect) {
|
|
286
460
|
const ok = await this.inner.update(id, scope, run, expect);
|
|
287
461
|
if (ok)
|
|
288
|
-
this.publishFleet(id, run);
|
|
462
|
+
this.publishFleet(id, run); // MF-Fleet: only on a successful CAS (the persisted state is what the fleet shows)
|
|
289
463
|
return ok;
|
|
290
464
|
}
|
|
291
465
|
get(id) {
|