@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,18 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SendUserFile — the dual-track public-link ISSUER (design: sema-deploy docs/design/S3-PUBLIC-LINKS.md,
|
|
3
|
+
* settled cross-repo 2026-07-14; tool name SendUserFile is final).
|
|
4
|
+
*
|
|
5
|
+
* The LLM hands a user a file over the web session as a URL the browser fetches DIRECTLY from S3/MinIO
|
|
6
|
+
* (server relay was rejected: double bandwidth on third-party S3 + loses CDN). Two tracks, keyed on ttl:
|
|
7
|
+
*
|
|
8
|
+
* - ttl == 0 (PERMANENT, the default): upload to the anonymous-GET public bucket under an unguessable
|
|
9
|
+
* `<scopeSeg>/<uuidv7>/<name>` key (capability URL — GET-only bucket policy, List disabled, so the prefix IS
|
|
10
|
+
* the secret; scopeSeg = hashed tenant segment, see {@link scopeSegment} — multi-tenant list/revoke handle,
|
|
11
|
+
* 1.190.0 [768]②a). URL = plain `<publicEndpoint>/<bucket>/<key>` — no signature, never expires, CDN-cacheable.
|
|
12
|
+
* Revocation = delete the object.
|
|
13
|
+
* ⚠️ Bucket-policy footgun (live-verified 2026-07-14): `mc anonymous set download` ALSO enables
|
|
14
|
+
* ListBucket — an anonymous `GET /<bucket>/?list-type=2` then enumerates every uuidv7 key and the
|
|
15
|
+
* capability design collapses. Deployments MUST use a GetObject-only JSON policy
|
|
16
|
+
* (`mc anonymous set-json` with Action=["s3:GetObject"] on `arn:aws:s3:::<bucket>/*`): GET 200, LIST 403.
|
|
17
|
+
* - 0 < ttl ≤ 7d: upload to the PRIVATE bucket and return a SigV4 presigned GET. The signature is
|
|
18
|
+
* computed against the PUBLIC endpoint (SigV4 binds Host — signing with the internal host would 403
|
|
19
|
+
* the user's browser; the reverse proxy must pass Host through).
|
|
20
|
+
* - ttl > 7d: fail-loud — SigV4 caps presign at 7 days (physical limit); the caller should use ttl=0.
|
|
21
|
+
*
|
|
22
|
+
* Uploads always go through the INTERNAL endpoint (server-side PUT, its own signed request — Host binding
|
|
23
|
+
* is per-request, so internal-for-write + public-for-read coexist). Zero-dependency posture: presignS3Url
|
|
24
|
+
* + fetch, same as MinioBlobBackend / the k8s adapter.
|
|
25
|
+
*/
|
|
1
26
|
import { createHmac } from "node:crypto";
|
|
2
27
|
import { uuidv7 } from "@sema-agent/core";
|
|
3
28
|
import { presignS3CopyUrl, presignS3Url } from "./s3-presign.js";
|
|
29
|
+
/** SigV4 physical ceiling (AWS: X-Amz-Expires max 604800s = 7 days). */
|
|
4
30
|
export const PRESIGN_MAX_TTL_SEC = 604_800;
|
|
31
|
+
/**
|
|
32
|
+
* Multi-tenant governance (clay ruling 2026-07-14, blackboard [768]② variant a with a HASHED segment): every
|
|
33
|
+
* object key carries a per-tenant SCOPE SEGMENT so tenants can be listed/revoked by prefix on the store itself,
|
|
34
|
+
* WITHOUT leaking the principal into the (user-visible, forwardable) URL. The segment is
|
|
35
|
+
* `HMAC-SHA256(secretKey, "sendfile-scope:" + scope)` hex, truncated to 32 chars:
|
|
36
|
+
* - keyed on the S3 SECRET KEY — server-held and stable, so the mapping is deterministic per deployment but
|
|
37
|
+
* unforgeable/unenumerable from outside (a URL holder cannot test principal guesses without the key);
|
|
38
|
+
* - the "sendfile-scope:" DOMAIN SEPARATOR pins this HMAC use — the same secret signs SigV4 requests, and a
|
|
39
|
+
* cross-protocol collision between "scope segment" and any other HMAC-of-attacker-controlled-string use is
|
|
40
|
+
* exactly what domain separation exists to prevent;
|
|
41
|
+
* - 32 hex chars (128 bits) is a NAMESPACE label, not a secret: the cross-tenant BIRTHDAY bound at even a
|
|
42
|
+
* billion tenants is ~n²/2^129 ≈ 2^-69 — negligible. (The earlier 16-hex/64-bit cut had a ~2.7% birthday
|
|
43
|
+
* collision probability at that scale — n²/2^65, NOT the per-pair 2^-32 the old note claimed; codex audit
|
|
44
|
+
* F4, widened pre-release at zero cost.) Capability secrecy still lives in the uuidv7 segment. Key budget
|
|
45
|
+
* stays comfortable: worst-case private-track key = 9 ("sendfile/") + 32 + 1 + 36 + 1 + 128 = 207 chars,
|
|
46
|
+
* under both sendfile_link.object_key VARCHAR(512) and S3's 1024-byte key ceiling.
|
|
47
|
+
* scope undefined/"" (single-user deployments have no principal) → the literal "_" sentinel (same sentinel the
|
|
48
|
+
* checkpoint scope column uses for anonymous), so the key SHAPE is uniform across deployments.
|
|
49
|
+
* ⚠️ Objects issued before 1.190.0 have NO scope segment (`<uuidv7>/<name>`); they stay valid — the segment is
|
|
50
|
+
* purely additive (URLs are capability-complete either way), zero migration.
|
|
51
|
+
*/
|
|
5
52
|
export function scopeSegment(secretKey, scope) {
|
|
6
53
|
if (scope === undefined || scope === "")
|
|
7
54
|
return "_";
|
|
8
55
|
return createHmac("sha256", secretKey).update(`sendfile-scope:${scope}`).digest("hex").slice(0, 32);
|
|
9
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Collapse an arbitrary client-supplied name to ONE safe path segment: strip directories (both
|
|
59
|
+
* separators), control/bidi characters (the session-title sanitize class), leading dots (no hidden
|
|
60
|
+
* files / no "." ".."), and cap the length (S3 key segment budget; keep the extension when truncating).
|
|
61
|
+
* Empty in ⇒ "file".
|
|
62
|
+
*/
|
|
10
63
|
export function sanitizeSendFileName(name) {
|
|
11
64
|
const base = (name ?? "").split(/[/\\]/).pop() ?? "";
|
|
12
65
|
let clean = base.replace(/[\u0000-\u001f\u007f\u200e\u200f\u202a-\u202e\u2066-\u2069]/g, "").replace(/^\.+/, "").trim();
|
|
13
66
|
if (clean.length > 128) {
|
|
14
67
|
const dot = clean.lastIndexOf(".");
|
|
15
68
|
const ext = dot > 0 && clean.length - dot <= 16 ? clean.slice(dot) : "";
|
|
69
|
+
// code-point 安全截断:UTF-16 slice 会切断代理对,留下 lone surrogate → encodeURIComponent 抛
|
|
70
|
+
// URIError(此时对象已上传,链接却铸不出来)。按 code point 截,预算=128-ext 的 UTF-16 units。
|
|
16
71
|
const budget = 128 - ext.length;
|
|
17
72
|
let head = "";
|
|
18
73
|
for (const ch of clean) {
|
|
@@ -51,9 +106,11 @@ export function createSendUserFileIssuer(cfg, deps) {
|
|
|
51
106
|
throw new Error(`SendUserFile ttl must be a non-negative INTEGER of seconds (0 = permanent; SigV4 X-Amz-Expires is integer-only); got ${ttl}`);
|
|
52
107
|
const name = sanitizeSendFileName(filename);
|
|
53
108
|
const publicBase = cfg.publicEndpoint.replace(/\/+$/, "");
|
|
54
|
-
const seg = scopeSegment(cfg.secretKey, scope);
|
|
109
|
+
const seg = scopeSegment(cfg.secretKey, scope); // hashed tenant segment ("_" single-user) — see scopeSegment doc
|
|
55
110
|
const scopeField = scope !== undefined && scope !== "" ? { scope } : {};
|
|
56
111
|
if (ttl === 0) {
|
|
112
|
+
// Permanent track: capability URL on the anonymous-GET bucket. Key = <scopeSeg>/<uuidv7>/<name>
|
|
113
|
+
// (pre-1.190 objects have no scope segment — additive, both shapes stay valid).
|
|
57
114
|
const key = `${seg}/${uuidv7()}/${name}`;
|
|
58
115
|
await upload(cfg.publicBucket, key, bytes);
|
|
59
116
|
const url = `${publicBase}/${cfg.publicBucket}/${key.split("/").map(encodeURIComponent).join("/")}`;
|
|
@@ -62,6 +119,7 @@ export function createSendUserFileIssuer(cfg, deps) {
|
|
|
62
119
|
if (ttl > PRESIGN_MAX_TTL_SEC) {
|
|
63
120
|
throw new Error(`SendUserFile ttl ${ttl}s exceeds the SigV4 presign ceiling (${PRESIGN_MAX_TTL_SEC}s = 7 days) — use ttl=0 for a permanent link`);
|
|
64
121
|
}
|
|
122
|
+
// Presigned track: private bucket upload, GET signed against the PUBLIC endpoint (Host-bound).
|
|
65
123
|
const key = `${cfg.privateKeyPrefix}${seg}/${uuidv7()}/${name}`;
|
|
66
124
|
await upload(cfg.privateBucket, key, bytes);
|
|
67
125
|
const url = presignS3Url({
|
|
@@ -77,6 +135,9 @@ export function createSendUserFileIssuer(cfg, deps) {
|
|
|
77
135
|
});
|
|
78
136
|
return { url, filename: name, size: bytes.byteLength, ttlSec: ttl, track: "presigned", bucket: cfg.privateBucket, key, ...scopeField };
|
|
79
137
|
};
|
|
138
|
+
// v2 (sandbox direct-upload): same key/track/URL algebra as issue(), split into sign-then-finalize so
|
|
139
|
+
// the BYTES move sandbox→S3 directly (server never relays; creds never enter the sandbox — only a
|
|
140
|
+
// single-object, method-bound, 120s PUT capability on a STAGING key does; see PreparedDirectUpload).
|
|
80
141
|
const prepareDirectUpload = (filename, ttlSec, scope) => {
|
|
81
142
|
if (cfg.publicEndpoint === undefined) {
|
|
82
143
|
throw new Error("SendUserFile is not configured for public links: set S3_PUBLIC_ENDPOINT (the S3/MinIO base URL users can reach; falls back to S3_ENDPOINT when that is already public)");
|
|
@@ -91,11 +152,21 @@ export function createSendUserFileIssuer(cfg, deps) {
|
|
|
91
152
|
const publicBase = cfg.publicEndpoint.replace(/\/+$/, "");
|
|
92
153
|
const putBase = (cfg.sandboxPutEndpoint ?? cfg.publicEndpoint).replace(/\/+$/, "");
|
|
93
154
|
const bucket = ttl === 0 ? cfg.publicBucket : cfg.privateBucket;
|
|
155
|
+
// Same key algebra as issue() incl. the hashed tenant segment — prepare/finalize share ONE key, so the
|
|
156
|
+
// segment is fixed here (the PUT capability is already scoped to it).
|
|
94
157
|
const seg = scopeSegment(cfg.secretKey, scope);
|
|
95
158
|
const scopeField = scope !== undefined && scope !== "" ? { scope } : {};
|
|
96
159
|
const key = ttl === 0 ? `${seg}/${uuidv7()}/${name}` : `${cfg.privateKeyPrefix}${seg}/${uuidv7()}/${name}`;
|
|
160
|
+
// 修3 staging two-phase: the sandbox-visible PUT capability is scoped to a STAGING key in the PRIVATE
|
|
161
|
+
// bucket (never anonymous-readable, never handed out as a URL) — the final key above gets its bytes via
|
|
162
|
+
// a server-side CopyObject only, so no /proc reader of the exec argv can ever rewrite what the user's
|
|
163
|
+
// link points at. The staging key keeps the tenant segment (orphan attribution on manual cleanup).
|
|
97
164
|
const stagingBucket = cfg.privateBucket;
|
|
98
165
|
const stagingKey = `${cfg.privateKeyPrefix}staging/${seg}/${uuidv7()}`;
|
|
166
|
+
// TTL 120s, not the uploader default 600: the URL rides the sandbox exec argv (visible to the sandbox's
|
|
167
|
+
// own processes / possibly an apiserver audit log) — anyone who reads it gains nothing they don't already
|
|
168
|
+
// have DURING the upload (the sandbox owns the file), and since 修3 the writable object is only the
|
|
169
|
+
// staging key anyway. Keep the window as short as a slow 100MiB upload allows.
|
|
99
170
|
const putUrl = presignS3Url({
|
|
100
171
|
endpoint: putBase,
|
|
101
172
|
bucket: stagingBucket,
|
|
@@ -120,16 +191,21 @@ export function createSendUserFileIssuer(cfg, deps) {
|
|
|
120
191
|
...(cfg.region !== undefined ? { region: cfg.region } : {}),
|
|
121
192
|
expiresSec: 60,
|
|
122
193
|
});
|
|
194
|
+
// 修2: best-effort reclaim — any failure AFTER the object landed must delete it before throwing
|
|
195
|
+
// (a transient HEAD 5xx used to strand an unledgered, undelivered orphan in the bucket). Returns the
|
|
196
|
+
// TRUE outcome so error messages never claim "reclaimed" for a delete that itself failed (F2 posture).
|
|
123
197
|
const reclaim = async (b, k) => {
|
|
124
198
|
try {
|
|
125
199
|
const r = await fetch(sign("DELETE", b, k), { method: "DELETE" });
|
|
126
|
-
return r.ok || r.status === 404;
|
|
200
|
+
return r.ok || r.status === 404; // S3 DELETE is idempotent — a missing key IS reclaimed
|
|
127
201
|
}
|
|
128
202
|
catch {
|
|
129
203
|
return false;
|
|
130
204
|
}
|
|
131
205
|
};
|
|
132
206
|
const outcome = (ok, what) => (ok ? `${what} reclaimed` : `${what} reclaim FAILED (orphan; operator cleanup needed)`);
|
|
207
|
+
// ① HEAD the STAGING object (internal endpoint, adapter creds): existence + a cheap early cap gate
|
|
208
|
+
// (fail before copying a huge object). Advisory only for size — the authoritative reading is ④.
|
|
133
209
|
let head;
|
|
134
210
|
try {
|
|
135
211
|
head = await fetch(sign("HEAD", stagingBucket, stagingKey), { method: "HEAD" });
|
|
@@ -141,7 +217,7 @@ export function createSendUserFileIssuer(cfg, deps) {
|
|
|
141
217
|
if (head.status === 404)
|
|
142
218
|
throw new Error("SendUserFile upload verification failed: object not found after upload (HEAD 404)");
|
|
143
219
|
if (!head.ok) {
|
|
144
|
-
const r = await reclaim(stagingBucket, stagingKey);
|
|
220
|
+
const r = await reclaim(stagingBucket, stagingKey); // transient 5xx ≠ missing — the object may be live; reclaim it
|
|
145
221
|
throw new Error(`SendUserFile upload verification failed: store error (HEAD ${head.status}) — ${outcome(r, "staged object")}`);
|
|
146
222
|
}
|
|
147
223
|
const staged = Number.parseInt(head.headers.get("content-length") ?? "", 10);
|
|
@@ -153,6 +229,9 @@ export function createSendUserFileIssuer(cfg, deps) {
|
|
|
153
229
|
await reclaim(stagingBucket, stagingKey);
|
|
154
230
|
throw new Error(`file exceeds the ${maxBytes >= 1024 * 1024 ? `${Math.floor(maxBytes / (1024 * 1024))} MiB` : `${maxBytes} byte`} per-file cap (uploaded object was ${staged} bytes — deleted)`);
|
|
155
231
|
}
|
|
232
|
+
// ② Server-side CopyObject staging→final (internal endpoint, signed x-amz-copy-source): the ONLY
|
|
233
|
+
// write the final key ever receives. NB: S3 can return 200 with an error BODY on slow copies — require
|
|
234
|
+
// the CopyObjectResult marker, not just the status.
|
|
156
235
|
const copy = presignS3CopyUrl({
|
|
157
236
|
endpoint: cfg.endpoint,
|
|
158
237
|
bucket,
|
|
@@ -173,10 +252,15 @@ export function createSendUserFileIssuer(cfg, deps) {
|
|
|
173
252
|
}
|
|
174
253
|
catch (e) {
|
|
175
254
|
const r1 = await reclaim(stagingBucket, stagingKey);
|
|
176
|
-
const r2 = await reclaim(bucket, key);
|
|
255
|
+
const r2 = await reclaim(bucket, key); // the copy may have half-landed — reclaim both sides
|
|
177
256
|
throw new Error(`SendUserFile upload verification failed: staging promotion failed (${e instanceof Error ? e.message : String(e)}) — ${outcome(r1 && r2, "uploaded object")}`);
|
|
178
257
|
}
|
|
258
|
+
// ③ Staging object served its purpose — reclaim (best-effort: a leftover is a private-bucket blob
|
|
259
|
+
// whose write capability expires in ≤120s; it can't become a public link).
|
|
179
260
|
await reclaim(stagingBucket, stagingKey);
|
|
261
|
+
// ④ Authoritative HEAD of the FINAL object — immutable to the sandbox, so THIS size is what the copy
|
|
262
|
+
// pinned and what the card must carry (re-checked against the cap: a /proc reader could have re-PUT
|
|
263
|
+
// staging between ① and ②, swapping in different bytes).
|
|
180
264
|
let fin;
|
|
181
265
|
try {
|
|
182
266
|
fin = await fetch(sign("HEAD", bucket, key), { method: "HEAD" });
|
|
@@ -220,6 +304,10 @@ export function createSendUserFileIssuer(cfg, deps) {
|
|
|
220
304
|
};
|
|
221
305
|
return { putUrl, verifySize, finalize };
|
|
222
306
|
};
|
|
307
|
+
// Ledger reclaim (fail-loud record path): delete an already-uploaded object whose ledger record failed —
|
|
308
|
+
// a public-track object with no ledger row would be unmanageable (no per-tenant list/revoke handle). Signed
|
|
309
|
+
// DELETE against the INTERNAL endpoint (adapter creds, same posture as verifySize). S3 DELETE is idempotent
|
|
310
|
+
// (204 on a missing key), so callers may retry safely.
|
|
223
311
|
const deleteObject = async (bucket, key) => {
|
|
224
312
|
if (deps?.remove)
|
|
225
313
|
return deps.remove(bucket, key);
|
|
@@ -2,20 +2,35 @@ import { type SessionPolicyStore, type SessionPermissionRules, type StoredSessio
|
|
|
2
2
|
import type { Pool as MySqlPool } from "mysql2/promise";
|
|
3
3
|
import type { Pool as PgPool } from "pg";
|
|
4
4
|
import { type SqlDriver } from "./sql-driver.js";
|
|
5
|
+
/** Dual-dialect durable SessionPolicyStore. See the file header for the dialect-delta ledger. */
|
|
5
6
|
export declare class SqlSessionPolicyStore implements SessionPolicyStore {
|
|
6
7
|
private readonly db;
|
|
7
8
|
constructor(db: SqlDriver);
|
|
9
|
+
/** Pick the dialect's SQL text. Both statements stay written out at the call site ON PURPOSE. */
|
|
8
10
|
private q;
|
|
9
11
|
private isDupKey;
|
|
12
|
+
/** Read the stored rules for (session, principal-owner); null when none (parity with InMemory). */
|
|
10
13
|
getRules(sessionId: string, principal?: string): Promise<StoredSessionRules | null>;
|
|
14
|
+
/** 2c session-sync: every (principal, rules) record for a session across ALL principals — getRules is
|
|
15
|
+
* per-(session,principal), but a cross-backend session EXPORT must bundle the WHOLE session's policy (the importer
|
|
16
|
+
* replays each via putRules). principal is read from the DENORMALIZED column (the sha256 policy_key is one-way);
|
|
17
|
+
* NULL → undefined = the session-wide row (parity with core's InMemory `principal ?? undefined`). Rules carry rev. */
|
|
11
18
|
listBySession(sessionId: string): Promise<SessionRulesRecord[]>;
|
|
19
|
+
/** Operator/owner write: CAS + tighten-only (unless operator) + rev bump, atomic in one txn; first-write race retried. */
|
|
12
20
|
putRules(sessionId: string, principal: string | undefined, rules: SessionPermissionRules, opts?: PutRulesOptions): Promise<StoredSessionRules>;
|
|
21
|
+
/** E21 purge — drop ALL policy rows for a session (every principal). Scoped by session_id (the route owner-gates).
|
|
22
|
+
* Return shape aligned to core 1.423's optional interface seam (`Promise<void>`) — the affectedRows count was
|
|
23
|
+
* incidental and unconsumed; the integration test asserts the post-state (listBySession empty) instead. */
|
|
13
24
|
deleteBySession(sessionId: string): Promise<void>;
|
|
25
|
+
/** `begin()` (NOT `beginPessimistic()`) — matches both twins' original call shape: TiDB's native
|
|
26
|
+
* `beginTransaction()` (optimistic), PG's plain `BEGIN`. Normalized by SqlDriver's shared tx plumbing. */
|
|
14
27
|
private tx;
|
|
15
28
|
}
|
|
29
|
+
/** MySQL-protocol (TiDB) binding — historical class name + ctor shape preserved. */
|
|
16
30
|
export declare class TiDBSessionPolicyStore extends SqlSessionPolicyStore {
|
|
17
31
|
constructor(pool: MySqlPool);
|
|
18
32
|
}
|
|
33
|
+
/** PostgreSQL binding — historical class name + ctor shape preserved. */
|
|
19
34
|
export declare class PgSessionPolicyStore extends SqlSessionPolicyStore {
|
|
20
35
|
constructor(pool: PgPool);
|
|
21
36
|
}
|
|
@@ -1,14 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable `SessionPolicyStore` (E6) — SINGLE-FILE DUAL-DIALECT (design/158 A12 定型半场). ONE
|
|
3
|
+
* implementation, TWO dialects; the historical `TiDBSessionPolicyStore` / `PgSessionPolicyStore` class
|
|
4
|
+
* names survive as thin ctor subclasses so every consumer (store-backend.ts, the session-sync
|
|
5
|
+
* fanout/push/pull suites, the E6 equivalence suite) is untouched.
|
|
6
|
+
*
|
|
7
|
+
* Operator-tightened per-session tool-permission rules (CAS-versioned, tighten-only) — the cross-replica
|
|
8
|
+
* twin of core's InMemorySessionPolicyStore (`@sema-agent/core` session-policy-store.js), backing core's
|
|
9
|
+
* `RunnerDeps.sessionPolicyStore`. core READS via getRules at prepare-time and folds the rules into the
|
|
10
|
+
* ToolPolicy FIRST (subtract-only); the service's operator endpoint WRITES via putRules.
|
|
11
|
+
*
|
|
12
|
+
* BYTE-MATCH discipline (the env-gated equivalence test vs InMemorySessionPolicyStore is the contract):
|
|
13
|
+
* - Key = `sha256(JSON.stringify([sessionId, principal ?? null]))` — the SHA of core's exact composite key. The hash
|
|
14
|
+
* (a) gives a FIXED 64-char PK regardless of how long / JSON-escapable the principal is (a VARCHAR(190) principal of
|
|
15
|
+
* control chars JSON-escapes past any fixed width — review finding), and (b) sidesteps PK collation pitfalls
|
|
16
|
+
* (trailing-space PAD). `principal` undefined/null collapse to one row, "" stays distinct (core's `?? null`).
|
|
17
|
+
* - putRules order is LOAD-BEARING: (1) CAS expectedRev (stale → `conflict`), (2) normalize+stripRev the incoming
|
|
18
|
+
* rules, (3) tighten gate via the REUSED core `loosenReasons` (non-operator loosen → `loosen_forbidden`), (4) rev
|
|
19
|
+
* bump (+1; first = 1). A missing row reads as rev 0 (so expectedRev:0 = create-once). The conflict + loosen_forbidden
|
|
20
|
+
* MESSAGES match core verbatim. read-check-write runs in ONE txn (SELECT…FOR UPDATE the prior so the loosen diff +
|
|
21
|
+
* CAS are atomic; the InMemory gets this free from single-threaded JS). The UPDATE additionally guards `AND rev=?`
|
|
22
|
+
* (defense beyond the row lock); a concurrent FIRST-write (no row → FOR UPDATE takes no lock) loses the PK race and
|
|
23
|
+
* is RETRIED (re-read takes the UPDATE path) so a no-expectedRev create matches core's serialized rev-2 outcome
|
|
24
|
+
* rather than a spurious conflict.
|
|
25
|
+
* - allowDirs is stored NORMALIZED (`..`-free) so a later read-back `prior` feeds loosenReasons in the same canonical
|
|
26
|
+
* shape as `clean` (the /work/../etc tighten-bypass blocker). The return is structuredClone'd (core's isolation).
|
|
27
|
+
* `operator` is honored verbatim — core never adjudicates it; the HTTP route FREEZES it from a verified principal
|
|
28
|
+
* (mirrors steer.trusted). `deleteBySession` is a service extension (E21 purge), not core's interface.
|
|
29
|
+
*
|
|
30
|
+
* ── Dialect deltas, kept EXPLICIT ────────────────────────────────────────────────────────────────────
|
|
31
|
+
* - `?` placeholders vs `$n`
|
|
32
|
+
* - dup-key errno `1062`/`ER_DUP_ENTRY` vs SQLSTATE `23505`
|
|
33
|
+
* - PG-only genuinely-divergent ALGORITHM: `pgHasUnstorable` rejects rules containing NUL / lone-surrogate
|
|
34
|
+
* bytes (fail-loud, never rewritten) before the write; TiDB has no equivalent guard. Kept as an explicit
|
|
35
|
+
* `if (this.db.dialect === "pg")` branch in putRules — see the inline 🔴 comment there for the codex
|
|
36
|
+
* R2-H1→R3 rationale (rules are the authorization semantic body: reject-only, never silently rewritten).
|
|
37
|
+
* - transaction plumbing: TiDB's native `beginTransaction/commit/rollback` vs PG's statement-issued
|
|
38
|
+
* `BEGIN/COMMIT/ROLLBACK` — normalized by `SqlDriver`'s shared `connect()`/`begin()`/`commit()`/`rollback()`
|
|
39
|
+
* (see sql-driver.ts); this store's original `beginTransaction()` call shape maps onto the driver's plain
|
|
40
|
+
* `begin()` (NOT `beginPessimistic()` — the original TiDB store never opted into `BEGIN PESSIMISTIC`).
|
|
41
|
+
*/
|
|
1
42
|
import { createHash } from "node:crypto";
|
|
2
43
|
import { loosenReasons, SessionPolicyError, normalizeRules, stripRev } from "@sema-agent/core";
|
|
3
44
|
import { pgHasUnstorable } from "./pg-safe-json.js";
|
|
4
45
|
import { mysqlDriver, pgDriver } from "./sql-driver.js";
|
|
5
46
|
const policyKey = (sessionId, principal) => createHash("sha256").update(JSON.stringify([sessionId, principal ?? null])).digest("hex");
|
|
6
|
-
const parseRules = (v) => (typeof v === "string" ? JSON.parse(v) : v);
|
|
47
|
+
const parseRules = (v) => (typeof v === "string" ? JSON.parse(v) : v); // R7 终形:拒绝式下库内恒干净,直读
|
|
48
|
+
/** Dual-dialect durable SessionPolicyStore. See the file header for the dialect-delta ledger. */
|
|
7
49
|
export class SqlSessionPolicyStore {
|
|
8
50
|
db;
|
|
9
51
|
constructor(db) {
|
|
10
52
|
this.db = db;
|
|
11
53
|
}
|
|
54
|
+
/** Pick the dialect's SQL text. Both statements stay written out at the call site ON PURPOSE. */
|
|
12
55
|
q(tidb, pg) {
|
|
13
56
|
return this.db.dialect === "tidb" ? tidb : pg;
|
|
14
57
|
}
|
|
@@ -17,12 +60,17 @@ export class SqlSessionPolicyStore {
|
|
|
17
60
|
? e?.code === "ER_DUP_ENTRY" || e?.errno === 1062
|
|
18
61
|
: e?.code === "23505";
|
|
19
62
|
}
|
|
63
|
+
/** Read the stored rules for (session, principal-owner); null when none (parity with InMemory). */
|
|
20
64
|
async getRules(sessionId, principal) {
|
|
21
65
|
const { rows } = await this.db.query(this.q("SELECT rules, rev FROM session_policy WHERE policy_key = ?", "SELECT rules, rev FROM session_policy WHERE policy_key = $1"), [policyKey(sessionId, principal)]);
|
|
22
66
|
if (!rows[0])
|
|
23
67
|
return null;
|
|
24
68
|
return { ...parseRules(rows[0].rules), rev: Number(rows[0].rev) };
|
|
25
69
|
}
|
|
70
|
+
/** 2c session-sync: every (principal, rules) record for a session across ALL principals — getRules is
|
|
71
|
+
* per-(session,principal), but a cross-backend session EXPORT must bundle the WHOLE session's policy (the importer
|
|
72
|
+
* replays each via putRules). principal is read from the DENORMALIZED column (the sha256 policy_key is one-way);
|
|
73
|
+
* NULL → undefined = the session-wide row (parity with core's InMemory `principal ?? undefined`). Rules carry rev. */
|
|
26
74
|
async listBySession(sessionId) {
|
|
27
75
|
const { rows } = await this.db.query(this.q("SELECT principal, rules, rev FROM session_policy WHERE session_id = ?", "SELECT principal, rules, rev FROM session_policy WHERE session_id = $1"), [sessionId]);
|
|
28
76
|
return rows.map((r) => ({
|
|
@@ -30,6 +78,7 @@ export class SqlSessionPolicyStore {
|
|
|
30
78
|
rules: { ...parseRules(r.rules), rev: Number(r.rev) },
|
|
31
79
|
}));
|
|
32
80
|
}
|
|
81
|
+
/** Operator/owner write: CAS + tighten-only (unless operator) + rev bump, atomic in one txn; first-write race retried. */
|
|
33
82
|
async putRules(sessionId, principal, rules, opts) {
|
|
34
83
|
const key = policyKey(sessionId, principal);
|
|
35
84
|
for (let attempt = 0;; attempt++) {
|
|
@@ -38,16 +87,24 @@ export class SqlSessionPolicyStore {
|
|
|
38
87
|
const { rows } = await conn.query(this.q("SELECT rules, rev FROM session_policy WHERE policy_key = ? FOR UPDATE", "SELECT rules, rev FROM session_policy WHERE policy_key = $1 FOR UPDATE"), [key]);
|
|
39
88
|
const prior = rows[0] ? { rules: parseRules(rows[0].rules), rev: Number(rows[0].rev) } : undefined;
|
|
40
89
|
const priorRev = prior?.rev ?? 0;
|
|
90
|
+
// (1) CAS — checked BEFORE the tighten gate; a missing row is rev 0 (expectedRev:0 = create-once).
|
|
41
91
|
if (opts?.expectedRev !== undefined && priorRev !== opts.expectedRev) {
|
|
42
92
|
throw new SessionPolicyError("conflict", `session rules rev mismatch (expected ${opts.expectedRev}, have ${priorRev})`);
|
|
43
93
|
}
|
|
94
|
+
// (2) normalize+stripRev the INCOMING rules (allowDirs `..`-free before store AND before the loosen diff).
|
|
44
95
|
const clean = normalizeRules(stripRev(rules));
|
|
96
|
+
// (3) tighten gate — ONLY when a prior exists AND not an operator write (strict !== true). REUSE core loosenReasons.
|
|
45
97
|
if (prior && opts?.operator !== true) {
|
|
46
98
|
const loosens = loosenReasons(prior.rules, clean);
|
|
47
99
|
if (loosens.length > 0) {
|
|
48
100
|
throw new SessionPolicyError("loosen_forbidden", `a non-operator session-rule write may only tighten; refused: ${loosens.join("; ")}`);
|
|
49
101
|
}
|
|
50
102
|
}
|
|
103
|
+
// 🔴 PG-only genuinely-divergent ALGORITHM (not a syntax difference): codex R2-H1→R3(NUL 批终形):
|
|
104
|
+
// rules=授权语义本体,**拒绝式**——含不可存字节的规则是垃圾输入(NUL 路径本不可用),fail-loud
|
|
105
|
+
// 拒收;绝不改写(有损=写读不一致)、绝不信封(R3:滚动部署/回滚窗口老 replica 读信封=字段全缺=
|
|
106
|
+
// fail-open 放行,授权面不赌版本偏斜)。TiDB's VARBINARY/utf8mb4 storage never chokes on these bytes
|
|
107
|
+
// the way PG's protocol-byte encoding does, so it carries no equivalent guard.
|
|
51
108
|
if (this.db.dialect === "pg" && pgHasUnstorable(clean)) {
|
|
52
109
|
throw new SessionPolicyError("loosen_forbidden", "rules contain unstorable bytes (NUL / lone surrogate) — refused; strip them at the source (authorization rules are never rewritten by the store)");
|
|
53
110
|
}
|
|
@@ -55,26 +112,38 @@ export class SqlSessionPolicyStore {
|
|
|
55
112
|
const json = JSON.stringify(clean);
|
|
56
113
|
const now = new Date();
|
|
57
114
|
if (prior) {
|
|
115
|
+
// `principal` is enumeration-only (not a key, not CAS): write it on UPDATE too so a pre-migration
|
|
116
|
+
// row (principal NULL) SELF-HEALS to its real principal on the next write — never changes the rev/CAS path.
|
|
58
117
|
const upd = await conn.query(this.q("UPDATE session_policy SET principal = ?, rules = ?, rev = ?, updated_at = ? WHERE policy_key = ? AND rev = ?", "UPDATE session_policy SET principal = $1, rules = $2::jsonb, rev = $3, updated_at = $4 WHERE policy_key = $5 AND rev = $6"), [principal ?? null, json, nextRev, now, key, priorRev]);
|
|
118
|
+
// Defense beyond the FOR UPDATE row lock: if rev moved under us, the CAS lost (treat as conflict).
|
|
59
119
|
if (upd.affected !== 1)
|
|
60
120
|
throw new SessionPolicyError("conflict", `session rules rev mismatch (expected ${opts?.expectedRev ?? priorRev}, have ${priorRev})`);
|
|
61
121
|
}
|
|
62
122
|
else {
|
|
123
|
+
// No prior → FOR UPDATE took no row lock; a concurrent first-writer can win the PK. Let the dup-key
|
|
124
|
+
// PROPAGATE (do NOT convert to conflict here) so the outer loop RETRIES — the re-read then finds the row
|
|
125
|
+
// and takes the UPDATE path (matching core's serialized rev-2 outcome / or an honest CAS conflict if
|
|
126
|
+
// expectedRev was supplied), instead of a spurious conflict. `principal ?? null` = the enumeration column.
|
|
63
127
|
await conn.query(this.q("INSERT INTO session_policy (policy_key, session_id, principal, rules, rev, created_at, updated_at) VALUES (?,?,?,?,?,?,?)", "INSERT INTO session_policy (policy_key, session_id, principal, rules, rev, created_at, updated_at) VALUES ($1,$2,$3,$4::jsonb,$5,$6,$7)"), [key, sessionId, principal ?? null, json, nextRev, now, now]);
|
|
64
128
|
}
|
|
65
|
-
return structuredClone({ ...clean, rev: nextRev });
|
|
129
|
+
return structuredClone({ ...clean, rev: nextRev }); // core hands back a fully isolated tree
|
|
66
130
|
});
|
|
67
131
|
}
|
|
68
132
|
catch (e) {
|
|
69
133
|
if (this.isDupKey(e) && attempt < 4)
|
|
70
|
-
continue;
|
|
134
|
+
continue; // concurrent first-write — re-read takes the UPDATE/CAS path
|
|
71
135
|
throw e;
|
|
72
136
|
}
|
|
73
137
|
}
|
|
74
138
|
}
|
|
139
|
+
/** E21 purge — drop ALL policy rows for a session (every principal). Scoped by session_id (the route owner-gates).
|
|
140
|
+
* Return shape aligned to core 1.423's optional interface seam (`Promise<void>`) — the affectedRows count was
|
|
141
|
+
* incidental and unconsumed; the integration test asserts the post-state (listBySession empty) instead. */
|
|
75
142
|
async deleteBySession(sessionId) {
|
|
76
143
|
await this.db.query(this.q("DELETE FROM session_policy WHERE session_id = ?", "DELETE FROM session_policy WHERE session_id = $1"), [sessionId]);
|
|
77
144
|
}
|
|
145
|
+
/** `begin()` (NOT `beginPessimistic()`) — matches both twins' original call shape: TiDB's native
|
|
146
|
+
* `beginTransaction()` (optimistic), PG's plain `BEGIN`. Normalized by SqlDriver's shared tx plumbing. */
|
|
78
147
|
async tx(fn) {
|
|
79
148
|
const conn = await this.db.connect();
|
|
80
149
|
try {
|
|
@@ -88,6 +157,7 @@ export class SqlSessionPolicyStore {
|
|
|
88
157
|
await conn.rollback();
|
|
89
158
|
}
|
|
90
159
|
catch {
|
|
160
|
+
/* ignore */
|
|
91
161
|
}
|
|
92
162
|
throw err;
|
|
93
163
|
}
|
|
@@ -96,11 +166,13 @@ export class SqlSessionPolicyStore {
|
|
|
96
166
|
}
|
|
97
167
|
}
|
|
98
168
|
}
|
|
169
|
+
/** MySQL-protocol (TiDB) binding — historical class name + ctor shape preserved. */
|
|
99
170
|
export class TiDBSessionPolicyStore extends SqlSessionPolicyStore {
|
|
100
171
|
constructor(pool) {
|
|
101
172
|
super(mysqlDriver(pool));
|
|
102
173
|
}
|
|
103
174
|
}
|
|
175
|
+
/** PostgreSQL binding — historical class name + ctor shape preserved. */
|
|
104
176
|
export class PgSessionPolicyStore extends SqlSessionPolicyStore {
|
|
105
177
|
constructor(pool) {
|
|
106
178
|
super(pgDriver(pool));
|
|
@@ -1,10 +1,55 @@
|
|
|
1
1
|
import { type SessionStore } from "@sema-agent/core";
|
|
2
2
|
import type { ServiceConfig } from "../config-types.js";
|
|
3
3
|
import type { StoreBackend } from "./store-backend.js";
|
|
4
|
+
/**
|
|
5
|
+
* Session-store plugin seam.
|
|
6
|
+
*
|
|
7
|
+
* - `memory` (default): core's in-memory `TtlSessionStore` (lost on restart, 7-day idle TTL).
|
|
8
|
+
* - `tidb`: the durable "Session event-log center" (L1) — append-only events + wake/resume +
|
|
9
|
+
* cross-instance CAS (F2), on whichever SQL engine `DB_BACKEND` selects (TiDB or PG, via the store backend).
|
|
10
|
+
* Optionally wrapped in a warm cache (reuse a hot session across turns; requires session affinity) —
|
|
11
|
+
* set `SESSION_CACHE_TTL_SEC=0` to disable.
|
|
12
|
+
*/
|
|
4
13
|
export declare function createSessionStore(config: ServiceConfig, backend?: StoreBackend, metrics?: {
|
|
5
14
|
inc(name: string, labels?: Record<string, string>, n?: number): void;
|
|
6
15
|
}): SessionStore;
|
|
16
|
+
/**
|
|
17
|
+
* core 1.382(design/153,[1561] 提货单①②)`SubagentToolOptions.background.ensureChildSessionDurable`
|
|
18
|
+
* 的实现体——从 main.ts 装配点抽出为可独立测试的纯函数(main.ts 里的其余装配闭包历来无单测覆盖,
|
|
19
|
+
* 这条因为承载真实的 park/veto 裁决,拆出来直接钉)。
|
|
20
|
+
*
|
|
21
|
+
* 只在 `sessionId` 已经真正路由到 HOST(durable)会话店、且**真有内容**时 resolve;其余(未路由到 host/
|
|
22
|
+
* 两店皆无/host 店只有一行空壳)一律 reject——veto,core 侧子代按 pre-153 settle failed,零回归,不是
|
|
23
|
+
* 危险失败模式(见函数体内注释)。
|
|
24
|
+
*
|
|
25
|
+
* 传入必须是 HOST 店本身(main.ts 里 `createSessionStore(...)` 的返回值),不能是 subRunner 用的
|
|
26
|
+
* `ForkRoutingSessionStore` 包装店——那层包装对 `acquire(id,{requireExisting:true})` 是 transient-优先
|
|
27
|
+
* (F-7 案:让"真会话在 transient"赢过"host 店里的同 id 空壳"),会把"只在 transient 店有真内容"误判成
|
|
28
|
+
* "host 店已经有了"。这里要问的是纯粹"host 店本身是否已经有这份真内容",必须绕开那层路由。
|
|
29
|
+
*
|
|
30
|
+
* 🔴 [1575] F3(cli 复查抓获,真红确认)调用方前提——**本函数假设 `hostStore.release()` 是"归还租约"
|
|
31
|
+
* 语义(不销毁内容)**,这对 core `TiDBSessionStore` 成立(`release()` 逐字注释「Durable history is
|
|
32
|
+
* retained; just drop any in-flight cache entry」),但对无 repo 构造的 `TtlSessionStore`(`createSessionStore`
|
|
33
|
+
* 在 `SESSION_BACKEND !== "tidb"` 时返回的那种)**不成立**——那个店形的 `release()` 是整删会话
|
|
34
|
+
* (`evictPolicy="delete"`)。本函数自己**不做**、也无法通用地做"这个 store 实例的 release 是否安全"的
|
|
35
|
+
* 判别(`SessionStore` 接口没有暴露这个信息,类型层面区分不了两种店形)——上文"误判方向永远偏保守"
|
|
36
|
+
* 只覆盖 `getLeafId` 那道校验,不覆盖"传了一个 release=delete 的店进来"这个前提违反。**调用方必须
|
|
37
|
+
* 保证只在真正 durable(release 非破坏性)的店上调用本函数**——server 侧的强制点在 main.ts 装配区:
|
|
38
|
+
* `ensureChildSessionDurable` 闭包只在 `config.sessionBackend === "tidb"` 时构造。
|
|
39
|
+
*/
|
|
7
40
|
export declare function ensureSessionDurable(hostStore: SessionStore, sessionId: string): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* [1593] 主动晋升版 ensureChildSessionDurable(cli TiDB 真机撞获:1.263 的只查不迁形在拆店生产形下
|
|
43
|
+
* 恒否决 park——bg 子会话住 subRunner 私有 TTL 店、从不在 host 店,core 收 veto 即 expire checkpoint
|
|
44
|
+
* (14ms~2s)+ settle failed,赎回窗口不存在):
|
|
45
|
+
* 1. 快路:已在 host 店且有真内容 → 直接通过(幂等/重入)。
|
|
46
|
+
* 2. 晋升:routing.promoteToHost 把子会话整树从私店拷进 host 店(拷贝→私店 forget→路由记 host,
|
|
47
|
+
* 三步见 ForkRoutingSessionStore.promoteToHost)。
|
|
48
|
+
* 3. 复验:同一把 `ensureSessionDurable` 严检(leaf 非空)——晋升产物必须达到与既有校验完全相同的
|
|
49
|
+
* durable 标准,任何一步失败=如实 reject=veto=pre-153 行为(不放大)。
|
|
50
|
+
* 调用方纪律与 {@link ensureSessionDurable} 同款:hostStore 必须是 release 非破坏性的真 durable 店
|
|
51
|
+
* (main.ts 的 `config.sessionBackend === "tidb"` 门不变)。
|
|
52
|
+
*/
|
|
8
53
|
export declare function ensureChildSessionDurableWithPromotion(hostStore: SessionStore, routing: {
|
|
9
54
|
promoteToHost(sessionId: string): Promise<void>;
|
|
10
55
|
}, sessionId: string): Promise<void>;
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { TtlSessionStore } from "@sema-agent/core";
|
|
2
2
|
import { CachingSessionStore } from "./caching-session-store.js";
|
|
3
|
+
/**
|
|
4
|
+
* Session-store plugin seam.
|
|
5
|
+
*
|
|
6
|
+
* - `memory` (default): core's in-memory `TtlSessionStore` (lost on restart, 7-day idle TTL).
|
|
7
|
+
* - `tidb`: the durable "Session event-log center" (L1) — append-only events + wake/resume +
|
|
8
|
+
* cross-instance CAS (F2), on whichever SQL engine `DB_BACKEND` selects (TiDB or PG, via the store backend).
|
|
9
|
+
* Optionally wrapped in a warm cache (reuse a hot session across turns; requires session affinity) —
|
|
10
|
+
* set `SESSION_CACHE_TTL_SEC=0` to disable.
|
|
11
|
+
*/
|
|
3
12
|
export function createSessionStore(config, backend, metrics) {
|
|
4
13
|
if (config.sessionBackend === "tidb") {
|
|
5
14
|
if (!backend)
|
|
@@ -11,9 +20,38 @@ export function createSessionStore(config, backend, metrics) {
|
|
|
11
20
|
}
|
|
12
21
|
return new TtlSessionStore({ defaultTtlDays: 7 });
|
|
13
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* core 1.382(design/153,[1561] 提货单①②)`SubagentToolOptions.background.ensureChildSessionDurable`
|
|
25
|
+
* 的实现体——从 main.ts 装配点抽出为可独立测试的纯函数(main.ts 里的其余装配闭包历来无单测覆盖,
|
|
26
|
+
* 这条因为承载真实的 park/veto 裁决,拆出来直接钉)。
|
|
27
|
+
*
|
|
28
|
+
* 只在 `sessionId` 已经真正路由到 HOST(durable)会话店、且**真有内容**时 resolve;其余(未路由到 host/
|
|
29
|
+
* 两店皆无/host 店只有一行空壳)一律 reject——veto,core 侧子代按 pre-153 settle failed,零回归,不是
|
|
30
|
+
* 危险失败模式(见函数体内注释)。
|
|
31
|
+
*
|
|
32
|
+
* 传入必须是 HOST 店本身(main.ts 里 `createSessionStore(...)` 的返回值),不能是 subRunner 用的
|
|
33
|
+
* `ForkRoutingSessionStore` 包装店——那层包装对 `acquire(id,{requireExisting:true})` 是 transient-优先
|
|
34
|
+
* (F-7 案:让"真会话在 transient"赢过"host 店里的同 id 空壳"),会把"只在 transient 店有真内容"误判成
|
|
35
|
+
* "host 店已经有了"。这里要问的是纯粹"host 店本身是否已经有这份真内容",必须绕开那层路由。
|
|
36
|
+
*
|
|
37
|
+
* 🔴 [1575] F3(cli 复查抓获,真红确认)调用方前提——**本函数假设 `hostStore.release()` 是"归还租约"
|
|
38
|
+
* 语义(不销毁内容)**,这对 core `TiDBSessionStore` 成立(`release()` 逐字注释「Durable history is
|
|
39
|
+
* retained; just drop any in-flight cache entry」),但对无 repo 构造的 `TtlSessionStore`(`createSessionStore`
|
|
40
|
+
* 在 `SESSION_BACKEND !== "tidb"` 时返回的那种)**不成立**——那个店形的 `release()` 是整删会话
|
|
41
|
+
* (`evictPolicy="delete"`)。本函数自己**不做**、也无法通用地做"这个 store 实例的 release 是否安全"的
|
|
42
|
+
* 判别(`SessionStore` 接口没有暴露这个信息,类型层面区分不了两种店形)——上文"误判方向永远偏保守"
|
|
43
|
+
* 只覆盖 `getLeafId` 那道校验,不覆盖"传了一个 release=delete 的店进来"这个前提违反。**调用方必须
|
|
44
|
+
* 保证只在真正 durable(release 非破坏性)的店上调用本函数**——server 侧的强制点在 main.ts 装配区:
|
|
45
|
+
* `ensureChildSessionDurable` 闭包只在 `config.sessionBackend === "tidb"` 时构造。
|
|
46
|
+
*/
|
|
14
47
|
export async function ensureSessionDurable(hostStore, sessionId) {
|
|
15
48
|
const acquired = await hostStore.acquire(sessionId, { requireExisting: true });
|
|
16
49
|
try {
|
|
50
|
+
// security.ts 的 authorizer 对任意调用方自报的 body.sessionId(无主时)会经 `register()` 在 host 店
|
|
51
|
+
// 插一行 session_meta(leaf_id NULL,零内容)——那是 claim-create 的副作用,不是真正落过内容的会话。
|
|
52
|
+
// 裸 acquire 命中不足以证明"这份 durable 记录里有真内容可供跨进程恢复";真实会话到 park 这一步
|
|
53
|
+
// (需要模型已经产出过至少一次带工具调用的回复)恒已有 ≥1 条树内条目。校验失败的唯一后果是多一次
|
|
54
|
+
// reject=veto=pre-153 行为,不会放大权限或掩盖错误——误判方向永远偏保守。
|
|
17
55
|
const leafId = await acquired.session.getLeafId();
|
|
18
56
|
if (leafId === null)
|
|
19
57
|
throw new Error(`session "${sessionId}" exists in the durable store but is empty (no leaf entry) — likely a claim-create shell row, not real durable content`);
|
|
@@ -22,12 +60,25 @@ export async function ensureSessionDurable(hostStore, sessionId) {
|
|
|
22
60
|
await hostStore.release(acquired.sessionId);
|
|
23
61
|
}
|
|
24
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* [1593] 主动晋升版 ensureChildSessionDurable(cli TiDB 真机撞获:1.263 的只查不迁形在拆店生产形下
|
|
65
|
+
* 恒否决 park——bg 子会话住 subRunner 私有 TTL 店、从不在 host 店,core 收 veto 即 expire checkpoint
|
|
66
|
+
* (14ms~2s)+ settle failed,赎回窗口不存在):
|
|
67
|
+
* 1. 快路:已在 host 店且有真内容 → 直接通过(幂等/重入)。
|
|
68
|
+
* 2. 晋升:routing.promoteToHost 把子会话整树从私店拷进 host 店(拷贝→私店 forget→路由记 host,
|
|
69
|
+
* 三步见 ForkRoutingSessionStore.promoteToHost)。
|
|
70
|
+
* 3. 复验:同一把 `ensureSessionDurable` 严检(leaf 非空)——晋升产物必须达到与既有校验完全相同的
|
|
71
|
+
* durable 标准,任何一步失败=如实 reject=veto=pre-153 行为(不放大)。
|
|
72
|
+
* 调用方纪律与 {@link ensureSessionDurable} 同款:hostStore 必须是 release 非破坏性的真 durable 店
|
|
73
|
+
* (main.ts 的 `config.sessionBackend === "tidb"` 门不变)。
|
|
74
|
+
*/
|
|
25
75
|
export async function ensureChildSessionDurableWithPromotion(hostStore, routing, sessionId) {
|
|
26
76
|
try {
|
|
27
77
|
await ensureSessionDurable(hostStore, sessionId);
|
|
28
78
|
return;
|
|
29
79
|
}
|
|
30
80
|
catch {
|
|
81
|
+
// 不在 host 店(或空壳行)→ 走晋升;晋升自身的失败在下方如实上浮
|
|
31
82
|
}
|
|
32
83
|
await routing.promoteToHost(sessionId);
|
|
33
84
|
await ensureSessionDurable(hostStore, sessionId);
|