@sema-agent/server 7.145.0 → 7.147.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 +876 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/USAGE.md +374 -99
- package/deploy/facts/CHANGES.json +25 -0
- package/deploy/facts/README.md +57 -0
- package/deploy/facts/lanes.json +61 -0
- package/deploy/facts/lanes.schema.json +57 -0
- package/deploy/facts/sandbox-rbac.json +113 -0
- package/deploy/facts/sandbox-rbac.schema.json +74 -0
- package/deploy/facts/sealed-key.json +9 -0
- package/deploy/facts/sealed-key.schema.json +52 -0
- package/deploy/sema-up/README.md +2 -2
- package/deploy/sema-up/chart/templates/_helpers.tpl +41 -0
- package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +47 -7
- package/deploy/sema-up/chart/templates/server-v4pro.yaml +9 -4
- package/deploy/sema-up/chart/templates/server.yaml +9 -4
- package/deploy/sema-up/chart/values.yaml +8 -1
- package/deploy/sema-up/cluster-up.sh +2 -1
- package/deploy/sema-up/lib/flagshape.sh +4 -0
- package/deploy/sema-up/lib/lanes.json +2 -5
- package/deploy/sema-up/sema-up.sh +12 -9
- package/deploy/sema-up/stack/compose.yaml +5 -4
- package/deploy/sema-up/stack/sema-stack-cluster.yaml +34 -17
- package/dist/adoption/import-limits.d.ts +6 -0
- package/dist/adoption/import-limits.js +7 -0
- package/dist/adoption/import.d.ts +62 -0
- package/dist/adoption/import.js +107 -0
- package/dist/adoption/wire.d.ts +52 -0
- package/dist/adoption/wire.js +9 -0
- package/dist/approval-ask-machine.d.ts +2 -2
- package/dist/approval-card.d.ts +7 -16
- package/dist/approval-card.js +0 -4
- package/dist/approval-gate-currency.d.ts +1 -1
- package/dist/approval-gate-currency.js +1 -1
- package/dist/approval-reconciler.d.ts +1 -1
- package/dist/approval.d.ts +3 -3
- package/dist/audit.js +2 -2
- package/dist/bench/s1/live-deps.js +3 -1
- package/dist/boot/crash-last.js +2 -1
- package/dist/boot/deployment-mods.d.ts +11 -1
- package/dist/boot/deployment-mods.js +15 -2
- package/dist/boot/device-lane.js +7 -5
- package/dist/boot/engine-lease.d.ts +195 -22
- package/dist/boot/engine-lease.js +146 -20
- package/dist/boot/execution-env.d.ts +5 -0
- package/dist/boot/execution-env.js +57 -33
- package/dist/boot/execution-lane.d.ts +37 -9
- package/dist/boot/execution-lane.js +74 -20
- package/dist/boot/exit-boundary.d.ts +50 -0
- package/dist/boot/exit-boundary.js +32 -0
- package/dist/boot/exit-last.d.ts +20 -4
- package/dist/boot/exit-last.js +23 -5
- package/dist/boot/governance-seams.d.ts +2 -2
- package/dist/boot/governance-seams.js +1 -0
- package/dist/boot/leader.js +1 -1
- package/dist/boot/memory-boundary.d.ts +11 -11
- package/dist/boot/memory-boundary.js +3 -3
- package/dist/boot/memory-consolidation.d.ts +42 -41
- package/dist/boot/memory-consolidation.js +26 -28
- package/dist/boot/org-memory.js +3 -2
- package/dist/boot/parked-revive-gate.d.ts +1 -1
- package/dist/boot/parked-revive-gate.js +1 -1
- package/dist/boot/reapers.d.ts +12 -4
- package/dist/boot/reapers.js +40 -16
- package/dist/boot/resolve-spec.d.ts +2 -0
- package/dist/boot/resolve-spec.js +126 -123
- package/dist/boot/retention-lane.d.ts +4 -42
- package/dist/boot/runner-deps.js +1 -1
- package/dist/boot/session-faces.d.ts +1 -0
- package/dist/boot/session-faces.js +11 -3
- package/dist/boot/shutdown.js +27 -9
- package/dist/boot/stage-01-config.d.ts +1 -1
- package/dist/boot/stage-01-config.js +7 -3
- package/dist/boot/stage-02-web-admin-pull.d.ts +1 -1
- package/dist/boot/stage-02-web-admin-pull.js +10 -8
- package/dist/boot/stage-03-store-backend.d.ts +2 -1
- package/dist/boot/stage-03-store-backend.js +2 -2
- package/dist/boot/stage-04-stores.d.ts +2 -1
- package/dist/boot/stage-05-execution-env.d.ts +4 -1
- package/dist/boot/stage-05-execution-env.js +6 -6
- package/dist/boot/stage-06-runners.d.ts +5 -2
- package/dist/boot/stage-06-runners.js +12 -13
- package/dist/boot/stage-07-capability-layer.d.ts +5 -2
- package/dist/boot/stage-07-capability-layer.js +7 -7
- package/dist/boot/stage-08-reapers.d.ts +5 -2
- package/dist/boot/stage-08-reapers.js +3 -3
- package/dist/boot/stage-09-leader.d.ts +6 -2
- package/dist/boot/stage-10-http-server.d.ts +6 -2
- package/dist/boot/stage-10-http-server.js +5 -3
- package/dist/boot/stores.d.ts +10 -0
- package/dist/boot/stores.js +53 -26
- package/dist/boot/web-admin.js +3 -3
- package/dist/boot/workflow-orchestration.d.ts +1 -1
- package/dist/boot/workflow-orchestration.js +2 -1
- package/dist/capabilities/center-plugins.d.ts +2 -0
- package/dist/capabilities/center-plugins.js +6 -2
- package/dist/capabilities/composite-members.d.ts +4 -1
- package/dist/capabilities/composite-members.js +4 -1
- package/dist/capabilities/execution-lane.d.ts +7 -7
- package/dist/capabilities/execution-lane.js +2 -2
- package/dist/capabilities/hands-lane.d.ts +1 -1
- package/dist/capabilities/scenarios.d.ts +34 -20
- package/dist/capabilities/scenarios.js +13 -9
- package/dist/capabilities/settings-contract.d.ts +29 -0
- package/dist/capabilities/settings-contract.js +14 -0
- package/dist/capabilities/skills.d.ts +5 -5
- package/dist/capability-facts.d.ts +56 -1
- package/dist/capability-facts.js +59 -8
- package/dist/center-credential-renewer.js +3 -2
- package/dist/checkpoint-ctx-seal-m28.d.ts +1 -1
- package/dist/config-catalog.js +19 -9
- package/dist/config-invariants.d.ts +18 -3
- package/dist/config-invariants.js +36 -19
- package/dist/config-lkg.d.ts +1 -1
- package/dist/config-lkg.js +5 -6
- package/dist/config-provider.d.ts +2 -2
- package/dist/config-provider.js +2 -0
- package/dist/config-types.d.ts +228 -185
- package/dist/config.d.ts +74 -6
- package/dist/config.js +146 -64
- package/dist/degenerate-instrument.js +1 -1
- package/dist/deleted-session-ids.d.ts +19 -0
- package/dist/deleted-session-ids.js +26 -0
- package/dist/deploy-facts.d.ts +155 -0
- package/dist/deploy-facts.js +95 -0
- package/dist/deployment-shape.d.ts +21 -12
- package/dist/deployment-shape.js +24 -6
- package/dist/device-ws-hub.d.ts +48 -3
- package/dist/device-ws-hub.js +48 -2
- package/dist/env-facts.d.ts +7 -5
- package/dist/env-facts.js +10 -6
- package/dist/error-code-family.d.ts +35 -0
- package/dist/error-code-family.js +26 -0
- package/dist/execution-config-root.d.ts +5 -5
- package/dist/execution-config-root.js +5 -5
- package/dist/execution-env-purpose.d.ts +14 -0
- package/dist/execution-env-purpose.js +29 -0
- package/dist/execution-lane-caps.d.ts +49 -86
- package/dist/execution-lane-caps.js +52 -62
- package/dist/execution-placement.d.ts +18 -2
- package/dist/execution-placement.js +19 -0
- package/dist/file-history-disclosure.d.ts +2 -2
- package/dist/fleet/drain-entry.d.ts +46 -3
- package/dist/fleet/drain-entry.js +6 -0
- package/dist/fleet/fleet-bus.d.ts +2 -1
- package/dist/fleet/serving-lease.js +3 -4
- package/dist/history-anchor.d.ts +22 -0
- package/dist/history-anchor.js +14 -0
- package/dist/hooks/branch-transcript.d.ts +1 -1
- package/dist/hooks/branch-transcript.js +1 -0
- package/dist/hooks/classic-stations.d.ts +25 -8
- package/dist/hooks/classic-stations.js +29 -3
- package/dist/hooks/command-spawn.d.ts +1 -4
- package/dist/hooks/command-spawn.js +0 -11
- package/dist/hooks/hook-runner.d.ts +85 -24
- package/dist/hooks/hook-runner.js +326 -109
- package/dist/hooks/hook-source.d.ts +14 -2
- package/dist/hooks/hook-source.js +8 -0
- package/dist/http/active-run-conflict.d.ts +1 -1
- package/dist/http/active-run-conflict.js +1 -1
- package/dist/http/admission.js +11 -1
- package/dist/http/approval-sweeps.d.ts +2 -5
- package/dist/http/approval-sweeps.js +3 -2
- package/dist/http/checkpoint-ctx.d.ts +76 -71
- package/dist/http/checkpoint-ctx.js +40 -40
- package/dist/http/claim-latency.d.ts +10 -0
- package/dist/http/claim-latency.js +12 -0
- package/dist/http/dispatch.js +19 -3
- package/dist/http/durable-run-leg.d.ts +5 -0
- package/dist/http/durable-run-leg.js +3 -1
- package/dist/http/execution-context.d.ts +7 -1
- package/dist/http/execution-context.js +13 -0
- package/dist/http/health-self-report.d.ts +6 -2
- package/dist/http/health-self-report.js +3 -2
- package/dist/http/memory-compliance-failure-reply.d.ts +2 -2
- package/dist/http/memory-compliance-failure-reply.js +5 -15
- package/dist/http/memory-control-plane-corrupt-reply.d.ts +25 -0
- package/dist/http/memory-control-plane-corrupt-reply.js +19 -0
- package/dist/http/memory-store-refusal-reply.d.ts +3 -3
- package/dist/http/memory-store-refusal-reply.js +3 -0
- package/dist/http/owner-access.d.ts +11 -0
- package/dist/http/owner-access.js +7 -4
- package/dist/http/principal-gate.d.ts +28 -16
- package/dist/http/principal-gate.js +10 -19
- package/dist/http/request-guards.d.ts +2 -2
- package/dist/http/request-guards.js +5 -5
- package/dist/http/resume-legs.d.ts +29 -10
- package/dist/http/resume-legs.js +94 -56
- package/dist/http/rewind-files-reply.d.ts +34 -0
- package/dist/http/rewind-files-reply.js +36 -0
- package/dist/http/route-ctx.d.ts +43 -26
- package/dist/http/route-table.js +4 -0
- package/dist/http/routes/adoption.d.ts +11 -0
- package/dist/http/routes/adoption.js +80 -6
- package/dist/http/routes/agents-roster.js +1 -0
- package/dist/http/routes/approvals-assistant.js +7 -26
- package/dist/http/routes/background.js +1 -0
- package/dist/http/routes/capabilities.js +12 -3
- package/dist/http/routes/devices.js +1 -1
- package/dist/http/routes/diagnostics.d.ts +9 -14
- package/dist/http/routes/diagnostics.js +10 -20
- package/dist/http/routes/engine.d.ts +31 -0
- package/dist/http/routes/engine.js +81 -0
- package/dist/http/routes/memory-bundle.js +3 -0
- package/dist/http/routes/memory-consolidation.js +3 -5
- package/dist/http/routes/memory-origin.d.ts +2 -2
- package/dist/http/routes/memory-origin.js +2 -4
- package/dist/http/routes/memory-policy.js +5 -1
- package/dist/http/routes/notify-wake.js +2 -1
- package/dist/http/routes/observability.js +3 -6
- package/dist/http/routes/projects.d.ts +70 -0
- package/dist/http/routes/projects.js +86 -0
- package/dist/http/routes/rules.js +30 -2
- package/dist/http/routes/runs.js +15 -11
- package/dist/http/routes/session-sync.js +39 -35
- package/dist/http/routes/sessions-list.d.ts +1 -1
- package/dist/http/routes/sessions.d.ts +7 -1
- package/dist/http/routes/sessions.js +108 -2
- package/dist/http/routes/tasks.js +32 -8
- package/dist/http/routes/trace-usage.js +1 -1
- package/dist/http/run-concurrency-cap.d.ts +86 -0
- package/dist/http/run-concurrency-cap.js +54 -0
- package/dist/http/server.d.ts +31 -14
- package/dist/http/server.js +29 -3
- package/dist/http/stored-body-replay.d.ts +12 -7
- package/dist/http/stored-body-replay.js +28 -5
- package/dist/http/wire-types.d.ts +27 -13
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/instruction-source-notice.d.ts +1 -1
- package/dist/json-body-limit.d.ts +8 -0
- package/dist/json-body-limit.js +2 -0
- package/dist/leader/diffout.d.ts +1 -1
- package/dist/local-data-root.d.ts +2 -2
- package/dist/memory-embedder-reembed.d.ts +38 -0
- package/dist/memory-embedder-reembed.js +20 -0
- package/dist/memory-incident-sink.d.ts +0 -28
- package/dist/memory-incident-sink.js +5 -0
- package/dist/memory-operator-faces.d.ts +9 -4
- package/dist/memory-scope-stores.d.ts +9 -7
- package/dist/memory-scope-stores.js +3 -14
- package/dist/memory-scope.d.ts +38 -2
- package/dist/memory-scope.js +33 -2
- package/dist/memory-store-refusal.d.ts +17 -4
- package/dist/memory-store-refusal.js +1 -1
- package/dist/memory-sync-client.d.ts +2 -0
- package/dist/memory-sync-client.js +13 -11
- package/dist/memory-sync.js +1 -1
- package/dist/model-compat.d.ts +35 -21
- package/dist/model-compat.js +49 -21
- package/dist/model-entry-refusal.d.ts +11 -1
- package/dist/model-entry-refusal.js +7 -0
- package/dist/namespaced-rule-name.d.ts +18 -1
- package/dist/namespaced-rule-name.js +12 -0
- package/dist/net-address.d.ts +53 -0
- package/dist/net-address.js +111 -0
- package/dist/observability/fail-open.d.ts +29 -17
- package/dist/observability/fail-open.js +29 -17
- package/dist/observability/metrics.d.ts +2 -0
- package/dist/observability/metrics.js +2 -0
- package/dist/observability/secret-env-scrub.d.ts +30 -4
- package/dist/observability/secret-env-scrub.js +15 -3
- package/dist/orchestration/workflow-agent-session-index.d.ts +7 -12
- package/dist/orchestration/workflow-agent-session-index.js +1 -6
- package/dist/orchestration/workflow-completion-inbox.d.ts +26 -6
- package/dist/orchestration/workflow-completion-inbox.js +63 -45
- package/dist/orchestration/workflow-notify-journal.js +2 -3
- package/dist/org-delivery-gate.js +1 -1
- package/dist/org-memory-admission.d.ts +5 -2
- package/dist/org-memory-admission.js +25 -10
- package/dist/parked-decide.d.ts +33 -26
- package/dist/parked-decide.js +36 -44
- package/dist/peer-directory.d.ts +6 -12
- package/dist/peer-directory.js +5 -1
- package/dist/plugins/approval-ask-store-file.d.ts +10 -2
- package/dist/plugins/approval-ask-store-file.js +37 -4
- package/dist/plugins/approval-ask-store-memory.d.ts +5 -3
- package/dist/plugins/approval-ask-store-memory.js +11 -2
- package/dist/plugins/approval-ask-store-sql.d.ts +3 -0
- package/dist/plugins/approval-ask-store-sql.js +3 -1
- package/dist/plugins/blob-backend.d.ts +38 -195
- package/dist/plugins/blob-backend.js +33 -229
- package/dist/plugins/caching-session-store.d.ts +12 -5
- package/dist/plugins/caching-session-store.js +3 -0
- package/dist/plugins/checkpoint-index-columns.d.ts +1 -1
- package/dist/plugins/checkpoint-index-columns.js +3 -2
- package/dist/plugins/checkpoint-store-face.d.ts +426 -0
- package/dist/plugins/checkpoint-store-face.js +152 -0
- package/dist/plugins/device-store-sql.d.ts +2 -2
- package/dist/plugins/device-store-sql.js +5 -10
- package/dist/plugins/execution-lane-rename-m40.d.ts +20 -0
- package/dist/plugins/execution-lane-rename-m40.js +10 -0
- package/dist/plugins/execution-placement-sql.d.ts +63 -88
- package/dist/plugins/execution-placement-sql.js +94 -54
- package/dist/plugins/file-run-store.d.ts +26 -18
- package/dist/plugins/file-run-store.js +64 -75
- package/dist/plugins/fork-routing-session-store.d.ts +6 -2
- package/dist/plugins/fork-routing-session-store.js +2 -2
- package/dist/plugins/imported-layer-root-retirement.d.ts +22 -11
- package/dist/plugins/imported-layer-root-retirement.js +4 -8
- package/dist/plugins/inbox-payload-column.d.ts +14 -0
- package/dist/plugins/inbox-payload-column.js +13 -0
- package/dist/plugins/local-checkpoint-store.d.ts +7 -2
- package/dist/plugins/local-checkpoint-store.js +13 -8
- package/dist/plugins/local-data-dir-modes-m39.d.ts +48 -0
- package/dist/plugins/local-data-dir-modes-m39.js +182 -0
- package/dist/plugins/local-engine-high-water.d.ts +11 -0
- package/dist/plugins/local-engine-high-water.js +59 -0
- package/dist/plugins/local-session-store.d.ts +119 -30
- package/dist/plugins/local-session-store.js +156 -53
- package/dist/plugins/local-task-attachment-store.d.ts +2 -0
- package/dist/plugins/local-task-attachment-store.js +10 -11
- package/dist/plugins/memory-embedder-fingerprint.d.ts +33 -10
- package/dist/plugins/memory-embedder-fingerprint.js +52 -12
- package/dist/plugins/memory-embedder.d.ts +1 -1
- package/dist/plugins/memory-embedder.js +2 -2
- package/dist/plugins/memory-engine-pg.d.ts +30 -6
- package/dist/plugins/memory-engine-pg.js +96 -214
- package/dist/plugins/memory-engine-sql-apply.d.ts +130 -0
- package/dist/plugins/memory-engine-sql-apply.js +358 -0
- package/dist/plugins/memory-engine-tidb.d.ts +38 -9
- package/dist/plugins/memory-engine-tidb.js +99 -230
- package/dist/plugins/memory-key-guards.d.ts +1 -1
- package/dist/plugins/memory-origin-law.d.ts +3 -3
- package/dist/plugins/one-time-migrations.d.ts +169 -83
- package/dist/plugins/one-time-migrations.js +295 -235
- package/dist/plugins/owner-only-file-mode.d.ts +29 -0
- package/dist/plugins/owner-only-file-mode.js +141 -0
- package/dist/plugins/permission-rule-store-file.d.ts +17 -3
- package/dist/plugins/permission-rule-store-file.js +20 -1
- package/dist/plugins/pg-pool.d.ts +2 -1
- package/dist/plugins/pg-pool.js +12 -5
- package/dist/plugins/release-version.d.ts +20 -0
- package/dist/plugins/release-version.js +28 -0
- package/dist/plugins/remote-env-adb.d.ts +20 -4
- package/dist/plugins/remote-env-adb.js +39 -11
- package/dist/plugins/remote-env-e2b.d.ts +12 -3
- package/dist/plugins/remote-env-e2b.js +6 -4
- package/dist/plugins/remote-env-host.d.ts +9 -1
- package/dist/plugins/remote-env-host.js +6 -5
- package/dist/plugins/remote-env-k8s.d.ts +5 -2
- package/dist/plugins/remote-env-k8s.js +15 -4
- package/dist/plugins/remote-env-local-docker.d.ts +4 -1
- package/dist/plugins/remote-env-local-docker.js +3 -3
- package/dist/plugins/remote-env-ssh.d.ts +8 -2
- package/dist/plugins/remote-env-ssh.js +26 -24
- package/dist/plugins/remote-shell.d.ts +59 -30
- package/dist/plugins/remote-shell.js +175 -62
- package/dist/plugins/retention-declaration.d.ts +37 -0
- package/dist/plugins/retention-declaration.js +9 -0
- package/dist/plugins/retention-deployment.d.ts +126 -0
- package/dist/plugins/retention-deployment.js +88 -0
- package/dist/plugins/retention-lane-store-sql.d.ts +74 -4
- package/dist/plugins/retention-lane-store-sql.js +103 -1
- package/dist/plugins/retention-sql.d.ts +29 -0
- package/dist/plugins/retention-sql.js +8 -0
- package/dist/plugins/run-store-sql.js +2 -2
- package/dist/plugins/schema-consistency.d.ts +0 -6
- package/dist/plugins/schema-consistency.js +1 -2
- package/dist/plugins/schema-ensure-legs.d.ts +1 -1
- package/dist/plugins/schema-ensure-legs.js +13 -3
- package/dist/plugins/session-ext-table.d.ts +74 -0
- package/dist/plugins/session-ext-table.js +121 -0
- package/dist/plugins/session-fork.d.ts +31 -9
- package/dist/plugins/session-fork.js +3 -2
- package/dist/plugins/session-placement.d.ts +4 -2
- package/dist/plugins/session-row-sql.d.ts +8 -7
- package/dist/plugins/session-row-sql.js +15 -7
- package/dist/plugins/session-store-sql.d.ts +131 -0
- package/dist/plugins/session-store-sql.js +298 -0
- package/dist/plugins/session-workspace-cell.d.ts +2 -2
- package/dist/plugins/session-workspace-cell.js +2 -2
- package/dist/plugins/shared-memory-store-sql.d.ts +10 -0
- package/dist/plugins/shared-memory-store-sql.js +13 -13
- package/dist/plugins/snapshot-bucket-presence.js +1 -1
- package/dist/plugins/sql-driver.d.ts +23 -7
- package/dist/plugins/sql-driver.js +41 -8
- package/dist/plugins/sql-errors.d.ts +5 -2
- package/dist/plugins/sql-errors.js +4 -0
- package/dist/plugins/sql-json-column.d.ts +5 -1
- package/dist/plugins/store-backend.d.ts +46 -16
- package/dist/plugins/store-backend.js +192 -69
- package/dist/plugins/store-contracts.d.ts +10 -13
- package/dist/plugins/store-contracts.js +1 -9
- package/dist/plugins/store-sweep.d.ts +63 -0
- package/dist/plugins/store-sweep.js +48 -0
- package/dist/plugins/task-attachment-store.d.ts +6 -8
- package/dist/plugins/task-attachment-store.js +15 -10
- package/dist/plugins/task-list-store-sql.d.ts +1 -1
- package/dist/plugins/text-column-capacity.d.ts +21 -0
- package/dist/plugins/text-column-capacity.js +5 -0
- package/dist/plugins/tidb-pool.d.ts +3 -2
- package/dist/plugins/tidb-pool.js +20 -32
- package/dist/plugins/user-input-ledger-sql.d.ts +1 -1
- package/dist/prompt-screen.d.ts +6 -9
- package/dist/prompt-screen.js +2 -5
- package/dist/read-face-posture.d.ts +6 -6
- package/dist/read-face-posture.js +2 -2
- package/dist/request-value-closure.d.ts +5 -5
- package/dist/request-value-closure.js +1 -1
- package/dist/rules-consent.d.ts +48 -27
- package/dist/rules-consent.js +147 -23
- package/dist/run-cancel-context.d.ts +4 -1
- package/dist/run-cancel-context.js +2 -1
- package/dist/run-local.js +17 -13
- package/dist/runs.d.ts +48 -30
- package/dist/runs.js +27 -17
- package/dist/runtime-caps-resolver.d.ts +15 -8
- package/dist/runtime-caps-resolver.js +14 -10
- package/dist/sandbox-egress-policy.js +6 -11
- package/dist/sandbox-rbac-rules.d.ts +88 -0
- package/dist/sandbox-rbac-rules.js +80 -0
- package/dist/sealed-key-layout.d.ts +16 -0
- package/dist/sealed-key-layout.js +3 -0
- package/dist/sealed-key.js +8 -6
- package/dist/security.d.ts +43 -9
- package/dist/security.js +9 -2
- package/dist/sema-config-dir.js +2 -2
- package/dist/server-secret-env.d.ts +2 -0
- package/dist/service-version.d.ts +2 -1
- package/dist/service-version.js +2 -2
- package/dist/session-error-wire.d.ts +1 -1
- package/dist/session-incarnation.d.ts +8 -5
- package/dist/session-notify-door.d.ts +5 -3
- package/dist/session-resident-background.d.ts +110 -0
- package/dist/session-resident-background.js +88 -0
- package/dist/session-row.d.ts +1 -1
- package/dist/session-sync-kernel.d.ts +53 -14
- package/dist/session-sync-kernel.js +70 -1
- package/dist/session-sync.d.ts +5 -3
- package/dist/session-workspace.d.ts +12 -2
- package/dist/session-workspace.js +6 -0
- package/dist/sighup-idle.d.ts +9 -7
- package/dist/sighup-idle.js +4 -5
- package/dist/spec-fields.d.ts +16 -0
- package/dist/spec-fields.js +7 -0
- package/dist/store-posture.d.ts +2 -1
- package/dist/stored-request-env-credentials-m38.d.ts +3 -0
- package/dist/stored-request-env-credentials-m38.js +8 -0
- package/dist/stored-request-hooks-m20.d.ts +3 -2
- package/dist/stored-request-hooks-m20.js +19 -14
- package/dist/stored-request-prompt-profile-m30.d.ts +1 -1
- package/dist/stores-sql/blob-index-sql.d.ts +148 -0
- package/dist/stores-sql/blob-index-sql.js +154 -0
- package/dist/{plugins → stores-sql}/checkpoint-store-sql.d.ts +14 -428
- package/dist/{plugins → stores-sql}/checkpoint-store-sql.js +43 -220
- package/dist/{plugins → stores-sql}/file-history-store-sql.d.ts +23 -11
- package/dist/{plugins → stores-sql}/file-history-store-sql.js +23 -18
- package/dist/{plugins → stores-sql}/mailbox-store-sql.d.ts +2 -2
- package/dist/{plugins → stores-sql}/mailbox-store-sql.js +4 -4
- package/dist/{plugins → stores-sql}/pg-session-storage.d.ts +27 -67
- package/dist/{plugins → stores-sql}/pg-session-storage.js +82 -207
- package/dist/{plugins → stores-sql}/retention-store-sql.d.ts +123 -217
- package/dist/{plugins → stores-sql}/retention-store-sql.js +66 -190
- package/dist/{plugins → stores-sql}/roster-store-sql.d.ts +2 -2
- package/dist/{plugins → stores-sql}/roster-store-sql.js +1 -1
- package/dist/{plugins → stores-sql}/session-event-payload.d.ts +1 -1
- package/dist/{plugins → stores-sql}/session-event-payload.js +1 -1
- package/dist/stores-sql/session-policy-store-sql.d.ts +1 -1
- package/dist/stores-sql/session-write-hooks.d.ts +59 -0
- package/dist/stores-sql/session-write-hooks.js +2 -0
- package/dist/{plugins → stores-sql}/tidb-session-storage.d.ts +10 -11
- package/dist/{plugins → stores-sql}/tidb-session-storage.js +10 -21
- package/dist/{plugins → stores-sql}/tidb-session-store.d.ts +35 -112
- package/dist/{plugins → stores-sql}/tidb-session-store.js +73 -169
- package/dist/{plugins → stores-sql}/tool-result-store-sql.d.ts +3 -3
- package/dist/{plugins → stores-sql}/tool-result-store-sql.js +6 -6
- package/dist/stores-sql/windowed-session-storage.d.ts +56 -0
- package/dist/stores-sql/windowed-session-storage.js +61 -0
- package/dist/{plugins → stores-sql}/workflow-journal-store-sql.d.ts +7 -12
- package/dist/{plugins → stores-sql}/workflow-journal-store-sql.js +3 -8
- package/dist/task-a2a.d.ts +7 -1
- package/dist/task-a2a.js +15 -14
- package/dist/task-cwd.d.ts +11 -3
- package/dist/task-cwd.js +17 -1
- package/dist/task-mcp.d.ts +10 -2
- package/dist/task-mcp.js +13 -16
- package/dist/task-settings.d.ts +68 -8
- package/dist/task-settings.js +63 -6
- package/dist/tool-approval.d.ts +23 -10
- package/dist/tool-approval.js +10 -6
- package/dist/trace/core-keyset-guard.d.ts +17 -10
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +4 -0
- package/dist/trace/exit-seat.d.ts +29 -1
- package/dist/trace/exit-seat.js +4 -0
- package/dist/trace/project.d.ts +33 -16
- package/dist/trace/project.js +21 -9
- package/dist/trace/redact.d.ts +13 -2
- package/dist/trace/redact.js +347 -78
- package/dist/trace/self-minted-wire.d.ts +587 -16
- package/dist/trace/self-minted-wire.js +215 -9
- package/dist/trace/sema-provenance.d.ts +45 -41
- package/dist/trace/sema-provenance.js +81 -10
- package/dist/trace/task-notification-facets.d.ts +70 -2
- package/dist/trace/task-notification-facets.js +71 -1
- package/dist/trace/wire-projection-faces.d.ts +39 -3
- package/dist/trace/wire-projection-faces.js +27 -2
- package/dist/unread-request-members.d.ts +66 -0
- package/dist/unread-request-members.js +41 -0
- package/dist/usage-analytics.d.ts +4 -0
- package/dist/user-input-ledger.d.ts +2 -2
- package/dist/web-admin-client/apply-effective.js +22 -26
- package/dist/web-admin-client/candidate-gate.d.ts +35 -3
- package/dist/web-admin-client/candidate-gate.js +53 -5
- package/dist/web-admin-client/center-request.d.ts +4 -1
- package/dist/web-admin-client/center-request.js +2 -1
- package/dist/web-admin-client/effective-keys.js +0 -1
- package/dist/web-admin-client/facade.d.ts +1 -1
- package/dist/web-admin-client/http-client.d.ts +1 -0
- package/dist/web-admin-client/http-client.js +17 -9
- package/dist/web-admin-client/restart-signal.js +5 -2
- package/dist/web-admin-client/skills-mcp.d.ts +20 -6
- package/dist/web-admin-client/skills-mcp.js +26 -21
- package/dist/web-admin-client/types.d.ts +34 -49
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +2 -2
- package/package.json +22 -13
- package/scripts/drain-check-local.mjs +8 -55
- package/scripts/drain-check.mysql.sql +3 -40
- package/scripts/drain-check.pg.sql +3 -40
- package/skills/update-config.md +1 -1
- package/wire-changes/server@7.146.0.json +3221 -0
- package/wire-changes/server@7.147.0.json +813 -0
- package/dist/approval-batch-census.d.ts +0 -27
- package/dist/approval-batch-census.js +0 -27
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { 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
|
-
import
|
|
4
|
-
import { type
|
|
5
|
-
import { type PlacementAcquireOpts } from "./session-placement.js";
|
|
3
|
+
import { WindowedSessionStorage } from "./windowed-session-storage.js";
|
|
4
|
+
import { type PlacementAcquireOpts } from "../plugins/session-placement.js";
|
|
6
5
|
import { type StagingHandle } from "../session-sync-kernel.js";
|
|
7
|
-
import { type InstructionSourceBaseline } from "../instruction-source-notice.js";
|
|
8
|
-
import type { SessionWorkspace } from "../session-workspace.js";
|
|
9
6
|
import type { RetentionDeclaration } from "@sema-agent/core";
|
|
10
|
-
import {
|
|
11
|
-
import { type
|
|
12
|
-
import { type
|
|
7
|
+
import type { SessionWriteHooks } from "./session-write-hooks.js";
|
|
8
|
+
import { type IndexSpec } from "../plugins/ensure-index.js";
|
|
9
|
+
import { type ForkPlacement, type ForkTarget, type ForkToOutcome } from "../plugins/session-fork.js";
|
|
13
10
|
/**
|
|
14
11
|
* PG-dialect DDL for the L1 session log, translated from tidb-pool.ts SCHEMA_STATEMENTS:
|
|
15
12
|
* JSON→JSONB, DATETIME(3)→TIMESTAMPTZ(3), inline `KEY`/`UNIQUE KEY`→separate CREATE INDEX (UNIQUE for uq_session_event_entry).
|
|
@@ -23,12 +20,12 @@ export declare const SESSION_INDEXES: readonly IndexSpec[];
|
|
|
23
20
|
/** Idempotent schema apply for this store's tables (the test calls this; production aggregation is separate). */
|
|
24
21
|
export declare function ensureSchema(pool: Pool | PoolClient): Promise<void>;
|
|
25
22
|
/**
|
|
26
|
-
* PostgreSQL-backed
|
|
23
|
+
* PostgreSQL-backed session storage — the durable L1 event log for one session (the window view and its reads: {@link WindowedSessionStorage}).
|
|
27
24
|
*
|
|
28
25
|
* Behaviourally identical to {@link TiDBSessionStorage}; see that file's class doc for the F2/F3 seams. The only
|
|
29
26
|
* differences are dialectal (placeholders, `IS NOT DISTINCT FROM`, jsonb, rowCount, transaction handle).
|
|
30
27
|
*/
|
|
31
|
-
export declare class PgSessionStorage extends
|
|
28
|
+
export declare class PgSessionStorage extends WindowedSessionStorage {
|
|
32
29
|
private readonly pool;
|
|
33
30
|
private readonly sessionId;
|
|
34
31
|
private readonly floorSeq;
|
|
@@ -40,16 +37,14 @@ export declare class PgSessionStorage extends BaseSessionStorage {
|
|
|
40
37
|
static wake(pool: Pool, sessionId: string): Promise<PgSessionStorage | null>;
|
|
41
38
|
/** The bounded-window floor = last compaction's firstKeptEntryId (and its seq). @see TiDBSessionStorage.computeFloor */
|
|
42
39
|
private static computeFloor;
|
|
43
|
-
/** Sub-floor lookups (e.g. a label target) aren't in the loaded tail —
|
|
44
|
-
|
|
45
|
-
appendEntry(entry: SessionTreeEntry, opts?: SessionWriteOptions): Promise<void>;
|
|
46
|
-
setLeafId(leafId: string | null, opts?: SessionWriteOptions): Promise<void>;
|
|
40
|
+
/** Sub-floor lookups (e.g. a label target) aren't in the loaded tail — read the durable log. */
|
|
41
|
+
protected readBelowFloor(id: string): Promise<SessionTreeEntry | undefined>;
|
|
47
42
|
/**
|
|
48
43
|
* Atomically: CAS the meta leaf (+bump leaf_seq), then append the event at the new seq.
|
|
49
44
|
* The `(session_id, seq)` primary key is the backstop — a racing writer that grabbed the same
|
|
50
45
|
* seq makes our INSERT fail with a duplicate-key (23505), which we translate to a conflict too.
|
|
51
46
|
*/
|
|
52
|
-
|
|
47
|
+
protected persist(entry: SessionTreeEntry, opts?: SessionWriteOptions): Promise<void>;
|
|
53
48
|
}
|
|
54
49
|
/**
|
|
55
50
|
* PostgreSQL-backed {@link SessionStore} — the PG twin of {@link TiDBSessionStore}. Maps the
|
|
@@ -57,7 +52,7 @@ export declare class PgSessionStorage extends BaseSessionStorage {
|
|
|
57
52
|
*/
|
|
58
53
|
export declare class PgSessionStore implements SessionStore {
|
|
59
54
|
private readonly pool;
|
|
60
|
-
/** 托管留存声明 = 装配交给它的词(TiDB 孪生同格)。读法见 `retention-
|
|
55
|
+
/** 托管留存声明 = 装配交给它的词(TiDB 孪生同格)。读法见 `retention-declaration.ts` 的 {@link MANAGED_RETENTION}。 */
|
|
61
56
|
readonly retention: RetentionDeclaration;
|
|
62
57
|
/** [ref] C18(core 5.48.0 [ref]):placement 声明(TiDB 孪生同格,义务逐条见
|
|
63
58
|
* `src/plugins/session-placement.ts` 顶注)。 */
|
|
@@ -70,12 +65,11 @@ export declare class PgSessionStore implements SessionStore {
|
|
|
70
65
|
readonly durability: "durable";
|
|
71
66
|
/** In-flight acquisitions keyed by id, so concurrent acquire(sameId) in one process share one. */
|
|
72
67
|
private readonly pending;
|
|
73
|
-
/**
|
|
74
|
-
readonly
|
|
75
|
-
|
|
76
|
-
private readonly db;
|
|
77
|
-
constructor(pool: Pool, opts?: {
|
|
68
|
+
/** 部署的事务内钩(TiDB 孪生同格,`session-write-hooks.ts`)。 */
|
|
69
|
+
private readonly hooks;
|
|
70
|
+
constructor(pool: Pool, opts: {
|
|
78
71
|
readonly retention?: RetentionDeclaration;
|
|
72
|
+
readonly hooks: SessionWriteHooks;
|
|
79
73
|
});
|
|
80
74
|
acquire(sessionId?: string, opts?: PlacementAcquireOpts): Promise<AcquiredSession>;
|
|
81
75
|
private load;
|
|
@@ -107,44 +101,10 @@ export declare class PgSessionStore implements SessionStore {
|
|
|
107
101
|
private deletePlacedSession;
|
|
108
102
|
/** [ref]:placed id 拒绝 session-sync 的整段换装(TiDB 孪生 {@link TiDBSessionStore.assertNotPlaced} 同义)。 */
|
|
109
103
|
private assertNotPlaced;
|
|
110
|
-
/** @see TiDBSessionStore.
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
cursor?: {
|
|
115
|
-
lastActivityAt: string;
|
|
116
|
-
sessionId: string;
|
|
117
|
-
};
|
|
118
|
-
limit: number;
|
|
119
|
-
q?: string;
|
|
120
|
-
}): Promise<SessionSummary[]>;
|
|
121
|
-
/** @see TiDBSessionStore.sessionSummaryOf — the PG twin (same projection, run-preview subqueries scoped to the id). */
|
|
122
|
-
sessionSummaryOf(sessionId: string): Promise<SessionSummary | undefined>;
|
|
123
|
-
/** @see TiDBSessionStore.listRowOf — the PG twin. */
|
|
124
|
-
private listRowOf;
|
|
125
|
-
/** @see TiDBSessionStore.writeSessionRowCells — the PG twin (the same SQL half, `session-row-sql.ts`; only the statement text differs). */
|
|
126
|
-
writeSessionRowCells(sessionId: string, cells: SessionRowPatch, how: ClientSessionRowWrite): Promise<SessionRowWriteOutcome<SessionSummary>>;
|
|
127
|
-
writeSessionRowCells(sessionId: string, cells: SessionRowPatch, how: ReplaySessionRowWrite): Promise<ReplaySessionRowWriteOutcome<SessionSummary>>;
|
|
128
|
-
/** @see TiDBSessionStore.selectListRows — the PG twin. `where` already carries `$n` placeholders numbered over `params`;
|
|
129
|
-
* the run scope and the LIMIT take the next numbers (PG binds by number, so text order does not matter). */
|
|
130
|
-
private selectListRows;
|
|
131
|
-
/** @see TiDBSessionStore.probeTitle — the PG twin. */
|
|
132
|
-
probeTitle(sessionId: string): Promise<"none" | "untitled" | "titled">;
|
|
133
|
-
/** @see TiDBSessionStore.setTitleIfNull — the PG twin (the auto-titler's NULL-only write). */
|
|
134
|
-
setTitleIfNull(sessionId: string, title: string): Promise<boolean>;
|
|
135
|
-
/** @see TiDBSessionStore.readInstructionSource — the PG twin. */
|
|
136
|
-
readInstructionSource(sessionId: string): Promise<InstructionSourceBaseline>;
|
|
137
|
-
/** @see TiDBSessionStore.recordInstructionSource — the PG twin (same CAS predicate, same per-query timeout; node-pg's
|
|
138
|
-
* `query_timeout` rejects AND pg-pool destroys that client, so a lock-stuck baseline write does not keep a pooled
|
|
139
|
-
* connection). */
|
|
140
|
-
recordInstructionSource(sessionId: string, expected: InstructionSourceBaseline, source: string | null): Promise<void>;
|
|
141
|
-
/** @see TiDBSessionStore.readSessionWorkspace — the PG twin (same projection-as-text, same cell owner). */
|
|
142
|
-
readSessionWorkspace(sessionId: string): Promise<SessionWorkspace | undefined>;
|
|
143
|
-
/** @see TiDBSessionStore.recordSessionWorkspace — the PG twin (whole-cell overwrite fenced to the occupant; `pgSafeJsonStringify` + `::jsonb`, like every PG JSON write;
|
|
144
|
-
* the general query surface — the pool-wide `DB_QUERY_TIMEOUT_MS` server-side `statement_timeout` bounds it, no per-call client timer). */
|
|
145
|
-
recordSessionWorkspace(sessionId: string, incarnation: string, workspace: SessionWorkspace): Promise<void>;
|
|
146
|
-
/** @see TiDBSessionStore.fork — core 契约 `fork(sourceId, owner?)` = {@link forkTo} 的投影(新铸 uuidv7 目标,`forkByFreshId`;三店同一只)。 */
|
|
147
|
-
fork(sourceId: string, owner?: string | null): Promise<string | null>;
|
|
104
|
+
/** @see TiDBSessionStore.fork — core 契约 `fork(sourceId, owner?, opts?)` = {@link forkTo} 的投影(新铸 uuidv7 目标,`forkByFreshId`;三店同一只;`opts.placement` 进委派分区)。 */
|
|
105
|
+
fork(sourceId: string, owner?: string | null, opts?: {
|
|
106
|
+
placement?: ForkPlacement;
|
|
107
|
+
}): Promise<string | null>;
|
|
148
108
|
/** @see TiDBSessionStore.forkTo — fork 续写的店契约(PG 孪生逐字同义):目标行 meta 先、主键排他;撞已在 ⇒ 回滚后在新视图读目标按属主与 fork 键分类;
|
|
149
109
|
* 读不到 ⇒ 整笔重跑一次,再不行 ⇒ {@link ForkTargetUnsettledError}。 */
|
|
150
110
|
forkTo(sourceId: string, owner: string | null, target: ForkTarget): Promise<ForkToOutcome | null>;
|
|
@@ -168,16 +128,16 @@ export declare class PgSessionStore implements SessionStore {
|
|
|
168
128
|
}): Promise<AsyncIterable<SessionTreeEntry> | null>;
|
|
169
129
|
/** @see TiDBSessionStore.importEntries — 2c session-sync IMPORT: core `validateEntriesForImport` gate FIRST (fail-closed),
|
|
170
130
|
* then persist its verbatim output (re-seq dense from 0, ids/parents/payload/ts unchanged) in ONE txn, **lock-or-create, meta first**
|
|
171
|
-
* (
|
|
131
|
+
* (占行 → 锁行判是不是本次占下的 → 部署的事务内钩 `import` → 事件);owner = the RE-STAMPED authenticated principal (never the bundle's). */
|
|
172
132
|
importEntries(sessionId: string, owner: string | null, entries: SessionTreeEntry[]): Promise<void>;
|
|
173
|
-
/** @see TiDBSessionStore.replaceEntries — 2c (§7/§8) IDEMPOTENT REPLACE: core `validateEntriesForImport` gate FIRST
|
|
174
|
-
* (
|
|
175
|
-
*
|
|
176
|
-
* ONE txn. Safe to run OVER an existing session (fast-forward / §8 retry)
|
|
133
|
+
/** @see TiDBSessionStore.replaceEntries — 2c (§7/§8) IDEMPOTENT REPLACE: core `validateEntriesForImport` gate FIRST (fail-closed), then the row
|
|
134
|
+
* (absent ⇒ created as a fresh occupant; existing ⇒ rewritten in place only when it is the importer's — `owner IS NOT DISTINCT FROM $n`, the PG
|
|
135
|
+
* `<=>` — and not a delegation transcript; the occupant keeps its incarnation and deployment cells), then the SHARED body (the hook, then the old
|
|
136
|
+
* events out and the new ones in), ALL in ONE txn. Safe to run OVER an existing session (fast-forward / §8 retry). */
|
|
177
137
|
replaceEntries(sessionId: string, owner: string | null, entries: SessionTreeEntry[]): Promise<void>;
|
|
178
138
|
/** @see TiDBSessionStore.writeImportedLog — the shared write half of importEntries / replaceEntries (ONE source, no drift):
|
|
179
|
-
* session_meta FIRST (create = occupy the row, an existing one ⇒ refused; replace =
|
|
180
|
-
*
|
|
139
|
+
* session_meta FIRST (create = occupy the row, an existing one ⇒ refused; replace = owner-guarded in-place rewrite of the locked row),
|
|
140
|
+
* the deployment's transaction hook `import` on THIS connection, then the entries re-seq dense from 0 (ids/parents/payload/ts verbatim).
|
|
181
141
|
* Runs inside the caller's open txn (`conn` already in BEGIN); the caller owns COMMIT/ROLLBACK + the validate gate. */
|
|
182
142
|
private writeImportedLog;
|
|
183
143
|
/** @see TiDBSessionStore.beginImportStaging — 2c P1d-β PUSH staging: stream batches into the shadow id
|
|
@@ -1,33 +1,38 @@
|
|
|
1
1
|
import { emitLeafAdvance } from "../session-leaf-bus.js";
|
|
2
|
-
import {
|
|
3
|
-
import { SESSION_TITLE_MAX_CHARS, sessionRowTagKey } from "../session-row.js";
|
|
4
|
-
import { writeSessionRowCellsSql } from "./session-row-sql.js";
|
|
5
|
-
import { escapeLike } from "./sql-escape.js";
|
|
2
|
+
import { StoredSession, SessionError, uuidv7, validateEntriesForImport, leafIdAfterEntry, admitSessionPlacement, } from "@sema-agent/core";
|
|
6
3
|
import { decodeSessionEventPayload } from "./session-event-payload.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
4
|
+
import { WindowedSessionStorage } from "./windowed-session-storage.js";
|
|
5
|
+
import { scanEpochAnchor } from "../plugins/session-epoch-anchor.js";
|
|
6
|
+
import { jsonColumnText } from "../plugins/sql-json-column.js";
|
|
7
|
+
import { pgSafeJsonStringify } from "../plugins/pg-safe-json.js";
|
|
8
|
+
import { toIso } from "../plugins/sql-row-helpers.js";
|
|
9
|
+
import { placementCapabilityOf, LIST_PLACED_BATCH, PLACEMENT_SELECT_COLUMNS, placedRowFromRow, placementFromRow, placementInsertValues, SESSION_DURABILITY, SESSION_PLACEMENTS, } from "../plugins/session-placement.js";
|
|
12
10
|
import { contentForkRelation, fastForwardSharedContentDiverged } from "../session-sync-content.js";
|
|
13
11
|
import { classifySyncRelationshipByIds, SyncConflictError, StagedCommitOwnerChangedError, stagingIdFor, STAGING_ID_MARKER, STAGING_GC_GRACE_MS, } from "../session-sync-kernel.js";
|
|
14
|
-
import { isPgUniqueViolation } from "
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import { ensureIndexes } from "./ensure-index.js";
|
|
20
|
-
import { assertFirstOwnerWritable, forkOwnerOf, isSessionOwnerValue, placedSessionOwnerOf } from "./session-ownership.js";
|
|
12
|
+
import { isPgUniqueViolation } from "../plugins/sql-errors.js";
|
|
13
|
+
import { MANAGED_RETENTION, UNMANAGED_RETENTION } from "../plugins/retention-declaration.js";
|
|
14
|
+
import { SESSION_META_INCARNATION_COLUMN, headFromRow, incarnationFromCell, mintSessionIncarnation, sessionFaceFromRow } from "../session-incarnation.js";
|
|
15
|
+
import { ensureIndexes } from "../plugins/ensure-index.js";
|
|
16
|
+
import { assertFirstOwnerWritable, forkOwnerOf, isSessionOwnerValue, placedSessionOwnerOf } from "../plugins/session-ownership.js";
|
|
21
17
|
import { selectForkRows } from "@sema-agent/core";
|
|
22
|
-
import { FORK_LOG_ROW_SHAPE, ForkTargetUnsettledError, classifyForkTarget, forkByFreshId, forkPointOf } from "
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
18
|
+
import { FORK_LOG_ROW_SHAPE, ForkTargetUnsettledError, classifyForkTarget, forkByFreshId, forkPointOf } from "../plugins/session-fork.js";
|
|
19
|
+
const statementsOn = (conn) => ({
|
|
20
|
+
async query(sql, params) {
|
|
21
|
+
const res = await conn.query(sql, params ?? []);
|
|
22
|
+
return { rows: res.rows, affected: res.rowCount ?? 0 };
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
class HookRefusal {
|
|
26
|
+
refuse;
|
|
27
|
+
constructor(refuse) {
|
|
28
|
+
this.refuse = refuse;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function settleHookVerdict(verdict) {
|
|
32
|
+
if (verdict !== "proceed")
|
|
33
|
+
throw new HookRefusal(verdict.refuse);
|
|
30
34
|
}
|
|
35
|
+
const isDupKey = isPgUniqueViolation;
|
|
31
36
|
export const PG_SESSION_SCHEMA = [
|
|
32
37
|
`CREATE TABLE IF NOT EXISTS session_meta (
|
|
33
38
|
session_id VARCHAR(64) COLLATE "C" NOT NULL,
|
|
@@ -36,16 +41,8 @@ export const PG_SESSION_SCHEMA = [
|
|
|
36
41
|
updated_at TIMESTAMPTZ(3) NOT NULL,
|
|
37
42
|
leaf_id VARCHAR(64) COLLATE "C",
|
|
38
43
|
leaf_seq BIGINT NOT NULL DEFAULT 0,
|
|
39
|
-
--
|
|
40
|
-
-- (
|
|
41
|
-
-- — fills NULL only; NULL = "untitled", the three-state probe distinguishes it from "no such session").
|
|
42
|
-
title VARCHAR(${SESSION_TITLE_MAX_CHARS}) COLLATE "C",
|
|
43
|
-
-- pinned: the user's pin (TiDB twin); every list row carries it, false by default.
|
|
44
|
-
pinned BOOLEAN NOT NULL DEFAULT FALSE,
|
|
45
|
-
-- instruction_source(S-690):换源通告的基线 —— TiDB 孪生同名同宽,三态读法(NULL / '' / 链成员相对路径)见那里的列注。
|
|
46
|
-
instruction_source VARCHAR(64) COLLATE "C",
|
|
47
|
-
-- workspace:本会话上一条腿自己的最后一帧 workspace_changed —— TiDB 孪生同名同义(列注与唯一属主见那里:src/plugins/session-workspace-cell.ts)。
|
|
48
|
-
${SESSION_META_WORKSPACE_COLUMN.pg},
|
|
44
|
+
-- 引擎座表只带引擎的列;本服务记在会话上的格(标题 / 置顶 / 指令基线 / 位置与项目 / 执行放置 / fork 键)住部署旁表 session_ext
|
|
45
|
+
-- (外键级联随本行走;唯一属主 src/plugins/session-ext-table.ts)。
|
|
49
46
|
-- #292 C18(core 5.48.0 design/323)subagent 转录持久化 —— placement 元组(TiDB 孪生同名同宽,
|
|
50
47
|
-- 语义与六条义务见 src/plugins/session-placement.ts 顶注):创建时一次落行、first-write 不可变;
|
|
51
48
|
-- placement_kind 在场 ⇒ 住在 placement 分区(不上宿主枚举 / claim 形 acquire 恒拒 / release=真删除 /
|
|
@@ -59,8 +56,6 @@ export const PG_SESSION_SCHEMA = [
|
|
|
59
56
|
placed_at TIMESTAMPTZ(3),
|
|
60
57
|
-- S-790 / S-506:会话化身(TiDB 孪生同名同义;列定义唯一属主 = src/session-incarnation.ts 的 SESSION_META_INCARNATION_COLUMN)。
|
|
61
58
|
${SESSION_META_INCARNATION_COLUMN.pg},
|
|
62
|
-
-- 执行放置(根会话行上这一位住客的一格:execution_* 七列)+ fork 键(fork_key)—— TiDB 孪生同名同义;列定义与语句的唯一属主 = src/plugins/execution-placement-sql.ts。
|
|
63
|
-
${sessionMetaPlacementColumnsDdl("pg")},
|
|
64
59
|
PRIMARY KEY (session_id)
|
|
65
60
|
)`,
|
|
66
61
|
`CREATE TABLE IF NOT EXISTS session_event (
|
|
@@ -85,12 +80,12 @@ export async function ensureSchema(pool) {
|
|
|
85
80
|
await pool.query(stmt);
|
|
86
81
|
await ensureIndexes((t, p) => pool.query(t, p), "pg", SESSION_INDEXES);
|
|
87
82
|
}
|
|
88
|
-
export class PgSessionStorage extends
|
|
83
|
+
export class PgSessionStorage extends WindowedSessionStorage {
|
|
89
84
|
pool;
|
|
90
85
|
sessionId;
|
|
91
86
|
floorSeq;
|
|
92
87
|
constructor(pool, sessionId, metadata, entries, leafId, floorEntryId = null, floorSeq = 0) {
|
|
93
|
-
super(metadata, entries, leafId,
|
|
88
|
+
super(metadata, entries, leafId, floorEntryId);
|
|
94
89
|
this.pool = pool;
|
|
95
90
|
this.sessionId = sessionId;
|
|
96
91
|
this.floorSeq = floorSeq;
|
|
@@ -126,24 +121,12 @@ export class PgSessionStorage extends BaseSessionStorage {
|
|
|
126
121
|
return { floorEntryId: null, floorSeq: 0 };
|
|
127
122
|
return { floorEntryId: fk, floorSeq: Number(seqRes.rows[0].seq) };
|
|
128
123
|
}
|
|
129
|
-
async
|
|
130
|
-
const inMem = await super.getEntry(id);
|
|
131
|
-
if (inMem)
|
|
132
|
-
return inMem;
|
|
124
|
+
async readBelowFloor(id) {
|
|
133
125
|
const res = await this.pool.query(`SELECT ${jsonColumnText("pg", "payload")} FROM session_event WHERE session_id = $1 AND entry_id = $2`, [this.sessionId, id]);
|
|
134
126
|
if (res.rows[0] === undefined)
|
|
135
127
|
return undefined;
|
|
136
128
|
return decodeSessionEventPayload(res.rows[0].payload, this.sessionId, id);
|
|
137
129
|
}
|
|
138
|
-
async appendEntry(entry, opts) {
|
|
139
|
-
await this.persist(entry, opts);
|
|
140
|
-
this.recordEntry(entry);
|
|
141
|
-
}
|
|
142
|
-
async setLeafId(leafId, opts) {
|
|
143
|
-
const leafEntry = this.createLeafEntry(leafId);
|
|
144
|
-
await this.persist(leafEntry, opts);
|
|
145
|
-
this.recordEntry(leafEntry);
|
|
146
|
-
}
|
|
147
130
|
async persist(entry, opts) {
|
|
148
131
|
const newLeaf = leafIdAfterEntry(entry);
|
|
149
132
|
const conditional = opts !== undefined && opts.expectedLeafId !== undefined;
|
|
@@ -189,13 +172,11 @@ export class PgSessionStore {
|
|
|
189
172
|
placements = SESSION_PLACEMENTS;
|
|
190
173
|
durability = SESSION_DURABILITY;
|
|
191
174
|
pending = new Map();
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
constructor(pool, opts = {}) {
|
|
175
|
+
hooks;
|
|
176
|
+
constructor(pool, opts) {
|
|
195
177
|
this.pool = pool;
|
|
196
178
|
this.retention = opts.retention ?? UNMANAGED_RETENTION;
|
|
197
|
-
this.
|
|
198
|
-
this.executionPlacement = createSqlExecutionPlacementSeat(this.db);
|
|
179
|
+
this.hooks = opts.hooks;
|
|
199
180
|
}
|
|
200
181
|
async acquire(sessionId, opts) {
|
|
201
182
|
if (!sessionId) {
|
|
@@ -319,108 +300,8 @@ export class PgSessionStore {
|
|
|
319
300
|
throw new SessionError("placement_refused", `session ${sessionId} belongs to the ${placed.kind} placement partition — a session-sync ${what} cannot overwrite a delegation transcript`);
|
|
320
301
|
}
|
|
321
302
|
}
|
|
322
|
-
async
|
|
323
|
-
|
|
324
|
-
const ph = (v) => {
|
|
325
|
-
params.push(v);
|
|
326
|
-
return `$${params.length}`;
|
|
327
|
-
};
|
|
328
|
-
const where = ["sm.placement_kind IS NULL"];
|
|
329
|
-
if (opts.owner !== undefined)
|
|
330
|
-
where.push(opts.includeUnowned ? `(sm.owner = ${ph(opts.owner)} OR sm.owner IS NULL)` : `sm.owner = ${ph(opts.owner)}`);
|
|
331
|
-
if (opts.q)
|
|
332
|
-
where.push(`EXISTS (SELECT 1 FROM task_run tr WHERE tr.session_id = sm.session_id AND LOWER(tr.objective_preview) LIKE LOWER(${ph(`%${escapeLike(opts.q)}%`)}) ESCAPE '\\')`);
|
|
333
|
-
if (opts.cursor) {
|
|
334
|
-
const a = ph(new Date(opts.cursor.lastActivityAt));
|
|
335
|
-
const b = ph(new Date(opts.cursor.lastActivityAt));
|
|
336
|
-
const sid = ph(opts.cursor.sessionId);
|
|
337
|
-
where.push(`(sm.updated_at < ${a} OR (sm.updated_at = ${b} AND sm.session_id < ${sid}))`);
|
|
338
|
-
}
|
|
339
|
-
return (await this.selectListRows({ where, params, limit: opts.limit })).map((a) => a.row);
|
|
340
|
-
}
|
|
341
|
-
async sessionSummaryOf(sessionId) {
|
|
342
|
-
return (await this.listRowOf(sessionId))?.row;
|
|
343
|
-
}
|
|
344
|
-
async listRowOf(sessionId) {
|
|
345
|
-
return (await this.selectListRows({ where: ["sm.placement_kind IS NULL", "sm.session_id = $1"], params: [sessionId], limit: 1, runSessionId: sessionId }))[0];
|
|
346
|
-
}
|
|
347
|
-
async writeSessionRowCells(sessionId, cells, how) {
|
|
348
|
-
return writeSessionRowCellsSql(this.db, sessionId, cells, how, (id) => this.listRowOf(id));
|
|
349
|
-
}
|
|
350
|
-
async selectListRows(q) {
|
|
351
|
-
const params = [...q.params];
|
|
352
|
-
const ph = (v) => {
|
|
353
|
-
params.push(v);
|
|
354
|
-
return `$${params.length}`;
|
|
355
|
-
};
|
|
356
|
-
const runWhere = q.runSessionId !== undefined ? ` WHERE session_id = ${ph(q.runSessionId)}` : "";
|
|
357
|
-
const sql = "SELECT sm.session_id, sm.owner, sm.incarnation, sm.updated_at AS last_activity, sm.created_at AS first_activity, sm.title, sm.pinned, " +
|
|
358
|
-
" COALESCE(r.run_count, 0) AS run_count, latest.objective_preview, latest.status, latest.task_id AS last_run_id, latest.last_task_run_id " +
|
|
359
|
-
"FROM session_meta sm " +
|
|
360
|
-
"LEFT JOIN (SELECT session_id, COUNT(*) AS run_count FROM task_run" + runWhere + " GROUP BY session_id) r ON r.session_id = sm.session_id " +
|
|
361
|
-
"LEFT JOIN (SELECT DISTINCT ON (session_id) session_id, objective_preview, status, task_id, result ->> 'runId' AS last_task_run_id FROM task_run" + runWhere +
|
|
362
|
-
" ORDER BY session_id, created_at DESC, task_id DESC) latest ON latest.session_id = sm.session_id " +
|
|
363
|
-
(q.where.length ? "WHERE " + q.where.join(" AND ") + " " : "") +
|
|
364
|
-
`ORDER BY sm.updated_at DESC, sm.session_id DESC LIMIT ${ph(q.limit)}`;
|
|
365
|
-
const res = await this.pool.query(sql, params);
|
|
366
|
-
return res.rows.map((r) => {
|
|
367
|
-
const runId = r.last_task_run_id;
|
|
368
|
-
const sessionId = String(r.session_id);
|
|
369
|
-
const owner = r.owner ?? null;
|
|
370
|
-
const incarnation = incarnationFromCell(sessionId, r.incarnation, "absent");
|
|
371
|
-
const title = r.title ?? null;
|
|
372
|
-
const pinned = r.pinned === true;
|
|
373
|
-
const row = {
|
|
374
|
-
sessionId,
|
|
375
|
-
owner,
|
|
376
|
-
lastActivityAt: r.last_activity instanceof Date ? r.last_activity.toISOString() : String(r.last_activity),
|
|
377
|
-
firstActivityAt: r.first_activity instanceof Date ? r.first_activity.toISOString() : String(r.first_activity),
|
|
378
|
-
runCount: Number(r.run_count),
|
|
379
|
-
objectivePreview: r.objective_preview ?? null,
|
|
380
|
-
lastStatus: r.status == null ? "" : String(r.status),
|
|
381
|
-
lastRunId: r.last_run_id ?? null,
|
|
382
|
-
...(typeof runId === "string" && runId !== "" ? { lastTaskRunId: runId } : {}),
|
|
383
|
-
title,
|
|
384
|
-
pinned,
|
|
385
|
-
...sessionRowTagKey({ sessionId, owner, incarnation, cells: { title, pinned } }),
|
|
386
|
-
};
|
|
387
|
-
return { row, occupant: { owner, incarnation } };
|
|
388
|
-
});
|
|
389
|
-
}
|
|
390
|
-
async probeTitle(sessionId) {
|
|
391
|
-
const res = await this.pool.query("SELECT title FROM session_meta WHERE session_id = $1", [sessionId]);
|
|
392
|
-
const row = res.rows[0];
|
|
393
|
-
if (!row)
|
|
394
|
-
return "none";
|
|
395
|
-
return row.title == null ? "untitled" : "titled";
|
|
396
|
-
}
|
|
397
|
-
async setTitleIfNull(sessionId, title) {
|
|
398
|
-
const res = await this.pool.query("UPDATE session_meta SET title = $1 WHERE session_id = $2 AND title IS NULL", [title, sessionId]);
|
|
399
|
-
return (res.rowCount ?? 0) > 0;
|
|
400
|
-
}
|
|
401
|
-
async readInstructionSource(sessionId) {
|
|
402
|
-
const res = await this.pool.query(baselineQuery("SELECT instruction_source FROM session_meta WHERE session_id = $1", [sessionId]));
|
|
403
|
-
return decodeInstructionSource(res.rows[0]?.instruction_source);
|
|
404
|
-
}
|
|
405
|
-
async recordInstructionSource(sessionId, expected, source) {
|
|
406
|
-
const next = encodeInstructionSource(source);
|
|
407
|
-
if (expected.recorded) {
|
|
408
|
-
await this.pool.query(baselineQuery("UPDATE session_meta SET instruction_source = $1 WHERE session_id = $2 AND instruction_source = $3", [next, sessionId, encodeInstructionSource(expected.source)]));
|
|
409
|
-
}
|
|
410
|
-
else {
|
|
411
|
-
await this.pool.query(baselineQuery("UPDATE session_meta SET instruction_source = $1 WHERE session_id = $2 AND instruction_source IS NULL", [next, sessionId]));
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
async readSessionWorkspace(sessionId) {
|
|
415
|
-
const res = await this.pool.query(`SELECT ${jsonColumnText("pg", "workspace")} FROM session_meta WHERE session_id = $1`, [sessionId]);
|
|
416
|
-
const row = res.rows[0];
|
|
417
|
-
return row === undefined ? undefined : decodeSessionWorkspaceCell(row.workspace, sessionId);
|
|
418
|
-
}
|
|
419
|
-
async recordSessionWorkspace(sessionId, incarnation, workspace) {
|
|
420
|
-
await this.pool.query("UPDATE session_meta SET workspace = $1::jsonb WHERE session_id = $2 AND incarnation = $3", [encodeSessionWorkspaceCell(workspace, pgSafeJsonStringify, sessionId), sessionId, incarnation]);
|
|
421
|
-
}
|
|
422
|
-
async fork(sourceId, owner) {
|
|
423
|
-
return forkByFreshId((s, o, t) => this.forkTo(s, o, t), sourceId, owner);
|
|
303
|
+
async fork(sourceId, owner, opts) {
|
|
304
|
+
return forkByFreshId((s, o, t) => this.forkTo(s, o, t), sourceId, owner, opts);
|
|
424
305
|
}
|
|
425
306
|
async forkTo(sourceId, owner, target) {
|
|
426
307
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
@@ -448,7 +329,8 @@ export class PgSessionStore {
|
|
|
448
329
|
try {
|
|
449
330
|
await conn.query("BEGIN");
|
|
450
331
|
try {
|
|
451
|
-
await conn.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation,
|
|
332
|
+
await conn.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation, " +
|
|
333
|
+
"placement_kind, placement_scope, placement_parent_session_id, placement_root_session_id, placement_handle, placed_at) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13)", [target.id, forkOwner, now, now, forkLeafId, rows.length, mintSessionIncarnation(), ...placementInsertValues(target.placement, now)]);
|
|
452
334
|
}
|
|
453
335
|
catch (err) {
|
|
454
336
|
if (!isDupKey(err))
|
|
@@ -456,6 +338,8 @@ export class PgSessionStore {
|
|
|
456
338
|
collided = true;
|
|
457
339
|
}
|
|
458
340
|
if (!collided) {
|
|
341
|
+
if (target.key !== undefined)
|
|
342
|
+
await this.hooks.keyedFork.record(statementsOn(conn), { rootSessionId: target.id, forkKey: target.key });
|
|
459
343
|
for (let seq = 0; seq < rows.length; seq++) {
|
|
460
344
|
const r = rows[seq];
|
|
461
345
|
const payload = String(r.payload);
|
|
@@ -472,14 +356,15 @@ export class PgSessionStore {
|
|
|
472
356
|
}
|
|
473
357
|
catch {
|
|
474
358
|
}
|
|
359
|
+
if (err instanceof HookRefusal)
|
|
360
|
+
throw err.refuse;
|
|
475
361
|
throw err instanceof SessionError ? err : new SessionError("storage", `fork of session ${sourceId} failed`, err instanceof Error ? err : undefined);
|
|
476
362
|
}
|
|
477
363
|
finally {
|
|
478
364
|
conn.release();
|
|
479
365
|
}
|
|
480
|
-
const existing = await this.
|
|
481
|
-
|
|
482
|
-
return row === undefined ? "unsettled" : classifyForkTarget(target, { owner: row.owner ?? null, forkKey: row.fork_key ?? null }, forkOwner);
|
|
366
|
+
const existing = await this.hooks.keyedFork.targetOf(target.id);
|
|
367
|
+
return existing === undefined ? "unsettled" : classifyForkTarget(target, existing, forkOwner);
|
|
483
368
|
}
|
|
484
369
|
async exportEntries(sessionId) {
|
|
485
370
|
const metaRes = await this.pool.query("SELECT 1 FROM session_meta WHERE session_id = $1", [sessionId]);
|
|
@@ -519,7 +404,7 @@ export class PgSessionStore {
|
|
|
519
404
|
const conn = await this.pool.connect();
|
|
520
405
|
try {
|
|
521
406
|
await conn.query("BEGIN");
|
|
522
|
-
await this.writeImportedLog(conn, sessionId, owner, validated,
|
|
407
|
+
await this.writeImportedLog(conn, sessionId, owner, validated, "create");
|
|
523
408
|
await conn.query("COMMIT");
|
|
524
409
|
}
|
|
525
410
|
catch (err) {
|
|
@@ -528,7 +413,9 @@ export class PgSessionStore {
|
|
|
528
413
|
}
|
|
529
414
|
catch {
|
|
530
415
|
}
|
|
531
|
-
if (err instanceof
|
|
416
|
+
if (err instanceof HookRefusal)
|
|
417
|
+
throw err.refuse;
|
|
418
|
+
if (err instanceof SessionError)
|
|
532
419
|
throw err;
|
|
533
420
|
throw new SessionError("storage", `import of session ${sessionId} failed`, err instanceof Error ? err : undefined);
|
|
534
421
|
}
|
|
@@ -542,21 +429,13 @@ export class PgSessionStore {
|
|
|
542
429
|
const conn = await this.pool.connect();
|
|
543
430
|
try {
|
|
544
431
|
await conn.query("BEGIN");
|
|
545
|
-
const
|
|
546
|
-
const prior =
|
|
432
|
+
const lockedRes = await conn.query("SELECT placement_kind FROM session_meta WHERE session_id = $1 FOR UPDATE", [sessionId]);
|
|
433
|
+
const prior = lockedRes.rows[0];
|
|
547
434
|
if (prior?.placement_kind != null) {
|
|
548
435
|
await conn.query("ROLLBACK");
|
|
549
436
|
throw new SessionError("placement_refused", `session ${sessionId} belongs to the ${String(prior.placement_kind)} placement partition — a session-sync replace cannot overwrite a delegation transcript`);
|
|
550
437
|
}
|
|
551
|
-
|
|
552
|
-
const swapped = (purged.rowCount ?? 0) === 1 ? prior : undefined;
|
|
553
|
-
const cells = carriedRowCells(sessionId, swapped === undefined ? undefined : { incarnation: swapped.incarnation, forkKey: swapped.fork_key });
|
|
554
|
-
const priorCell = swapped === undefined ? undefined : placementCellFromRow(sessionId, swapped);
|
|
555
|
-
if (priorCell?.role === "writer") {
|
|
556
|
-
await conn.query("ROLLBACK");
|
|
557
|
-
throw new SessionWriterPresentError(sessionId, priorCell.lane);
|
|
558
|
-
}
|
|
559
|
-
await this.writeImportedLog(conn, sessionId, owner, validated, cells, "replace");
|
|
438
|
+
await this.writeImportedLog(conn, sessionId, owner, validated, prior === undefined ? "create" : "replace");
|
|
560
439
|
await conn.query("COMMIT");
|
|
561
440
|
}
|
|
562
441
|
catch (err) {
|
|
@@ -565,7 +444,9 @@ export class PgSessionStore {
|
|
|
565
444
|
}
|
|
566
445
|
catch {
|
|
567
446
|
}
|
|
568
|
-
if (err instanceof
|
|
447
|
+
if (err instanceof HookRefusal)
|
|
448
|
+
throw err.refuse;
|
|
449
|
+
if (err instanceof SessionError)
|
|
569
450
|
throw err;
|
|
570
451
|
throw new SessionError("storage", `replace of session ${sessionId} failed`, err instanceof Error ? err : undefined);
|
|
571
452
|
}
|
|
@@ -573,23 +454,26 @@ export class PgSessionStore {
|
|
|
573
454
|
conn.release();
|
|
574
455
|
}
|
|
575
456
|
}
|
|
576
|
-
async writeImportedLog(conn, sessionId, owner, validated,
|
|
457
|
+
async writeImportedLog(conn, sessionId, owner, validated, mode) {
|
|
577
458
|
let leafId = null;
|
|
578
459
|
for (const e of validated)
|
|
579
460
|
leafId = leafIdAfterEntry(e);
|
|
580
461
|
const now = new Date();
|
|
581
|
-
const imported = importedRowCells(cells.forkKey);
|
|
582
|
-
await conn.query(`INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation, ${imported.columns}) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)` +
|
|
583
|
-
(mode === "create" ? " ON CONFLICT (session_id) DO NOTHING" : ""), [sessionId, owner, now, now, leafId, validated.length, cells.incarnation, ...imported.values]);
|
|
584
462
|
if (mode === "create") {
|
|
463
|
+
const minted = mintSessionIncarnation();
|
|
464
|
+
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) ON CONFLICT (session_id) DO NOTHING", [sessionId, owner, now, now, leafId, validated.length, minted]);
|
|
585
465
|
const held = await conn.query("SELECT incarnation FROM session_meta WHERE session_id = $1 FOR UPDATE", [sessionId]);
|
|
586
|
-
if (String(held.rows[0]?.incarnation) !==
|
|
466
|
+
if (String(held.rows[0]?.incarnation) !== minted) {
|
|
587
467
|
throw new SessionError("storage", `import of session ${sessionId} failed`, new Error(`session ${sessionId} already exists — importEntries only creates a session (replaceEntries rewrites one); nothing was written`));
|
|
588
468
|
}
|
|
589
469
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
470
|
+
else {
|
|
471
|
+
const rewritten = await conn.query("UPDATE session_meta SET owner = $1, created_at = $2, updated_at = $3, leaf_id = $4, leaf_seq = $5 WHERE session_id = $6 AND owner IS NOT DISTINCT FROM $1 AND placement_kind IS NULL", [owner, now, now, leafId, validated.length, sessionId]);
|
|
472
|
+
if ((rewritten.rowCount ?? 0) !== 1) {
|
|
473
|
+
throw new SessionError("storage", `replace of session ${sessionId} failed`, new Error(`session ${sessionId} belongs to another owner — a replace rewrites only the importer's own session; nothing was written`));
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
settleHookVerdict(await this.hooks.beforeCommit(statementsOn(conn), { op: "import", rootSessionId: sessionId }));
|
|
593
477
|
if (mode === "replace")
|
|
594
478
|
await conn.query("DELETE FROM session_event WHERE session_id = $1", [sessionId]);
|
|
595
479
|
for (let seq = 0; seq < validated.length; seq++) {
|
|
@@ -598,7 +482,7 @@ export class PgSessionStore {
|
|
|
598
482
|
}
|
|
599
483
|
}
|
|
600
484
|
beginImportStaging(realSessionId, token) {
|
|
601
|
-
return new PgStagingHandle(this.pool, realSessionId, stagingIdFor(realSessionId, token));
|
|
485
|
+
return new PgStagingHandle(this.pool, this.hooks, realSessionId, stagingIdFor(realSessionId, token));
|
|
602
486
|
}
|
|
603
487
|
async readStagedEntries(stagingId) {
|
|
604
488
|
const res = await this.pool.query(`SELECT seq, ${jsonColumnText("pg", "payload")} FROM session_event WHERE session_id = $1 ORDER BY seq ASC`, [stagingId]);
|
|
@@ -620,11 +504,9 @@ export class PgSessionStore {
|
|
|
620
504
|
const conn = await this.pool.connect();
|
|
621
505
|
try {
|
|
622
506
|
await conn.query("BEGIN");
|
|
623
|
-
const
|
|
624
|
-
if (
|
|
625
|
-
await
|
|
626
|
-
throw new SessionActiveRunError(sessionId, claim.activeTaskId);
|
|
627
|
-
}
|
|
507
|
+
const locked = await conn.query("SELECT session_id FROM session_meta WHERE session_id = $1 FOR UPDATE", [sessionId]);
|
|
508
|
+
if (locked.rows.length > 0)
|
|
509
|
+
settleHookVerdict(await this.hooks.beforeCommit(statementsOn(conn), { op: "delete", rootSessionId: sessionId }));
|
|
628
510
|
const metaRes = await conn.query("DELETE FROM session_meta WHERE session_id = $1 AND owner IS NOT DISTINCT FROM $2", [sessionId, owner]);
|
|
629
511
|
if ((metaRes.rowCount ?? 0) > 0)
|
|
630
512
|
await conn.query("DELETE FROM session_event WHERE session_id = $1", [sessionId]);
|
|
@@ -637,6 +519,8 @@ export class PgSessionStore {
|
|
|
637
519
|
}
|
|
638
520
|
catch {
|
|
639
521
|
}
|
|
522
|
+
if (err instanceof HookRefusal)
|
|
523
|
+
throw err.refuse;
|
|
640
524
|
throw err;
|
|
641
525
|
}
|
|
642
526
|
finally {
|
|
@@ -652,12 +536,14 @@ export class PgSessionStore {
|
|
|
652
536
|
}
|
|
653
537
|
class PgStagingHandle {
|
|
654
538
|
pool;
|
|
539
|
+
hooks;
|
|
655
540
|
realSessionId;
|
|
656
541
|
stagingId;
|
|
657
542
|
nextSeq = 0;
|
|
658
543
|
seen = new Set();
|
|
659
|
-
constructor(pool, realSessionId, stagingId) {
|
|
544
|
+
constructor(pool, hooks, realSessionId, stagingId) {
|
|
660
545
|
this.pool = pool;
|
|
546
|
+
this.hooks = hooks;
|
|
661
547
|
this.realSessionId = realSessionId;
|
|
662
548
|
this.stagingId = stagingId;
|
|
663
549
|
}
|
|
@@ -704,11 +590,7 @@ class PgStagingHandle {
|
|
|
704
590
|
throw new StagedCommitOwnerChangedError(this.realSessionId);
|
|
705
591
|
}
|
|
706
592
|
}
|
|
707
|
-
|
|
708
|
-
if (claim.activeTaskId !== null) {
|
|
709
|
-
await conn.query("ROLLBACK");
|
|
710
|
-
throw new SessionActiveRunError(this.realSessionId, claim.activeTaskId);
|
|
711
|
-
}
|
|
593
|
+
settleHookVerdict(await this.hooks.beforeCommit(statementsOn(conn), { op: "import", rootSessionId: this.realSessionId }));
|
|
712
594
|
const staged = await conn.query("SELECT entry_id FROM session_event WHERE session_id = $1 ORDER BY seq ASC FOR UPDATE", [this.stagingId]);
|
|
713
595
|
const dst = await conn.query("SELECT entry_id FROM session_event WHERE session_id = $1 ORDER BY seq ASC FOR UPDATE", [this.realSessionId]);
|
|
714
596
|
const dstMeta = await conn.query("SELECT placement_kind FROM session_meta WHERE session_id = $1", [this.realSessionId]);
|
|
@@ -741,16 +623,7 @@ class PgStagingHandle {
|
|
|
741
623
|
let leafId = null;
|
|
742
624
|
if (lastRes.rows[0])
|
|
743
625
|
leafId = leafIdAfterEntry(decodeSessionEventPayload(lastRes.rows[0].payload, this.realSessionId, "last staged row"));
|
|
744
|
-
|
|
745
|
-
if ((settle.rowCount ?? 0) !== 1) {
|
|
746
|
-
const locked = readPlacementRowStatement("pg", this.realSessionId, { lock: true });
|
|
747
|
-
const lockedRow = (await conn.query(locked.text, [...locked.values])).rows[0];
|
|
748
|
-
const cell = lockedRow === undefined ? undefined : placementCellFromRow(this.realSessionId, lockedRow);
|
|
749
|
-
await conn.query("ROLLBACK");
|
|
750
|
-
if (cell?.role === "writer")
|
|
751
|
-
throw new SessionWriterPresentError(this.realSessionId, cell.lane);
|
|
752
|
-
throw new SessionError("storage", `staged commit for ${this.realSessionId}: the commit point matched no row under a held row lock and the row is not a writer — refusing to guess`);
|
|
753
|
-
}
|
|
626
|
+
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]);
|
|
754
627
|
const afterMeta = await conn.query("SELECT placement_kind, owner, incarnation FROM session_meta WHERE session_id = $1", [this.realSessionId]);
|
|
755
628
|
if (afterMeta.rows[0]?.placement_kind != null) {
|
|
756
629
|
await conn.query("ROLLBACK");
|
|
@@ -765,7 +638,9 @@ class PgStagingHandle {
|
|
|
765
638
|
await conn.query("ROLLBACK");
|
|
766
639
|
}
|
|
767
640
|
catch { }
|
|
768
|
-
if (err instanceof
|
|
641
|
+
if (err instanceof HookRefusal)
|
|
642
|
+
throw err.refuse;
|
|
643
|
+
if (err instanceof SyncConflictError || err instanceof SessionError)
|
|
769
644
|
throw err;
|
|
770
645
|
throw new SessionError("storage", `staged commit for ${this.realSessionId} failed`, err instanceof Error ? err : undefined);
|
|
771
646
|
}
|