@sema-agent/server 1.323.0 → 2.0.0
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-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 +546 -0
- package/dist/config.d.ts +64 -0
- package/dist/config.js +455 -60
- 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 +2132 -96
- 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,10 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TiDB-backed Session event-log center (L1) — the durable "persistence center".
|
|
3
|
+
*
|
|
4
|
+
* Implements core's `SessionStore`. `acquire` rebuilds a session from its append-only event log
|
|
5
|
+
* (= wake/resume), so any stateless runner instance can continue it. Cross-instance write safety is
|
|
6
|
+
* enforced by the storage layer's compare-and-set (core's F2 seam); crash consistency (orphan
|
|
7
|
+
* tool-call after a mid-turn crash) is handled by the Runner calling `reconcileInterruptedSession`
|
|
8
|
+
* on acquire (core's F1).
|
|
9
|
+
*
|
|
10
|
+
* Persistence only. **Tenant authorization (who may wake which session) lives in the service layer**
|
|
11
|
+
* (`src/security.ts`), which uses `ownerOf` / `register` below. The store does not infer identity.
|
|
12
|
+
*/
|
|
13
|
+
// 🔴 core 1.403 迁移(2026-07-25):runtime 构造用法,`Session` → `StoredSession`(见 audit.ts 同款注释)。
|
|
1
14
|
import { StoredSession, SessionError, uuidv7, validateEntriesForImport, leafIdAfterEntry } from "@sema-agent/core";
|
|
2
15
|
import { TiDBSessionStorage } from "./tidb-session-storage.js";
|
|
3
16
|
import { escapeLike } from "./sql-escape.js";
|
|
4
17
|
import { classifySyncRelationshipByIds, SyncConflictError, stagingIdFor, STAGING_ID_MARKER, } from "../session-sync-kernel.js";
|
|
18
|
+
/** Staging-row reaper grace window — a staging session's rows are GC'able only if it has NO session_meta (never
|
|
19
|
+
* committed) AND its OLDEST row is older than this (an in-flight stream keeps appending fresh rows → safe). Mirrors
|
|
20
|
+
* the file-snapshot store's BLOB_GC_GRACE_MS discipline (an in-flight import must never be reaped mid-stream). */
|
|
5
21
|
const STAGING_GC_GRACE_MS = 3_600_000;
|
|
6
22
|
export class TiDBSessionStore {
|
|
7
23
|
pool;
|
|
24
|
+
/** In-flight acquisitions keyed by id, so concurrent acquire(sameId) in one process share one. */
|
|
8
25
|
pending = new Map();
|
|
9
26
|
constructor(pool) {
|
|
10
27
|
this.pool = pool;
|
|
@@ -15,6 +32,10 @@ export class TiDBSessionStore {
|
|
|
15
32
|
await this.register(id, null);
|
|
16
33
|
return this.load(id);
|
|
17
34
|
}
|
|
35
|
+
// design/114 Phase3 (F1): a `requireExisting` acquire NEVER creates and can REJECT (not_found) — it MUST bypass
|
|
36
|
+
// the shared create-dedup `pending`, else a concurrent create-capable waiter awaiting that same promise would
|
|
37
|
+
// inherit the not_found rejection instead of creating-on-miss. Two concurrent requireExisting opens each just
|
|
38
|
+
// wake independently (both resolve or both fail loud) — harmless (it only opens, never double-creates).
|
|
18
39
|
if (opts?.requireExisting)
|
|
19
40
|
return this.load(sessionId, true);
|
|
20
41
|
const inflight = this.pending.get(sessionId);
|
|
@@ -27,18 +48,25 @@ export class TiDBSessionStore {
|
|
|
27
48
|
async load(sessionId, requireExisting = false) {
|
|
28
49
|
let storage = await TiDBSessionStorage.wake(this.pool, sessionId);
|
|
29
50
|
if (!storage) {
|
|
51
|
+
// design/114 Phase3: `requireExisting` ⇒ FAIL LOUD on a genuinely missing session (no session_meta row),
|
|
52
|
+
// never the silent create-on-miss that turns a reuse-style warm-resume into a fresh empty run. core's
|
|
53
|
+
// prepare-task maps this store `not_found` to the caller-facing `resume.session_not_found`.
|
|
30
54
|
if (requireExisting)
|
|
31
55
|
throw new SessionError("not_found", `session ${sessionId} not found`);
|
|
56
|
+
// No meta row yet (a caller-chosen brand-new id the service didn't pre-register). Create it
|
|
57
|
+
// ownerless; only the caller who chose this id can reach it, and it holds no prior data.
|
|
32
58
|
await this.register(sessionId, null);
|
|
33
59
|
storage = await TiDBSessionStorage.wake(this.pool, sessionId);
|
|
34
60
|
}
|
|
35
61
|
return { session: new StoredSession(storage), sessionId };
|
|
36
62
|
}
|
|
63
|
+
/** Idempotently create a session_meta row with an owner. Existing owner is never overwritten. */
|
|
37
64
|
async register(sessionId, owner) {
|
|
38
65
|
const now = new Date();
|
|
39
66
|
await this.pool.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq) " +
|
|
40
67
|
"VALUES (?,?,?,?,NULL,0) ON DUPLICATE KEY UPDATE session_id = session_id", [sessionId, owner, now, now]);
|
|
41
68
|
}
|
|
69
|
+
/** Owner principal of a session, `null` if registered ownerless, `undefined` if no such session. */
|
|
42
70
|
async ownerOf(sessionId) {
|
|
43
71
|
const [rows] = await this.pool.query("SELECT owner FROM session_meta WHERE session_id = ?", [sessionId]);
|
|
44
72
|
if (rows.length === 0)
|
|
@@ -51,39 +79,73 @@ export class TiDBSessionStore {
|
|
|
51
79
|
sessionId,
|
|
52
80
|
]);
|
|
53
81
|
}
|
|
82
|
+
/** E18 — the session's current leaf `SessionTreeEntry.id` (`session_meta.leaf_id`, set transactionally on every
|
|
83
|
+
* persist). A single SELECT, NO acquire/lock/CAS — safe to call mid-run (write-safety is the append-time CAS, not
|
|
84
|
+
* an acquire lease) and cache-bypassing (the resume-at turn-capture reads it without touching the warm session).
|
|
85
|
+
* Returns the leaf id, or null when the session has none / no such session. */
|
|
54
86
|
async getLeafId(sessionId) {
|
|
55
87
|
const [rows] = await this.pool.query("SELECT leaf_id FROM session_meta WHERE session_id = ?", [sessionId]);
|
|
56
88
|
return rows[0] ? (rows[0].leaf_id ?? null) : null;
|
|
57
89
|
}
|
|
90
|
+
/** [1196] 六轮复审:owner+leafId **单行原子快照**(SSE watch 探针的租户围栏读)——两独立 SELECT 在
|
|
91
|
+
* delete/reclaim 缝里可拼出「旧 owner+新 leaf」混世代组合;单 SELECT 单行=天然一致。undefined=无此会话。 */
|
|
58
92
|
async getHead(sessionId) {
|
|
59
93
|
const [rows] = await this.pool.query("SELECT owner, leaf_id FROM session_meta WHERE session_id = ?", [sessionId]);
|
|
60
94
|
if (rows.length === 0)
|
|
61
95
|
return undefined;
|
|
62
96
|
return { owner: rows[0].owner ?? null, leafId: rows[0].leaf_id ?? null };
|
|
63
97
|
}
|
|
98
|
+
/** Durable history is retained; just drop any in-flight cache entry. */
|
|
64
99
|
async release(sessionId) {
|
|
65
100
|
this.pending.delete(sessionId);
|
|
66
101
|
}
|
|
102
|
+
// ───────────────────────── SessionRepo seam (§0.5 session-ownership) ─────────────────────────
|
|
103
|
+
// E16/E17/E21: the service EXTENDS core's `SessionRepo` (list/fork/delete) over its OWN session_meta /
|
|
104
|
+
// session_event tables — a `TiDBSessionRepo`-style implementation (core's `PgSessionRepo` targets core's
|
|
105
|
+
// own `agent_*` tables, so it can't be consumed directly; the dossier §0.5 prescribes a backend-mirrored
|
|
106
|
+
// impl here). Enumeration + lifecycle now come from the SESSION abstraction (session identity), not the
|
|
107
|
+
// `task_run` runs ledger; `task_run` retreats to a pure runs ledger that only SUPPLIES run-level preview.
|
|
108
|
+
/**
|
|
109
|
+
* E16 — list the caller's sessions (CC /resume picker), keyset-paginated newest-first by last activity.
|
|
110
|
+
* Session IDENTITY/enumeration comes from `session_meta` (owner-filtered on the service's OWN owner column);
|
|
111
|
+
* a LEFT JOIN onto the `task_run` runs ledger supplies run-level PREVIEW (latest run's objective/status,
|
|
112
|
+
* run count) but never gates which sessions exist — a session with zero runs still lists (preview null/0).
|
|
113
|
+
* Keyset on `(updated_at, session_id)`; `updated_at` is the session's last-activity marker (touch on every
|
|
114
|
+
* run/append). The shipped {@link SessionSummary} shape is preserved (additive): `lastActivityAt` =
|
|
115
|
+
* `session_meta.updated_at`, `firstActivityAt` = `session_meta.created_at`.
|
|
116
|
+
*/
|
|
67
117
|
async listSessions(opts) {
|
|
68
118
|
const params = [];
|
|
69
119
|
const where = [];
|
|
70
120
|
if (opts.owner) {
|
|
71
|
-
where.push("sm.owner = ?");
|
|
121
|
+
where.push("sm.owner = ?"); // idx_owner — the per-user view (a principal is PINNED to its own at the route)
|
|
72
122
|
params.push(opts.owner);
|
|
73
123
|
}
|
|
74
124
|
if (opts.q) {
|
|
125
|
+
// `?q=` search: match sessions where ANY run's objective preview contains q (EXISTS on the
|
|
126
|
+
// runs ledger — better recall than latest-run-only, still session-granular). LOWER-LIKE = collation-proof
|
|
127
|
+
// case-insensitivity (TiDB table collation may be _bin); escapeLike + ESCAPE pins the wildcard semantics
|
|
128
|
+
// (deployment baseline: default sql_mode — under NO_BACKSLASH_ESCAPES the '\\' literal would break, a
|
|
129
|
+
// known escapeLike-family boundary shared with the tool-result store).
|
|
130
|
+
// No tr.owner predicate BY DESIGN: the outer WHERE already proves the session is the caller's;
|
|
131
|
+
// a same-session_id foreign-owner run requires DB write access to fabricate (session_meta PK + uuidv7),
|
|
132
|
+
// and an owner predicate would drop the LEGITIMATE mixed-owner form (anonymous run → principal attach,
|
|
133
|
+
// the E21 diverged-owner case) — recall loss for a 1-bit defense against an attacker who can already write.
|
|
75
134
|
where.push("EXISTS (SELECT 1 FROM task_run tr WHERE tr.session_id = sm.session_id AND LOWER(tr.objective_preview) LIKE LOWER(?) ESCAPE '\\\\')");
|
|
76
135
|
params.push(`%${escapeLike(opts.q)}%`);
|
|
77
136
|
}
|
|
78
137
|
if (opts.cursor) {
|
|
79
|
-
where.push("(sm.updated_at < ? OR (sm.updated_at = ? AND sm.session_id < ?))");
|
|
138
|
+
where.push("(sm.updated_at < ? OR (sm.updated_at = ? AND sm.session_id < ?))"); // keyset: strictly older, ties by id
|
|
80
139
|
params.push(new Date(opts.cursor.lastActivityAt), new Date(opts.cursor.lastActivityAt), opts.cursor.sessionId);
|
|
81
140
|
}
|
|
141
|
+
// The run-preview JOIN: the LATEST run per session (rn=1, ties broken by task_id so EXACTLY one row joins —
|
|
142
|
+
// a bare MAX(created_at) JOIN would fan out the outer row when two runs share a created_at) + a count rollup.
|
|
143
|
+
// A LEFT JOIN keeps sessions with NO runs (preview NULL, run_count 0). Bounded by per-tenant data today.
|
|
82
144
|
const sql = "SELECT sm.session_id, sm.owner, sm.updated_at AS last_activity, sm.created_at AS first_activity, sm.title, " +
|
|
83
145
|
" COALESCE(r.run_count, 0) AS run_count, latest.objective_preview, latest.status, latest.task_id AS last_run_id " +
|
|
84
146
|
"FROM session_meta sm " +
|
|
85
147
|
"LEFT JOIN (SELECT session_id, COUNT(*) AS run_count FROM task_run GROUP BY session_id) r ON r.session_id = sm.session_id " +
|
|
86
|
-
"LEFT JOIN (SELECT session_id, objective_preview, status, task_id FROM (" +
|
|
148
|
+
"LEFT JOIN (SELECT session_id, objective_preview, status, task_id FROM (" + // K-5c: carry the rn=1 run's task_id
|
|
87
149
|
" SELECT session_id, objective_preview, status, task_id, " +
|
|
88
150
|
" ROW_NUMBER() OVER (PARTITION BY session_id ORDER BY created_at DESC, task_id DESC) AS rn FROM task_run" +
|
|
89
151
|
" ) lr WHERE rn = 1) latest ON latest.session_id = sm.session_id " +
|
|
@@ -99,33 +161,55 @@ export class TiDBSessionStore {
|
|
|
99
161
|
runCount: Number(r.run_count),
|
|
100
162
|
objectivePreview: r.objective_preview ?? null,
|
|
101
163
|
lastStatus: r.status == null ? "" : String(r.status),
|
|
102
|
-
lastRunId: r.last_run_id ?? null,
|
|
103
|
-
title: r.title ?? null,
|
|
164
|
+
lastRunId: r.last_run_id ?? null, // K-5c: latest run id = the live-tail re-attach anchor (null if no runs)
|
|
165
|
+
title: r.title ?? null, // auto-generated title (null = not yet titled)
|
|
104
166
|
}));
|
|
105
167
|
}
|
|
168
|
+
/** Cheap pre-LLM probe: "none" = session_meta row not yet REGISTERED (createRun fires
|
|
169
|
+
* before the Runner's session acquire — the titler retries), "untitled" / "titled" as read. */
|
|
106
170
|
async probeTitle(sessionId) {
|
|
107
171
|
const [rows] = await this.pool.query("SELECT title FROM session_meta WHERE session_id = ?", [sessionId]);
|
|
108
172
|
if (!rows[0])
|
|
109
173
|
return "none";
|
|
110
174
|
return rows[0].title == null ? "untitled" : "titled";
|
|
111
175
|
}
|
|
176
|
+
/** Write-once auto-title: lands ONLY when title IS NULL (a concurrent/prior title wins;
|
|
177
|
+
* the user-facing rename layer is BFF-side). Returns whether this call set it. */
|
|
112
178
|
async setTitleIfNull(sessionId, title) {
|
|
113
179
|
const [r] = await this.pool.query("UPDATE session_meta SET title = ? WHERE session_id = ? AND title IS NULL", [title, sessionId]);
|
|
114
180
|
return r.affectedRows > 0;
|
|
115
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* E17 — fork a session: copy its WHOLE durable history to a NEW uuidv7 session owned by `owner`, mirroring
|
|
184
|
+
* core's `PgSessionRepo.fork`. Entry ids / parent links are per-session and copy VERBATIM; only `seq` is
|
|
185
|
+
* re-densified from 0 in the new session. The new leaf = the source leaf (session_meta.leaf_id).
|
|
186
|
+
* Returns the new sessionId, or null if the source does not exist (the route maps that to 404).
|
|
187
|
+
*
|
|
188
|
+
* 🔴 FULL history, NOT `wake().getEntries()` (adversarial-review MEDIUM): `wake` loads only the bounded tail
|
|
189
|
+
* `[floor..leaf]` from the latest compaction floor (F3), so forking via it would copy only the active tail of a
|
|
190
|
+
* COMPACTED session and SILENTLY LOSE all pre-compaction history (contradicting the whole-history-copy intent).
|
|
191
|
+
* We instead SELECT the entire `session_event` log ordered by seq — every durable entry, compaction floor
|
|
192
|
+
* bypassed — and re-seq it dense from 0 into the new session, ids/parents/payload byte-verbatim.
|
|
193
|
+
*/
|
|
116
194
|
async fork(sourceId, owner) {
|
|
195
|
+
// The source's authoritative leaf (and existence check) come from session_meta — null meta ⇒ unknown source.
|
|
117
196
|
const [metaRows] = await this.pool.query("SELECT leaf_id FROM session_meta WHERE session_id = ?", [sourceId]);
|
|
118
197
|
if (!metaRows[0])
|
|
119
198
|
return null;
|
|
120
199
|
const leafId = metaRows[0].leaf_id ?? null;
|
|
200
|
+
// FULL durable history (compaction floor bypassed) — every entry, oldest first. payload is the stored entry blob.
|
|
121
201
|
const [eventRows] = await this.pool.query("SELECT entry_id, parent_id, type, payload, ts FROM session_event WHERE session_id = ? ORDER BY seq ASC", [sourceId]);
|
|
122
202
|
const newId = uuidv7();
|
|
123
203
|
const now = new Date();
|
|
124
204
|
const conn = await this.pool.getConnection();
|
|
125
205
|
try {
|
|
126
206
|
await conn.beginTransaction();
|
|
207
|
+
// Entries land BEFORE the session_meta row (mirrors core BUG-1): the meta row is the commit point —
|
|
208
|
+
// a mid-fork failure leaves orphan events with NO meta row (invisible to wake), never a half-session.
|
|
127
209
|
for (let seq = 0; seq < eventRows.length; seq++) {
|
|
128
210
|
const r = eventRows[seq];
|
|
211
|
+
// Copy the payload blob VERBATIM (string or already-parsed object); re-stringify only an object so the
|
|
212
|
+
// INSERT binds a JSON string. entry_id/parent_id/type/ts are carried over unchanged; only seq is re-densified.
|
|
129
213
|
const payload = typeof r.payload === "string" ? r.payload : JSON.stringify(r.payload);
|
|
130
214
|
await conn.query("INSERT INTO session_event (session_id, seq, entry_id, parent_id, type, payload, ts) VALUES (?,?,?,?,?,?,?)", [newId, seq, r.entry_id, r.parent_id ?? null, r.type, payload, r.ts]);
|
|
131
215
|
}
|
|
@@ -138,6 +222,7 @@ export class TiDBSessionStore {
|
|
|
138
222
|
await conn.rollback();
|
|
139
223
|
}
|
|
140
224
|
catch {
|
|
225
|
+
/* ignore */
|
|
141
226
|
}
|
|
142
227
|
throw err instanceof SessionError ? err : new SessionError("storage", `fork of session ${sourceId} failed`, err instanceof Error ? err : undefined);
|
|
143
228
|
}
|
|
@@ -145,46 +230,100 @@ export class TiDBSessionStore {
|
|
|
145
230
|
conn.release();
|
|
146
231
|
}
|
|
147
232
|
}
|
|
233
|
+
/**
|
|
234
|
+
* 2c session-sync — EXPORT a session's full durable entry log for cross-backend migration. This is the
|
|
235
|
+
* READ half of {@link fork}, surfaced as the core `SessionRepo.exportEntries` seam: the WHOLE `session_event` log,
|
|
236
|
+
* oldest-first, ids/parents/payload/ts VERBATIM, with the compaction floor BYPASSED (NOT `wake().getEntries()`,
|
|
237
|
+
* which returns only the bounded `[floor..leaf]` tail and would silently truncate pre-floor history — see fork's
|
|
238
|
+
* note at :149-152). Returns `null` if the source does not exist (no `session_meta` row), mirroring fork's null
|
|
239
|
+
* semantics; an existing-but-empty session returns `[]`. The owner is NOT exported — import re-stamps it (§9).
|
|
240
|
+
*/
|
|
148
241
|
async exportEntries(sessionId) {
|
|
242
|
+
// Existence check = session_meta (a session with zero events but a meta row is a real, empty session ⇒ []).
|
|
149
243
|
const [metaRows] = await this.pool.query("SELECT 1 FROM session_meta WHERE session_id = ?", [sessionId]);
|
|
150
244
|
if (!metaRows[0])
|
|
151
245
|
return null;
|
|
246
|
+
// FULL durable log (compaction floor bypassed) — every entry, oldest first. payload is the stored entry blob.
|
|
152
247
|
const [eventRows] = await this.pool.query("SELECT entry_id, parent_id, type, payload, ts FROM session_event WHERE session_id = ? ORDER BY seq ASC", [sessionId]);
|
|
248
|
+
// The export contract is `SessionTreeEntry[]` (the full entry blob), not the column projection. The stored
|
|
249
|
+
// `payload` IS the verbatim entry (id/parentId/timestamp + variant fields) — parse a string blob, pass an
|
|
250
|
+
// already-parsed object (mysql2 returns JSON columns parsed) through.
|
|
153
251
|
return eventRows.map((r) => (typeof r.payload === "string" ? JSON.parse(r.payload) : r.payload));
|
|
154
252
|
}
|
|
253
|
+
/**
|
|
254
|
+
* 2c session-sync P1d-α (PULL streaming) — the IDS-ONLY projection of {@link exportEntries}: the entry ids of the
|
|
255
|
+
* WHOLE durable log, oldest-first (`SELECT entry_id … ORDER BY seq ASC`), with no payloads. Small even for a huge
|
|
256
|
+
* session, so the PULL `GET /sync/manifest` can carry it (and feed it to `classifySyncRelationshipByIds`) instead of
|
|
257
|
+
* the entries. Returns `null` if the source does not exist (no `session_meta` row), mirroring {@link exportEntries}.
|
|
258
|
+
*/
|
|
155
259
|
async listEntryIds(sessionId) {
|
|
156
260
|
const [metaRows] = await this.pool.query("SELECT 1 FROM session_meta WHERE session_id = ?", [sessionId]);
|
|
157
261
|
if (!metaRows[0])
|
|
158
|
-
return null;
|
|
262
|
+
return null; // no such session (mirror exportEntries' null)
|
|
159
263
|
const [rows] = await this.pool.query("SELECT entry_id FROM session_event WHERE session_id = ? ORDER BY seq ASC", [sessionId]);
|
|
160
264
|
return rows.map((r) => String(r.entry_id));
|
|
161
265
|
}
|
|
266
|
+
/**
|
|
267
|
+
* 2c session-sync P1d-α (PULL streaming) — STREAM the WHOLE durable log (compaction floor bypassed), oldest-first,
|
|
268
|
+
* as an async generator that KEYSET-PAGES the `session_event` table: mysql2 buffers a plain SELECT, so a 1M-row
|
|
269
|
+
* session would OOM if read in one shot. Instead the generator loops `… WHERE seq > cursor ORDER BY seq ASC LIMIT K`
|
|
270
|
+
* (K = `opts.batchSize ?? 500`), advancing the cursor to the last `seq` of each batch, until a SHORT page (fewer than
|
|
271
|
+
* K rows) ends the stream. Each batch is its OWN `pool.query` (a pooled connection acquired + released per batch), so
|
|
272
|
+
* a slow NDJSON consumer can NEVER pin one connection across the whole stream (the clay-mandated concurrency
|
|
273
|
+
* discipline). Start cursor = `opts.afterSeq ?? -1` (seq is 0-based dense, so `> -1` = from seq 0).
|
|
274
|
+
*
|
|
275
|
+
* Resolves to `null` (NOT an empty async-iterable) when the session does not exist — checked via `session_meta`
|
|
276
|
+
* EAGERLY here (before returning the iterable), exactly like {@link exportEntries}, so a missing session is distinct
|
|
277
|
+
* from a real-but-empty one (an empty stream). payload is parsed like {@link exportEntries} (string → JSON.parse,
|
|
278
|
+
* object passthrough). The check is eager (the method is async) because a generator body is lazy — it can't decide
|
|
279
|
+
* `null` vs. iterable on its own first pull.
|
|
280
|
+
*/
|
|
162
281
|
async exportEntriesStream(sessionId, opts) {
|
|
163
282
|
const [metaRows] = await this.pool.query("SELECT 1 FROM session_meta WHERE session_id = ?", [sessionId]);
|
|
164
283
|
if (!metaRows[0])
|
|
165
|
-
return null;
|
|
284
|
+
return null; // no such session (mirror exportEntries' null) — NOT an empty iterable
|
|
166
285
|
const pool = this.pool;
|
|
167
286
|
const batchSize = opts?.batchSize ?? 500;
|
|
168
287
|
async function* gen() {
|
|
169
|
-
let cursor = opts?.afterSeq ?? -1;
|
|
288
|
+
let cursor = opts?.afterSeq ?? -1; // seq is 0-based dense → > -1 starts at seq 0
|
|
170
289
|
for (;;) {
|
|
290
|
+
// ONE pooled query per batch (NOT a held connection across the whole stream) — keyset on (session_id, seq).
|
|
171
291
|
const [rows] = await pool.query("SELECT seq, payload FROM session_event WHERE session_id = ? AND seq > ? ORDER BY seq ASC LIMIT ?", [sessionId, cursor, batchSize]);
|
|
172
292
|
for (const r of rows) {
|
|
173
293
|
const p = r.payload;
|
|
174
294
|
yield (typeof p === "string" ? JSON.parse(p) : p);
|
|
175
295
|
}
|
|
176
296
|
if (rows.length < batchSize)
|
|
177
|
-
return;
|
|
178
|
-
cursor = Number(rows[rows.length - 1].seq);
|
|
297
|
+
return; // short page = end of log (no further round-trip)
|
|
298
|
+
cursor = Number(rows[rows.length - 1].seq); // advance the keyset cursor to the last seq seen
|
|
179
299
|
}
|
|
180
300
|
}
|
|
181
301
|
return gen();
|
|
182
302
|
}
|
|
303
|
+
/**
|
|
304
|
+
* 2c session-sync — IMPORT a verbatim entry log (from another backend's {@link exportEntries}) into a
|
|
305
|
+
* (typically NEW) `sessionId`. This is the WRITE half of {@link fork}, surfaced as `SessionRepo.importEntries`,
|
|
306
|
+
* with two 2c-mandated differences from fork:
|
|
307
|
+
* (a) OWNER RE-STAMP (§9): `owner` is the AUTHENTICATED importing principal passed by the caller; the bundle's
|
|
308
|
+
* own owner is NEVER trusted (a local push is always owner=null → would become an ownerless cloud session
|
|
309
|
+
* any anonymous caller could attach to). Mirrors the fork route's server-side `scope.gateOwner`.
|
|
310
|
+
* (b) the single INVARIANT GATE is core's {@link validateEntriesForImport} (unique ids / parent-before-child /
|
|
311
|
+
* exactly-one-root / leaf-resolvable / structural refs present) — we persist its VERBATIM output and do NOT
|
|
312
|
+
* re-implement any of those checks in SQL ([[core-service-boundary]]; the same discipline fork's hand-SQL
|
|
313
|
+
* was warned about). A violation throws `SessionError("invalid_session")` and NOTHING is written.
|
|
314
|
+
* The write itself mirrors fork: re-seq dense from 0, entry_id/parent_id/payload/ts verbatim, in ONE txn with
|
|
315
|
+
* `session_meta` LAST as the commit point (a mid-import crash leaves orphan events with no meta = invisible to
|
|
316
|
+
* wake, never a half-session). The leaf is the replayed leaf (core's `leafIdAfterEntry`), matching core's import.
|
|
317
|
+
*/
|
|
183
318
|
async importEntries(sessionId, owner, entries) {
|
|
319
|
+
// Fail-CLOSED invariant gate FIRST (before any write) — returns the entries verbatim or throws invalid_session.
|
|
184
320
|
const validated = validateEntriesForImport(entries);
|
|
185
321
|
const conn = await this.pool.getConnection();
|
|
186
322
|
try {
|
|
187
323
|
await conn.beginTransaction();
|
|
324
|
+
// The shared insert body (entries re-seq dense from 0, then session_meta LAST as the commit point). A plain
|
|
325
|
+
// INSERT — assumes a FRESH sessionId; a duplicate (session already present) crashes on the PK. replaceEntries
|
|
326
|
+
// is the idempotent-over-existing path (it DELETEs first inside the same txn).
|
|
188
327
|
await this.insertValidatedLog(conn, sessionId, owner, validated);
|
|
189
328
|
await conn.commit();
|
|
190
329
|
}
|
|
@@ -193,22 +332,36 @@ export class TiDBSessionStore {
|
|
|
193
332
|
await conn.rollback();
|
|
194
333
|
}
|
|
195
334
|
catch {
|
|
335
|
+
/* ignore */
|
|
196
336
|
}
|
|
197
337
|
if (err instanceof SessionError)
|
|
198
|
-
throw err;
|
|
338
|
+
throw err; // invalid_session (gate) / conflict — pass through
|
|
199
339
|
throw new SessionError("storage", `import of session ${sessionId} failed`, err instanceof Error ? err : undefined);
|
|
200
340
|
}
|
|
201
341
|
finally {
|
|
202
342
|
conn.release();
|
|
203
343
|
}
|
|
204
344
|
}
|
|
345
|
+
/**
|
|
346
|
+
* 2c session-sync (§7/§8) — IDEMPOTENT REPLACE: purge any existing conversation log for `sessionId` then re-import
|
|
347
|
+
* `entries` verbatim under `owner`, ALL in ONE txn. Unlike {@link importEntries} (a plain INSERT that crashes on a
|
|
348
|
+
* duplicate PK when the session already exists at the destination), replace is safe to run OVER an existing session
|
|
349
|
+
* — the §7 fast-forward path and a §8 retry-to-completion both land on it. The validate gate runs FIRST (fail-closed,
|
|
350
|
+
* before any write), then DELETE events + the OWNER-GUARDED meta delete (defense-in-depth, mirrors {@link deleteSession}
|
|
351
|
+
* — a wrong-owner caller can never replace another tenant's session), then the SAME shared insert body importEntries
|
|
352
|
+
* uses (so there is ONE source of the insert logic, no copy-paste drift). session_meta is still written LAST = commit
|
|
353
|
+
* point, so a mid-replace crash (post-delete, pre-insert) leaves orphan-free state (no meta = invisible to wake).
|
|
354
|
+
*/
|
|
205
355
|
async replaceEntries(sessionId, owner, entries) {
|
|
206
|
-
const validated = validateEntriesForImport(entries);
|
|
356
|
+
const validated = validateEntriesForImport(entries); // fail-closed gate BEFORE any write (mirrors importEntries)
|
|
207
357
|
const conn = await this.pool.getConnection();
|
|
208
358
|
try {
|
|
209
359
|
await conn.beginTransaction();
|
|
360
|
+
// Purge the existing log first (idempotent: a missing session deletes 0 rows). Events first, then the
|
|
361
|
+
// owner-guarded meta delete (`owner <=> ?` — a legacy null-owner session matches owner=null), defense-in-depth.
|
|
210
362
|
await conn.query("DELETE FROM session_event WHERE session_id = ?", [sessionId]);
|
|
211
363
|
await conn.query("DELETE FROM session_meta WHERE session_id = ? AND owner <=> ?", [sessionId, owner]);
|
|
364
|
+
// …then re-insert the validated log (entries + meta-as-commit-point) via the SHARED body.
|
|
212
365
|
await this.insertValidatedLog(conn, sessionId, owner, validated);
|
|
213
366
|
await conn.commit();
|
|
214
367
|
}
|
|
@@ -217,35 +370,70 @@ export class TiDBSessionStore {
|
|
|
217
370
|
await conn.rollback();
|
|
218
371
|
}
|
|
219
372
|
catch {
|
|
373
|
+
/* ignore */
|
|
220
374
|
}
|
|
221
375
|
if (err instanceof SessionError)
|
|
222
|
-
throw err;
|
|
376
|
+
throw err; // invalid_session (gate) / conflict — pass through
|
|
223
377
|
throw new SessionError("storage", `replace of session ${sessionId} failed`, err instanceof Error ? err : undefined);
|
|
224
378
|
}
|
|
225
379
|
finally {
|
|
226
380
|
conn.release();
|
|
227
381
|
}
|
|
228
382
|
}
|
|
383
|
+
/**
|
|
384
|
+
* Shared insert half of {@link importEntries} / {@link replaceEntries} (ONE source of the insert logic, no drift):
|
|
385
|
+
* write the validated entries re-seq dense from 0 (entry_id/parent_id/type/ts verbatim), THEN session_meta LAST as
|
|
386
|
+
* the commit point. Runs inside the caller's open transaction (`conn`) so the caller owns begin/commit/rollback.
|
|
387
|
+
* `validated` is the OUTPUT of {@link validateEntriesForImport} (the caller runs the gate before opening the txn).
|
|
388
|
+
*/
|
|
229
389
|
async insertValidatedLog(conn, sessionId, owner, validated) {
|
|
390
|
+
// The replayed leaf (last entry's leaf target, mirroring core resolveLeafId / validateEntriesForImport).
|
|
230
391
|
let leafId = null;
|
|
231
392
|
for (const e of validated)
|
|
232
|
-
leafId = leafIdAfterEntry(e);
|
|
393
|
+
leafId = leafIdAfterEntry(e); // replayed leaf — core's exported helper (no service copy)
|
|
233
394
|
const now = new Date();
|
|
395
|
+
// Entries land BEFORE session_meta (mirrors fork / core BUG-1): the meta row is the commit point.
|
|
234
396
|
for (let seq = 0; seq < validated.length; seq++) {
|
|
235
397
|
const e = validated[seq];
|
|
398
|
+
// payload = the verbatim entry blob (stringified for the JSON column bind); entry_id/parent_id/type/ts carried
|
|
399
|
+
// over unchanged from the entry; only seq is re-densified. ts comes from the entry's own ISO timestamp.
|
|
236
400
|
await conn.query("INSERT INTO session_event (session_id, seq, entry_id, parent_id, type, payload, ts) VALUES (?,?,?,?,?,?,?)", [sessionId, seq, e.id, e.parentId ?? null, e.type, JSON.stringify(e), new Date(e.timestamp)]);
|
|
237
401
|
}
|
|
402
|
+
// session_meta LAST = commit point. owner = the RE-STAMPED authenticated principal (never the bundle's).
|
|
238
403
|
await conn.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq) VALUES (?,?,?,?,?,?)", [sessionId, owner, now, now, leafId, validated.length]);
|
|
239
404
|
}
|
|
405
|
+
/**
|
|
406
|
+
* 2c session-sync P1d-β (PUSH streaming, §15) — begin a STAGED import into `realSessionId` under the shadow id
|
|
407
|
+
* `${realSessionId}#stg-${token}`. The route mints the uuidv7 `token` (the store never calls uuidv7/Date.now itself
|
|
408
|
+
* → the staging id is deterministic + the route owns the lease key). Returns a {@link TiDBStagingHandle} the route
|
|
409
|
+
* streams entry batches into ({@link StagingHandle.appendBatch}) then commits ({@link StagingHandle.commit}, the
|
|
410
|
+
* atomic swap) or aborts. No row is touched here — the handle is a stateless cursor over this pool.
|
|
411
|
+
*/
|
|
240
412
|
beginImportStaging(realSessionId, token) {
|
|
241
413
|
return new TiDBStagingHandle(this.pool, realSessionId, stagingIdFor(realSessionId, token));
|
|
242
414
|
}
|
|
415
|
+
/**
|
|
416
|
+
* 2c session-sync P1d-β (staged-row inspection) — read the rows STAGED under `stagingId` (NO session_meta probe — a
|
|
417
|
+
* staging id has no meta row by construction, so {@link exportEntries} would return null), oldest-first. The Phase-B
|
|
418
|
+
* import gate is now the per-line {@link StreamingImportValidator} (core 1.143.0) run during the stream, so this
|
|
419
|
+
* is no longer the validation path; it remains the staged-row inspection seam (tests/diagnostics). Returns `[]` for an
|
|
420
|
+
* empty/unknown staging id.
|
|
421
|
+
*/
|
|
243
422
|
async readStagedEntries(stagingId) {
|
|
244
423
|
const [rows] = await this.pool.query("SELECT payload FROM session_event WHERE session_id = ? ORDER BY seq ASC", [stagingId]);
|
|
245
424
|
return rows.map((r) => (typeof r.payload === "string" ? JSON.parse(r.payload) : r.payload));
|
|
246
425
|
}
|
|
426
|
+
/**
|
|
427
|
+
* 2c session-sync P1d-β (§4) — reaper for ABANDONED staging sessions: DELETE session_event rows under a staging id
|
|
428
|
+
* (`session_id LIKE '%#stg-%'`) that has NO session_meta (never committed → invisible orphans) AND whose OLDEST
|
|
429
|
+
* staged row is older than the grace window (an IN-FLIGHT stream keeps appending fresh `ts` rows → its MIN(ts) is
|
|
430
|
+
* recent → it is NOT reaped, exactly like sweepOrphanBlobs' created_at grace). Per-staging-id, so a slow but live
|
|
431
|
+
* stream is never reaped while another abandoned one is. Returns the affected row count. Driven by main.ts's reaper.
|
|
432
|
+
*/
|
|
247
433
|
async sweepStagingSessions() {
|
|
248
434
|
const cutoff = new Date(Date.now() - STAGING_GC_GRACE_MS);
|
|
435
|
+
// Candidate staging ids = those with rows older than the cutoff AND no meta row. GROUP BY so an in-flight id (its
|
|
436
|
+
// MIN(ts) recent) is excluded; LEFT JOIN session_meta so a (defensively) committed staging id is never touched.
|
|
249
437
|
const [cands] = await this.pool.query("SELECT se.session_id FROM session_event se LEFT JOIN session_meta sm ON sm.session_id = se.session_id " +
|
|
250
438
|
"WHERE se.session_id LIKE ? AND sm.session_id IS NULL " +
|
|
251
439
|
"GROUP BY se.session_id HAVING MIN(se.ts) < ?", [`%${STAGING_ID_MARKER}%`, cutoff]);
|
|
@@ -256,11 +444,28 @@ export class TiDBSessionStore {
|
|
|
256
444
|
}
|
|
257
445
|
return removed;
|
|
258
446
|
}
|
|
447
|
+
/**
|
|
448
|
+
* E21 — purge a session's CONVERSATION HISTORY (session_meta + session_event). Idempotent: a missing
|
|
449
|
+
* session is a no-op (returns false). The service's OWN run-ledger / checkpoint / tool-result rows are
|
|
450
|
+
* purged separately by the route's `purgeSession` coordinator — this method owns only the session
|
|
451
|
+
* abstraction's tables (the `SessionRepo.delete` half).
|
|
452
|
+
*
|
|
453
|
+
* `owner` is the single-DB-fleet null-safe SQL owner guard on `session_meta.owner` (defense-in-depth — the
|
|
454
|
+
* route already owner-gates; `<=>` so a legacy null-owner session matches owner=null). session_event carries
|
|
455
|
+
* no owner column, so its purge is GATED on the owner-matched meta delete: if the meta DELETE matched 0 rows
|
|
456
|
+
* (wrong owner / already gone) we skip the event delete — so a wrong-owner caller can never strand-delete
|
|
457
|
+
* another tenant's events. Meta is deleted FIRST (mirrors core BUG-2: once it's gone, wake is `null`
|
|
458
|
+
* regardless of leftover events; if a crash interleaves, orphan events with NO meta are invisible, never a
|
|
459
|
+
* half-session), and the event purge only runs after that owner-matched commit point.
|
|
460
|
+
*/
|
|
259
461
|
async deleteSession(sessionId, owner) {
|
|
260
462
|
const conn = await this.pool.getConnection();
|
|
261
463
|
try {
|
|
262
464
|
await conn.beginTransaction();
|
|
465
|
+
// Meta row FIRST + owner-guarded: once it's gone, wake is `null` regardless of leftover events.
|
|
263
466
|
const [metaRes] = await conn.query("DELETE FROM session_meta WHERE session_id = ? AND owner <=> ?", [sessionId, owner]);
|
|
467
|
+
// Only purge events when the OWNER-matched meta row was removed — gates the un-owner-columned event table
|
|
468
|
+
// through the meta owner check (a wrong-owner / already-gone session removes 0 meta rows → skip the events).
|
|
264
469
|
if (metaRes.affectedRows > 0)
|
|
265
470
|
await conn.query("DELETE FROM session_event WHERE session_id = ?", [sessionId]);
|
|
266
471
|
await conn.commit();
|
|
@@ -271,6 +476,7 @@ export class TiDBSessionStore {
|
|
|
271
476
|
await conn.rollback();
|
|
272
477
|
}
|
|
273
478
|
catch {
|
|
479
|
+
/* ignore */
|
|
274
480
|
}
|
|
275
481
|
throw err;
|
|
276
482
|
}
|
|
@@ -279,30 +485,53 @@ export class TiDBSessionStore {
|
|
|
279
485
|
}
|
|
280
486
|
}
|
|
281
487
|
get size() {
|
|
488
|
+
// Best-effort: durable store reloads per acquire (no long-lived cache), so report in-flight only.
|
|
282
489
|
return this.pending.size;
|
|
283
490
|
}
|
|
284
491
|
dispose() {
|
|
492
|
+
// The pool is owned by main.ts and closed there; nothing process-local to release.
|
|
285
493
|
this.pending.clear();
|
|
286
494
|
}
|
|
287
495
|
}
|
|
496
|
+
/**
|
|
497
|
+
* 2c session-sync P1d-β (PUSH streaming, §15 + §4) — a TiDB {@link StagingHandle}: stream entry batches into a SHADOW
|
|
498
|
+
* `session_event` partition (the staging id, NO meta row → invisible to wake), then atomically SWAP them into the real
|
|
499
|
+
* session id. Server memory is bounded by the in-flight batch (one txn per batch), not the whole log. The swap is a
|
|
500
|
+
* row-key RENAME (`UPDATE session_event SET session_id`) — metadata-only, NO payload re-copy — so the commit cost is
|
|
501
|
+
* the rename + a DELETE of the (purged) dst rows, in ONE txn, with session_meta written LAST = the sole commit point.
|
|
502
|
+
*/
|
|
288
503
|
class TiDBStagingHandle {
|
|
289
504
|
pool;
|
|
290
505
|
realSessionId;
|
|
291
506
|
stagingId;
|
|
507
|
+
/** Running DENSE seq counter for the staging rows. Advanced ONLY for a logically-NEW entry id (tracked in `seen`), so
|
|
508
|
+
* a re-streamed entry (Phase-B resume) is skipped and the staged seqs stay dense 0..N-1 (NO holes that would collide
|
|
509
|
+
* with a future F2 leaf_seq append on the committed session). */
|
|
292
510
|
nextSeq = 0;
|
|
511
|
+
/** Entry ids already staged by THIS handle — a resume re-streams from the start, so dedup keeps seqs dense + idempotent. */
|
|
293
512
|
seen = new Set();
|
|
294
513
|
constructor(pool, realSessionId, stagingId) {
|
|
295
514
|
this.pool = pool;
|
|
296
515
|
this.realSessionId = realSessionId;
|
|
297
516
|
this.stagingId = stagingId;
|
|
298
517
|
}
|
|
518
|
+
/** Append a batch to the staging id, re-seq'd DENSE from the running counter (skipping ids already staged), IDEMPOTENTLY
|
|
519
|
+
* (ON DUPLICATE KEY UPDATE on the `(session_id, entry_id)` uk is the durable backstop too). One txn per batch
|
|
520
|
+
* (bounded). NO meta row is written (the staging id stays invisible). ts is the entry's own ISO timestamp (verbatim). */
|
|
299
521
|
async appendBatch(entries) {
|
|
522
|
+
// Build the NEW rows (skip already-staged ids — resume dedup keeps seqs dense), then ONE multi-row INSERT for the
|
|
523
|
+
// whole batch (bounded → bounded packet/txn) instead of N round-trips. ON DUPLICATE KEY UPDATE is the durable
|
|
524
|
+
// backstop for the rare case a dup slips past `seen` (e.g. a concurrent re-stream).
|
|
300
525
|
const rows = [];
|
|
301
526
|
const tuples = [];
|
|
302
527
|
for (const e of entries) {
|
|
303
528
|
if (this.seen.has(e.id))
|
|
304
529
|
continue;
|
|
305
530
|
tuples.push("(?,?,?,?,?,?,?)");
|
|
531
|
+
// 对抗复查 A-1(CRITICAL):staging 行的 ts 必须是「行插入时间」——写源 entry 的历史 timestamp 会让
|
|
532
|
+
// reaper 的 MIN(ts)<cutoff 判据把「正在导入的历史会话」当成弃置 staging 删掉(导入 1 小时前的会话
|
|
533
|
+
// =导入中被 reap)。真时间源恒在 payload(e.timestamp);ts 列的唯一读消费=fork 的行拷贝(carry-over
|
|
534
|
+
// 一致)与 reaper 活性判据,写 now 零语义破坏。
|
|
306
535
|
rows.push(this.stagingId, this.nextSeq, e.id, e.parentId ?? null, e.type, JSON.stringify(e), new Date());
|
|
307
536
|
this.seen.add(e.id);
|
|
308
537
|
this.nextSeq++;
|
|
@@ -317,22 +546,40 @@ class TiDBStagingHandle {
|
|
|
317
546
|
throw err instanceof SessionError ? err : new SessionError("storage", `staged append for ${this.realSessionId} failed`, err instanceof Error ? err : undefined);
|
|
318
547
|
}
|
|
319
548
|
}
|
|
549
|
+
/**
|
|
550
|
+
* The ATOMIC swap (ONE txn), §15 commit:
|
|
551
|
+
* 1. IN-COMMIT RE-CLASSIFY (closes the Phase-A plan→commit TOCTOU): read the dst's CURRENT entry ids + the staged
|
|
552
|
+
* ids under `FOR UPDATE` (row-locks serialize a concurrent commit/run) → classifySyncRelationshipByIds. A flip
|
|
553
|
+
* to fork/stale WITHOUT `resolution:"overwrite-dst"` → ROLLBACK → throw SyncConflictError (route → 409).
|
|
554
|
+
* 2. DELETE the dst's existing session_event rows (the replaceEntries purge).
|
|
555
|
+
* 3. UPDATE session_event SET session_id = real WHERE session_id = staging (the row-key RENAME — metadata only).
|
|
556
|
+
* 4. INSERT/UPSERT session_meta(real, owner, leaf_id=replayedLeaf, leaf_seq=N) LAST = the sole commit point.
|
|
557
|
+
* A crash before COMMIT: the DELETE+rename roll back (dst untouched) and the staging rows keep no meta = invisible
|
|
558
|
+
* orphans (reaped by sweepStagingSessions). `owner` is the re-stamped importing principal (§9).
|
|
559
|
+
*/
|
|
320
560
|
async commit(owner, opts) {
|
|
321
561
|
const conn = await this.pool.getConnection();
|
|
322
562
|
try {
|
|
323
563
|
await conn.beginTransaction();
|
|
564
|
+
// (1) in-commit re-classify — read BOTH logs' ids oldest-first under FOR UPDATE (serialize against a racing
|
|
565
|
+
// commit/append to the dst). The staged ids ARE the source (already dense 0..N-1 under the staging id).
|
|
324
566
|
const [stagedRows] = await conn.query("SELECT entry_id FROM session_event WHERE session_id = ? ORDER BY seq ASC FOR UPDATE", [this.stagingId]);
|
|
325
567
|
const [dstRows] = await conn.query("SELECT entry_id FROM session_event WHERE session_id = ? ORDER BY seq ASC FOR UPDATE", [this.realSessionId]);
|
|
326
568
|
const [dstMeta] = await conn.query("SELECT 1 FROM session_meta WHERE session_id = ?", [this.realSessionId]);
|
|
327
569
|
const srcIds = stagedRows.map((r) => String(r.entry_id));
|
|
328
|
-
const dstIds = dstMeta[0] ? dstRows.map((r) => String(r.entry_id)) : null;
|
|
570
|
+
const dstIds = dstMeta[0] ? dstRows.map((r) => String(r.entry_id)) : null; // no dst meta ⇒ fresh (no dst log to relate to)
|
|
329
571
|
const rel = classifySyncRelationshipByIds(srcIds, dstIds);
|
|
330
572
|
if ((rel.relation === "fork" || rel.relation === "stale") && opts?.resolution !== "overwrite-dst") {
|
|
331
573
|
await conn.rollback();
|
|
332
574
|
throw new SyncConflictError(this.realSessionId, rel);
|
|
333
575
|
}
|
|
576
|
+
// (2) purge the dst's existing log (the replaceEntries purge; idempotent — fresh deletes 0).
|
|
334
577
|
await conn.query("DELETE FROM session_event WHERE session_id = ?", [this.realSessionId]);
|
|
578
|
+
// (3) RENAME the staged rows onto the real session id (row-key UPDATE — metadata only, NO payload re-copy). seq is
|
|
579
|
+
// already dense 0..N-1 under the staging id (appendBatch re-seq'd it), so no re-densify is needed on rename.
|
|
335
580
|
await conn.query("UPDATE session_event SET session_id = ? WHERE session_id = ?", [this.realSessionId, this.stagingId]);
|
|
581
|
+
// (4) session_meta LAST = the sole commit point. The replayed leaf = the last staged entry's leaf target — read
|
|
582
|
+
// the last staged payload (now renamed) to compute leafIdAfterEntry (core's helper; no service re-statement).
|
|
336
583
|
const [lastRow] = await conn.query("SELECT payload FROM session_event WHERE session_id = ? ORDER BY seq DESC LIMIT 1", [this.realSessionId]);
|
|
337
584
|
let leafId = null;
|
|
338
585
|
if (lastRow[0]) {
|
|
@@ -350,7 +597,7 @@ class TiDBStagingHandle {
|
|
|
350
597
|
try {
|
|
351
598
|
await conn.rollback();
|
|
352
599
|
}
|
|
353
|
-
catch { }
|
|
600
|
+
catch { /* ignore */ }
|
|
354
601
|
if (err instanceof SyncConflictError || err instanceof SessionError)
|
|
355
602
|
throw err;
|
|
356
603
|
throw new SessionError("storage", `staged commit for ${this.realSessionId} failed`, err instanceof Error ? err : undefined);
|
|
@@ -359,6 +606,7 @@ class TiDBStagingHandle {
|
|
|
359
606
|
conn.release();
|
|
360
607
|
}
|
|
361
608
|
}
|
|
609
|
+
/** Drop the staging rows (Phase B aborted before commit). Idempotent (a missing staging id deletes 0). */
|
|
362
610
|
async abort() {
|
|
363
611
|
await this.pool.query("DELETE FROM session_event WHERE session_id = ?", [this.stagingId]);
|
|
364
612
|
}
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MySQL-protocol (TiDB) `ToolResultStore` — the implementation now lives in the single-file dual-dialect
|
|
3
|
+
* `tool-result-store-sql.ts` (design/158 A12 定型半场). This file is the historical import path: the class
|
|
4
|
+
* name and ctor shape are re-exported unchanged, so store-backend.ts and every suite are untouched.
|
|
5
|
+
*/
|
|
1
6
|
export { TiDBToolResultStore, SqlToolResultStore } from "./tool-result-store-sql.js";
|
|
2
7
|
export { escapeLike } from "./sql-escape.js";
|
|
3
8
|
//# sourceMappingURL=tidb-tool-result-store.d.ts.map
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MySQL-protocol (TiDB) `ToolResultStore` — the implementation now lives in the single-file dual-dialect
|
|
3
|
+
* `tool-result-store-sql.ts` (design/158 A12 定型半场). This file is the historical import path: the class
|
|
4
|
+
* name and ctor shape are re-exported unchanged, so store-backend.ts and every suite are untouched.
|
|
5
|
+
*/
|
|
1
6
|
export { TiDBToolResultStore, SqlToolResultStore } from "./tool-result-store-sql.js";
|
|
7
|
+
// Back-compat re-export: escapeLike LIVES in sql-escape.ts (design/158 A3, backend-neutral leaf);
|
|
8
|
+
// import from there in new code.
|
|
2
9
|
export { escapeLike } from "./sql-escape.js";
|
|
3
10
|
//# sourceMappingURL=tidb-tool-result-store.js.map
|
|
@@ -1,2 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin re-export shim (design/158 A12 collapse). The real implementation is the single-file dual-dialect
|
|
3
|
+
* {@link import("./workflow-journal-store-sql.js").SqlWorkflowJournalStore} — see that file for the durable
|
|
4
|
+
* {@link WorkflowJournalStore} contract, the schema, and the TiDB/PG dialect-delta ledger. This name (and ctor
|
|
5
|
+
* arity: `(pool: MySqlPool)`) survives unchanged so every consumer (store-backend.ts, the extreme/integration/
|
|
6
|
+
* resume-fork-probe suites) is untouched.
|
|
7
|
+
*/
|
|
1
8
|
export { TiDBWorkflowJournalStore } from "./workflow-journal-store-sql.js";
|
|
2
9
|
//# sourceMappingURL=tidb-workflow-journal-store.d.ts.map
|
|
@@ -1,2 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin re-export shim (design/158 A12 collapse). The real implementation is the single-file dual-dialect
|
|
3
|
+
* {@link import("./workflow-journal-store-sql.js").SqlWorkflowJournalStore} — see that file for the durable
|
|
4
|
+
* {@link WorkflowJournalStore} contract, the schema, and the TiDB/PG dialect-delta ledger. This name (and ctor
|
|
5
|
+
* arity: `(pool: MySqlPool)`) survives unchanged so every consumer (store-backend.ts, the extreme/integration/
|
|
6
|
+
* resume-fork-probe suites) is untouched.
|
|
7
|
+
*/
|
|
1
8
|
export { TiDBWorkflowJournalStore } from "./workflow-journal-store-sql.js";
|
|
2
9
|
//# sourceMappingURL=tidb-workflow-journal-store.js.map
|
|
@@ -1,2 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin re-export shim (design/158 A12 扇出): the TiDB `WorkflowRunStore`/`WorkflowCompletionInbox`/
|
|
3
|
+
* `WorkflowNotifyJournalStore` twins now live collapsed with their PG dialect siblings in the
|
|
4
|
+
* single-file dual-dialect `workflow-run-store-sql.ts`. Class names, ctor arity, and the exported
|
|
5
|
+
* `MAX_RUN_BLOB_BYTES` / `slimOversizeRun` / `InboxWarn` surface stay EXACTLY as they were — every
|
|
6
|
+
* consumer (store-backend.ts, the DB-integration + slim-oversize-run suites) imports from THIS path
|
|
7
|
+
* unchanged.
|
|
8
|
+
*/
|
|
1
9
|
export { MAX_RUN_BLOB_BYTES, slimOversizeRun, type InboxWarn, TiDBWorkflowRunStore, TiDBWorkflowCompletionInbox, TiDBWorkflowNotifyJournalStore, } from "./workflow-run-store-sql.js";
|
|
2
10
|
//# sourceMappingURL=tidb-workflow-run-store.d.ts.map
|