@sema-agent/server 7.133.0 → 7.135.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 +221 -0
- package/README.md +2 -2
- package/README.zh-CN.md +2 -2
- package/USAGE.md +222 -36
- package/deploy/sema-up/README.md +16 -0
- 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 +14 -0
- package/deploy/sema-up/chart/templates/server.yaml +14 -0
- package/deploy/sema-up/chart/values-full-single.yaml +5 -1
- package/deploy/sema-up/chart/values.yaml +34 -0
- package/dist/adoption/plan.d.ts +1 -1
- package/dist/approval-ask-audit-store.d.ts +25 -25
- package/dist/approval-ask-machine.d.ts +2 -2
- package/dist/approval-card.d.ts +66 -154
- package/dist/approval-card.js +33 -52
- package/dist/approval-content-kind.d.ts +26 -6
- package/dist/approval-content-kind.js +23 -3
- package/dist/approval-deny-reasons.d.ts +1 -1
- package/dist/approval-gate-currency.d.ts +91 -0
- package/dist/approval-gate-currency.js +120 -0
- package/dist/approval-hmac.d.ts +7 -7
- package/dist/approval-policy-settlement.d.ts +2 -2
- package/dist/approval-reconciler.d.ts +3 -3
- package/dist/approval.d.ts +14 -8
- package/dist/approval.js +35 -16
- package/dist/auth-bridge.d.ts +1 -1
- package/dist/auto-mode-face.d.ts +4 -4
- package/dist/background-session-reap.d.ts +2 -2
- package/dist/bake-runner/main.d.ts +2 -2
- package/dist/bake-runner/runner.d.ts +4 -4
- package/dist/bench/s1/live-deps.d.ts +1 -1
- package/dist/bench/s1/run-firm.d.ts +1 -1
- package/dist/boot/coordinators.d.ts +7 -2
- package/dist/boot/coordinators.js +8 -5
- package/dist/boot/deferred-sandbox-path-env.d.ts +0 -6
- package/dist/boot/deferred-sandbox-path-env.js +0 -5
- package/dist/boot/engine-lease.d.ts +5 -5
- package/dist/boot/execution-env.d.ts +16 -15
- package/dist/boot/execution-env.js +19 -21
- package/dist/boot/fatal-exit.d.ts +2 -2
- package/dist/boot/late-binds.d.ts +1 -1
- package/dist/boot/leader.d.ts +2 -0
- package/dist/boot/leader.js +2 -1
- package/dist/boot/memory-boundary.d.ts +2 -2
- package/dist/boot/memory-consolidation.d.ts +15 -15
- package/dist/boot/parked-revive-gate.d.ts +3 -3
- package/dist/boot/parked-revive-gate.js +3 -2
- package/dist/boot/read-deny-anchor-audit.js +2 -2
- package/dist/boot/reapers.d.ts +4 -4
- package/dist/boot/resolve-spec.d.ts +1 -1
- package/dist/boot/resolve-spec.js +28 -24
- package/dist/boot/runner-deps.d.ts +29 -16
- package/dist/boot/runner-deps.js +12 -2
- package/dist/boot/session-faces.d.ts +3 -3
- package/dist/boot/session-faces.js +11 -30
- package/dist/boot/shutdown.d.ts +5 -5
- package/dist/boot/side-query-lane.d.ts +2 -2
- package/dist/boot/stage-05-execution-env.js +4 -4
- package/dist/boot/stage-06-runners.d.ts +4 -3
- package/dist/boot/stage-06-runners.js +15 -4
- package/dist/boot/stage-07-capability-layer.d.ts +2 -1
- package/dist/boot/stage-08-reapers.d.ts +2 -1
- package/dist/boot/stage-09-leader.d.ts +2 -1
- package/dist/boot/stage-09-leader.js +2 -2
- package/dist/boot/stage-10-http-server.d.ts +2 -1
- package/dist/boot/stage-10-http-server.js +2 -1
- package/dist/boot/stores.d.ts +8 -0
- package/dist/boot/stores.js +10 -13
- package/dist/boot/web-admin.d.ts +21 -15
- package/dist/boot/web-admin.js +16 -12
- package/dist/boot/workflow-orchestration.js +2 -9
- package/dist/brain.d.ts +11 -11
- package/dist/budget.d.ts +4 -4
- package/dist/budget.js +5 -1
- package/dist/caller-text-echo.d.ts +2 -0
- package/dist/caller-text-echo.js +2 -1
- package/dist/canonical-json.d.ts +2 -41
- package/dist/canonical-json.js +2 -13
- package/dist/capabilities/approvals-push.d.ts +4 -4
- package/dist/capabilities/builtin-tools.d.ts +5 -5
- package/dist/capabilities/center-plugins.d.ts +5 -5
- package/dist/capabilities/center-plugins.js +1 -1
- package/dist/capabilities/center-prompts.d.ts +1 -1
- package/dist/capabilities/center-wiring.d.ts +1 -1
- package/dist/capabilities/composite-members.d.ts +2 -2
- package/dist/capabilities/composite-members.js +1 -1
- package/dist/capabilities/execution-lane.d.ts +5 -5
- package/dist/capabilities/execution-lane.js +2 -2
- package/dist/capabilities/hands-lane.d.ts +5 -5
- package/dist/capabilities/mcp-probe.d.ts +10 -7
- package/dist/capabilities/mcp-probe.js +2 -2
- package/dist/capabilities/sandbox-file-send.d.ts +1 -18
- package/dist/capabilities/sandbox-file-send.js +0 -7
- package/dist/capabilities/scenarios.d.ts +28 -10
- package/dist/capabilities/scenarios.js +15 -5
- package/dist/capabilities/send-user-file-tool.d.ts +1 -1
- package/dist/capabilities/skills.d.ts +31 -12
- package/dist/capabilities/skills.js +23 -17
- package/dist/capabilities/tool-defer.d.ts +1 -1
- package/dist/capability-facts.d.ts +27 -24
- package/dist/capability-facts.js +18 -20
- package/dist/center-config-tier.d.ts +1 -1
- package/dist/center-credential.d.ts +4 -4
- 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 +5 -0
- package/dist/checkpoint-identity.js +14 -0
- package/dist/config-catalog.d.ts +12 -12
- package/dist/config-invariants.d.ts +4 -4
- package/dist/config-invariants.js +3 -2
- package/dist/config-lkg.d.ts +1 -1
- package/dist/config-provider.d.ts +13 -13
- package/dist/config-provider.js +2 -1
- package/dist/config-types.d.ts +83 -83
- package/dist/config.d.ts +26 -24
- package/dist/config.js +12 -10
- package/dist/cross-session-settings.d.ts +1 -1
- package/dist/delegated-session-owner.d.ts +16 -13
- package/dist/delegated-session-owner.js +9 -10
- package/dist/deployment-governance.d.ts +16 -16
- package/dist/deployment-shape.d.ts +127 -0
- package/dist/deployment-shape.js +72 -0
- package/dist/device-store.d.ts +1 -1
- package/dist/device-ws-hub.d.ts +1 -1
- package/dist/egress-redirect.d.ts +1 -1
- package/dist/entry-mode-clamp-notice.d.ts +2 -2
- package/dist/env-facts.d.ts +10 -9
- package/dist/env-facts.js +1 -1
- package/dist/env-name-allowlist-knobs.d.ts +5 -5
- package/dist/exec-bounds.d.ts +2 -2
- package/dist/execution-lane-caps.d.ts +31 -43
- package/dist/execution-lane-caps.js +0 -13
- package/dist/field-conflict.d.ts +2 -1
- package/dist/field-conflict.js +1 -0
- package/dist/file-history-capture.d.ts +9 -9
- package/dist/file-history-disclosure.d.ts +50 -9
- package/dist/file-history-disclosure.js +8 -13
- package/dist/fleet/drain-entry.d.ts +2 -2
- package/dist/fleet/fleet-bus.d.ts +26 -14
- package/dist/fleet/fleet-bus.js +6 -5
- package/dist/fleet/fleet-reconciler.d.ts +9 -9
- package/dist/fleet/subagent-tail-bus.d.ts +4 -4
- package/dist/fleet-client.d.ts +13 -13
- package/dist/fleet-lease.d.ts +16 -16
- package/dist/governance-http-status.d.ts +2 -2
- package/dist/hooks/hook-llm.d.ts +7 -7
- package/dist/hooks/hook-runner.d.ts +57 -30
- package/dist/hooks/hook-runner.js +38 -20
- package/dist/host-decision.d.ts +5 -5
- package/dist/host-lsp-manager.d.ts +5 -5
- package/dist/hosted-posture.d.ts +23 -13
- package/dist/hosted-posture.js +4 -9
- package/dist/http/admission.d.ts +2 -2
- package/dist/http/admission.js +28 -5
- package/dist/http/approval-sweeps.d.ts +1 -1
- package/dist/http/approval-sweeps.js +5 -2
- package/dist/http/checkpoint-ctx.d.ts +183 -2
- package/dist/http/checkpoint-ctx.js +197 -2
- package/dist/http/dispatch.d.ts +3 -3
- package/dist/http/dispatch.js +3 -3
- package/dist/http/durable-run-leg.js +7 -5
- package/dist/http/idempotency.d.ts +4 -4
- package/dist/http/idempotency.js +2 -2
- package/dist/http/mcp-dial-deadline.d.ts +7 -5
- package/dist/http/memory-compliance-failure-reply.d.ts +4 -4
- package/dist/http/owner-access.d.ts +4 -6
- package/dist/http/owner-access.js +1 -1
- package/dist/http/principal-gate.d.ts +39 -13
- package/dist/http/principal-gate.js +30 -6
- package/dist/http/readiness.d.ts +1 -1
- package/dist/http/refusal-body.d.ts +12 -0
- package/dist/http/refusal-body.js +11 -0
- package/dist/http/request-guards.d.ts +4 -13
- package/dist/http/request-guards.js +4 -17
- package/dist/http/resume-ctx-read.d.ts +53 -6
- package/dist/http/resume-ctx-read.js +26 -3
- package/dist/http/resume-legs.d.ts +5 -4
- package/dist/http/resume-legs.js +47 -29
- package/dist/http/route-ctx.d.ts +29 -38
- package/dist/http/route-table.d.ts +9 -9
- package/dist/http/routes/agents-roster.d.ts +1 -1
- package/dist/http/routes/agents-roster.js +2 -3
- package/dist/http/routes/approvals-assistant.d.ts +5 -5
- package/dist/http/routes/approvals-assistant.js +91 -17
- package/dist/http/routes/background.d.ts +3 -3
- package/dist/http/routes/background.js +2 -2
- package/dist/http/routes/capabilities.d.ts +1 -1
- package/dist/http/routes/capabilities.js +16 -8
- package/dist/http/routes/config-catalog.d.ts +1 -1
- package/dist/http/routes/devices.d.ts +1 -1
- package/dist/http/routes/diagnostics.d.ts +6 -6
- package/dist/http/routes/diagnostics.js +1 -1
- package/dist/http/routes/fleet.d.ts +1 -1
- package/dist/http/routes/fleet.js +5 -4
- package/dist/http/routes/mcp-reconnect.d.ts +4 -4
- package/dist/http/routes/mcp-reconnect.js +3 -3
- package/dist/http/routes/memory-consolidation.d.ts +1 -1
- package/dist/http/routes/memory-origin.d.ts +1 -1
- package/dist/http/routes/notify-wake.d.ts +1 -1
- package/dist/http/routes/notify-wake.js +2 -2
- package/dist/http/routes/rules.d.ts +1 -1
- package/dist/http/routes/rules.js +4 -4
- package/dist/http/routes/runs.js +44 -11
- package/dist/http/routes/session-sync.d.ts +3 -2
- package/dist/http/routes/session-sync.js +109 -62
- package/dist/http/routes/sessions-list.d.ts +1 -1
- package/dist/http/routes/sessions-list.js +4 -5
- package/dist/http/routes/sessions.js +34 -48
- package/dist/http/routes/tasks.d.ts +1 -1
- package/dist/http/routes/tasks.js +21 -16
- package/dist/http/routes/trace-usage.d.ts +1 -1
- package/dist/http/routes/trace-usage.js +7 -9
- package/dist/http/routes/workflows.d.ts +3 -0
- package/dist/http/routes/workflows.js +31 -28
- package/dist/http/send.d.ts +5 -5
- package/dist/http/server.d.ts +39 -33
- package/dist/http/server.js +5 -2
- package/dist/http/session-inherit-env.d.ts +3 -3
- package/dist/http/sse-lifecycle.d.ts +1 -1
- package/dist/http/sse-log.d.ts +2 -2
- package/dist/http/submit-ctx.d.ts +4 -3
- package/dist/http/submit-ctx.js +19 -3
- package/dist/http/sync-submit-body.d.ts +1 -1
- package/dist/http/verify-rounds.d.ts +1 -1
- package/dist/http/wire-types.d.ts +35 -24
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/instruction-source-notice.d.ts +2 -2
- package/dist/key-resolver.d.ts +3 -3
- package/dist/lane-home-probe.d.ts +1 -1
- package/dist/leader/diffout.d.ts +1 -1
- package/dist/leader/diffup.d.ts +2 -2
- package/dist/leader/endpoint.d.ts +1 -1
- package/dist/leader/fanout.d.ts +1 -1
- package/dist/leader/leader.d.ts +4 -4
- package/dist/leader/merge.d.ts +3 -3
- package/dist/leader/repair-wire.d.ts +1 -1
- package/dist/leader/wire.d.ts +9 -5
- package/dist/leader/wire.js +8 -7
- package/dist/list-entry-display.d.ts +1 -1
- package/dist/lsp-evict.d.ts +1 -1
- package/dist/main.js +1 -1
- package/dist/mcp-assertion-header.d.ts +1 -1
- package/dist/memory-layer-legacy-lock.d.ts +1 -1
- package/dist/memory-operator-faces.d.ts +1 -1
- package/dist/memory-origin-fence.js +2 -2
- package/dist/memory-posture.js +4 -2
- package/dist/memory-project-layer.d.ts +1 -1
- package/dist/memory-scope.d.ts +19 -19
- package/dist/memory-store-refusal.d.ts +4 -4
- package/dist/model-compat.d.ts +1 -1
- package/dist/model-entry-headers.d.ts +2 -2
- package/dist/model-entry-refusal.d.ts +17 -17
- package/dist/model-provider.d.ts +10 -10
- package/dist/model-reach-scope.d.ts +1 -1
- package/dist/model-route-endpoint.d.ts +6 -6
- package/dist/model-route-url.d.ts +5 -5
- package/dist/model-select.d.ts +15 -6
- package/dist/model-select.js +5 -0
- package/dist/model-unreachable-notice.d.ts +3 -3
- package/dist/namespaced-rule-name.d.ts +15 -5
- package/dist/namespaced-rule-name.js +3 -2
- package/dist/observability/corrupt-read-seat.d.ts +9 -9
- package/dist/observability/cost-taxonomy.d.ts +1 -1
- package/dist/observability/err-text.d.ts +1 -1
- package/dist/observability/fail-open.d.ts +12 -8
- package/dist/observability/fail-open.js +12 -8
- package/dist/observability/metrics.d.ts +3 -3
- package/dist/observability/prompt-manifest.d.ts +19 -8
- package/dist/observability/prompt-manifest.js +9 -8
- package/dist/observability/run-terminal.d.ts +36 -36
- package/dist/observability/secret-env-scrub.d.ts +3 -3
- package/dist/observability/tool-trace.d.ts +1 -1
- package/dist/observability/tool-trace.js +8 -9
- package/dist/operator-ask.d.ts +4 -4
- package/dist/orchestration/workflow-agent-session-index.d.ts +3 -3
- package/dist/orchestration/workflow-completion-inbox.d.ts +20 -5
- package/dist/orchestration/workflow-completion-inbox.js +29 -7
- package/dist/orchestration/workflow-notify-journal.d.ts +43 -19
- package/dist/orchestration/workflow-notify-journal.js +17 -0
- package/dist/orchestration/workflow-park-decision.d.ts +1 -1
- package/dist/orchestration/workflow-park-decision.js +1 -0
- package/dist/outbound-dispatcher.d.ts +4 -4
- package/dist/parent-watch.d.ts +4 -4
- package/dist/parked-decide.d.ts +20 -20
- package/dist/parked-decide.js +8 -5
- package/dist/parked-revive-surface.d.ts +7 -7
- package/dist/parked-revive-surface.js +2 -3
- package/dist/peer-directory.d.ts +3 -3
- package/dist/peer-directory.js +1 -1
- package/dist/permission-rule-list.d.ts +10 -1
- package/dist/permission-rule-list.js +7 -5
- package/dist/permission-rule-vocab.d.ts +3 -25
- package/dist/permission-rule-vocab.js +0 -13
- package/dist/plugins/adoption-log-sql.d.ts +1 -1
- package/dist/plugins/approval-ask-store-file.d.ts +8 -1
- package/dist/plugins/approval-ask-store-file.js +6 -3
- package/dist/plugins/approval-ask-store-memory.d.ts +11 -7
- package/dist/plugins/approval-ask-store-memory.js +11 -7
- package/dist/plugins/approval-ask-store-sql.d.ts +34 -15
- package/dist/plugins/approval-ask-store-sql.js +18 -5
- package/dist/plugins/approval-exemption-store.d.ts +3 -3
- package/dist/plugins/approval-nonce-store.d.ts +3 -3
- package/dist/plugins/approval-payload-fingerprint.d.ts +14 -6
- package/dist/plugins/approval-payload-fingerprint.js +1 -1
- package/dist/plugins/background-agent-store-sql.d.ts +1 -1
- package/dist/plugins/background-shell-support.d.ts +2 -2
- package/dist/plugins/blob-backend.d.ts +1 -1
- package/dist/plugins/caching-session-store.d.ts +24 -33
- package/dist/plugins/caching-session-store.js +8 -19
- package/dist/plugins/checkpoint-index-columns.d.ts +81 -0
- package/dist/plugins/checkpoint-index-columns.js +62 -0
- package/dist/plugins/checkpoint-store-sql.d.ts +66 -67
- package/dist/plugins/checkpoint-store-sql.js +100 -103
- package/dist/plugins/device-store-sql.d.ts +7 -6
- package/dist/plugins/e2b-orphan-reclaim.d.ts +1 -1
- package/dist/plugins/ensure-schema-lock-policy.d.ts +1 -1
- package/dist/plugins/file-history-store-sql.d.ts +9 -7
- package/dist/plugins/file-history-store-sql.js +27 -15
- package/dist/plugins/file-run-store.d.ts +8 -8
- package/dist/plugins/fork-routing-session-store.d.ts +2 -2
- package/dist/plugins/fork-routing-session-store.js +1 -1
- package/dist/plugins/host-platform.d.ts +10 -10
- package/dist/plugins/image-bake-store-sql.d.ts +1 -1
- package/dist/plugins/image-index-sql.d.ts +1 -1
- package/dist/plugins/leader-run-store-sql.d.ts +1 -1
- package/dist/plugins/local-checkpoint-store.d.ts +23 -12
- package/dist/plugins/local-checkpoint-store.js +29 -4
- package/dist/plugins/local-session-store.d.ts +44 -45
- package/dist/plugins/local-session-store.js +42 -52
- package/dist/plugins/local-session-window.d.ts +12 -6
- package/dist/plugins/local-session-window.js +1 -17
- package/dist/plugins/mailbox-store-sql.d.ts +2 -2
- package/dist/plugins/memory-engine-pg.d.ts +1 -1
- package/dist/plugins/memory-run-store.d.ts +4 -4
- package/dist/plugins/memory-sync-store-pg.d.ts +3 -3
- package/dist/plugins/one-time-migrations.d.ts +76 -39
- package/dist/plugins/one-time-migrations.js +127 -34
- package/dist/plugins/outcome-ledger-sql.d.ts +1 -1
- package/dist/plugins/park-commit-watch.d.ts +3 -3
- package/dist/plugins/permission-rule-store-file.d.ts +5 -5
- package/dist/plugins/permission-rule-store-file.js +16 -19
- package/dist/plugins/permission-rule-store-sql.d.ts +38 -4
- package/dist/plugins/permission-rule-store-sql.js +74 -31
- package/dist/plugins/pg-cost-quota.d.ts +1 -1
- package/dist/plugins/pg-pool.d.ts +2 -2
- package/dist/plugins/pg-pool.js +5 -2
- package/dist/plugins/pg-rate-limiter.d.ts +1 -1
- package/dist/plugins/pg-session-storage.d.ts +9 -21
- package/dist/plugins/pg-session-storage.js +51 -42
- package/dist/plugins/remote-env-adb.d.ts +10 -10
- package/dist/plugins/remote-env-device.d.ts +22 -14
- package/dist/plugins/remote-env-device.js +28 -16
- package/dist/plugins/remote-env-e2b.d.ts +3 -3
- package/dist/plugins/remote-env-host.d.ts +16 -16
- package/dist/plugins/remote-env-k8s.d.ts +8 -8
- package/dist/plugins/remote-env-local-docker.d.ts +2 -2
- package/dist/plugins/remote-env-ssh.d.ts +4 -4
- package/dist/plugins/remote-scratchpad.d.ts +2 -3
- package/dist/plugins/remote-scratchpad.js +0 -4
- package/dist/plugins/remote-shell.d.ts +6 -6
- package/dist/plugins/retention-lane-store-sql.d.ts +4 -4
- package/dist/plugins/retention-store-sql.d.ts +4 -4
- package/dist/plugins/roster-store-sql.d.ts +1 -1
- package/dist/plugins/run-store-sql.d.ts +8 -8
- package/dist/plugins/schema-consistency.d.ts +2 -2
- package/dist/plugins/schema-ensure-legs.d.ts +3 -3
- package/dist/plugins/schema-indexes.d.ts +1 -1
- package/dist/plugins/schema-live-columns.d.ts +1 -1
- package/dist/plugins/session-event-payload.d.ts +4 -4
- package/dist/plugins/session-fork.d.ts +34 -0
- package/dist/plugins/session-fork.js +29 -0
- 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-policy-store-sql.d.ts +12 -12
- package/dist/plugins/session-store.d.ts +2 -2
- package/dist/plugins/session-store.js +2 -2
- package/dist/plugins/shared-memory-store-sql.d.ts +2 -2
- package/dist/plugins/sql-driver.d.ts +8 -8
- package/dist/plugins/sql-errors.d.ts +1 -1
- package/dist/plugins/sql-json-column.d.ts +21 -21
- package/dist/plugins/sql-row-helpers.d.ts +2 -2
- package/dist/plugins/store-backend.d.ts +17 -16
- package/dist/plugins/store-backend.js +3 -3
- package/dist/plugins/store-contracts.d.ts +21 -21
- package/dist/plugins/task-attachment-store.d.ts +2 -2
- package/dist/plugins/task-list-store-sql.d.ts +1 -1
- package/dist/plugins/tidb-pool.d.ts +2 -2
- package/dist/plugins/tidb-pool.js +9 -2
- package/dist/plugins/tidb-session-store.d.ts +17 -27
- package/dist/plugins/tidb-session-store.js +52 -42
- package/dist/plugins/tool-result-store-sql.d.ts +1 -1
- package/dist/plugins/usage-window-store-sql.d.ts +3 -3
- package/dist/plugins/user-input-ledger-file.d.ts +1 -1
- package/dist/plugins/user-input-ledger-sql.d.ts +3 -3
- package/dist/plugins/web-search.d.ts +19 -5
- package/dist/plugins/web-search.js +19 -4
- package/dist/{orchestration/workflow-journal-entry.d.ts → plugins/workflow-journal-payload.d.ts} +23 -32
- package/dist/plugins/workflow-journal-payload.js +32 -0
- package/dist/plugins/workflow-journal-store-sql.d.ts +28 -14
- package/dist/plugins/workflow-journal-store-sql.js +34 -15
- package/dist/plugins/workflow-run-store-sql.d.ts +4 -4
- package/dist/posture-source.d.ts +11 -11
- package/dist/principal-jwt.d.ts +6 -6
- package/dist/project-identity.d.ts +3 -3
- package/dist/project-memory.d.ts +4 -4
- package/dist/read-face-posture.d.ts +7 -7
- package/dist/request-key-closure.d.ts +1 -1
- package/dist/request-key-closure.js +1 -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 +15 -8
- package/dist/request-value-closure.js +2 -0
- package/dist/router/route-orchestration.d.ts +11 -16
- package/dist/router/route-orchestration.js +2 -12
- package/dist/rule-entry-list.d.ts +8 -7
- package/dist/rule-entry-list.js +2 -9
- package/dist/rules-consent.d.ts +6 -6
- package/dist/rules-consent.js +2 -0
- package/dist/run-cancel-context.d.ts +5 -5
- package/dist/run-local.d.ts +10 -10
- package/dist/run-local.js +1 -0
- package/dist/runs.d.ts +15 -12
- package/dist/runs.js +19 -4
- package/dist/runtime-caps-resolver.d.ts +6 -6
- package/dist/runtime-governance.d.ts +9 -9
- package/dist/sandbox-pkg-source.d.ts +9 -31
- package/dist/sandbox-pkg-source.js +14 -8
- package/dist/sealed-key.d.ts +34 -2
- package/dist/sealed-key.js +55 -1
- package/dist/security.d.ts +31 -32
- package/dist/security.js +12 -9
- package/dist/server-secret-env.d.ts +10 -10
- package/dist/session-children-retire.d.ts +1 -1
- package/dist/session-error-wire.d.ts +19 -12
- package/dist/session-error-wire.js +4 -1
- package/dist/session-incarnation.d.ts +52 -35
- package/dist/session-incarnation.js +23 -8
- package/dist/session-policy-wire.d.ts +27 -5
- package/dist/session-policy-wire.js +15 -1
- package/dist/session-records-skipped-notice.d.ts +1 -1
- package/dist/session-sync-kernel.d.ts +38 -11
- package/dist/session-sync-kernel.js +10 -0
- package/dist/session-sync.d.ts +49 -6
- package/dist/session-sync.js +52 -10
- package/dist/session-titler.d.ts +7 -6
- package/dist/session-titler.js +8 -13
- package/dist/session-watch.d.ts +3 -3
- package/dist/session-watch.js +4 -4
- package/dist/sighup-idle.d.ts +2 -2
- package/dist/spec-fields.d.ts +4 -4
- package/dist/sql-engine-posture.d.ts +3 -3
- package/dist/store-posture.d.ts +2 -2
- package/dist/stored-request-tool-axes-m19.d.ts +1 -1
- package/dist/task-a2a.d.ts +3 -1
- package/dist/task-a2a.js +38 -5
- package/dist/task-cwd.d.ts +7 -88
- package/dist/task-cwd.js +2 -15
- package/dist/task-mcp.d.ts +14 -10
- package/dist/task-mcp.js +39 -12
- package/dist/task-settings.d.ts +34 -18
- package/dist/task-settings.js +34 -4
- package/dist/task-workflow.d.ts +0 -24
- package/dist/task-workflow.js +2 -1
- package/dist/terminal.d.ts +5 -5
- package/dist/tighten-only-seats.d.ts +7 -6
- package/dist/tighten-only-seats.js +1 -1
- package/dist/tool-approval.d.ts +273 -228
- package/dist/tool-approval.js +125 -47
- package/dist/tool-rule-names.d.ts +7 -1
- package/dist/trace/core-keyset-guard.d.ts +15 -9
- package/dist/trace/engine-notice-wire.d.ts +5 -5
- package/dist/trace/engine-notice-wire.js +1 -0
- package/dist/trace/exit-seat.d.ts +40 -1
- package/dist/trace/exit-seat.js +46 -14
- package/dist/trace/ledger-events.d.ts +6 -4
- package/dist/trace/ledger-sink.d.ts +1 -1
- package/dist/trace/model-segment.d.ts +3 -3
- package/dist/trace/project.d.ts +65 -46
- package/dist/trace/project.js +49 -5
- package/dist/trace/redact.d.ts +44 -27
- package/dist/trace/redact.js +343 -73
- package/dist/trace/self-minted-wire.d.ts +39 -14
- package/dist/trace/self-minted-wire.js +9 -0
- package/dist/trace/sema-provenance.d.ts +1 -1
- package/dist/trace/task-notification-facets.d.ts +4 -4
- package/dist/trace/wire-projection-faces.d.ts +9 -3
- package/dist/trace/wire-projection-faces.js +7 -1
- package/dist/unattended-permission-mode.d.ts +5 -5
- package/dist/url-credential.d.ts +52 -0
- package/dist/url-credential.js +49 -0
- package/dist/url-refusal.d.ts +6 -2
- package/dist/url-refusal.js +8 -1
- package/dist/usage-analytics.d.ts +2 -2
- package/dist/user-input-ledger.d.ts +23 -23
- package/dist/web-admin-client/apply-effective.d.ts +5 -5
- package/dist/web-admin-client/apply-effective.js +2 -1
- package/dist/web-admin-client/apply-ledger.d.ts +2 -2
- package/dist/web-admin-client/center-request.d.ts +2 -2
- package/dist/web-admin-client/facade.d.ts +5 -5
- package/dist/web-admin-client/facade.js +1 -1
- package/dist/web-admin-client/hot-keys-registry.d.ts +2 -2
- package/dist/web-admin-client/http-client.d.ts +4 -4
- package/dist/web-admin-client/mcp-revocation.d.ts +1 -1
- package/dist/web-admin-client/pull-state.d.ts +1 -1
- package/dist/web-admin-client/read-face.d.ts +2 -2
- package/dist/web-admin-client/read-warnings.d.ts +5 -5
- package/dist/web-admin-client/restart-signal.d.ts +6 -6
- package/dist/web-admin-client/skills-mcp.d.ts +29 -4
- package/dist/web-admin-client/skills-mcp.js +31 -4
- package/dist/web-admin-client/types.d.ts +17 -10
- package/dist/workspace-space.d.ts +5 -3
- package/dist/write-protection.d.ts +2 -2
- package/package.json +4 -3
- package/scripts/drain-check-local.mjs +35 -2
- package/scripts/drain-check.mysql.sql +22 -2
- package/scripts/drain-check.pg.sql +22 -2
- package/skills/loop.md +1 -1
- package/dist/mcp-tool-faces.d.ts +0 -69
- package/dist/mcp-tool-faces.js +0 -104
- package/dist/orchestration/workflow-journal-entry.js +0 -27
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { applyTombstones, assertDeleteDeltaCarriesNoAdd, assertRedemptionNotQuarantined, assertWriteDeltaScopeDurable, foldDelta, isValidDurableScope, parseRuleText, readRowBehavior, screenRuleSyncState, PERMISSION_RULE_WRITER, RULE_BEHAVIORS, RULE_SYNC_DROP_CODES, EMPTY_DURABLE_RULE_PARTITION, } from "@sema-agent/core";
|
|
3
|
+
import { applyTombstones, assertDeleteDeltaCarriesNoAdd, assertRedemptionNotQuarantined, assertWriteDeltaScopeDurable, foldDelta, isValidDurableScope, parseRuleText, readRowBehavior, screenRuleSyncState, PERMISSION_RULE_WRITER, RULE_ADD_ORIGINS, RULE_APPROVAL_KINDS, RULE_APPROVAL_RECORD_SCHEMA, RULE_APPROVAL_STATES, RULE_BEHAVIORS, RULE_SYNC_DROP_CODES, EDITED_RULE_BREADTH_WARNING_CODES, PERSISTED_RULE_MATCHES, UNCOVERED_SEGMENT_REASONS, EMPTY_DURABLE_RULE_PARTITION, PERMISSION_RULES_LATER_GRAMMAR_CODE, RULE_OFFER_KINDS, SESSION_RULES_GRAMMAR, readRuleGrammarStamp, readRuleOwner, } from "@sema-agent/core";
|
|
4
4
|
import { dialectProtocolJsonEncoder, sqlDbNowMs, SQL_DB_NOW_MS } from "./sql-driver.js";
|
|
5
5
|
import { isMissingColumnError } from "./sql-errors.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { canonicalJsonStringify } from "@sema-agent/core";
|
|
7
|
+
import { isPersistedRuleTool } from "../permission-rule-vocab.js";
|
|
8
8
|
import { ensureIndexes } from "./ensure-index.js";
|
|
9
9
|
import { createZodColumnReader, decodeJsonColumn, foldJsonColumn, jsonColumnText, readJsonObjectColumn, SqlJsonColumnError } from "./sql-json-column.js";
|
|
10
10
|
import { redactSecrets } from "../trace/redact.js";
|
|
@@ -25,7 +25,7 @@ export const DurableRuleScopeSchema = z.union([
|
|
|
25
25
|
]);
|
|
26
26
|
const RuleDotSchema = z.object({ actor: z.string().min(1), counter: z.number().int().nonnegative().safe() }).strict();
|
|
27
27
|
const RuleAddSchema = z
|
|
28
|
-
.object({ dot: RuleDotSchema, origin: z.enum(
|
|
28
|
+
.object({ dot: RuleDotSchema, origin: z.enum(RULE_ADD_ORIGINS), createdAt: z.string() })
|
|
29
29
|
.strict();
|
|
30
30
|
const PersistedRuleRowSchema = z
|
|
31
31
|
.object({
|
|
@@ -64,11 +64,24 @@ function readBucketRowBehavior(raw, what, ownerKey) {
|
|
|
64
64
|
}
|
|
65
65
|
class BucketRefusedError extends Error {
|
|
66
66
|
verdict;
|
|
67
|
-
|
|
67
|
+
code;
|
|
68
|
+
constructor(verdict, evidence, code) {
|
|
68
69
|
super(`${verdict} ${evidence}`);
|
|
69
70
|
this.verdict = verdict;
|
|
71
|
+
if (code !== undefined)
|
|
72
|
+
this.code = code;
|
|
70
73
|
}
|
|
71
74
|
}
|
|
75
|
+
function readBucketStamp(ownerKey, stamp) {
|
|
76
|
+
const absent = stamp === null || stamp === undefined;
|
|
77
|
+
const read = readRuleGrammarStamp(absent ? undefined : stamp, SESSION_RULES_GRAMMAR, { rules: `the rules in permission_rule bucket ${ownerKey}`, held: "rows" });
|
|
78
|
+
if (!("got" in read))
|
|
79
|
+
return absent ? undefined : read.grammar;
|
|
80
|
+
if (read.later === true) {
|
|
81
|
+
throw new BucketRefusedError(read.sentence, "— loading zero rules and refusing every write until then; the row is left as it is", PERMISSION_RULES_LATER_GRAMMAR_CODE);
|
|
82
|
+
}
|
|
83
|
+
throw new BucketRefusedError(read.sentence, "— refusing the whole bucket; the row is left as it is");
|
|
84
|
+
}
|
|
72
85
|
const WritablePersistedRuleSchema = PersistedRuleRowSchema.extend({ behavior: z.enum(RULE_BEHAVIORS) });
|
|
73
86
|
const WritableRuleTombstoneSchema = RuleTombstoneSchema.extend({ behavior: z.enum(RULE_BEHAVIORS) });
|
|
74
87
|
const FrontierSchema = z.record(z.string(), z.number().int().nonnegative().safe());
|
|
@@ -77,9 +90,9 @@ const RedeemedDotsSchema = z.record(z.string(), RuleDotSchema);
|
|
|
77
90
|
const RuleEditedSchema = z
|
|
78
91
|
.object({ index: z.number().int().nonnegative().safe(), text: z.string(), at: z.string(), warnings: z.array(z.enum(EDITED_RULE_BREADTH_WARNING_CODES)).optional() })
|
|
79
92
|
.strict();
|
|
80
|
-
const
|
|
81
|
-
z.object({ kind: z.literal("single"), candidate: z.number().int().nonnegative().safe() }).strict(),
|
|
82
|
-
z
|
|
93
|
+
const RULE_OFFER2_ARMS = {
|
|
94
|
+
single: z.object({ kind: z.literal("single"), candidate: z.number().int().nonnegative().safe() }).strict(),
|
|
95
|
+
batch: z
|
|
83
96
|
.object({
|
|
84
97
|
kind: z.literal("batch"),
|
|
85
98
|
candidates: z.array(z.number().int().nonnegative().safe()),
|
|
@@ -88,7 +101,8 @@ const RuleOffer2Schema = z.union([
|
|
|
88
101
|
uncoveredDetail: z.array(z.object({ segment: z.string(), reason: z.enum(UNCOVERED_SEGMENT_REASONS) }).strict()).optional(),
|
|
89
102
|
})
|
|
90
103
|
.strict(),
|
|
91
|
-
|
|
104
|
+
};
|
|
105
|
+
export const RuleOffer2Schema = z.union([RULE_OFFER2_ARMS.single, RULE_OFFER2_ARMS.batch]);
|
|
92
106
|
export const PERMISSION_RULE_TABLE = "permission_rule";
|
|
93
107
|
export const PERMISSION_RULE_APPROVAL_TABLE = "permission_rule_approval";
|
|
94
108
|
export const PERMISSION_RULE_TICKET_TABLE = "permission_rule_ticket";
|
|
@@ -113,9 +127,13 @@ export const TIDB_PERMISSION_RULE_STATEMENTS = [
|
|
|
113
127
|
observed_vector_json LONGTEXT NULL,
|
|
114
128
|
last_round_at_ms BIGINT NULL,
|
|
115
129
|
-- checksum:core \`ruleStoreChecksum\` 同精神的完整性指纹(覆盖内容三列 + observed_vector 在场时的
|
|
116
|
-
--
|
|
130
|
+
-- 观测向量 + grammar 非 NULL 时的文法戳,不含 rev/counter——算法单点=buildRuleBucketChecksum)。
|
|
117
131
|
-- 读回不符 ⇒ throw(绝不静默当空店 —— core 对读失败的姿势明令如此)。
|
|
118
132
|
checksum VARCHAR(190) NOT NULL,
|
|
133
|
+
-- grammar:这只桶的规则行写在哪一版**规则文法**里(引擎 \`SESSION_RULES_GRAMMAR\` 的数,每次写盖章;core 7.55.0 起 File 店同样每桶一戳)。
|
|
134
|
+
-- NULL = 戳存在之前写下的存量行,按本引擎的文法读、下一次写盖戳;晚于本引擎的戳整桶拒(码 permission_rules.later_grammar,一个字节不动)。
|
|
135
|
+
-- 非 NULL 的戳进 checksum(戳列与指纹同一条语句写;存量 NULL 行的指纹与升级前逐字节相同):不读戳的旧副本按旧算法重算比不上,整桶拒。可空整数:PG 的 INTEGER 驱动交回 number(BIGINT 会交回串)。
|
|
136
|
+
grammar INT NULL,
|
|
119
137
|
created_at_ms BIGINT NOT NULL,
|
|
120
138
|
updated_at_ms BIGINT NOT NULL,
|
|
121
139
|
PRIMARY KEY (owner_key)
|
|
@@ -277,6 +295,7 @@ export async function ensurePgPermissionRuleSchema(q) {
|
|
|
277
295
|
observed_vector_json TEXT COLLATE "C",
|
|
278
296
|
last_round_at_ms BIGINT,
|
|
279
297
|
checksum VARCHAR(190) COLLATE "C" NOT NULL,
|
|
298
|
+
grammar INTEGER,
|
|
280
299
|
created_at_ms BIGINT NOT NULL,
|
|
281
300
|
updated_at_ms BIGINT NOT NULL,
|
|
282
301
|
PRIMARY KEY (owner_key)
|
|
@@ -336,7 +355,7 @@ export function buildRuleOwnerKey(owner) {
|
|
|
336
355
|
return createHash("sha256").update(material, "utf8").digest("hex");
|
|
337
356
|
}
|
|
338
357
|
export function buildRuleBucketChecksum(body) {
|
|
339
|
-
return `sha256:${createHash("sha256").update(
|
|
358
|
+
return `sha256:${createHash("sha256").update(canonicalJsonStringify(body), "utf8").digest("hex")}`;
|
|
340
359
|
}
|
|
341
360
|
const NEXT_DOT_MAX_ATTEMPTS = 8;
|
|
342
361
|
const EMPTY_BUCKET = { actor: "", counter: 0, rev: 0, rules: [], tombstones: [], quarantined: [] };
|
|
@@ -357,15 +376,16 @@ async function refusingUnreadableColumns(what, fn) {
|
|
|
357
376
|
}
|
|
358
377
|
}
|
|
359
378
|
function bucketRowColumns(dialect) {
|
|
360
|
-
return `actor, counter, rev, ${jsonColumnText(dialect, "rules_json")}, ${jsonColumnText(dialect, "tombstones_json")}, ${jsonColumnText(dialect, "quarantined_json")}, ${jsonColumnText(dialect, "observed_vector_json")}, checksum`;
|
|
379
|
+
return `actor, counter, rev, ${jsonColumnText(dialect, "rules_json")}, ${jsonColumnText(dialect, "tombstones_json")}, ${jsonColumnText(dialect, "quarantined_json")}, ${jsonColumnText(dialect, "observed_vector_json")}, checksum, grammar`;
|
|
361
380
|
}
|
|
362
381
|
function decodeBucketRow(ownerKey, row) {
|
|
363
382
|
const at = `${PERMISSION_RULE_TABLE} bucket ${ownerKey}`;
|
|
383
|
+
const grammar = readBucketStamp(ownerKey, row.grammar);
|
|
364
384
|
const rulesRaw = decodeJsonColumn(row.rules_json, readRulesJson, `${at}.rules_json`);
|
|
365
385
|
const tombstonesRaw = decodeJsonColumn(row.tombstones_json, readTombstonesJson, `${at}.tombstones_json`);
|
|
366
386
|
const quarantinedRaw = decodeJsonColumn(row.quarantined_json, readQuarantinedJson, `${at}.quarantined_json`);
|
|
367
387
|
const observedVector = row.observed_vector_json === null || row.observed_vector_json === undefined ? undefined : decodeJsonColumn(row.observed_vector_json, readObservedVectorJson, `${at}.observed_vector_json`);
|
|
368
|
-
const expected = buildRuleBucketChecksum({ rules: rulesRaw, tombstones: tombstonesRaw, quarantined: quarantinedRaw, ...(observedVector !== undefined ? { observedVector } : {}) });
|
|
388
|
+
const expected = buildRuleBucketChecksum({ rules: rulesRaw, tombstones: tombstonesRaw, quarantined: quarantinedRaw, ...(observedVector !== undefined ? { observedVector } : {}), ...(grammar !== undefined ? { grammar } : {}) });
|
|
369
389
|
if (String(row.checksum ?? "") !== expected) {
|
|
370
390
|
throw new BucketRefusedError(`permission_rule bucket ${ownerKey} failed its integrity check`, `(stored ${String(row.checksum)}, computed ${expected}) — refusing the whole bucket rather than reporting a partial rule set`);
|
|
371
391
|
}
|
|
@@ -392,6 +412,14 @@ function decodeBucketRow(ownerKey, row) {
|
|
|
392
412
|
tombstones,
|
|
393
413
|
quarantined,
|
|
394
414
|
...(observedVector !== undefined ? { observedVector } : {}),
|
|
415
|
+
...(grammar !== undefined ? { grammar } : {}),
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
function fencedCasWhere(dialect, firstPlaceholder, ownerKey, cas) {
|
|
419
|
+
const ph = (offset) => (dialect === "tidb" ? "?" : `$${firstPlaceholder + offset}`);
|
|
420
|
+
return {
|
|
421
|
+
sql: `owner_key = ${ph(0)} AND ${cas.column} = ${ph(1)} AND (grammar IS NULL OR grammar <= ${ph(2)})`,
|
|
422
|
+
params: [ownerKey, cas.expected, SESSION_RULES_GRAMMAR],
|
|
395
423
|
};
|
|
396
424
|
}
|
|
397
425
|
async function writeBucket(exec, dialect, ownerKey, cur, next, nowMs) {
|
|
@@ -401,16 +429,17 @@ async function writeBucket(exec, dialect, ownerKey, cur, next, nowMs) {
|
|
|
401
429
|
}
|
|
402
430
|
const body = { rules: next.rules, tombstones: next.tombstones, quarantined: cur.quarantined, ...(cur.observedVector !== undefined ? { observedVector: cur.observedVector } : {}) };
|
|
403
431
|
const enc = dialectProtocolJsonEncoder(dialect);
|
|
432
|
+
const where = fencedCasWhere(dialect, 7, ownerKey, { column: "rev", expected: cur.rev });
|
|
404
433
|
const affected = await exec(dialect === "tidb"
|
|
405
|
-
? `UPDATE ${PERMISSION_RULE_TABLE} SET rules_json = ?, tombstones_json = ?, quarantined_json = ?, checksum = ?, rev = rev + 1, updated_at_ms = ? WHERE
|
|
406
|
-
: `UPDATE ${PERMISSION_RULE_TABLE} SET rules_json = $1, tombstones_json = $2, quarantined_json = $3, checksum = $4, rev = rev + 1, updated_at_ms = $
|
|
434
|
+
? `UPDATE ${PERMISSION_RULE_TABLE} SET rules_json = ?, tombstones_json = ?, quarantined_json = ?, checksum = ?, grammar = ?, rev = rev + 1, updated_at_ms = ? WHERE ${where.sql}`
|
|
435
|
+
: `UPDATE ${PERMISSION_RULE_TABLE} SET rules_json = $1, tombstones_json = $2, quarantined_json = $3, checksum = $4, grammar = $5, rev = rev + 1, updated_at_ms = $6 WHERE ${where.sql}`, [
|
|
407
436
|
enc(next.rules, "permission_rule.rules_json"),
|
|
408
437
|
enc(next.tombstones, "permission_rule.tombstones_json"),
|
|
409
438
|
enc(cur.quarantined, "permission_rule.quarantined_json"),
|
|
410
|
-
buildRuleBucketChecksum(body),
|
|
439
|
+
buildRuleBucketChecksum({ ...body, grammar: SESSION_RULES_GRAMMAR }),
|
|
440
|
+
SESSION_RULES_GRAMMAR,
|
|
411
441
|
nowMs,
|
|
412
|
-
|
|
413
|
-
cur.rev,
|
|
442
|
+
...where.params,
|
|
414
443
|
]);
|
|
415
444
|
return affected === 1;
|
|
416
445
|
}
|
|
@@ -446,9 +475,9 @@ class SqlDurableRulePartition {
|
|
|
446
475
|
async ensureBucket() {
|
|
447
476
|
const nowMs = this.now();
|
|
448
477
|
const empty = { rules: [], tombstones: [], quarantined: [] };
|
|
449
|
-
await this.db.query(this.q(`INSERT IGNORE INTO ${PERMISSION_RULE_TABLE} (owner_key, owner_kind, principal, actor, counter, rev, rules_json, tombstones_json, quarantined_json, observed_vector_json, last_round_at_ms, checksum, created_at_ms, updated_at_ms)
|
|
450
|
-
VALUES (?, ?, ?, ?, 0, 0, ?, ?, ?, NULL, NULL, ?, ?, ?)`, `INSERT INTO ${PERMISSION_RULE_TABLE} (owner_key, owner_kind, principal, actor, counter, rev, rules_json, tombstones_json, quarantined_json, observed_vector_json, last_round_at_ms, checksum, created_at_ms, updated_at_ms)
|
|
451
|
-
VALUES ($1, $2, $3, $4, 0, 0, $5, $6, $7, NULL, NULL, $8, $9, $10) ON CONFLICT (owner_key) DO NOTHING`), [
|
|
478
|
+
await this.db.query(this.q(`INSERT IGNORE INTO ${PERMISSION_RULE_TABLE} (owner_key, owner_kind, principal, actor, counter, rev, rules_json, tombstones_json, quarantined_json, observed_vector_json, last_round_at_ms, checksum, grammar, created_at_ms, updated_at_ms)
|
|
479
|
+
VALUES (?, ?, ?, ?, 0, 0, ?, ?, ?, NULL, NULL, ?, ?, ?, ?)`, `INSERT INTO ${PERMISSION_RULE_TABLE} (owner_key, owner_kind, principal, actor, counter, rev, rules_json, tombstones_json, quarantined_json, observed_vector_json, last_round_at_ms, checksum, grammar, created_at_ms, updated_at_ms)
|
|
480
|
+
VALUES ($1, $2, $3, $4, 0, 0, $5, $6, $7, NULL, NULL, $8, $9, $10, $11) ON CONFLICT (owner_key) DO NOTHING`), [
|
|
452
481
|
this.ownerKey,
|
|
453
482
|
this.owner.kind,
|
|
454
483
|
this.owner.kind === "principal" ? this.owner.principal : null,
|
|
@@ -456,14 +485,15 @@ class SqlDurableRulePartition {
|
|
|
456
485
|
this.enc([], "permission_rule.rules_json"),
|
|
457
486
|
this.enc([], "permission_rule.tombstones_json"),
|
|
458
487
|
this.enc([], "permission_rule.quarantined_json"),
|
|
459
|
-
buildRuleBucketChecksum(empty),
|
|
488
|
+
buildRuleBucketChecksum({ ...empty, grammar: SESSION_RULES_GRAMMAR }),
|
|
489
|
+
SESSION_RULES_GRAMMAR,
|
|
460
490
|
nowMs,
|
|
461
491
|
nowMs,
|
|
462
492
|
]);
|
|
463
493
|
}
|
|
464
494
|
async list() {
|
|
465
495
|
const s = await this.read();
|
|
466
|
-
return { rules: applyTombstones(s.rules, s.tombstones), tombstones: s.tombstones, rev: s.rev, checksum: buildRuleBucketChecksum({ rules: s.rules, tombstones: s.tombstones, quarantined: s.quarantined, ...(s.observedVector !== undefined ? { observedVector: s.observedVector } : {}) }) };
|
|
496
|
+
return { rules: applyTombstones(s.rules, s.tombstones), tombstones: s.tombstones, rev: s.rev, checksum: buildRuleBucketChecksum({ rules: s.rules, tombstones: s.tombstones, quarantined: s.quarantined, ...(s.observedVector !== undefined ? { observedVector: s.observedVector } : {}), ...(s.grammar !== undefined ? { grammar: s.grammar } : {}) }) };
|
|
467
497
|
}
|
|
468
498
|
async quarantined() {
|
|
469
499
|
return (await this.read()).quarantined;
|
|
@@ -475,13 +505,15 @@ class SqlDurableRulePartition {
|
|
|
475
505
|
nextDot: async () => {
|
|
476
506
|
await this.ensureBucket();
|
|
477
507
|
for (let attempt = 0; attempt < NEXT_DOT_MAX_ATTEMPTS; attempt++) {
|
|
478
|
-
const { rows } = await this.db.query(this.q(`SELECT actor, counter FROM ${PERMISSION_RULE_TABLE} WHERE owner_key = ?`, `SELECT actor, counter FROM ${PERMISSION_RULE_TABLE} WHERE owner_key = $1`), [this.ownerKey]);
|
|
508
|
+
const { rows } = await this.db.query(this.q(`SELECT actor, counter, grammar FROM ${PERMISSION_RULE_TABLE} WHERE owner_key = ?`, `SELECT actor, counter, grammar FROM ${PERMISSION_RULE_TABLE} WHERE owner_key = $1`), [this.ownerKey]);
|
|
479
509
|
const row = rows[0];
|
|
480
510
|
if (row === undefined)
|
|
481
511
|
throw new Error(`permission_rule bucket ${this.ownerKey} vanished while minting a dot`);
|
|
512
|
+
readBucketStamp(this.ownerKey, row.grammar);
|
|
482
513
|
const cur = Number(row.counter);
|
|
483
514
|
const next = cur + 1;
|
|
484
|
-
const
|
|
515
|
+
const where = fencedCasWhere(this.db.dialect, 3, this.ownerKey, { column: "counter", expected: cur });
|
|
516
|
+
const { affected } = await this.db.query(this.q(`UPDATE ${PERMISSION_RULE_TABLE} SET counter = ?, updated_at_ms = ? WHERE ${where.sql}`, `UPDATE ${PERMISSION_RULE_TABLE} SET counter = $1, updated_at_ms = $2 WHERE ${where.sql}`), [next, this.now(), ...where.params]);
|
|
485
517
|
if (affected === 1)
|
|
486
518
|
return { actor: String(row.actor), counter: next };
|
|
487
519
|
}
|
|
@@ -554,6 +586,12 @@ export class SqlDurableRulePartitionProvider {
|
|
|
554
586
|
return new SqlDurableRulePartition(this.db, { kind: "local-owner" }, this.now);
|
|
555
587
|
}
|
|
556
588
|
}
|
|
589
|
+
export function recordOwnerOf(record) {
|
|
590
|
+
return readRuleOwner(record.owner ?? { kind: "principal", principal: record.principal });
|
|
591
|
+
}
|
|
592
|
+
export function refusedOwnerSentence(recordId) {
|
|
593
|
+
return `refusing to create approval record ${recordId}: its owner is not one the engine reads as an owner (a principal name must be a non-blank string; the kind must be a member of the owner set) — nothing was written`;
|
|
594
|
+
}
|
|
557
595
|
export class SqlRuleApprovalRecordStore {
|
|
558
596
|
db;
|
|
559
597
|
now;
|
|
@@ -575,9 +613,12 @@ export class SqlRuleApprovalRecordStore {
|
|
|
575
613
|
const row = rows[0];
|
|
576
614
|
if (row === undefined)
|
|
577
615
|
return undefined;
|
|
578
|
-
const ownerKind = String(row.owner_kind);
|
|
579
616
|
const principal = row.principal === null || row.principal === undefined ? undefined : String(row.principal);
|
|
580
|
-
const
|
|
617
|
+
const rowOwner = readRuleOwner({ kind: row.owner_kind, ...(principal !== undefined ? { principal } : {}) });
|
|
618
|
+
if (rowOwner === undefined) {
|
|
619
|
+
throw new Error(`${PERMISSION_RULE_APPROVAL_TABLE} row ${id} carries an owner the engine does not read as an owner (owner_kind ${JSON.stringify(String(row.owner_kind))}) — refusing the record rather than reading it as another owner's`);
|
|
620
|
+
}
|
|
621
|
+
const identity = rowOwner.kind === "principal" ? { principal: rowOwner.principal } : { owner: rowOwner };
|
|
581
622
|
if (Number(row.version) !== RULE_APPROVAL_RECORD_SCHEMA) {
|
|
582
623
|
return { staleSchema: true, id: String(row.record_id), ...identity };
|
|
583
624
|
}
|
|
@@ -586,8 +627,8 @@ export class SqlRuleApprovalRecordStore {
|
|
|
586
627
|
const offers = decodeJsonColumn(row.offers_json, readOffersJson, `${at}.offers_json`);
|
|
587
628
|
const dotsRaw = row.redeemed_dots_json === null || row.redeemed_dots_json === undefined ? undefined : decodeJsonColumn(row.redeemed_dots_json, readRedeemedDotsJson, `${at}.redeemed_dots_json`);
|
|
588
629
|
const edited = row.edited_json === null || row.edited_json === undefined ? undefined : decodeJsonColumn(row.edited_json, readEditedJson, `${at}.edited_json`);
|
|
589
|
-
const kind = z.enum(
|
|
590
|
-
const state = z.enum(
|
|
630
|
+
const kind = z.enum(RULE_APPROVAL_KINDS).safeParse(row.kind);
|
|
631
|
+
const state = z.enum(RULE_APPROVAL_STATES).safeParse(row.state);
|
|
591
632
|
if (!kind.success || !state.success) {
|
|
592
633
|
throw new Error(`${PERMISSION_RULE_APPROVAL_TABLE} row ${id} carries an unreadable kind/state (${String(row.kind)}/${String(row.state)})`);
|
|
593
634
|
}
|
|
@@ -612,7 +653,9 @@ export class SqlRuleApprovalRecordStore {
|
|
|
612
653
|
}
|
|
613
654
|
async create(record) {
|
|
614
655
|
const nowMs = this.now();
|
|
615
|
-
const owner = record
|
|
656
|
+
const owner = recordOwnerOf(record);
|
|
657
|
+
if (owner === undefined)
|
|
658
|
+
throw new Error(refusedOwnerSentence(record.id));
|
|
616
659
|
await this.db.query(this.q(`INSERT INTO ${PERMISSION_RULE_APPROVAL_TABLE} (record_id, owner_key, owner_kind, principal, kind, state, version, rev, candidates_json, offers_json, selected_offer, redeemed_dots_json, tool_call_id, bound_input_hash, command, edited_json, created_at_iso, created_at_ms, updated_at_ms)
|
|
617
660
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, `INSERT INTO ${PERMISSION_RULE_APPROVAL_TABLE} (record_id, owner_key, owner_kind, principal, kind, state, version, rev, candidates_json, offers_json, selected_offer, redeemed_dots_json, tool_call_id, bound_input_hash, command, edited_json, created_at_iso, created_at_ms, updated_at_ms)
|
|
618
661
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19)`), [
|
|
@@ -662,7 +705,7 @@ export class SqlRuleApprovalRecordStore {
|
|
|
662
705
|
}
|
|
663
706
|
export const RULE_IMPORT_TICKET_GC_GRACE_MS = 10 * 60_000;
|
|
664
707
|
export function buildRulePayloadHash(candidates) {
|
|
665
|
-
return createHash("sha256").update(
|
|
708
|
+
return createHash("sha256").update(canonicalJsonStringify(candidates), "utf8").digest("hex");
|
|
666
709
|
}
|
|
667
710
|
export class SqlRuleImportTicketStore {
|
|
668
711
|
db;
|
|
@@ -36,7 +36,7 @@ export declare class PgCostQuota implements QuotaTracker {
|
|
|
36
36
|
* DATETIME(3)→TIMESTAMPTZ(3), inline `KEY idx_window`→separate CREATE INDEX). DISJOINT from other stores'
|
|
37
37
|
* tables, so a self-contained ensureSchema is safe (the central pg-pool aggregation is done separately). */
|
|
38
38
|
export declare const PG_COST_QUOTA_SCHEMA: string[];
|
|
39
|
-
/**
|
|
39
|
+
/** [ref]:本 store 的索引**声明**(两方言共用一份)。PG 侧由下面的 `ensureSchema` 应用,MySQL 侧由 `tidb-pool.ts` 的中央 `ensureSchema` 应用(那里内联 `KEY` 已在 `CREATE TABLE` 里 ⇒ 新建库探到即零 DDL,存量库缺谁补谁)。加索引以外的 schema 变更仍归运维,见 `plugins/ensure-index.ts` 头注。 */
|
|
40
40
|
export declare const COST_QUOTA_INDEXES: readonly IndexSpec[];
|
|
41
41
|
/** Idempotent schema apply for the cost_quota table (the PG twin of tidb-pool.ts ensureSchema). */
|
|
42
42
|
export declare function ensureSchema(pool: Pool | PoolClient): Promise<void>;
|
|
@@ -80,7 +80,7 @@ export declare const ENSURE_SCHEMA_LOCK_KEY = 4906;
|
|
|
80
80
|
* `tidb-pool.ts acquireEnsureSchemaLock` 的逐条同形(循环共用 `ensure-schema-lock-policy.ts`)。轮到拿到为止;坏连接连续抛上抛
|
|
81
81
|
* (每条 DDL 本来就各自幂等),而不是把启动挂死。
|
|
82
82
|
*
|
|
83
|
-
* 🔴 **为什么不能再用阻塞的 `pg_advisory_lock()`**(
|
|
83
|
+
* 🔴 **为什么不能再用阻塞的 `pg_advisory_lock()`**([ref] 复审 H1,对 7.82.1 的回归):本仓自 [ref] 起在
|
|
84
84
|
* 锁窗内发 `CREATE INDEX CONCURRENTLY`,而 CIC 的末相要**等掉同库里所有 xmin 比它老的运行中后端**
|
|
85
85
|
* (`WaitForOlderSnapshots`)。一条卡在 `SELECT pg_advisory_lock($1)` 里的副本**正是**一条持着快照的运行中
|
|
86
86
|
* 查询 —— 它等锁、锁等 CIC、CIC 等它:三方互等。命中形是「全新 PG 库 + 多副本同时起」(表刚建完 ⇒ 索引
|
|
@@ -101,7 +101,7 @@ export declare function releasePgEnsureSchemaLock(client: pg.PoolClient, tag: st
|
|
|
101
101
|
* calls ensurePgSchema), and in production every replica calls it at boot. Concurrent DDL on the SAME table makes
|
|
102
102
|
* PG raise `tuple concurrently updated` in its catalog. We serialize the entire run behind a session-level
|
|
103
103
|
* advisory lock, acquired NON-BLOCKINGLY with polling ({@link acquirePgEnsureSchemaLock} — 阻塞形会与本函数
|
|
104
|
-
* 自
|
|
104
|
+
* 自 [ref] 起发的 `CREATE INDEX CONCURRENTLY` 三方互等,理由写在那只函数的头注里): the first caller does
|
|
105
105
|
* the real CREATE/index work, the rest poll from OUTSIDE the database then find every statement already
|
|
106
106
|
* satisfied (IF NOT EXISTS ⇒ no-op), so there is no catalog race. The DDL semantics are
|
|
107
107
|
* unchanged — only its timing is serialized. The advisory lock is SESSION (connection)-level, so the lock and
|
package/dist/plugins/pg-pool.js
CHANGED
|
@@ -93,6 +93,8 @@ export const PG_SCHEMA_STATEMENTS = [
|
|
|
93
93
|
reopen_reason VARCHAR(32) COLLATE "C",
|
|
94
94
|
terminal_at_ms BIGINT,
|
|
95
95
|
gate_kind VARCHAR(32) COLLATE "C",
|
|
96
|
+
-- approval_card(core 7.55.0 #1468 P1 阶段二):门上的卡词照存;语义、读者与「本仓不发 ALTER」见 MySQL 孪生(tidb-pool.ts 同名列的行内注)。
|
|
97
|
+
approval_card TEXT COLLATE "C",
|
|
96
98
|
bound_input_hash VARCHAR(190) COLLATE "C",
|
|
97
99
|
-- pending_steer_queue / pending_steer_rev:队列化后的 durable steering(core 5.14.0 #147)。两列的语义、
|
|
98
100
|
-- 退役的单座列 pending_steer 为何删列(以及存量库里仍带着它时的拒启臂)、48000 字节预算、
|
|
@@ -340,8 +342,9 @@ export const PG_SCHEMA_STATEMENTS = [
|
|
|
340
342
|
summary TEXT COLLATE "C" NOT NULL,
|
|
341
343
|
enqueued_at_ms BIGINT NOT NULL,
|
|
342
344
|
-- 1.109 delivery envelope. kind NULL = a LEGACY \`workflow_complete\` row (the shape that predates the
|
|
343
|
-
-- envelope); readers must keep treating NULL as that kind, it is not "unknown". payload = the kind's
|
|
344
|
-
--
|
|
345
|
+
-- envelope); readers must keep treating NULL as that kind, it is not "unknown". payload = the kind's JSON
|
|
346
|
+
-- extras: task_notification: task_type / result / …; workflow_complete: at most {"completionId": …} (the
|
|
347
|
+
-- engine's own correlation id, verbatim; NULL when the engine minted none).
|
|
345
348
|
kind VARCHAR(24) COLLATE "C",
|
|
346
349
|
payload TEXT COLLATE "C",
|
|
347
350
|
PRIMARY KEY (session_id, run_id),
|
|
@@ -40,7 +40,7 @@ export declare class PgRateLimiter implements RateGate {
|
|
|
40
40
|
* atomic ON CONFLICT increment.
|
|
41
41
|
*/
|
|
42
42
|
export declare const PG_RATE_LIMITER_SCHEMA: string[];
|
|
43
|
-
/**
|
|
43
|
+
/** [ref]:本 store 的索引**声明**(两方言共用一份)。PG 侧由下面的 `ensureSchema` 应用,MySQL 侧由 `tidb-pool.ts` 的中央 `ensureSchema` 应用(那里内联 `KEY` 已在 `CREATE TABLE` 里 ⇒ 新建库探到即零 DDL,存量库缺谁补谁)。加索引以外的 schema 变更仍归运维,见 `plugins/ensure-index.ts` 头注。 */
|
|
44
44
|
export declare const RATE_LIMIT_INDEXES: readonly IndexSpec[];
|
|
45
45
|
/** Self-contained schema bootstrap for tests (the rate_limit table is disjoint from other stores). */
|
|
46
46
|
export declare function ensureSchema(pool: Pool | PoolClient): Promise<void>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseSessionStorage, type AcquiredSession, type PlacedSessionRow, type SessionMetadata, type SessionPlacementRecord, type SessionStore, type SessionTreeEntry, type SessionWriteOptions } from "@sema-agent/core";
|
|
1
|
+
import { BaseSessionStorage, type AcquiredSession, type PlacedSessionRow, type SessionHead, type SessionMetadata, type SessionPlacementRecord, type SessionStore, type SessionTreeEntry, type SessionWriteOptions } from "@sema-agent/core";
|
|
2
2
|
import type { Pool, PoolClient } from "pg";
|
|
3
3
|
import type { SessionSummary } from "./store-contracts.js";
|
|
4
4
|
import { type SessionRowPatch } from "../session-row.js";
|
|
@@ -16,7 +16,7 @@ import { type IndexSpec } from "./ensure-index.js";
|
|
|
16
16
|
* drop-and-recreate, and NO new `ALTER TABLE` seams are added here; fold into the CREATE instead.
|
|
17
17
|
*/
|
|
18
18
|
export declare const PG_SESSION_SCHEMA: string[];
|
|
19
|
-
/**
|
|
19
|
+
/** [ref]:本 store 的索引**声明**(两方言共用一份)。PG 侧由下面的 `ensureSchema` 应用,MySQL 侧由 `tidb-pool.ts` 的中央 `ensureSchema` 应用(那里内联 `KEY` 已在 `CREATE TABLE` 里 ⇒ 新建库探到即零 DDL,存量库缺谁补谁)。加索引以外的 schema 变更仍归运维,见 `plugins/ensure-index.ts` 头注。 */
|
|
20
20
|
export declare const SESSION_INDEXES: readonly IndexSpec[];
|
|
21
21
|
/** Idempotent schema apply for this store's tables (the test calls this; production aggregation is separate). */
|
|
22
22
|
export declare function ensureSchema(pool: Pool | PoolClient): Promise<void>;
|
|
@@ -51,7 +51,7 @@ export declare class PgSessionStorage extends BaseSessionStorage {
|
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
53
53
|
* PostgreSQL-backed {@link SessionStore} — the PG twin of {@link TiDBSessionStore}. Maps the
|
|
54
|
-
* `SessionStore` surface (acquire / register /
|
|
54
|
+
* `SessionStore` surface (acquire / register / headOf / touch / release) onto {@link PgSessionStorage}.
|
|
55
55
|
*/
|
|
56
56
|
export declare class PgSessionStore implements SessionStore {
|
|
57
57
|
private readonly pool;
|
|
@@ -75,11 +75,11 @@ export declare class PgSessionStore implements SessionStore {
|
|
|
75
75
|
private load;
|
|
76
76
|
/** Idempotently create a session_meta row with an owner (core `SessionStore.register`). Existing owner is never overwritten. */
|
|
77
77
|
register(sessionId: string, owner: string | null): Promise<void>;
|
|
78
|
-
/** acquire 的建行(唯一一处;
|
|
78
|
+
/** acquire 的建行(唯一一处;[ref] 委派继承律,只住在 `createPlacedSessionRow`,本店交既有读写口 —— TiDB 孪生同名方法同注)。 */
|
|
79
79
|
private createOnAcquire;
|
|
80
|
-
/**
|
|
80
|
+
/** [ref] 委派继承律的认领口(TiDB 孪生 {@link TiDBSessionStore.claimOwnerless} 同义;句 = `CLAIM_OWNERLESS_PLACED_SQL`,规则在 `delegated-session-owner.ts`)。 */
|
|
81
81
|
claimOwnerless(sessionId: string, incarnation: string, owner: string): Promise<boolean>;
|
|
82
|
-
/** 若干来源行的会话面(属主 + 化身)一次读回(TiDB
|
|
82
|
+
/** 若干来源行的会话面(属主 + 化身)一次读回(TiDB 孪生同义;活住客的投影 `sessionFaceFromRow`,空化身 ⇒ 响亮拒)。 */
|
|
83
83
|
private sourceFaces;
|
|
84
84
|
/** The one session_meta INSERT. Existing owner is never overwritten.
|
|
85
85
|
* [ref]:`placement` 与行同一条 INSERT;`DO NOTHING` 臂 ⇒ 已存在行的元组永不被重写(first-write 不可变)。 */
|
|
@@ -92,21 +92,9 @@ export declare class PgSessionStore implements SessionStore {
|
|
|
92
92
|
olderThanMs?: number;
|
|
93
93
|
scope?: string;
|
|
94
94
|
}): Promise<PlacedSessionRow[]>;
|
|
95
|
-
/**
|
|
96
|
-
|
|
97
|
-
/** S-790:属主 + 化身一次读(TiDB 孪生 {@link TiDBSessionStore.sessionFaceOf} 同义;空化身 ⇒ 响亮拒)。 */
|
|
98
|
-
sessionFaceOf(sessionId: string): Promise<SessionFace | undefined>;
|
|
99
|
-
/** core `SessionStore.incarnationOf`:{@link sessionFaceOf} 的化身那一半(TiDB 孪生同义)。 */
|
|
100
|
-
incarnationOf(sessionId: string): Promise<string | undefined>;
|
|
95
|
+
/** core `SessionStore.headOf` —— 属主、住客、叶子一条 PK SELECT 一行(TiDB 孪生 {@link TiDBSessionStore.headOf} 同义:本店按 id 读一行的唯一读口)。 */
|
|
96
|
+
headOf(sessionId: string): Promise<SessionHead | undefined>;
|
|
101
97
|
touch(sessionId: string): Promise<void>;
|
|
102
|
-
/** E18 — the session's current leaf SessionTreeEntry.id (session_meta.leaf_id). @see TiDBSessionStore.getLeafId
|
|
103
|
-
* (single SELECT, no acquire/lock, cache-bypassing — safe to read mid-run for the resume-at turn-capture). */
|
|
104
|
-
getLeafId(sessionId: string): Promise<string | null>;
|
|
105
|
-
/** [ref] 六轮复审:owner+leafId 单行原子快照(@see TiDBSessionStore.getHead——混世代组合防)。 */
|
|
106
|
-
getHead(sessionId: string): Promise<{
|
|
107
|
-
owner: string | null;
|
|
108
|
-
leafId: string | null;
|
|
109
|
-
} | undefined>;
|
|
110
98
|
/** Durable history is retained; just drop any in-flight cache entry.
|
|
111
99
|
* 🔴 [ref] 义务 4:**placed 会话例外 —— release 是真删除**(理由/ABA 条件删/失败上浮三条读法逐字见
|
|
112
100
|
* {@link TiDBSessionStore.release},本腿是它的 PG 孪生)。 */
|
|
@@ -149,7 +137,7 @@ export declare class PgSessionStore implements SessionStore {
|
|
|
149
137
|
recordInstructionSource(sessionId: string, expected: InstructionSourceBaseline, source: string | null): Promise<void>;
|
|
150
138
|
/** @see TiDBSessionStore.fork — copy the WHOLE durable history (full session_event log, compaction floor bypassed,
|
|
151
139
|
* NOT wake's bounded tail) to a NEW uuidv7 session, re-seq dense from 0, ids/parents verbatim, owner=caller. */
|
|
152
|
-
fork(sourceId: string, owner
|
|
140
|
+
fork(sourceId: string, owner?: string | null): Promise<string | null>;
|
|
153
141
|
/** @see TiDBSessionStore.exportEntries — 2c session-sync EXPORT: the FULL durable session_event log (compaction floor
|
|
154
142
|
* bypassed, NOT wake's tail) as `SessionTreeEntry[]`, oldest-first, verbatim. null if the source is unknown. */
|
|
155
143
|
exportEntries(sessionId: string): Promise<SessionTreeEntry[] | null>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { emitLeafAdvance } from "../session-leaf-bus.js";
|
|
2
|
-
import { BaseSessionStorage, StoredSession, SessionError, uuidv7, validateEntriesForImport, leafIdAfterEntry, } from "@sema-agent/core";
|
|
2
|
+
import { BaseSessionStorage, StoredSession, SessionError, uuidv7, validateEntriesForImport, leafIdAfterEntry, admitSessionPlacement, } from "@sema-agent/core";
|
|
3
3
|
import { SESSION_TITLE_MAX_CHARS } from "../session-row.js";
|
|
4
4
|
import { escapeLike } from "./sql-escape.js";
|
|
5
5
|
import { decodeSessionEventPayload } from "./session-event-payload.js";
|
|
@@ -7,16 +7,18 @@ import { scanEpochAnchor } from "./session-epoch-anchor.js";
|
|
|
7
7
|
import { jsonColumnText } from "./sql-json-column.js";
|
|
8
8
|
import { pgSafeJsonStringify } from "./pg-safe-json.js";
|
|
9
9
|
import { toIso } from "./sql-row-helpers.js";
|
|
10
|
-
import {
|
|
10
|
+
import { placementCapabilityOf, LIST_PLACED_BATCH, PLACEMENT_SELECT_COLUMNS, placedRowFromRow, placementFromRow, placementInsertValues, SQL_SESSION_PLACEMENTS, } from "./session-placement.js";
|
|
11
11
|
import { contentForkRelation, fastForwardSharedContentDiverged } from "../session-sync-content.js";
|
|
12
12
|
import { CLAIM_OWNERLESS_PLACED_SQL, claimOwnerlessParams, createPlacedSessionRow } from "../delegated-session-owner.js";
|
|
13
13
|
import { createLogger } from "../observability/logger.js";
|
|
14
|
-
import { classifySyncRelationshipByIds, SyncConflictError, stagingIdFor, STAGING_ID_MARKER, STAGING_GC_GRACE_MS, } from "../session-sync-kernel.js";
|
|
14
|
+
import { classifySyncRelationshipByIds, SyncConflictError, StagedCommitOwnerChangedError, stagingIdFor, STAGING_ID_MARKER, STAGING_GC_GRACE_MS, } from "../session-sync-kernel.js";
|
|
15
15
|
import { isPgUniqueViolation } from "./sql-errors.js";
|
|
16
16
|
import { BASELINE_IO_DEADLINE_MS, decodeInstructionSource, encodeInstructionSource } from "../instruction-source-notice.js";
|
|
17
17
|
import { MANAGED_RETENTION } from "./retention-store-sql.js";
|
|
18
|
-
import { SESSION_META_INCARNATION_COLUMN, carriedIncarnation, incarnationFromCell, mintSessionIncarnation, sessionFaceFromRow } from "../session-incarnation.js";
|
|
18
|
+
import { SESSION_META_INCARNATION_COLUMN, carriedIncarnation, headFromRow, incarnationFromCell, mintSessionIncarnation, sessionFaceFromRow } from "../session-incarnation.js";
|
|
19
19
|
import { ensureIndexes } from "./ensure-index.js";
|
|
20
|
+
import { assertFirstOwnerWritable, forkOwnerOf } from "./session-ownership.js";
|
|
21
|
+
import { forkCopyOf, forkPointOf } from "./session-fork.js";
|
|
20
22
|
const isDupKey = isPgUniqueViolation;
|
|
21
23
|
function baselineQuery(text, values) {
|
|
22
24
|
return { text, values, query_timeout: BASELINE_IO_DEADLINE_MS };
|
|
@@ -193,7 +195,7 @@ export class PgSessionStore {
|
|
|
193
195
|
const inflight = this.pending.get(sessionId);
|
|
194
196
|
if (inflight) {
|
|
195
197
|
const acquired = await inflight;
|
|
196
|
-
|
|
198
|
+
admitSessionPlacement(sessionId, await this.placementOf(sessionId), opts ?? {}, placementCapabilityOf(this));
|
|
197
199
|
return acquired;
|
|
198
200
|
}
|
|
199
201
|
const p = this.load(sessionId, false, opts).finally(() => this.pending.delete(sessionId));
|
|
@@ -203,7 +205,7 @@ export class PgSessionStore {
|
|
|
203
205
|
async load(sessionId, requireExisting = false, opts) {
|
|
204
206
|
let storage = await PgSessionStorage.wake(this.pool, sessionId);
|
|
205
207
|
if (storage) {
|
|
206
|
-
|
|
208
|
+
admitSessionPlacement(sessionId, (await storage.getMetadata()).placement, opts ?? {}, placementCapabilityOf(this));
|
|
207
209
|
return { session: new StoredSession(storage), sessionId };
|
|
208
210
|
}
|
|
209
211
|
if (requireExisting)
|
|
@@ -213,10 +215,15 @@ export class PgSessionStore {
|
|
|
213
215
|
if (!storage) {
|
|
214
216
|
throw new SessionError("storage", `session ${sessionId} vanished between register and wake (a concurrent purge won the race) — retry`);
|
|
215
217
|
}
|
|
216
|
-
|
|
218
|
+
admitSessionPlacement(sessionId, (await storage.getMetadata()).placement, opts ?? {}, placementCapabilityOf(this));
|
|
217
219
|
return { session: new StoredSession(storage), sessionId };
|
|
218
220
|
}
|
|
219
221
|
async register(sessionId, owner) {
|
|
222
|
+
if (typeof owner !== "string" && owner !== null) {
|
|
223
|
+
if ((await this.headOf(sessionId)) !== undefined)
|
|
224
|
+
return;
|
|
225
|
+
assertFirstOwnerWritable(sessionId, owner);
|
|
226
|
+
}
|
|
220
227
|
await this.insertMeta(sessionId, owner, undefined);
|
|
221
228
|
}
|
|
222
229
|
async createOnAcquire(sessionId, placement) {
|
|
@@ -225,7 +232,7 @@ export class PgSessionStore {
|
|
|
225
232
|
await createPlacedSessionRow(sessionId, placement, {
|
|
226
233
|
readFaces: (ids) => this.sourceFaces(ids),
|
|
227
234
|
insert: (owner) => this.insertMeta(sessionId, owner, placement),
|
|
228
|
-
readOwner: () => this.
|
|
235
|
+
readOwner: async () => (await this.headOf(sessionId))?.owner,
|
|
229
236
|
}, this.log);
|
|
230
237
|
}
|
|
231
238
|
async claimOwnerless(sessionId, incarnation, owner) {
|
|
@@ -260,20 +267,12 @@ export class PgSessionStore {
|
|
|
260
267
|
const res = await this.pool.query(sql, params);
|
|
261
268
|
return res.rows.map((r) => placedRowFromRow(String(r.session_id), r, r.updated_at instanceof Date ? r.updated_at.getTime() : undefined));
|
|
262
269
|
}
|
|
263
|
-
async
|
|
264
|
-
const res = await this.pool.query("SELECT owner FROM session_meta WHERE session_id = $1", [sessionId]);
|
|
270
|
+
async headOf(sessionId) {
|
|
271
|
+
const res = await this.pool.query("SELECT owner, incarnation, leaf_id FROM session_meta WHERE session_id = $1", [sessionId]);
|
|
265
272
|
if (res.rows.length === 0)
|
|
266
273
|
return undefined;
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
async sessionFaceOf(sessionId) {
|
|
270
|
-
const res = await this.pool.query("SELECT owner, incarnation FROM session_meta WHERE session_id = $1", [sessionId]);
|
|
271
|
-
if (res.rows.length === 0)
|
|
272
|
-
return undefined;
|
|
273
|
-
return sessionFaceFromRow(sessionId, res.rows[0].owner ?? null, res.rows[0].incarnation);
|
|
274
|
-
}
|
|
275
|
-
async incarnationOf(sessionId) {
|
|
276
|
-
return (await this.sessionFaceOf(sessionId))?.incarnation;
|
|
274
|
+
const r = res.rows[0];
|
|
275
|
+
return headFromRow(sessionId, r.owner ?? null, r.incarnation, r.leaf_id ?? null);
|
|
277
276
|
}
|
|
278
277
|
async touch(sessionId) {
|
|
279
278
|
await this.pool.query("UPDATE session_meta SET updated_at = $1 WHERE session_id = $2", [
|
|
@@ -281,16 +280,6 @@ export class PgSessionStore {
|
|
|
281
280
|
sessionId,
|
|
282
281
|
]);
|
|
283
282
|
}
|
|
284
|
-
async getLeafId(sessionId) {
|
|
285
|
-
const r = await this.pool.query("SELECT leaf_id FROM session_meta WHERE session_id = $1", [sessionId]);
|
|
286
|
-
return r.rows[0] ? (r.rows[0].leaf_id ?? null) : null;
|
|
287
|
-
}
|
|
288
|
-
async getHead(sessionId) {
|
|
289
|
-
const r = await this.pool.query("SELECT owner, leaf_id FROM session_meta WHERE session_id = $1", [sessionId]);
|
|
290
|
-
if (r.rows.length === 0)
|
|
291
|
-
return undefined;
|
|
292
|
-
return { owner: r.rows[0].owner ?? null, leafId: r.rows[0].leaf_id ?? null };
|
|
293
|
-
}
|
|
294
283
|
async release(sessionId) {
|
|
295
284
|
this.pending.delete(sessionId);
|
|
296
285
|
const placed = await this.placementOf(sessionId);
|
|
@@ -429,12 +418,17 @@ export class PgSessionStore {
|
|
|
429
418
|
}
|
|
430
419
|
}
|
|
431
420
|
async fork(sourceId, owner) {
|
|
432
|
-
const metaRes = await this.pool.query("SELECT leaf_id FROM session_meta WHERE session_id = $1", [sourceId]);
|
|
421
|
+
const metaRes = await this.pool.query("SELECT leaf_id, owner FROM session_meta WHERE session_id = $1", [sourceId]);
|
|
433
422
|
if (!metaRes.rows[0])
|
|
434
423
|
return null;
|
|
435
424
|
const leafId = metaRes.rows[0].leaf_id ?? null;
|
|
425
|
+
const forkOwner = forkOwnerOf(owner, metaRes.rows[0].owner ?? null) ?? null;
|
|
426
|
+
const storage = await PgSessionStorage.wake(this.pool, sourceId);
|
|
427
|
+
if (storage === null)
|
|
428
|
+
return null;
|
|
429
|
+
const point = await forkPointOf(storage);
|
|
436
430
|
const eventRes = await this.pool.query(`SELECT entry_id, parent_id, type, ${jsonColumnText("pg", "payload")}, ts FROM session_event WHERE session_id = $1 ORDER BY seq ASC`, [sourceId]);
|
|
437
|
-
const rows = eventRes.rows;
|
|
431
|
+
const { rows, leafId: forkLeafId } = forkCopyOf(eventRes.rows, leafId, point);
|
|
438
432
|
const newId = uuidv7();
|
|
439
433
|
const now = new Date();
|
|
440
434
|
const conn = await this.pool.connect();
|
|
@@ -445,7 +439,7 @@ export class PgSessionStore {
|
|
|
445
439
|
const payload = String(r.payload);
|
|
446
440
|
await conn.query("INSERT INTO session_event (session_id, seq, entry_id, parent_id, type, payload, ts) VALUES ($1,$2,$3,$4,$5,$6::jsonb,$7)", [newId, seq, r.entry_id, r.parent_id ?? null, r.type, payload, r.ts]);
|
|
447
441
|
}
|
|
448
|
-
await conn.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation) VALUES ($1,$2,$3,$4,$5,$6,$7)", [newId,
|
|
442
|
+
await conn.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation) VALUES ($1,$2,$3,$4,$5,$6,$7)", [newId, forkOwner, now, now, forkLeafId, rows.length, mintSessionIncarnation()]);
|
|
449
443
|
await conn.query("COMMIT");
|
|
450
444
|
return newId;
|
|
451
445
|
}
|
|
@@ -642,6 +636,23 @@ class PgStagingHandle {
|
|
|
642
636
|
const conn = await this.pool.connect();
|
|
643
637
|
try {
|
|
644
638
|
await conn.query("BEGIN");
|
|
639
|
+
const now = new Date();
|
|
640
|
+
const minted = mintSessionIncarnation();
|
|
641
|
+
if (opts.expectedOwner !== undefined) {
|
|
642
|
+
const cas = await conn.query("UPDATE session_meta SET leaf_seq = leaf_seq + 1 WHERE session_id = $1 AND owner IS NOT DISTINCT FROM $2", [this.realSessionId, opts.expectedOwner]);
|
|
643
|
+
if (cas.rowCount !== 1) {
|
|
644
|
+
await conn.query("ROLLBACK");
|
|
645
|
+
throw new StagedCommitOwnerChangedError(this.realSessionId);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
else {
|
|
649
|
+
await conn.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation) VALUES ($1,$2,$3,$4,NULL,0,$5) ON CONFLICT (session_id) DO NOTHING", [this.realSessionId, owner, now, now, minted]);
|
|
650
|
+
const claimed = await conn.query("SELECT incarnation FROM session_meta WHERE session_id = $1 FOR UPDATE", [this.realSessionId]);
|
|
651
|
+
if (String(claimed.rows[0]?.incarnation) !== minted) {
|
|
652
|
+
await conn.query("ROLLBACK");
|
|
653
|
+
throw new StagedCommitOwnerChangedError(this.realSessionId);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
645
656
|
const staged = await conn.query("SELECT entry_id FROM session_event WHERE session_id = $1 ORDER BY seq ASC FOR UPDATE", [this.stagingId]);
|
|
646
657
|
const dst = await conn.query("SELECT entry_id FROM session_event WHERE session_id = $1 ORDER BY seq ASC FOR UPDATE", [this.realSessionId]);
|
|
647
658
|
const dstMeta = await conn.query("SELECT placement_kind FROM session_meta WHERE session_id = $1", [this.realSessionId]);
|
|
@@ -650,15 +661,15 @@ class PgStagingHandle {
|
|
|
650
661
|
throw new SessionError("placement_refused", `session ${this.realSessionId} belongs to the ${dstMeta.rows[0].placement_kind} placement partition — a session-sync staged commit cannot overwrite a delegation transcript`);
|
|
651
662
|
}
|
|
652
663
|
const srcIds = staged.rows.map((r) => String(r.entry_id));
|
|
653
|
-
const dstIds =
|
|
664
|
+
const dstIds = opts.expectedOwner !== undefined ? dst.rows.map((r) => String(r.entry_id)) : null;
|
|
654
665
|
const rel = classifySyncRelationshipByIds(srcIds, dstIds);
|
|
655
|
-
if ((rel.relation === "fork" || rel.relation === "stale") && opts
|
|
666
|
+
if ((rel.relation === "fork" || rel.relation === "stale") && opts.resolution !== "overwrite-dst") {
|
|
656
667
|
await conn.query("ROLLBACK");
|
|
657
668
|
throw new SyncConflictError(this.realSessionId, rel);
|
|
658
669
|
}
|
|
659
|
-
if (rel.relation === "fast_forward" && opts
|
|
660
|
-
const dstFull = await conn.query(`SELECT seq, ${jsonColumnText("pg", "payload")} FROM session_event WHERE session_id = $1 ORDER BY seq ASC`, [this.realSessionId]);
|
|
661
|
-
const stagedFull = await conn.query(`SELECT entry_id, ${jsonColumnText("pg", "payload")} FROM session_event WHERE session_id = $1 ORDER BY seq ASC`, [this.stagingId]);
|
|
670
|
+
if (rel.relation === "fast_forward" && opts.resolution !== "overwrite-dst" && dstIds !== null && dstIds.length > 0) {
|
|
671
|
+
const dstFull = await conn.query(`SELECT seq, ${jsonColumnText("pg", "payload")} FROM session_event WHERE session_id = $1 ORDER BY seq ASC FOR UPDATE`, [this.realSessionId]);
|
|
672
|
+
const stagedFull = await conn.query(`SELECT entry_id, ${jsonColumnText("pg", "payload")} FROM session_event WHERE session_id = $1 ORDER BY seq ASC FOR UPDATE`, [this.stagingId]);
|
|
662
673
|
const dstSet = new Set(dstIds);
|
|
663
674
|
const dstEntries = dstFull.rows.map((r) => decodeSessionEventPayload(r.payload, this.realSessionId, r.seq));
|
|
664
675
|
const stagedShared = stagedFull.rows.filter((r) => dstSet.has(String(r.entry_id))).map((r) => decodeSessionEventPayload(r.payload, this.stagingId, r.entry_id));
|
|
@@ -670,13 +681,11 @@ class PgStagingHandle {
|
|
|
670
681
|
}
|
|
671
682
|
await conn.query("DELETE FROM session_event WHERE session_id = $1", [this.realSessionId]);
|
|
672
683
|
await conn.query("UPDATE session_event SET session_id = $1 WHERE session_id = $2", [this.realSessionId, this.stagingId]);
|
|
673
|
-
const lastRes = await conn.query(`SELECT ${jsonColumnText("pg", "payload")} FROM session_event WHERE session_id = $1 ORDER BY seq DESC LIMIT 1`, [this.realSessionId]);
|
|
684
|
+
const lastRes = await conn.query(`SELECT ${jsonColumnText("pg", "payload")} FROM session_event WHERE session_id = $1 ORDER BY seq DESC LIMIT 1 FOR UPDATE`, [this.realSessionId]);
|
|
674
685
|
let leafId = null;
|
|
675
686
|
if (lastRes.rows[0])
|
|
676
687
|
leafId = leafIdAfterEntry(decodeSessionEventPayload(lastRes.rows[0].payload, this.realSessionId, "last staged row"));
|
|
677
|
-
|
|
678
|
-
await conn.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation) VALUES ($1,$2,$3,$4,$5,$6,$7) " +
|
|
679
|
-
"ON CONFLICT (session_id) DO UPDATE SET owner = EXCLUDED.owner, updated_at = EXCLUDED.updated_at, leaf_id = EXCLUDED.leaf_id, leaf_seq = EXCLUDED.leaf_seq", [this.realSessionId, owner, now, now, leafId, srcIds.length, mintSessionIncarnation()]);
|
|
688
|
+
await conn.query("UPDATE session_meta SET owner = $1, updated_at = $2, leaf_id = $3, leaf_seq = $4 WHERE session_id = $5", [owner, now, leafId, srcIds.length, this.realSessionId]);
|
|
680
689
|
const afterMeta = await conn.query("SELECT placement_kind, owner, incarnation FROM session_meta WHERE session_id = $1", [this.realSessionId]);
|
|
681
690
|
if (afterMeta.rows[0]?.placement_kind != null) {
|
|
682
691
|
await conn.query("ROLLBACK");
|