@sema-agent/server 7.132.0 → 7.134.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/MIGRATION.md +353 -0
- package/README.md +8 -9
- package/README.zh-CN.md +7 -6
- package/USAGE.md +301 -64
- package/deploy/sema-up/README.md +35 -1
- package/deploy/sema-up/chart/Chart.yaml +1 -1
- package/deploy/sema-up/chart/templates/NOTES.txt +15 -0
- package/deploy/sema-up/chart/templates/_helpers.tpl +129 -0
- package/deploy/sema-up/chart/templates/sealed-key.yaml +24 -0
- package/deploy/sema-up/chart/templates/server-v4pro.yaml +19 -2
- package/deploy/sema-up/chart/templates/server.yaml +19 -2
- package/deploy/sema-up/chart/values-full-single.yaml +5 -1
- package/deploy/sema-up/chart/values.yaml +34 -0
- package/deploy/sema-up/stack/compose.yaml +3 -0
- package/deploy/sema-up/stack/sema-stack-cluster.yaml +4 -2
- package/dist/adoption/plan.d.ts +3 -2
- package/dist/adoption/plan.js +7 -5
- package/dist/adoption/runner.js +3 -10
- package/dist/adoption/sql.d.ts +3 -2
- package/dist/adoption/sql.js +3 -5
- package/dist/adoption/wire.d.ts +4 -1
- package/dist/adoption/wire.js +1 -0
- package/dist/approval-ask-audit-store.d.ts +32 -14
- package/dist/approval-ask-audit-store.js +272 -74
- package/dist/approval-ask-machine.d.ts +15 -0
- package/dist/approval-ask-machine.js +6 -0
- package/dist/approval-ask-retention.d.ts +136 -0
- package/dist/approval-ask-retention.js +102 -0
- package/dist/approval-card.d.ts +83 -23
- package/dist/approval-card.js +45 -15
- package/dist/approval-content-kind.d.ts +5 -2
- package/dist/approval-content-kind.js +16 -3
- package/dist/approval-deny-reasons.d.ts +1 -1
- package/dist/approval-gate-currency.d.ts +72 -0
- package/dist/approval-gate-currency.js +94 -0
- package/dist/approval.d.ts +15 -0
- package/dist/approval.js +55 -17
- package/dist/audit.d.ts +27 -10
- package/dist/audit.js +12 -16
- package/dist/bake-runner/main.js +3 -2
- package/dist/boot/coordinators.d.ts +6 -1
- package/dist/boot/coordinators.js +6 -3
- package/dist/boot/deferred-sandbox-path-env.js +1 -1
- package/dist/boot/execution-env.js +9 -7
- package/dist/boot/execution-lane.js +11 -0
- package/dist/boot/leader.d.ts +2 -0
- package/dist/boot/leader.js +4 -1
- package/dist/boot/memory-boundary.js +2 -2
- package/dist/boot/memory-consolidation.d.ts +5 -4
- package/dist/boot/memory-consolidation.js +7 -6
- package/dist/boot/org-delivery.d.ts +28 -0
- package/dist/boot/org-delivery.js +20 -0
- package/dist/boot/read-deny-anchor-audit.js +2 -2
- package/dist/boot/reapers.d.ts +3 -0
- package/dist/boot/reapers.js +21 -1
- package/dist/boot/resolve-spec.d.ts +3 -0
- package/dist/boot/resolve-spec.js +60 -81
- package/dist/boot/runner-deps.d.ts +18 -5
- package/dist/boot/runner-deps.js +10 -0
- package/dist/boot/session-faces.d.ts +1 -0
- package/dist/boot/session-faces.js +4 -2
- package/dist/boot/stage-02-web-admin-pull.js +4 -1
- package/dist/boot/stage-06-runners.d.ts +6 -3
- package/dist/boot/stage-06-runners.js +20 -4
- package/dist/boot/stage-07-capability-layer.d.ts +4 -1
- package/dist/boot/stage-07-capability-layer.js +24 -3
- package/dist/boot/stage-08-reapers.d.ts +4 -1
- package/dist/boot/stage-08-reapers.js +2 -2
- package/dist/boot/stage-09-leader.d.ts +5 -1
- package/dist/boot/stage-09-leader.js +2 -2
- package/dist/boot/stage-10-http-server.d.ts +5 -1
- package/dist/boot/stage-10-http-server.js +6 -6
- package/dist/boot/stores.d.ts +8 -0
- package/dist/boot/stores.js +10 -14
- package/dist/boot/web-admin.d.ts +10 -0
- package/dist/boot/web-admin.js +162 -65
- package/dist/boot/workflow-orchestration.d.ts +2 -0
- package/dist/boot/workflow-orchestration.js +2 -1
- package/dist/brain.js +8 -3
- package/dist/breaker-key.d.ts +6 -0
- package/dist/breaker-key.js +29 -0
- package/dist/budget.js +5 -1
- package/dist/caller-text-echo.d.ts +11 -6
- package/dist/caller-text-echo.js +10 -12
- package/dist/canonical-json.d.ts +2 -41
- package/dist/canonical-json.js +2 -13
- package/dist/capabilities/center-plugins.d.ts +7 -0
- package/dist/capabilities/center-plugins.js +1 -1
- package/dist/capabilities/composite-members.d.ts +1 -1
- package/dist/capabilities/composite-members.js +1 -1
- package/dist/capabilities/execution-lane.js +2 -2
- package/dist/capabilities/hands-lane.d.ts +21 -0
- package/dist/capabilities/hands-lane.js +10 -0
- package/dist/capabilities/mcp-probe.d.ts +36 -4
- package/dist/capabilities/mcp-probe.js +29 -6
- package/dist/capabilities/scenarios.d.ts +63 -7
- package/dist/capabilities/scenarios.js +39 -7
- package/dist/capability-facts.d.ts +36 -4
- package/dist/capability-facts.js +30 -4
- package/dist/center-config-tier.d.ts +69 -0
- package/dist/center-config-tier.js +42 -0
- package/dist/checkpoint-ctx-seal-m28.d.ts +42 -0
- package/dist/checkpoint-ctx-seal-m28.js +19 -0
- package/dist/checkpoint-identity.d.ts +16 -0
- package/dist/checkpoint-identity.js +19 -0
- package/dist/config-catalog.d.ts +17 -10
- package/dist/config-catalog.js +43 -28
- package/dist/config-d-migration.d.ts +3 -1
- package/dist/config-d-migration.js +2 -2
- package/dist/config-dir-trust.d.ts +114 -0
- package/dist/config-dir-trust.js +388 -0
- package/dist/config-invariants.d.ts +18 -5
- package/dist/config-invariants.js +35 -3
- package/dist/config-provider.d.ts +6 -1
- package/dist/config-provider.js +6 -2
- package/dist/config-types.d.ts +71 -14
- package/dist/config-types.js +8 -0
- package/dist/config.d.ts +13 -0
- package/dist/config.js +121 -59
- package/dist/delegated-session-owner.d.ts +185 -0
- package/dist/delegated-session-owner.js +119 -0
- package/dist/device-protocol.d.ts +5 -3
- package/dist/device-protocol.js +1 -1
- package/dist/device-ws-hub.js +21 -2
- package/dist/device-ws-protocol.d.ts +70 -6
- package/dist/device-ws-protocol.js +50 -23
- package/dist/elicitation.js +3 -2
- package/dist/entry-problems.d.ts +34 -0
- package/dist/entry-problems.js +14 -0
- package/dist/execution-lane-caps.d.ts +49 -10
- package/dist/execution-lane-caps.js +43 -4
- package/dist/field-conflict.d.ts +44 -0
- package/dist/field-conflict.js +21 -0
- package/dist/file-history-disclosure.d.ts +34 -5
- package/dist/file-history-disclosure.js +2 -2
- package/dist/fleet/fleet-bus.d.ts +84 -6
- package/dist/fleet/fleet-bus.js +42 -9
- package/dist/hooks/classic-stations.d.ts +10 -5
- package/dist/hooks/classic-stations.js +4 -4
- package/dist/hooks/command-spawn.d.ts +57 -0
- package/dist/hooks/command-spawn.js +139 -0
- package/dist/hooks/hook-llm.js +2 -2
- package/dist/hooks/hook-runner.d.ts +127 -25
- package/dist/hooks/hook-runner.js +668 -287
- package/dist/hooks/hook-source.d.ts +44 -0
- package/dist/hooks/hook-source.js +40 -0
- package/dist/http/active-run-conflict.d.ts +5 -13
- package/dist/http/active-run-conflict.js +17 -27
- package/dist/http/admission.js +111 -60
- package/dist/http/approval-sweeps.js +58 -19
- package/dist/http/caller-trust.d.ts +77 -0
- package/dist/http/caller-trust.js +143 -0
- package/dist/http/checkpoint-ctx.d.ts +173 -18
- package/dist/http/checkpoint-ctx.js +171 -12
- package/dist/http/claimed-run.d.ts +4 -3
- package/dist/http/device-placement.d.ts +2 -4
- package/dist/http/device-placement.js +2 -1
- package/dist/http/dispatch.js +13 -7
- package/dist/http/durable-run-leg.d.ts +4 -32
- package/dist/http/durable-run-leg.js +37 -12
- package/dist/http/idempotency.d.ts +2 -2
- package/dist/http/idempotency.js +2 -2
- package/dist/http/owner-access.d.ts +56 -0
- package/dist/http/owner-access.js +42 -0
- package/dist/http/principal-gate.d.ts +78 -27
- package/dist/http/principal-gate.js +64 -14
- package/dist/http/readiness.d.ts +11 -5
- package/dist/http/readiness.js +10 -3
- package/dist/http/refusal-body.d.ts +12 -0
- package/dist/http/refusal-body.js +11 -0
- package/dist/http/request-guards.d.ts +16 -32
- package/dist/http/request-guards.js +31 -30
- package/dist/http/resume-ctx-read.d.ts +50 -6
- package/dist/http/resume-ctx-read.js +33 -7
- package/dist/http/resume-legs.d.ts +5 -5
- package/dist/http/resume-legs.js +163 -91
- package/dist/http/retired-service-deps.d.ts +43 -0
- package/dist/http/retired-service-deps.js +18 -0
- package/dist/http/route-ctx.d.ts +44 -37
- package/dist/http/route-ctx.js +4 -0
- package/dist/http/route-table.js +2 -0
- package/dist/http/routes/a2a-serve.js +3 -4
- package/dist/http/routes/admin-config-refresh.js +1 -1
- package/dist/http/routes/admin-drain.js +1 -1
- package/dist/http/routes/admin-memory-optout.js +5 -1
- package/dist/http/routes/adoption.js +2 -9
- package/dist/http/routes/agents-roster.js +2 -3
- package/dist/http/routes/approvals-assistant.js +91 -56
- package/dist/http/routes/background.js +3 -2
- package/dist/http/routes/capabilities.js +20 -4
- package/dist/http/routes/config-catalog.d.ts +3 -3
- package/dist/http/routes/config-catalog.js +1 -1
- package/dist/http/routes/devices.js +3 -17
- package/dist/http/routes/diagnostics.d.ts +3 -3
- package/dist/http/routes/diagnostics.js +6 -2
- package/dist/http/routes/fleet.js +4 -4
- package/dist/http/routes/images.js +6 -7
- package/dist/http/routes/leader.js +1 -8
- package/dist/http/routes/mcp-reconnect.d.ts +1 -1
- package/dist/http/routes/mcp-reconnect.js +3 -2
- package/dist/http/routes/memory-bundle.js +1 -1
- package/dist/http/routes/memory-compliance.js +1 -1
- package/dist/http/routes/memory-consolidation.js +1 -1
- package/dist/http/routes/memory-origin.d.ts +1 -1
- package/dist/http/routes/memory-origin.js +1 -1
- package/dist/http/routes/memory-policy.d.ts +1 -1
- package/dist/http/routes/memory-policy.js +3 -2
- package/dist/http/routes/notify-wake.js +8 -21
- package/dist/http/routes/observability.js +2 -2
- package/dist/http/routes/principal-inbox.d.ts +39 -0
- package/dist/http/routes/principal-inbox.js +76 -0
- package/dist/http/routes/retention-ops.js +2 -9
- package/dist/http/routes/rules.js +9 -22
- package/dist/http/routes/runs.d.ts +5 -0
- package/dist/http/routes/runs.js +63 -115
- package/dist/http/routes/session-sync.d.ts +3 -2
- package/dist/http/routes/session-sync.js +53 -48
- package/dist/http/routes/sessions-list.d.ts +1 -1
- package/dist/http/routes/sessions-list.js +3 -4
- package/dist/http/routes/sessions.js +33 -43
- package/dist/http/routes/side-query.js +6 -4
- package/dist/http/routes/tasks.js +71 -30
- package/dist/http/routes/trace-usage.js +7 -9
- package/dist/http/routes/workflows.d.ts +351 -0
- package/dist/http/routes/workflows.js +153 -119
- package/dist/http/run-declared-families.d.ts +16 -0
- package/dist/http/run-declared-families.js +12 -0
- package/dist/http/send.d.ts +24 -3
- package/dist/http/send.js +14 -2
- package/dist/http/server.d.ts +23 -18
- package/dist/http/server.js +20 -15
- package/dist/http/stored-body-replay.js +17 -4
- package/dist/http/submit-ctx.d.ts +4 -3
- package/dist/http/submit-ctx.js +23 -6
- package/dist/http/submit-replay.d.ts +4 -3
- package/dist/http/submit-replay.js +5 -4
- package/dist/http/wire-types.d.ts +17 -10
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -1
- package/dist/lane-home-probe.d.ts +78 -9
- package/dist/lane-home-probe.js +78 -12
- package/dist/leader/endpoint.d.ts +4 -1
- package/dist/leader/endpoint.js +11 -10
- package/dist/leader/wire.d.ts +4 -0
- package/dist/leader/wire.js +8 -7
- package/dist/limit-refusal.d.ts +17 -5
- package/dist/limit-refusal.js +4 -1
- package/dist/main.js +1 -1
- package/dist/mcp-assertion-header.d.ts +31 -10
- package/dist/mcp-assertion-header.js +10 -3
- package/dist/memory-origin-fence.js +2 -2
- package/dist/memory-project-layer.d.ts +37 -0
- package/dist/memory-project-layer.js +46 -0
- package/dist/memory-scope.d.ts +23 -16
- package/dist/memory-scope.js +24 -8
- package/dist/memory-sync-client.d.ts +27 -13
- package/dist/memory-sync-client.js +107 -18
- package/dist/model-compat.d.ts +1 -1
- package/dist/model-entry-refusal.d.ts +9 -1
- package/dist/model-entry-refusal.js +6 -1
- package/dist/model-plane-face.d.ts +51 -0
- package/dist/model-plane-face.js +24 -0
- package/dist/model-provider.d.ts +5 -0
- package/dist/model-provider.js +4 -2
- package/dist/model-route-endpoint.d.ts +8 -11
- package/dist/model-route-endpoint.js +4 -7
- package/dist/model-route-env.d.ts +22 -0
- package/dist/model-route-env.js +2 -0
- package/dist/model-route-gap.d.ts +60 -30
- package/dist/model-route-gap.js +21 -15
- package/dist/observability/corrupt-read-seat.d.ts +7 -0
- package/dist/observability/corrupt-read-seat.js +9 -0
- package/dist/observability/err-text.d.ts +6 -0
- package/dist/observability/err-text.js +18 -4
- package/dist/observability/fail-open.d.ts +35 -9
- package/dist/observability/fail-open.js +35 -9
- package/dist/observability/metrics.js +1 -0
- package/dist/observability/prompt-manifest.d.ts +22 -25
- package/dist/observability/prompt-manifest.js +54 -50
- package/dist/observability/tool-trace.js +9 -10
- package/dist/operator-ask.d.ts +10 -0
- package/dist/operator-ask.js +3 -0
- package/dist/orchestration/workflow-agent-session-index.d.ts +12 -12
- package/dist/orchestration/workflow-agent-session-index.js +9 -13
- package/dist/orchestration/workflow-completion-inbox.d.ts +42 -15
- package/dist/orchestration/workflow-completion-inbox.js +13 -0
- package/dist/orchestration/workflow-notify-journal.d.ts +13 -32
- package/dist/orchestration/workflow-notify-journal.js +27 -34
- package/dist/orchestration/workflow-park-decision.d.ts +89 -0
- package/dist/orchestration/workflow-park-decision.js +119 -0
- package/dist/org-delivery-gate.d.ts +81 -0
- package/dist/org-delivery-gate.js +59 -0
- package/dist/panel-tool-names.d.ts +23 -0
- package/dist/panel-tool-names.js +8 -0
- package/dist/parked-decide.d.ts +43 -11
- package/dist/parked-decide.js +100 -51
- package/dist/parked-revive-surface.d.ts +9 -2
- package/dist/parked-revive-surface.js +11 -8
- package/dist/permission-rule-list.d.ts +1 -1
- package/dist/permission-rule-list.js +2 -2
- package/dist/permission-rule-vocab.d.ts +1 -23
- package/dist/permission-rule-vocab.js +0 -13
- package/dist/plugins/approval-ask-store-file.d.ts +16 -1
- package/dist/plugins/approval-ask-store-file.js +21 -0
- package/dist/plugins/approval-ask-store-memory.d.ts +18 -1
- package/dist/plugins/approval-ask-store-memory.js +34 -2
- package/dist/plugins/approval-ask-store-sql.d.ts +25 -0
- package/dist/plugins/approval-ask-store-sql.js +37 -1
- package/dist/plugins/approval-payload-fingerprint.d.ts +14 -6
- package/dist/plugins/approval-payload-fingerprint.js +1 -1
- package/dist/plugins/blob-backend.d.ts +38 -25
- package/dist/plugins/blob-backend.js +29 -19
- package/dist/plugins/breaker-state-sql.d.ts +2 -2
- package/dist/plugins/breaker-state-sql.js +4 -4
- package/dist/plugins/caching-session-store.d.ts +4 -1
- package/dist/plugins/caching-session-store.js +7 -3
- package/dist/plugins/checkpoint-store-sql.d.ts +36 -1
- package/dist/plugins/checkpoint-store-sql.js +65 -3
- package/dist/plugins/file-history-store-sql.d.ts +5 -2
- package/dist/plugins/file-history-store-sql.js +3 -1
- package/dist/plugins/file-run-store.d.ts +5 -13
- package/dist/plugins/file-run-store.js +9 -0
- package/dist/plugins/fork-routing-session-store.d.ts +9 -1
- package/dist/plugins/fork-routing-session-store.js +12 -1
- package/dist/plugins/host-platform.d.ts +2 -0
- package/dist/plugins/host-platform.js +2 -1
- package/dist/plugins/local-checkpoint-store.d.ts +15 -2
- package/dist/plugins/local-checkpoint-store.js +32 -1
- package/dist/plugins/local-session-store.d.ts +13 -10
- package/dist/plugins/local-session-store.js +23 -14
- package/dist/plugins/local-session-window.d.ts +45 -0
- package/dist/plugins/local-session-window.js +38 -0
- package/dist/plugins/mailbox-recipient-lifecycle.d.ts +3 -2
- package/dist/plugins/mailbox-store-sql.d.ts +30 -48
- package/dist/plugins/mailbox-store-sql.js +77 -52
- package/dist/plugins/memory-embedder-fingerprint.js +2 -1
- package/dist/plugins/memory-run-store.d.ts +5 -13
- package/dist/plugins/memory-run-store.js +8 -0
- package/dist/plugins/one-time-migrations.d.ts +121 -5
- package/dist/plugins/one-time-migrations.js +332 -8
- package/dist/plugins/org-delivery-quota-store.d.ts +123 -0
- package/dist/plugins/org-delivery-quota-store.js +228 -0
- package/dist/plugins/permission-rule-store-file.js +10 -6
- package/dist/plugins/permission-rule-store-sql.js +8 -8
- package/dist/plugins/pg-pool.js +39 -7
- package/dist/plugins/pg-session-storage.d.ts +18 -5
- package/dist/plugins/pg-session-storage.js +68 -23
- package/dist/plugins/remote-env-adb.d.ts +6 -12
- package/dist/plugins/remote-env-adb.js +12 -2
- package/dist/plugins/remote-env-device.d.ts +14 -7
- package/dist/plugins/remote-env-device.js +23 -15
- package/dist/plugins/remote-env-k8s.d.ts +8 -12
- package/dist/plugins/remote-env-k8s.js +23 -5
- package/dist/plugins/remote-env-local-docker.d.ts +10 -15
- package/dist/plugins/remote-env-local-docker.js +12 -9
- package/dist/plugins/remote-env-ssh.d.ts +10 -16
- package/dist/plugins/remote-env-ssh.js +17 -11
- package/dist/plugins/run-store-sql.d.ts +16 -14
- package/dist/plugins/run-store-sql.js +22 -2
- package/dist/plugins/schema-indexes.js +2 -0
- package/dist/plugins/send-user-file.js +2 -2
- package/dist/plugins/session-epoch-anchor.d.ts +32 -6
- package/dist/plugins/session-epoch-anchor.js +2 -3
- package/dist/plugins/session-ownership.d.ts +11 -0
- package/dist/plugins/session-ownership.js +11 -0
- package/dist/plugins/session-placement.d.ts +12 -14
- package/dist/plugins/session-placement.js +2 -15
- package/dist/plugins/session-store.d.ts +18 -8
- package/dist/plugins/session-store.js +15 -4
- package/dist/plugins/sql-driver.d.ts +74 -11
- package/dist/plugins/sql-driver.js +210 -12
- package/dist/plugins/sql-errors.d.ts +23 -0
- package/dist/plugins/sql-errors.js +21 -0
- package/dist/plugins/store-backend.d.ts +36 -1
- package/dist/plugins/store-backend.js +40 -14
- package/dist/plugins/store-contracts.d.ts +42 -0
- package/dist/plugins/store-contracts.js +1 -0
- package/dist/plugins/tidb-pool.d.ts +6 -1
- package/dist/plugins/tidb-pool.js +44 -10
- package/dist/plugins/tidb-session-storage.d.ts +3 -2
- package/dist/plugins/tidb-session-storage.js +2 -2
- package/dist/plugins/tidb-session-store.d.ts +25 -5
- package/dist/plugins/tidb-session-store.js +67 -21
- package/dist/plugins/workflow-journal-store-sql.d.ts +73 -19
- package/dist/plugins/workflow-journal-store-sql.js +19 -6
- package/dist/plugins/workflow-run-store-sql.d.ts +30 -8
- package/dist/plugins/workflow-run-store-sql.js +50 -9
- package/dist/posture-source.d.ts +18 -10
- package/dist/posture-source.js +4 -1
- package/dist/project-identity.d.ts +45 -6
- package/dist/project-identity.js +162 -8
- package/dist/project-layer-key.d.ts +28 -0
- package/dist/project-layer-key.js +29 -0
- package/dist/question.js +3 -2
- package/dist/read-deny-entry.d.ts +25 -0
- package/dist/read-deny-entry.js +12 -0
- package/dist/read-face-posture.d.ts +37 -11
- package/dist/read-face-posture.js +32 -3
- package/dist/request-key-closure.d.ts +7 -0
- package/dist/request-key-closure.js +9 -1
- package/dist/request-tool-axes.d.ts +12 -0
- package/dist/request-tool-axes.js +20 -0
- package/dist/request-value-closure.d.ts +5 -0
- package/dist/request-value-closure.js +11 -3
- package/dist/rule-entry-list.d.ts +26 -2
- package/dist/rule-entry-list.js +72 -12
- package/dist/rules-consent.js +3 -4
- package/dist/run-acceptance.d.ts +19 -3
- package/dist/run-acceptance.js +13 -1
- package/dist/run-local.js +37 -6
- package/dist/runs.d.ts +12 -10
- package/dist/runs.js +25 -6
- package/dist/runtime-caps-resolver.d.ts +16 -6
- package/dist/runtime-caps-resolver.js +18 -5
- package/dist/sandbox-pkg-source.d.ts +19 -1
- package/dist/sandbox-pkg-source.js +29 -7
- package/dist/sealed-key.d.ts +32 -0
- package/dist/sealed-key.js +55 -1
- package/dist/security.d.ts +95 -15
- package/dist/security.js +52 -9
- package/dist/session-error-wire.d.ts +31 -7
- package/dist/session-error-wire.js +9 -2
- package/dist/session-sync-kernel.d.ts +37 -10
- package/dist/session-sync-kernel.js +10 -0
- package/dist/session-titled-frame.d.ts +33 -0
- package/dist/session-titled-frame.js +4 -0
- package/dist/session-titler.d.ts +9 -1
- package/dist/session-titler.js +15 -4
- package/dist/spec-fields.js +21 -2
- package/dist/sql-engine-posture.d.ts +3 -1
- package/dist/task-a2a.js +4 -3
- package/dist/task-cwd.d.ts +1 -1
- package/dist/task-mcp.d.ts +5 -5
- package/dist/task-mcp.js +27 -74
- package/dist/task-settings.d.ts +93 -88
- package/dist/task-settings.js +71 -63
- package/dist/text-bidi.d.ts +0 -4
- package/dist/text-bidi.js +1 -3
- package/dist/tighten-only-seats.d.ts +150 -0
- package/dist/tighten-only-seats.js +196 -0
- package/dist/tool-approval.d.ts +451 -136
- package/dist/tool-approval.js +733 -256
- package/dist/tool-rule-names.d.ts +6 -0
- package/dist/trace/core-keyset-guard.d.ts +108 -25
- package/dist/trace/exit-seat.d.ts +40 -0
- package/dist/trace/exit-seat.js +130 -0
- package/dist/trace/ledger-events.d.ts +6 -3
- package/dist/trace/project.d.ts +98 -13
- package/dist/trace/project.js +57 -32
- package/dist/trace/projection-table.d.ts +100 -0
- package/dist/trace/projection-table.js +130 -0
- package/dist/trace/redact.d.ts +57 -0
- package/dist/trace/redact.js +962 -168
- package/dist/trace/self-minted-wire.d.ts +349 -1
- package/dist/trace/self-minted-wire.js +118 -1
- package/dist/trace/wire-projection-faces.d.ts +304 -2
- package/dist/trace/wire-projection-faces.js +160 -13
- package/dist/url-refusal.js +2 -0
- package/dist/web-admin-client/apply-effective.d.ts +12 -0
- package/dist/web-admin-client/apply-effective.js +56 -5
- package/dist/web-admin-client/facade.d.ts +1 -1
- package/dist/web-admin-client/facade.js +1 -1
- package/dist/web-admin-client/read-face.js +5 -9
- package/dist/web-admin-client/skills-mcp.d.ts +1 -1
- package/dist/web-admin-client/skills-mcp.js +6 -8
- package/dist/web-admin-client/types.d.ts +1 -1
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +57 -9
- package/package.json +8 -4
- package/scripts/drain-check-local.mjs +34 -2
- package/scripts/drain-check.mysql.sql +20 -2
- package/scripts/drain-check.pg.sql +20 -2
- package/dist/mcp-tool-faces.d.ts +0 -69
- package/dist/mcp-tool-faces.js +0 -106
|
@@ -21,6 +21,8 @@ const JOURNAL_OPS = [
|
|
|
21
21
|
"deferReconcile",
|
|
22
22
|
"resolveProvisional",
|
|
23
23
|
"deleteByTask",
|
|
24
|
+
"reapSettledAsks",
|
|
25
|
+
"reapEmptyBatches",
|
|
24
26
|
];
|
|
25
27
|
const JOURNAL_OP_WORDS = new Set(JOURNAL_OPS);
|
|
26
28
|
export const JOURNAL_OP_TABLE_GATE = true;
|
|
@@ -159,6 +161,11 @@ export class FileApprovalAskStore {
|
|
|
159
161
|
refuse("call record without a clock reading");
|
|
160
162
|
if (probe.op === "claimTerminal" && !JOURNAL_V1_CLAIM_INTENT_WORDS.has(String(raw.intent)))
|
|
161
163
|
refuse("claimTerminal intent outside this build's journal set (a newer server wrote it)");
|
|
164
|
+
if (probe.op === "reapSettledAsks") {
|
|
165
|
+
const anchors = raw.keepAnchors;
|
|
166
|
+
if (!Array.isArray(anchors) || !anchors.every((a) => typeof a === "string"))
|
|
167
|
+
refuse("reapSettledAsks record without a string-array keepAnchors");
|
|
168
|
+
}
|
|
162
169
|
const rec = raw;
|
|
163
170
|
this.pinnedNow = rec.t;
|
|
164
171
|
try {
|
|
@@ -197,6 +204,10 @@ export class FileApprovalAskStore {
|
|
|
197
204
|
return this.core.resolveProvisional(rec.askId, rec.from, rec.to, rec.patch);
|
|
198
205
|
case "deleteByTask":
|
|
199
206
|
return this.core.deleteByTask(rec.taskId);
|
|
207
|
+
case "reapSettledAsks":
|
|
208
|
+
return this.core.reapSettledAsks(rec.cutoffMs, rec.limit, new Set(rec.keepAnchors));
|
|
209
|
+
case "reapEmptyBatches":
|
|
210
|
+
return this.core.reapEmptyBatches(rec.cutoffMs, rec.limit);
|
|
200
211
|
default: {
|
|
201
212
|
const unreachable = rec;
|
|
202
213
|
throw new Error(`FileApprovalAskStore: unreachable journal op ${JSON.stringify(unreachable)}`);
|
|
@@ -244,6 +255,16 @@ export class FileApprovalAskStore {
|
|
|
244
255
|
async deleteByTask(taskId) {
|
|
245
256
|
return this.write({ op: "deleteByTask", taskId }, (c) => this.core.deleteByTask(c.taskId));
|
|
246
257
|
}
|
|
258
|
+
async reapSettledAsks(cutoffMs, limit, keepAnchors) {
|
|
259
|
+
if (this.core.planReapSettledAsks(cutoffMs, limit, keepAnchors).length === 0)
|
|
260
|
+
return 0;
|
|
261
|
+
return this.write({ op: "reapSettledAsks", cutoffMs, limit, keepAnchors: [...keepAnchors].sort() }, (c) => this.core.reapSettledAsks(c.cutoffMs, c.limit, new Set(c.keepAnchors)));
|
|
262
|
+
}
|
|
263
|
+
async reapEmptyBatches(cutoffMs, limit) {
|
|
264
|
+
if (this.core.planReapEmptyBatches(cutoffMs, limit).length === 0)
|
|
265
|
+
return 0;
|
|
266
|
+
return this.write({ op: "reapEmptyBatches", cutoffMs, limit }, (c) => this.core.reapEmptyBatches(c.cutoffMs, c.limit));
|
|
267
|
+
}
|
|
247
268
|
listByState(state, limit) {
|
|
248
269
|
return this.core.listByState(state, limit);
|
|
249
270
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* 🔴 **S-384:本类同时是 `FileApprovalAskStore` 的内核**(local 车道的持久形不再是「第二份手抄」——
|
|
8
8
|
* 它把本类**原样**包起来,每次写前先把**调用本身**落 JSONL、重放时用同一只内核重放同一串调用)。
|
|
9
|
-
*
|
|
9
|
+
* 为此本类多了这几件、且**只**多这几件(全是数据面,零转移判定):
|
|
10
10
|
* · {@link InMemoryApprovalAskStore.constructor} 的可注入钟 `now` —— 五个动词(decideAsk / claimTerminal /
|
|
11
11
|
* expireAsk / bindBatch / abortBatch)原先直读 `Date.now()`,那是重放路上唯一的不确定源:同一串调用
|
|
12
12
|
* 重放出来的 `updatedAtMs`/`decidedAtMs` 必须与当初**逐字节**相同,否则「重放等价」是假的。
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
* · {@link InMemoryApprovalAskStore.exportRows} / {@link InMemoryApprovalAskStore.importRows} —— 压实用的
|
|
17
17
|
* 整本快照口(纯搬行,不经任何谓词)。压实要把「一串调用」折成「一串终局行」,没有这一对就只能在
|
|
18
18
|
* File 形里另写一套「哪些行还算数」的判据 —— 那才是第二份手抄。
|
|
19
|
+
* · {@link InMemoryApprovalAskStore.planReapSettledAsks} / {@link InMemoryApprovalAskStore.planReapEmptyBatches} —— 两只留存动词的
|
|
20
|
+
* **纯读选择**(删哪些行)。File 形据它判「这一次有没有东西可删」,空的一次不落账本;删除本身仍是接口动词,与选择共用这一只。
|
|
19
21
|
*
|
|
20
22
|
* 行为口径与 `SqlApprovalAskStore` 逐条对齐:
|
|
21
23
|
* - ensureAsk 幂等 upsert(ask_id 已在 ⇒ 返回既有行,不改写;batch 行若不存在则以 OPEN 补齐)。
|
|
@@ -106,6 +108,21 @@ export declare class InMemoryApprovalAskStore implements ApprovalAskStore {
|
|
|
106
108
|
resolveProvisional(askId: string, from: AskState, to: AskState, patch: AskTransitionPatch): Promise<boolean>;
|
|
107
109
|
deleteByTask(taskId: string): Promise<void>;
|
|
108
110
|
getBatch(batchId: string): Promise<BatchRow | null>;
|
|
111
|
+
/**
|
|
112
|
+
* 留存律的问行半场(谓词见接口注,与 SQL twin 的 `DELETE … LIMIT` 同答)。方法体全同步(零 `await`)⇒ File 形把它当一次落账的调用
|
|
113
|
+
* 重放时逐字复现(cutoff / limit / 挂靠集都是调用实参,不读钟)。
|
|
114
|
+
*/
|
|
115
|
+
reapSettledAsks(cutoffMs: number, limit: number, keepAnchors: ReadonlySet<string>): Promise<number>;
|
|
116
|
+
/** 留存律的批行半场(谓词见接口注,与 SQL twin 同答;同上,全同步)。 */
|
|
117
|
+
reapEmptyBatches(cutoffMs: number, limit: number): Promise<number>;
|
|
118
|
+
/**
|
|
119
|
+
* 两只留存动词会删哪些行(纯读,零写)。File 形据此判「这一次有没有东西可删」—— 空的一次不落账本(否则每次清扫都往账本里写一条什么都
|
|
120
|
+
* 没做的调用)。与删除共用这一只选择,没有第二份谓词。选哪几只:按 `(updatedAtMs, id)` 升序取前 `limit` 只 —— 确定性只为重放,律本身
|
|
121
|
+
* 不依赖次序(SQL twin 不排序)。
|
|
122
|
+
*/
|
|
123
|
+
planReapSettledAsks(cutoffMs: number, limit: number, keepAnchors: ReadonlySet<string>): readonly string[];
|
|
124
|
+
/** {@link reapEmptyBatches} 的选择(同上)。 */
|
|
125
|
+
planReapEmptyBatches(cutoffMs: number, limit: number): readonly string[];
|
|
109
126
|
}
|
|
110
127
|
export type { AskDecision, AskState, BatchState };
|
|
111
128
|
//# sourceMappingURL=approval-ask-store-memory.d.ts.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { canAskTransition } from "../approval-ask-machine.js";
|
|
1
|
+
import { ASK_TERMINAL, canAskTransition } from "../approval-ask-machine.js";
|
|
2
2
|
import { APPROVAL_ENDING_CAUSES } from "../approval-deny-reasons.js";
|
|
3
|
-
import { askRowOnCall, assertDecideSettledBy, assertIdempotencyKeyShape, assertIntegralEpochMs, ApprovalAskReadAbortedError, claimTargetState, POLICY_REFUSAL_DECISION, projectAskTerminalClaimTruth } from "./approval-ask-store-sql.js";
|
|
3
|
+
import { askRowOnCall, assertDecideSettledBy, assertIdempotencyKeyShape, assertIntegralEpochMs, assertReapBounds, ApprovalAskReadAbortedError, claimTargetState, POLICY_REFUSAL_DECISION, projectAskTerminalClaimTruth } from "./approval-ask-store-sql.js";
|
|
4
4
|
function applyPatch(row, patch) {
|
|
5
5
|
if (patch.decision !== undefined)
|
|
6
6
|
row.decision = patch.decision;
|
|
@@ -359,5 +359,37 @@ export class InMemoryApprovalAskStore {
|
|
|
359
359
|
const b = this.batches.get(batchId);
|
|
360
360
|
return b ? { ...b } : null;
|
|
361
361
|
}
|
|
362
|
+
async reapSettledAsks(cutoffMs, limit, keepAnchors) {
|
|
363
|
+
const ids = this.planReapSettledAsks(cutoffMs, limit, keepAnchors);
|
|
364
|
+
for (const id of ids)
|
|
365
|
+
this.asks.delete(id);
|
|
366
|
+
return ids.length;
|
|
367
|
+
}
|
|
368
|
+
async reapEmptyBatches(cutoffMs, limit) {
|
|
369
|
+
const ids = this.planReapEmptyBatches(cutoffMs, limit);
|
|
370
|
+
for (const id of ids)
|
|
371
|
+
this.batches.delete(id);
|
|
372
|
+
return ids.length;
|
|
373
|
+
}
|
|
374
|
+
planReapSettledAsks(cutoffMs, limit, keepAnchors) {
|
|
375
|
+
assertReapBounds(cutoffMs, limit);
|
|
376
|
+
const anchored = (r) => keepAnchors.has(r.taskId) || keepAnchors.has(r.sessionId) || keepAnchors.has(r.sourceTaskId);
|
|
377
|
+
return [...this.asks.values()]
|
|
378
|
+
.filter((r) => ASK_TERMINAL.has(r.state) && !r.provisional && r.updatedAtMs < cutoffMs && !anchored(r))
|
|
379
|
+
.sort((a, b) => a.updatedAtMs - b.updatedAtMs || (a.askId < b.askId ? -1 : a.askId > b.askId ? 1 : 0))
|
|
380
|
+
.slice(0, limit)
|
|
381
|
+
.map((r) => r.askId);
|
|
382
|
+
}
|
|
383
|
+
planReapEmptyBatches(cutoffMs, limit) {
|
|
384
|
+
assertReapBounds(cutoffMs, limit);
|
|
385
|
+
const occupied = new Set();
|
|
386
|
+
for (const r of this.asks.values())
|
|
387
|
+
occupied.add(r.batchId);
|
|
388
|
+
return [...this.batches.values()]
|
|
389
|
+
.filter((b) => b.updatedAtMs < cutoffMs && !occupied.has(b.batchId))
|
|
390
|
+
.sort((a, b) => a.updatedAtMs - b.updatedAtMs || (a.batchId < b.batchId ? -1 : a.batchId > b.batchId ? 1 : 0))
|
|
391
|
+
.slice(0, limit)
|
|
392
|
+
.map((b) => b.batchId);
|
|
393
|
+
}
|
|
362
394
|
}
|
|
363
395
|
//# sourceMappingURL=approval-ask-store-memory.js.map
|
|
@@ -268,6 +268,8 @@ export type DecideResult = {
|
|
|
268
268
|
* 隐式截断 —— 两方言不同答;这里改成两方言同一句 TypeError(响亮,点名字段与值),铸点侧(`tool-approval.ts`
|
|
269
269
|
* 的 deadline 铸点)负责给整数,本守卫只拦漏网。 */
|
|
270
270
|
export declare function assertIntegralEpochMs(field: string, value: number): void;
|
|
271
|
+
/** 留存动词两只的入参门(三 twin 同判):cutoff 是整数毫秒、limit 是正整数(limit 在 SQL twin 里内联进语句)。 */
|
|
272
|
+
export declare function assertReapBounds(cutoffMs: number, limit: number): void;
|
|
271
273
|
export declare function assertIdempotencyKeyShape(key: string): void;
|
|
272
274
|
/** `settled_by` 列宽 = 这一格的唯一尺(两方言 DDL 与下面的形门读同一个常量,不手抄第二份)。 */
|
|
273
275
|
export declare const MAX_SETTLED_BY_CHARS = 16;
|
|
@@ -510,6 +512,21 @@ export interface ApprovalAskStore {
|
|
|
510
512
|
getByIdempotencyKey(taskId: string, key: string): Promise<AskRow | null>;
|
|
511
513
|
resolveProvisional(askId: string, from: AskState, to: AskState, patch: AskTransitionPatch): Promise<boolean>;
|
|
512
514
|
deleteByTask(taskId: string): Promise<void>;
|
|
515
|
+
/**
|
|
516
|
+
* **审批账本留存律的问账半场之一:问行**(律、节律与「还能动的 run」一处成文在 `approval-ask-retention.ts`;维护 tick 是唯一调用方)。
|
|
517
|
+
* 删至多 `limit` 只**结了**的问行 —— `state ∈ ASK_TERMINAL` ∧ `provisional = false`(provisional 行还能被 {@link resolveProvisional} 改判,没结)
|
|
518
|
+
* ∧ `updatedAtMs < cutoffMs` ∧ 行挂靠的三格(`taskId` / `sessionId` / `sourceTaskId`)都不在 `keepAnchors`(还能动的东西的挂靠集:
|
|
519
|
+
* 它还会回读这些行 —— 会话授权的争议判据 {@link listByCall} 把「这次调用在腿内没有行」读成可以代答)。非终态行多老都不删(它们归对账器
|
|
520
|
+
* 与起服收敛)。只删不改。
|
|
521
|
+
* 一条语句:要么删了并交回删掉的行数,要么一行没删就抛。三形同答(InMemory = 内核;File 形把这次调用照常落成一条账本动词,
|
|
522
|
+
* 空的一次不落;SQL 两方言 = 一条 `DELETE … LIMIT`,行谓词,多副本同扫只是一方删到零行)。
|
|
523
|
+
*/
|
|
524
|
+
reapSettledAsks(cutoffMs: number, limit: number, keepAnchors: ReadonlySet<string>): Promise<number>;
|
|
525
|
+
/**
|
|
526
|
+
* **问账半场之二:批行**。删至多 `limit` 只批里已经**没有任何问行**、且自己的 `updatedAtMs < cutoffMs` 的批 —— 批是问的容器,问走光了它才走。
|
|
527
|
+
* 一条语句,同上;三形同答。
|
|
528
|
+
*/
|
|
529
|
+
reapEmptyBatches(cutoffMs: number, limit: number): Promise<number>;
|
|
513
530
|
/** 🔴 车1 追加(不在设计定稿 §4 的接口字面清单里):设计的 `ApprovalAskStore` 接口本身没有批读方法,
|
|
514
531
|
* 但 §6 测试钉(3/6/7 条)要求断言批行的 state/boundAskId 变化——没有读口就无法验证 store 真做对了
|
|
515
532
|
* 它自己承诺的事。加一个纯观测方法(不改变任何写路径行为),SQL 双方言 + InMemory 三形都实现,
|
|
@@ -667,6 +684,14 @@ export declare class SqlApprovalAskStore implements ApprovalAskStore {
|
|
|
667
684
|
deferReconcile(askId: string, expectedState: AskState, expectedRev: number, nowMs: number): Promise<boolean>;
|
|
668
685
|
resolveProvisional(askId: string, from: AskState, to: AskState, patch: AskTransitionPatch): Promise<boolean>;
|
|
669
686
|
deleteByTask(taskId: string): Promise<void>;
|
|
687
|
+
/**
|
|
688
|
+
* 留存律的问行半场(谓词见接口注)。一条带 `LIMIT` 的 `DELETE`(PG 无 `DELETE … LIMIT` ⇒ `ctid IN (SELECT … LIMIT)`,与
|
|
689
|
+
* `permission-rule-store-sql.ts` 的清扫同形);终态闭集从 {@link ASK_TERMINAL} 展开成占位符,不在 SQL 里手抄态名;挂靠集对三格各一句
|
|
690
|
+
* (MySQL `NOT IN (?, …)` 三次展开、PG 同一只 `text[]` 参数用三次 `<> ALL`),空集不加。`limit` 先过整数门再内联(本仓常量,门只防将来的误用)。
|
|
691
|
+
*/
|
|
692
|
+
reapSettledAsks(cutoffMs: number, limit: number, keepAnchors: ReadonlySet<string>): Promise<number>;
|
|
693
|
+
/** 留存律的批行半场(谓词见接口注):一条带 `LIMIT` 的 `DELETE`,批里有没有问 = 对问表的 `NOT EXISTS`。 */
|
|
694
|
+
reapEmptyBatches(cutoffMs: number, limit: number): Promise<number>;
|
|
670
695
|
getBatch(batchId: string): Promise<BatchRow | null>;
|
|
671
696
|
}
|
|
672
697
|
/** MySQL-protocol(TiDB)绑定。 */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { mysqlDriver, pgDriver, dialectJsonEncoder } from "./sql-driver.js";
|
|
2
|
-
import { canAskTransition } from "../approval-ask-machine.js";
|
|
2
|
+
import { ASK_TERMINAL, canAskTransition } from "../approval-ask-machine.js";
|
|
3
3
|
import { APPROVAL_ENDING_CAUSES, causeOfAskRow } from "../approval-deny-reasons.js";
|
|
4
4
|
import { isDupKeyError, isMissingColumnError } from "./sql-errors.js";
|
|
5
5
|
import { decodeJsonColumn, foldJsonColumn, jsonColumnText, readJsonObjectColumn, readTriStateJsonValueColumn } from "./sql-json-column.js";
|
|
@@ -9,6 +9,11 @@ export function assertIntegralEpochMs(field, value) {
|
|
|
9
9
|
throw new TypeError(`approval-ask store: ${field} must be an integer epoch-ms (BIGINT column), got ${String(value)}`);
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
|
+
export function assertReapBounds(cutoffMs, limit) {
|
|
13
|
+
assertIntegralEpochMs("cutoffMs", cutoffMs);
|
|
14
|
+
if (!Number.isSafeInteger(limit) || limit <= 0)
|
|
15
|
+
throw new Error(`approval-ask-store: reap limit must be a positive integer, got ${String(limit)}`);
|
|
16
|
+
}
|
|
12
17
|
export function assertIdempotencyKeyShape(key) {
|
|
13
18
|
if (key.length === 0 || key.length > 255 || key.trim() !== key) {
|
|
14
19
|
throw new Error(`invalid idempotency key: must be 1-255 chars with no leading/trailing whitespace (got ${JSON.stringify(key.slice(0, 32))}${key.length > 32 ? "…" : ""}, length ${key.length})`);
|
|
@@ -722,6 +727,37 @@ export class SqlApprovalAskStore {
|
|
|
722
727
|
await this.db.query(this.q(`DELETE FROM ${APPROVAL_ASK_TABLE} WHERE task_id = ?`, `DELETE FROM ${APPROVAL_ASK_TABLE} WHERE task_id = $1`), [taskId]);
|
|
723
728
|
await this.db.query(this.q(`DELETE FROM ${APPROVAL_BATCH_TABLE} WHERE task_id = ?`, `DELETE FROM ${APPROVAL_BATCH_TABLE} WHERE task_id = $1`), [taskId]);
|
|
724
729
|
}
|
|
730
|
+
async reapSettledAsks(cutoffMs, limit, keepAnchors) {
|
|
731
|
+
assertReapBounds(cutoffMs, limit);
|
|
732
|
+
const terminal = [...ASK_TERMINAL];
|
|
733
|
+
const keep = [...keepAnchors];
|
|
734
|
+
const dialect = this.db.dialect;
|
|
735
|
+
let n = 0;
|
|
736
|
+
const statePh = terminal.map(() => ph(dialect, ++n)).join(", ");
|
|
737
|
+
const cutPh = ph(dialect, ++n);
|
|
738
|
+
const ANCHOR_COLUMNS = ["task_id", "session_id", "source_task_id"];
|
|
739
|
+
let keepSql = "";
|
|
740
|
+
let keepParams = [];
|
|
741
|
+
if (keep.length > 0 && dialect === "tidb") {
|
|
742
|
+
const list = keep.map(() => "?").join(", ");
|
|
743
|
+
keepSql = ANCHOR_COLUMNS.map((c) => ` AND ${c} NOT IN (${list})`).join("");
|
|
744
|
+
keepParams = ANCHOR_COLUMNS.flatMap(() => keep);
|
|
745
|
+
}
|
|
746
|
+
else if (keep.length > 0) {
|
|
747
|
+
const arr = ph(dialect, ++n);
|
|
748
|
+
keepSql = ANCHOR_COLUMNS.map((c) => ` AND ${c} <> ALL(${arr}::text[])`).join("");
|
|
749
|
+
keepParams = [keep];
|
|
750
|
+
}
|
|
751
|
+
const where = `state IN (${statePh}) AND provisional = 0 AND updated_at_ms < ${cutPh}${keepSql}`;
|
|
752
|
+
const params = [...terminal, cutoffMs, ...keepParams];
|
|
753
|
+
const res = await this.db.query(this.q(`DELETE FROM ${APPROVAL_ASK_TABLE} WHERE ${where} LIMIT ${limit}`, `DELETE FROM ${APPROVAL_ASK_TABLE} WHERE ctid IN (SELECT ctid FROM ${APPROVAL_ASK_TABLE} WHERE ${where} LIMIT ${limit})`), params);
|
|
754
|
+
return res.affected;
|
|
755
|
+
}
|
|
756
|
+
async reapEmptyBatches(cutoffMs, limit) {
|
|
757
|
+
assertReapBounds(cutoffMs, limit);
|
|
758
|
+
const res = await this.db.query(this.q(`DELETE FROM ${APPROVAL_BATCH_TABLE} WHERE updated_at_ms < ? AND NOT EXISTS (SELECT 1 FROM ${APPROVAL_ASK_TABLE} a WHERE a.batch_id = ${APPROVAL_BATCH_TABLE}.batch_id) LIMIT ${limit}`, `DELETE FROM ${APPROVAL_BATCH_TABLE} WHERE ctid IN (SELECT b.ctid FROM ${APPROVAL_BATCH_TABLE} b WHERE b.updated_at_ms < $1 AND NOT EXISTS (SELECT 1 FROM ${APPROVAL_ASK_TABLE} a WHERE a.batch_id = b.batch_id) LIMIT ${limit})`), [cutoffMs]);
|
|
759
|
+
return res.affected;
|
|
760
|
+
}
|
|
725
761
|
async getBatch(batchId) {
|
|
726
762
|
const { rows } = await this.db.query(this.q(`SELECT ${BATCH_COLS} FROM ${APPROVAL_BATCH_TABLE} WHERE batch_id = ?`, `SELECT ${BATCH_COLS} FROM ${APPROVAL_BATCH_TABLE} WHERE batch_id = $1`), [batchId]);
|
|
727
763
|
return rows[0] ? mapBatchRow(rows[0]) : null;
|
|
@@ -1,5 +1,17 @@
|
|
|
1
|
+
/** 决议载荷 —— 指纹的输入形(两条车道 + workflow 停驻决定共用一份;成员顺序见 {@link approvalPayloadFingerprint})。 */
|
|
2
|
+
export interface ApprovalPayload {
|
|
3
|
+
updatedInput?: unknown;
|
|
4
|
+
answer?: unknown;
|
|
5
|
+
reason?: string;
|
|
6
|
+
/** core 7.54.0:人写在卡上的话(引擎逐字记在赢家上,空白也记)。 */
|
|
7
|
+
feedback?: string;
|
|
8
|
+
}
|
|
1
9
|
/**
|
|
2
|
-
* [ref] 件1(codex R2-[high] 一)—— 决议载荷的比对指纹:`[updatedInput, answer, reason]`
|
|
10
|
+
* [ref] 件1(codex R2-[high] 一)—— 决议载荷的比对指纹:`[updatedInput, answer, reason]` 三件的规范串,其后**按在场追加**后来的成员(今天一员:`feedback`)。
|
|
11
|
+
*
|
|
12
|
+
* 🔴 成员律(一条):首发的三件恒占位(缺席折 `null`);之后加进来的成员**只在在场时**追加在尾部 —— workflow 停驻决定的同一性指纹
|
|
13
|
+
* ({@link parkDecisionFingerprint} 包它)是**落了库**的,新成员若恒占位,存量决定的指纹全部换字节、一次合法的重放被判成「另一个决定」;
|
|
14
|
+
* 按在场追加 ⇒ 不带新成员的决定字节不变,带了的与不带的不可能撞(串的元素个数不同)。
|
|
3
15
|
*
|
|
4
16
|
* 为什么是指纹而不是把三件原样交出来:`updatedInput` 可能携人写的命令原文/密钥形字节,而本读口的
|
|
5
17
|
* 契约是「窄读、非凭据面」—— 交指纹够比对,也不给任何调用点把它误投上 wire 的机会。
|
|
@@ -15,9 +27,5 @@
|
|
|
15
27
|
* 铸点丢掉的字节行上就没有,请求侧多喂一件会把一次合法重试误判成「不同的决议」。
|
|
16
28
|
* 序列化失败(循环引用/BigInt 等)⇒ `null` = 判别不出,调用方据此拒绝回放(fail-closed)。
|
|
17
29
|
*/
|
|
18
|
-
export declare function approvalPayloadFingerprint(v:
|
|
19
|
-
updatedInput?: unknown;
|
|
20
|
-
answer?: unknown;
|
|
21
|
-
reason?: string;
|
|
22
|
-
}): string | null;
|
|
30
|
+
export declare function approvalPayloadFingerprint(v: ApprovalPayload): string | null;
|
|
23
31
|
//# sourceMappingURL=approval-payload-fingerprint.d.ts.map
|
|
@@ -12,7 +12,7 @@ function canonicalFingerprintJson(v) {
|
|
|
12
12
|
}
|
|
13
13
|
export function approvalPayloadFingerprint(v) {
|
|
14
14
|
try {
|
|
15
|
-
const fp = canonicalFingerprintJson([v.updatedInput ?? null, v.answer ?? null, v.reason ?? null]);
|
|
15
|
+
const fp = canonicalFingerprintJson([v.updatedInput ?? null, v.answer ?? null, v.reason ?? null, ...(v.feedback !== undefined ? [v.feedback] : [])]);
|
|
16
16
|
return fp;
|
|
17
17
|
}
|
|
18
18
|
catch (e) {
|
|
@@ -1,29 +1,25 @@
|
|
|
1
1
|
import type { Pool as MysqlPool } from "mysql2/promise";
|
|
2
2
|
import type { Pool as PgPool } from "pg";
|
|
3
|
-
import { type SqlEngineReading, type SqlWallSource, type SqlWallsReading } from "./sql-driver.js";
|
|
3
|
+
import { type SqlEngineFacts, type SqlEngineReading, type SqlWallSource, type SqlWallsReading } from "./sql-driver.js";
|
|
4
|
+
import { type Logger } from "../observability/logger.js";
|
|
4
5
|
/**
|
|
5
6
|
* 文本协议的 blob 膨胀律 —— mysql2 `query()` 把 Buffer 参数写成 `X'<hex>'`,一字节两个字符,所以承载一个 blob 的语句至少是它的 2 倍。
|
|
6
7
|
* 包墙折成「能装下多大的 blob」只用这一只常量,推导里不手写 `/ 2`。
|
|
7
8
|
*/
|
|
8
9
|
export declare const SQL_TEXT_PROTOCOL_BLOB_INFLATION = 2;
|
|
9
10
|
/**
|
|
10
|
-
* 包墙下能装多大的 blob:语句里的载荷量({@link packetWallStatementBytes}
|
|
11
|
-
*
|
|
11
|
+
* 包墙下能装多大的 blob:语句里的载荷量({@link packetWallStatementBytes})再按 {@link SQL_TEXT_PROTOCOL_BLOB_INFLATION} 折半。
|
|
12
|
+
* blob 以十六进制字面进语句、有 2× —— 膨胀律是 blob 腿自己的,余量不是。
|
|
12
13
|
*/
|
|
13
14
|
export declare function packetWallBlobCap(maxPacketBytes: number): number;
|
|
14
|
-
/**
|
|
15
|
-
* **事实本身还没建立**时 MySQL 协议方言的缺省帽(池上还没借出过连接,或最近一次连接初始化被拒:`SqlDriver.facts === null`)= 事实缺席时的墙
|
|
16
|
-
* (`sql-driver.ts` `SQL_FACTS_ABSENT_WALLS`,判官 `sqlWallsOf`;提交受理口读同一只)走下面同一个推导 —— 不是第二份落点,只是那个推导在这组墙上的值
|
|
17
|
-
* (今天 = TiDB 缺省行墙走 blob 行的折算 6 MiB − 1 KiB:最矮的包墙缺省 16 MiB 扣语句余量再折半仍近 8 MiB,行腿更矮)。
|
|
18
|
-
*/
|
|
19
|
-
export declare const SQL_BLOB_FACTS_ABSENT_CAP_BYTES: number;
|
|
20
15
|
/**
|
|
21
16
|
* SQL 快照 blob 的**缺省**帽(运维没设 `SNAPSHOT_BLOB_SQL_MAX_BYTES` 时)的唯一推导 —— 入参是判官 `sqlWallsOf` 给的墙(不读事实、不判缺席、不看方言),**两堵墙取最小**:
|
|
22
17
|
* 行墙腿(`SqlEngineFacts.maxRowBytes`,TiDB:{@link rowWallPayloadBytes} 的 `blob` = 行墙 − blob 行的余量 1 KiB —— 按这一行的真实形算,不借 JSON 提交体行的 64 KiB)
|
|
23
18
|
* 与包墙腿(`SqlEngineFacts.maxPacketBytes`,InnoDB 与 TiDB 都有:{@link packetWallBlobCap} = ⌊(包墙 − 1 KiB 语句余量) ÷ 2⌋)。余量只在 `sql-driver.ts` 定义一次
|
|
24
19
|
* (墙 × 行的形各一只),提交体受理口(`http/request-guards.ts` `refuseSubmitBody`)读同一张表的 `json` 那一只,不各抄。
|
|
25
|
-
* 墙的 `null` 只表示「这堵墙不存在」;两堵都不存在(PG ——
|
|
26
|
-
*
|
|
20
|
+
* 墙的 `null` 只表示「这堵墙不存在」;两堵都不存在(PG —— 扩展协议无这两堵墙 —— 或没有 SQL 后端)⇒ `undefined` 不设帽。
|
|
21
|
+
* 「事实本身缺席」不是这里的入参:店的写前判读的是借出连接自己的事实(恒已建立,见 {@link SqlConnectionReading}),缺席的读法只在提交受理口读池级读口时出现(判官 `sqlWallsOf` 给墙)。
|
|
22
|
+
* 显式旋钮是运维的策略帽,在本推导之上(见 {@link sqlBlobCapBytes})。
|
|
27
23
|
*
|
|
28
24
|
* 两堵墙的真库读数(clay 拍 a+c,2026-07-27;k3s8 tidb-test v7.5.1 默认配置探针):7 MiB / 31 MiB = `entry too large`(行墙,默认 6291456)、
|
|
29
25
|
* 32 MiB = `max_allowed_packet`(包墙,64 MiB ÷ ~2× 文本协议膨胀)。帽=诚实拒绝:超限 blob 在 query 之前 typed 拒(消费面 HTTP 413 / capture warn
|
|
@@ -43,15 +39,29 @@ export interface SqlBlobCapReading {
|
|
|
43
39
|
*/
|
|
44
40
|
export declare function sqlBlobDefaultCapOf(walls: SqlWallsReading): SqlBlobCapReading | undefined;
|
|
45
41
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
42
|
+
* 一条**借出连接**的引擎读口:方言(装配事实)+ 它自己那次初始化回读的事实。与 {@link SqlEngineReading} 的差别只有一处 —— `facts` 恒在:连接是借出时才有的,
|
|
43
|
+
* 借出即已初始化({@link connectJudged} 交给判决的就是借出连接的 `sqlConnectionFacts`),所以「事实缺席」在这条读法上**不可表示**(编译期拒,不是运行期一个没人走的分支)。
|
|
44
|
+
* 事实缺席(池上还没建立过、或最近一次初始化被拒)只出现在读**池级**读口的地方 —— 提交受理口 —— 那一格的墙由判官 `sqlWallsOf` 给,不经过帽的推导。
|
|
49
45
|
*/
|
|
50
|
-
export
|
|
46
|
+
export interface SqlConnectionReading {
|
|
47
|
+
readonly dialect: SqlEngineReading["dialect"];
|
|
48
|
+
readonly facts: SqlEngineFacts;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* 一个 SQL 快照 blob 此刻的帽 —— **显式旋钮 ?? 判官给的墙走缺省推导**的唯一一只函数。唯一读者 = 店的写前判({@link SqlBlobBackend.putBlob}),传的读口({@link SqlConnectionReading})是
|
|
52
|
+
* **将要执行写入的那条连接**的(方言 + 它自己那次初始化回读的事实,墙经与提交受理口同一只判官 `sqlWallsOf`):包墙是连接的会话值,所以判决只在写入的那条连接上做,
|
|
53
|
+
* HTTP 面不替店另算一份帽。
|
|
54
|
+
*/
|
|
55
|
+
export declare function sqlBlobCapOf(explicitCapBytes: number | undefined, connection: SqlConnectionReading): SqlBlobCapReading | undefined;
|
|
51
56
|
/** {@link sqlBlobCapOf} 的字节投影(同一个推导,不另读)。 */
|
|
52
|
-
export declare function sqlBlobCapBytes(explicitCapBytes: number | undefined,
|
|
57
|
+
export declare function sqlBlobCapBytes(explicitCapBytes: number | undefined, connection: SqlConnectionReading): number | undefined;
|
|
58
|
+
/**
|
|
59
|
+
* 这一拒是不是**那条连接的包墙**定的:帽是缺省推导出的(不是运维旋钮)、那条连接有包墙、且包墙腿的折算恰是这个帽(包墙腿是最矮的一腿,或与行墙腿并列)。
|
|
60
|
+
* 只有这种拒才值得问「这条连接过时了吗」({@link connectJudged}) —— 旋钮的拒、行墙的拒与连接新旧无关。`walls` = 判出这个帽的那一份墙({@link sqlWallsOf})。
|
|
61
|
+
*/
|
|
62
|
+
export declare function capBoundByPacketWall(cap: SqlBlobCapReading, walls: SqlWallsReading): boolean;
|
|
53
63
|
/**
|
|
54
|
-
* 一次 blob 超帽的拒句 —— **唯一的组句器**(
|
|
64
|
+
* 一次 blob 超帽的拒句 —— **唯一的组句器**(店的 typed {@link BlobTooLargeError} 调它;`PUT /sync/blobs` 的 413 原样转出它的句子):句子按帽的出处({@link SqlBlobCapReading.source})
|
|
55
65
|
* 分三形,不把假定说成库的上限、也不把运维的旋钮说成库的上限。
|
|
56
66
|
* · `operator` —— 帽是运维设的 `SNAPSHOT_BLOB_SQL_MAX_BYTES`;
|
|
57
67
|
* · `reported` —— 帽由库报的两堵墙推出(单行上限与包墙÷文本协议膨胀的较小者);
|
|
@@ -97,21 +107,24 @@ export interface BlobBackend {
|
|
|
97
107
|
export declare class SqlBlobBackend implements BlobBackend {
|
|
98
108
|
private readonly dialect;
|
|
99
109
|
private readonly pool;
|
|
100
|
-
/** Explicit per-blob cap (the operator's `SNAPSHOT_BLOB_SQL_MAX_BYTES`); absent ⇒ the derivation in {@link
|
|
110
|
+
/** Explicit per-blob cap (the operator's `SNAPSHOT_BLOB_SQL_MAX_BYTES`); absent ⇒ the derivation in {@link sqlBlobCapOf} over the walls of the connection each write borrows. */
|
|
101
111
|
private readonly explicitCapBytes;
|
|
102
|
-
/** This pool's engine reading (dialect + live facts, from the pool's own driver) — the one source the cap's walls are read from. */
|
|
103
|
-
private readonly engine;
|
|
104
112
|
/** Blob-domain table ([ref] blob 域隔离:file-history 域用自己的表,reap/GC 不跨域)。代码内字面量,非用户输入。 */
|
|
105
113
|
private readonly table;
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
private
|
|
114
|
+
/** The write path's connection source: every `putBlob` borrows ONE connection from it (through {@link connectJudged}), judges on that connection's walls, writes on it. */
|
|
115
|
+
private readonly db;
|
|
116
|
+
/** Where a stale connection's replacement is logged ({@link connectJudged}); the boot assembly passes the service logger. */
|
|
117
|
+
private readonly log;
|
|
118
|
+
constructor(dialect: "tidb" | "pg", pool: MysqlPool | PgPool, maxBytes?: number, table?: string, log?: Pick<Logger, "warn">);
|
|
110
119
|
/** The created_at-refreshing content-addressed upsert — VERBATIM the tidb/pg stores' `putBlobRow`. Refreshes
|
|
111
120
|
* created_at even on a dedup hit so a re-used/just-written blob is "touched=now" and GC-grace-protected.
|
|
112
121
|
* (a): an over-cap blob is REJECTED (typed {@link BlobTooLargeError}) BEFORE the driver ever sees the bytes —
|
|
113
122
|
* the alternative is an inscrutable packet-limit error deep in the driver, and on the HTTP face a 502 that
|
|
114
|
-
* invites the peer to retry a blob that can never land.
|
|
123
|
+
* invites the peer to retry a blob that can never land.
|
|
124
|
+
* 🔴 The cap is judged ON THE CONNECTION THAT WRITES (S-1746): `max_allowed_packet` is a session value, fixed per
|
|
125
|
+
* connection, so the write borrows one connection through {@link connectJudged}, which reads ITS walls, judges, and — when
|
|
126
|
+
* the refusal is that connection's packet wall and the server's global wall is higher — replaces the stale connection and
|
|
127
|
+
* judges once more; the INSERT runs on the connection that passed. Never a pool-level reading, never a bare `pool.query`. */
|
|
115
128
|
putBlob(hash: string, bytes: Uint8Array): Promise<void>;
|
|
116
129
|
/** The bytes for `hash` (undefined if absent) — VERBATIM the tidb/pg stores' getBlob SELECT. A pool/query error
|
|
117
130
|
* PROPAGATES (a transient DB fault is loud, not a silent "missing blob" — parity with the MinIO backend's throw). */
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { presignS3Url, presignS3ListUrl } from "./s3-presign.js";
|
|
3
|
-
import {
|
|
3
|
+
import { connectJudged, mysqlDriver, packetWallStatementBytes, pgDriver, rowWallPayloadBytes, sqlWallsOf } from "./sql-driver.js";
|
|
4
|
+
import { createLogger } from "../observability/logger.js";
|
|
4
5
|
const BLOB_GC_GRACE_MS = 3_600_000;
|
|
5
6
|
export const SQL_TEXT_PROTOCOL_BLOB_INFLATION = 2;
|
|
6
7
|
export function packetWallBlobCap(maxPacketBytes) {
|
|
7
8
|
return Math.floor(packetWallStatementBytes(maxPacketBytes) / SQL_TEXT_PROTOCOL_BLOB_INFLATION);
|
|
8
9
|
}
|
|
9
|
-
export const SQL_BLOB_FACTS_ABSENT_CAP_BYTES = sqlBlobDefaultCapBytes(SQL_FACTS_ABSENT_WALLS);
|
|
10
10
|
export function sqlBlobDefaultCapBytes(walls) {
|
|
11
11
|
return sqlBlobDefaultCapOf(walls)?.capBytes;
|
|
12
12
|
}
|
|
@@ -18,11 +18,14 @@ export function sqlBlobDefaultCapOf(walls) {
|
|
|
18
18
|
legs.push({ capBytes: packetWallBlobCap(walls.maxPacketBytes), source: walls.sources.maxPacketBytes });
|
|
19
19
|
return legs.reduce((best, leg) => (best === undefined || leg.capBytes < best.capBytes ? leg : best), undefined);
|
|
20
20
|
}
|
|
21
|
-
export function sqlBlobCapOf(explicitCapBytes,
|
|
22
|
-
return explicitCapBytes !== undefined ? { capBytes: explicitCapBytes, source: "operator" } : sqlBlobDefaultCapOf(sqlWallsOf(
|
|
21
|
+
export function sqlBlobCapOf(explicitCapBytes, connection) {
|
|
22
|
+
return explicitCapBytes !== undefined ? { capBytes: explicitCapBytes, source: "operator" } : sqlBlobDefaultCapOf(sqlWallsOf(connection));
|
|
23
23
|
}
|
|
24
|
-
export function sqlBlobCapBytes(explicitCapBytes,
|
|
25
|
-
return sqlBlobCapOf(explicitCapBytes,
|
|
24
|
+
export function sqlBlobCapBytes(explicitCapBytes, connection) {
|
|
25
|
+
return sqlBlobCapOf(explicitCapBytes, connection)?.capBytes;
|
|
26
|
+
}
|
|
27
|
+
export function capBoundByPacketWall(cap, walls) {
|
|
28
|
+
return cap.source !== "operator" && walls.maxPacketBytes !== null && packetWallBlobCap(walls.maxPacketBytes) === cap.capBytes;
|
|
26
29
|
}
|
|
27
30
|
export function sqlBlobCapRefusalText(byteLen, cap) {
|
|
28
31
|
const objectStorage = "configure MinIO object storage (MINIO_ENDPOINT/MINIO_ACCESS_KEY/MINIO_SECRET_KEY) for large snapshot blobs";
|
|
@@ -64,27 +67,34 @@ export class SqlBlobBackend {
|
|
|
64
67
|
dialect;
|
|
65
68
|
pool;
|
|
66
69
|
explicitCapBytes;
|
|
67
|
-
engine;
|
|
68
70
|
table;
|
|
69
|
-
|
|
71
|
+
db;
|
|
72
|
+
log;
|
|
73
|
+
constructor(dialect, pool, maxBytes, table = "snapshot_blob", log) {
|
|
70
74
|
this.dialect = dialect;
|
|
71
75
|
this.pool = pool;
|
|
72
76
|
this.explicitCapBytes = maxBytes;
|
|
73
77
|
this.table = table;
|
|
74
|
-
this.
|
|
75
|
-
|
|
76
|
-
cap() {
|
|
77
|
-
return sqlBlobCapOf(this.explicitCapBytes, this.engine);
|
|
78
|
+
this.db = dialect === "tidb" ? mysqlDriver(pool) : pgDriver(pool);
|
|
79
|
+
this.log = log ?? createLogger();
|
|
78
80
|
}
|
|
79
81
|
async putBlob(hash, bytes) {
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
const judged = await connectJudged(this.db, (facts) => {
|
|
83
|
+
const connection = { dialect: this.dialect, facts };
|
|
84
|
+
const cap = sqlBlobCapOf(this.explicitCapBytes, connection);
|
|
85
|
+
if (cap === undefined || bytes.byteLength <= cap.capBytes)
|
|
86
|
+
return undefined;
|
|
87
|
+
return { cap, packetBound: capBoundByPacketWall(cap, sqlWallsOf(connection)) };
|
|
88
|
+
}, (refusal) => refusal.packetBound, this.log);
|
|
89
|
+
if (judged.refusal !== undefined)
|
|
90
|
+
throw new BlobTooLargeError(bytes.byteLength, judged.refusal.cap);
|
|
91
|
+
try {
|
|
92
|
+
await judged.conn.query(this.dialect === "tidb"
|
|
93
|
+
? `INSERT INTO ${this.table} (blob_hash, byte_len, bytes, created_at) VALUES (?,?,?,?) ON DUPLICATE KEY UPDATE created_at = VALUES(created_at)`
|
|
94
|
+
: `INSERT INTO ${this.table} (blob_hash, byte_len, bytes, created_at) VALUES ($1,$2,$3,$4) ON CONFLICT (blob_hash) DO UPDATE SET created_at = EXCLUDED.created_at`, [hash, bytes.byteLength, Buffer.from(bytes), new Date()]);
|
|
85
95
|
}
|
|
86
|
-
|
|
87
|
-
|
|
96
|
+
finally {
|
|
97
|
+
judged.conn.release();
|
|
88
98
|
}
|
|
89
99
|
}
|
|
90
100
|
async getBlob(hash) {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* core's `createCircuitBreakerBrain` defaults to a per-PROCESS Map → on a multi-replica deployment
|
|
8
8
|
* every replica re-discovers a provider outage independently (slow fleet-wide reaction). This backs
|
|
9
|
-
* the breaker with the shared `
|
|
9
|
+
* the breaker with the shared `breaker_state` table so a trip on one replica is learned by the others.
|
|
10
10
|
*
|
|
11
11
|
* IMPORTANT — `BreakerState.get/set` are **synchronous** (core calls them on the hot path of every
|
|
12
12
|
* brain invocation; a DB round-trip per call is untenable). So this is a **write-through cache**:
|
|
@@ -40,7 +40,7 @@ import type { Pool as MySqlPool } from "mysql2/promise";
|
|
|
40
40
|
import type { Pool as PgPool, PoolClient } from "pg";
|
|
41
41
|
import type { BreakerSnapshot, BreakerState } from "@sema-agent/core";
|
|
42
42
|
import { type SqlDriver } from "./sql-driver.js";
|
|
43
|
-
/** PG translation of the tidb-pool.ts `
|
|
43
|
+
/** PG translation of the tidb-pool.ts `breaker_state` DDL (DATETIME(3)→TIMESTAMPTZ(3)). Disjoint from other
|
|
44
44
|
* stores' tables, so a self-contained ensureSchema is safe (no central-schema shadowing). TiDB's DDL lives
|
|
45
45
|
* centrally in tidb-pool.ts (no PG-side twin needed there — production wires ONE ensureTidbSchema). */
|
|
46
46
|
export declare const PG_BREAKER_STATE_SCHEMA: string[];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mysqlDriver, pgDriver } from "./sql-driver.js";
|
|
2
2
|
export const PG_BREAKER_STATE_SCHEMA = [
|
|
3
|
-
`CREATE TABLE IF NOT EXISTS
|
|
3
|
+
`CREATE TABLE IF NOT EXISTS breaker_state (
|
|
4
4
|
breaker_key VARCHAR(190) COLLATE "C" NOT NULL,
|
|
5
5
|
phase VARCHAR(16) COLLATE "C" NOT NULL,
|
|
6
6
|
failures INTEGER NOT NULL DEFAULT 0,
|
|
@@ -37,8 +37,8 @@ export class SqlBreakerState {
|
|
|
37
37
|
}
|
|
38
38
|
async writeThrough(key, snap) {
|
|
39
39
|
try {
|
|
40
|
-
await this.db.query(this.q("INSERT INTO
|
|
41
|
-
"ON DUPLICATE KEY UPDATE phase=VALUES(phase), failures=VALUES(failures), opened_at=VALUES(opened_at), updated_at=VALUES(updated_at)", "INSERT INTO
|
|
40
|
+
await this.db.query(this.q("INSERT INTO breaker_state (breaker_key, phase, failures, opened_at, updated_at) VALUES (?,?,?,?,?) " +
|
|
41
|
+
"ON DUPLICATE KEY UPDATE phase=VALUES(phase), failures=VALUES(failures), opened_at=VALUES(opened_at), updated_at=VALUES(updated_at)", "INSERT INTO breaker_state (breaker_key, phase, failures, opened_at, updated_at) VALUES ($1,$2,$3,$4,$5) " +
|
|
42
42
|
"ON CONFLICT (breaker_key) DO UPDATE SET phase=EXCLUDED.phase, failures=EXCLUDED.failures, opened_at=EXCLUDED.opened_at, updated_at=EXCLUDED.updated_at"), [key, snap.phase, snap.failures, snap.openedAt != null ? new Date(snap.openedAt) : null, new Date()]);
|
|
43
43
|
this.pending.delete(key);
|
|
44
44
|
if (this.writeFailStreak > 0) {
|
|
@@ -60,7 +60,7 @@ export class SqlBreakerState {
|
|
|
60
60
|
try {
|
|
61
61
|
const pendingAtStart = new Set(this.pending);
|
|
62
62
|
const guarded = (key) => this.pending.has(key) || pendingAtStart.has(key);
|
|
63
|
-
const { rows } = await this.db.query("SELECT breaker_key, phase, failures, opened_at FROM
|
|
63
|
+
const { rows } = await this.db.query("SELECT breaker_key, phase, failures, opened_at FROM breaker_state");
|
|
64
64
|
const seen = new Set();
|
|
65
65
|
for (const r of rows) {
|
|
66
66
|
const key = String(r.breaker_key);
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type AcquiredSession, type PlacedSessionRow, type RetentionDeclaration, type SessionPlacementRecord, type SessionStore, type SessionTreeEntry } from "@sema-agent/core";
|
|
2
2
|
import type { OwnerAwareSessionStore, SessionListItem } from "../security.js";
|
|
3
3
|
import type { StagingHandle } from "../session-sync-kernel.js";
|
|
4
4
|
import { type PlacementAcquireOpts } from "./session-placement.js";
|
|
5
5
|
import type { InstructionSourceStore } from "../instruction-source-notice.js";
|
|
6
6
|
import type { SessionFaceReader } from "../session-incarnation.js";
|
|
7
|
+
import type { OwnerlessClaim } from "../delegated-session-owner.js";
|
|
7
8
|
export interface CachingOptions {
|
|
8
9
|
/** Idle seconds a woken session is kept warm. 0 disables caching (pure passthrough). */
|
|
9
10
|
ttlSec: number;
|
|
@@ -59,6 +60,8 @@ export declare class CachingSessionStore implements SessionStore {
|
|
|
59
60
|
* (缓存的会话活得比「另一只店删掉又重建了这一行」长)。🔴 必须转发:k8s 工作区空间的每 run 读与起服门读的就是本类。 */
|
|
60
61
|
readonly incarnationOf?: SessionStore["incarnationOf"];
|
|
61
62
|
readonly register?: (sessionId: string, owner: string | null) => Promise<void>;
|
|
63
|
+
/** S-1770 委派继承律的认领口,纯转发(写的是行上的属主那一格 —— 只 CAS 子行自己,不碰暖缓存里的会话;停驻挂点在默认部署上读的就是本类 —— 与 `placementOf` 同转发同缺:内店记录委派树 ⇒ 两只都在)。 */
|
|
64
|
+
readonly claimOwnerless?: OwnerlessClaim;
|
|
62
65
|
/** Delegated SessionRepo seam (§0.5) — list/fork/delete operate on the DURABLE tables directly (they bypass the
|
|
63
66
|
* warm cache by design; a fork/delete must read/write durable state, not a possibly-stale cached view). */
|
|
64
67
|
readonly listSessions?: (opts: {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { admitSessionPlacement } from "@sema-agent/core";
|
|
2
|
+
import { placementCapabilityOf } from "./session-placement.js";
|
|
2
3
|
export class CachingSessionStore {
|
|
3
4
|
inner;
|
|
4
5
|
cache = new Map();
|
|
@@ -10,6 +11,7 @@ export class CachingSessionStore {
|
|
|
10
11
|
sessionFaceOf;
|
|
11
12
|
incarnationOf;
|
|
12
13
|
register;
|
|
14
|
+
claimOwnerless;
|
|
13
15
|
listSessions;
|
|
14
16
|
fork;
|
|
15
17
|
deleteSession;
|
|
@@ -57,6 +59,8 @@ export class CachingSessionStore {
|
|
|
57
59
|
this.incarnationOf = inner.incarnationOf.bind(inner);
|
|
58
60
|
if (ownerAware.register)
|
|
59
61
|
this.register = ownerAware.register.bind(inner);
|
|
62
|
+
if (ownerAware.claimOwnerless)
|
|
63
|
+
this.claimOwnerless = ownerAware.claimOwnerless.bind(inner);
|
|
60
64
|
if (ownerAware.listSessions)
|
|
61
65
|
this.listSessions = ownerAware.listSessions.bind(inner);
|
|
62
66
|
if (ownerAware.sessionSummaryOf)
|
|
@@ -141,7 +145,7 @@ export class CachingSessionStore {
|
|
|
141
145
|
}
|
|
142
146
|
const hit = await this.freshHit(sessionId);
|
|
143
147
|
if (hit && opts?.requireExisting !== true && this.placements !== undefined && this.placementOf !== undefined) {
|
|
144
|
-
|
|
148
|
+
admitSessionPlacement(sessionId, await this.placementOf(sessionId), opts ?? {}, placementCapabilityOf(this));
|
|
145
149
|
}
|
|
146
150
|
if (hit) {
|
|
147
151
|
hit.expiresAt = Date.now() + this.ttlMs;
|
|
@@ -156,7 +160,7 @@ export class CachingSessionStore {
|
|
|
156
160
|
if (inflight) {
|
|
157
161
|
const acquired = await inflight;
|
|
158
162
|
if (this.placements !== undefined && this.placementOf !== undefined) {
|
|
159
|
-
|
|
163
|
+
admitSessionPlacement(sessionId, await this.placementOf(sessionId), opts ?? {}, placementCapabilityOf(this));
|
|
160
164
|
}
|
|
161
165
|
return acquired;
|
|
162
166
|
}
|