@sema-agent/server 7.146.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 +384 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/USAGE.md +170 -66
- package/deploy/facts/lanes.json +7 -0
- package/deploy/sema-up/README.md +2 -2
- package/deploy/sema-up/chart/templates/_helpers.tpl +22 -0
- package/deploy/sema-up/chart/templates/server-v4pro.yaml +5 -4
- package/deploy/sema-up/chart/templates/server.yaml +5 -4
- package/deploy/sema-up/chart/values.yaml +1 -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 +4 -3
- package/dist/approval-card.d.ts +4 -4
- 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/device-lane.js +5 -5
- package/dist/boot/engine-lease.d.ts +12 -11
- package/dist/boot/execution-env.d.ts +2 -1
- package/dist/boot/execution-env.js +45 -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 +1 -1
- package/dist/boot/exit-last.js +2 -1
- package/dist/boot/governance-seams.d.ts +2 -2
- 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 +13 -24
- package/dist/boot/memory-consolidation.js +0 -13
- 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.js +1 -1
- package/dist/boot/resolve-spec.js +91 -111
- package/dist/boot/retention-lane.d.ts +4 -42
- package/dist/boot/session-faces.d.ts +1 -0
- package/dist/boot/session-faces.js +8 -2
- package/dist/boot/stage-01-config.d.ts +1 -1
- package/dist/boot/stage-01-config.js +6 -2
- 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 +1 -1
- package/dist/boot/stage-04-stores.d.ts +1 -1
- package/dist/boot/stage-05-execution-env.d.ts +2 -2
- package/dist/boot/stage-05-execution-env.js +3 -3
- package/dist/boot/stage-06-runners.d.ts +3 -3
- package/dist/boot/stage-06-runners.js +1 -3
- package/dist/boot/stage-07-capability-layer.d.ts +3 -3
- package/dist/boot/stage-07-capability-layer.js +4 -9
- package/dist/boot/stage-08-reapers.d.ts +3 -3
- package/dist/boot/stage-09-leader.d.ts +4 -3
- package/dist/boot/stage-10-http-server.d.ts +4 -3
- package/dist/boot/stage-10-http-server.js +2 -1
- package/dist/boot/stores.d.ts +9 -0
- package/dist/boot/stores.js +18 -7
- package/dist/boot/web-admin.js +3 -3
- package/dist/capabilities/center-plugins.d.ts +2 -0
- package/dist/capabilities/center-plugins.js +6 -2
- package/dist/capabilities/composite-members.d.ts +1 -0
- package/dist/capabilities/composite-members.js +1 -0
- 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 +32 -18
- 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 +38 -0
- package/dist/capability-facts.js +34 -1
- package/dist/center-credential-renewer.js +3 -2
- package/dist/config-catalog.js +6 -5
- 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.js +2 -0
- package/dist/config-types.d.ts +190 -186
- package/dist/config.d.ts +68 -5
- package/dist/config.js +80 -54
- package/dist/degenerate-instrument.js +1 -1
- package/dist/deploy-facts.js +3 -2
- package/dist/deployment-shape.d.ts +21 -12
- package/dist/deployment-shape.js +24 -6
- package/dist/env-facts.d.ts +7 -5
- package/dist/env-facts.js +10 -6
- 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 +17 -10
- package/dist/fleet/drain-entry.js +1 -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/command-spawn.d.ts +1 -4
- package/dist/hooks/command-spawn.js +0 -11
- package/dist/hooks/hook-runner.d.ts +52 -8
- package/dist/hooks/hook-runner.js +160 -36
- package/dist/http/approval-sweeps.d.ts +2 -5
- package/dist/http/approval-sweeps.js +3 -2
- package/dist/http/checkpoint-ctx.d.ts +17 -0
- package/dist/http/checkpoint-ctx.js +6 -0
- package/dist/http/claim-latency.d.ts +10 -0
- package/dist/http/claim-latency.js +12 -0
- package/dist/http/dispatch.js +1 -1
- package/dist/http/durable-run-leg.js +2 -1
- package/dist/http/execution-context.d.ts +3 -1
- package/dist/http/execution-context.js +4 -0
- package/dist/http/memory-compliance-failure-reply.d.ts +1 -1
- package/dist/http/memory-compliance-failure-reply.js +3 -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 +3 -14
- package/dist/http/principal-gate.js +0 -13
- package/dist/http/request-guards.d.ts +1 -1
- package/dist/http/resume-legs.d.ts +7 -10
- package/dist/http/resume-legs.js +79 -50
- 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 +32 -16
- package/dist/http/route-table.js +2 -0
- package/dist/http/routes/approvals-assistant.js +5 -25
- package/dist/http/routes/capabilities.js +8 -2
- package/dist/http/routes/devices.js +1 -1
- package/dist/http/routes/memory-origin.d.ts +1 -1
- package/dist/http/routes/notify-wake.js +2 -1
- 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 +8 -8
- package/dist/http/routes/session-sync.js +27 -10
- package/dist/http/routes/sessions.d.ts +7 -1
- package/dist/http/routes/sessions.js +104 -2
- package/dist/http/routes/tasks.js +9 -3
- package/dist/http/server.d.ts +24 -10
- package/dist/http/server.js +17 -6
- package/dist/http/stored-body-replay.d.ts +5 -3
- package/dist/http/stored-body-replay.js +10 -1
- package/dist/http/wire-types.d.ts +19 -10
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/instruction-source-notice.d.ts +1 -1
- 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-scope.d.ts +2 -2
- package/dist/memory-scope.js +1 -1
- 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/observability/fail-open.d.ts +17 -9
- package/dist/observability/fail-open.js +17 -9
- 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/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/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 +9 -4
- 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 +1 -1
- 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 -12
- package/dist/plugins/file-run-store.js +63 -63
- package/dist/plugins/imported-layer-root-retirement.d.ts +17 -8
- 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 +5 -0
- package/dist/plugins/local-checkpoint-store.js +11 -7
- 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 +91 -25
- package/dist/plugins/local-session-store.js +124 -45
- 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 +4 -3
- package/dist/plugins/memory-engine-pg.js +4 -4
- package/dist/plugins/memory-engine-sql-apply.d.ts +13 -4
- package/dist/plugins/memory-engine-sql-apply.js +1 -1
- package/dist/plugins/memory-engine-tidb.d.ts +8 -5
- package/dist/plugins/memory-engine-tidb.js +7 -8
- package/dist/plugins/one-time-migrations.d.ts +145 -35
- package/dist/plugins/one-time-migrations.js +272 -120
- 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 +8 -8
- package/dist/plugins/permission-rule-store-file.js +14 -2
- package/dist/plugins/pg-pool.d.ts +2 -1
- package/dist/plugins/pg-pool.js +11 -4
- 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/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 +11 -1
- 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 +1 -1
- 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/store-backend.d.ts +35 -8
- package/dist/plugins/store-backend.js +184 -62
- package/dist/plugins/store-contracts.d.ts +9 -2
- package/dist/plugins/store-contracts.js +1 -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 +19 -31
- package/dist/plugins/user-input-ledger-sql.d.ts +1 -1
- 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 +41 -13
- package/dist/rules-consent.js +101 -53
- package/dist/run-local.js +17 -13
- package/dist/runs.d.ts +48 -30
- package/dist/runs.js +26 -16
- package/dist/runtime-caps-resolver.d.ts +15 -8
- package/dist/runtime-caps-resolver.js +14 -10
- package/dist/sealed-key-layout.d.ts +2 -3
- package/dist/sealed-key-layout.js +0 -1
- package/dist/sealed-key.js +7 -5
- package/dist/security.d.ts +5 -3
- 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-resident-background.d.ts +85 -32
- package/dist/session-resident-background.js +75 -7
- package/dist/session-row.d.ts +1 -1
- package/dist/session-sync-kernel.d.ts +1 -1
- package/dist/session-workspace.d.ts +11 -2
- package/dist/session-workspace.js +5 -0
- package/dist/sighup-idle.d.ts +1 -1
- package/dist/store-posture.d.ts +2 -1
- package/dist/stored-request-hooks-m20.d.ts +3 -2
- package/dist/stored-request-hooks-m20.js +19 -14
- package/dist/stores-sql/blob-index-sql.d.ts +148 -0
- package/dist/stores-sql/blob-index-sql.js +154 -0
- package/dist/stores-sql/checkpoint-store-sql.d.ts +1 -1
- package/dist/stores-sql/checkpoint-store-sql.js +11 -27
- 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}/pg-session-storage.d.ts +23 -76
- package/dist/{plugins → stores-sql}/pg-session-storage.js +80 -209
- 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}/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 +32 -122
- package/dist/{plugins → stores-sql}/tidb-session-store.js +71 -171
- package/dist/stores-sql/tool-result-store-sql.d.ts +1 -1
- package/dist/stores-sql/tool-result-store-sql.js +1 -1
- package/dist/stores-sql/windowed-session-storage.d.ts +56 -0
- package/dist/stores-sql/windowed-session-storage.js +61 -0
- 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 +9 -4
- package/dist/task-settings.js +17 -6
- package/dist/tool-approval.d.ts +21 -8
- package/dist/tool-approval.js +10 -6
- package/dist/trace/core-keyset-guard.d.ts +9 -3
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +2 -0
- package/dist/trace/exit-seat.d.ts +23 -1
- package/dist/trace/exit-seat.js +3 -0
- package/dist/trace/project.d.ts +33 -16
- package/dist/trace/project.js +12 -9
- package/dist/trace/redact.d.ts +1 -1
- package/dist/trace/redact.js +61 -73
- package/dist/trace/self-minted-wire.d.ts +263 -10
- package/dist/trace/self-minted-wire.js +90 -2
- 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 +38 -2
- package/dist/trace/wire-projection-faces.js +26 -1
- package/dist/unread-request-members.d.ts +66 -0
- package/dist/unread-request-members.js +41 -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 +13 -12
- package/scripts/drain-check-local.mjs +2 -28
- package/scripts/drain-check.mysql.sql +2 -18
- package/scripts/drain-check.pg.sql +2 -18
- package/wire-changes/server@7.147.0.json +813 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **SQL 会话店 = 引擎座店 + 部署的会话格**(两方言同一只类;组合座 `store-backend.ts` 装配)。
|
|
3
|
+
*
|
|
4
|
+
* ## 两半,一只店
|
|
5
|
+
* · **座**(`src/stores-sql/` 的方言店):引擎座表 `session_meta` / `session_event` 的唯一写者 —— 唤醒、追加、fork、导入、暂存、删除。
|
|
6
|
+
* · **部署格**(本模块):本服务记在一只会话上的格,全部住旁表 `session_ext`(属主 `session-ext-table.ts`)—— 列表投影的标题 / 置顶 / 行标签、
|
|
7
|
+
* 换源通告的基线、位置与项目、执行放置那一格与 fork 键。读引擎座表只经它的公布列(身份、属主、化身、时刻、委派判别位);写只写旁表。
|
|
8
|
+
* 座在它的写事务里锁住根行之后调部署的**事务内钩**({@link createSqlSessionWriteHooks},契约在 `stores-sql/session-write-hooks.ts`)——
|
|
9
|
+
* 认领、本处写者、导入落 mirror、fork 键都在座的同一事务里由钩判 / 写(同事务两句:锁根行一句、钩的读写各一句)。
|
|
10
|
+
*
|
|
11
|
+
* ## 一条列表读口
|
|
12
|
+
* 列表面(`listSessions` / `listSessionRows` / `sessionSummaryOf`)是一条 SELECT、一只行映射({@link SqlSessionStore.selectListRows}):
|
|
13
|
+
* `session_meta` LEFT JOIN 旁表(标题 / 置顶;行不在 = 未命名、未置顶)LEFT JOIN run 账本的预览。两方言只差语句文本(窗函数 / `DISTINCT ON`、JSON 标量提取、
|
|
14
|
+
* LIKE 的转义字面量、布尔的读法)。
|
|
15
|
+
*
|
|
16
|
+
* ## 旁表的写序(一条,与执行放置的写序律同一条)
|
|
17
|
+
* 每个格写者在一只事务里:锁根 `session_meta` 行(行不在 ⇒ 不写)→ 补旁表行(行在不动)→ 要判格的再锁定读那一行 → 一句按 `session_id` 定址的格写。根行先 ⇒ 与座的删除 /
|
|
18
|
+
* 导入 / 换装同一锁序,外键的补行不会与删除的级联互等;补行先于锁定读 ⇒ 锁定读永远落在一行在的记录上(不拿 InnoDB 间隙锁)。
|
|
19
|
+
*/
|
|
20
|
+
import type { AcquiredSession, PlacedSessionRow, SessionHead, SessionPlacementRecord, SessionStore, SessionTreeEntry } from "@sema-agent/core";
|
|
21
|
+
import type { TiDBSessionStore } from "../stores-sql/tidb-session-store.js";
|
|
22
|
+
import type { SessionWriteHooks } from "../stores-sql/session-write-hooks.js";
|
|
23
|
+
import type { SqlDriver } from "./sql-driver.js";
|
|
24
|
+
import { type ExecutionPlacementSeat } from "../execution-placement.js";
|
|
25
|
+
import { type InstructionSourceBaseline } from "../instruction-source-notice.js";
|
|
26
|
+
import { type ClientSessionRowWrite, type ReplaySessionRowWrite, type ReplaySessionRowWriteOutcome, type SessionRowAnswer, type SessionRowPatch, type SessionRowWriteOutcome } from "../session-row.js";
|
|
27
|
+
import type { SessionSummary } from "./store-contracts.js";
|
|
28
|
+
import type { SessionWorkspace } from "../session-workspace.js";
|
|
29
|
+
/** 座的面:座的公开成员(两方言同形 —— PG 孪生按结构赋给它,组合座里 tsc 判)。 */
|
|
30
|
+
export type SessionSeat = Pick<TiDBSessionStore, "retention" | "placements" | "durability" | "acquire" | "register" | "placementOf" | "listPlaced" | "headOf" | "touch" | "release" | "forget" | "fork" | "forkTo" | "exportEntries" | "listEntryIds" | "exportEntriesStream" | "importEntries" | "replaceEntries" | "beginImportStaging" | "readStagedEntries" | "sweepStagingSessions" | "deleteSession" | "size" | "dispose">;
|
|
31
|
+
/** 列表面的查询(`OwnerAwareSessionStore.listSessions` 的入参形)。 */
|
|
32
|
+
export interface SessionListOptions {
|
|
33
|
+
owner?: string;
|
|
34
|
+
includeUnowned?: boolean;
|
|
35
|
+
cursor?: {
|
|
36
|
+
lastActivityAt: string;
|
|
37
|
+
sessionId: string;
|
|
38
|
+
};
|
|
39
|
+
limit: number;
|
|
40
|
+
q?: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 会话店的事务内钩(座锁住根行之后、写之前调;同一事务、同一连接):
|
|
44
|
+
* · `import` —— 判官 {@link judgeImportWrite}(local 店的写锁内同调这一只):认领在 ⇒ 拒 `SessionActiveRunError`;这一处是写者 ⇒ 拒 `SessionWriterPresentError`;放行 ⇒ 这一格落 mirror(写者在别处);
|
|
45
|
+
* · `delete` / `retention-delete` —— 认领在 ⇒ 拒 `SessionActiveRunError`(级联随根行带走旁表行,钩不另删);
|
|
46
|
+
* · 键控 fork(服务扩展)—— `record`:新行的 fork 键落旁表(新行是本事务刚插下的,本事务持着它的锁);`targetOf`:撞上已在的目标时的一次池读(属主 + fork 键)。
|
|
47
|
+
*/
|
|
48
|
+
export declare function createSqlSessionWriteHooks(db: Pick<SqlDriver, "dialect" | "query">): SessionWriteHooks;
|
|
49
|
+
export declare class SqlSessionStore implements SessionStore {
|
|
50
|
+
private readonly db;
|
|
51
|
+
/** 座(引擎座表的唯一写者)。 */
|
|
52
|
+
private readonly seat;
|
|
53
|
+
/** 托管留存声明 / placement 声明 / 耐久声明:座的声明逐字转发。 */
|
|
54
|
+
readonly retention: SessionSeat["retention"];
|
|
55
|
+
readonly placements: SessionSeat["placements"];
|
|
56
|
+
readonly durability: SessionSeat["durability"];
|
|
57
|
+
/** 执行席(规则在 `src/execution-placement.ts`;实现与语句的唯一属主 = `execution-placement-sql.ts`)。 */
|
|
58
|
+
readonly executionPlacement: ExecutionPlacementSeat;
|
|
59
|
+
/** `db` = 座的池包成的 SqlDriver(同一只池);`seatOn` 拿部署的事务内钩建座。 */
|
|
60
|
+
constructor(db: SqlDriver, seatOn: (hooks: SessionWriteHooks) => SessionSeat);
|
|
61
|
+
acquire(...a: Parameters<SessionSeat["acquire"]>): Promise<AcquiredSession>;
|
|
62
|
+
register(sessionId: string, owner: string | null): Promise<void>;
|
|
63
|
+
placementOf(sessionId: string): Promise<SessionPlacementRecord | undefined>;
|
|
64
|
+
listPlaced(...a: Parameters<SessionSeat["listPlaced"]>): Promise<PlacedSessionRow[]>;
|
|
65
|
+
headOf(sessionId: string): Promise<SessionHead | undefined>;
|
|
66
|
+
touch(sessionId: string): Promise<void>;
|
|
67
|
+
release(sessionId: string): Promise<void>;
|
|
68
|
+
forget(sessionId: string): void;
|
|
69
|
+
fork(...a: Parameters<SessionSeat["fork"]>): ReturnType<SessionSeat["fork"]>;
|
|
70
|
+
forkTo(...a: Parameters<SessionSeat["forkTo"]>): ReturnType<SessionSeat["forkTo"]>;
|
|
71
|
+
exportEntries(sessionId: string): Promise<SessionTreeEntry[] | null>;
|
|
72
|
+
listEntryIds(sessionId: string): Promise<string[] | null>;
|
|
73
|
+
exportEntriesStream(...a: Parameters<SessionSeat["exportEntriesStream"]>): ReturnType<SessionSeat["exportEntriesStream"]>;
|
|
74
|
+
importEntries(sessionId: string, owner: string | null, entries: SessionTreeEntry[]): Promise<void>;
|
|
75
|
+
replaceEntries(sessionId: string, owner: string | null, entries: SessionTreeEntry[]): Promise<void>;
|
|
76
|
+
beginImportStaging(...a: Parameters<SessionSeat["beginImportStaging"]>): ReturnType<SessionSeat["beginImportStaging"]>;
|
|
77
|
+
readStagedEntries(stagingId: string): Promise<SessionTreeEntry[]>;
|
|
78
|
+
sweepStagingSessions(): Promise<number>;
|
|
79
|
+
deleteSession(sessionId: string, owner: string | null): Promise<boolean>;
|
|
80
|
+
get size(): number;
|
|
81
|
+
dispose(): void;
|
|
82
|
+
/**
|
|
83
|
+
* E16 — list the caller's sessions (CC /resume picker), keyset-paginated newest-first by last activity. Session IDENTITY/enumeration comes from
|
|
84
|
+
* `session_meta` (owner-filtered on its owner column); the side table supplies the title / pin; a LEFT JOIN onto the `task_run` runs ledger
|
|
85
|
+
* supplies run-level PREVIEW (latest run's objective/status, run count) but never gates which sessions exist. Keyset on `(updated_at, session_id)`.
|
|
86
|
+
*/
|
|
87
|
+
listSessions(opts: SessionListOptions): Promise<SessionSummary[]>;
|
|
88
|
+
/** {@link listSessions} with each row's occupant (owner + incarnation) from the same SELECT — `OwnerAwareSessionStore.listSessionRows`; the incarnation never enters the row. */
|
|
89
|
+
listSessionRows(opts: SessionListOptions): Promise<Array<SessionRowAnswer<SessionSummary>>>;
|
|
90
|
+
/** ONE session's list row — {@link listSessions}' projection (same SELECT, same mapper) scoped to one id; `undefined` = not a row of the list face
|
|
91
|
+
* (no such session / placed). The run-preview subqueries are scoped to the same id. */
|
|
92
|
+
sessionSummaryOf(sessionId: string): Promise<SessionSummary | undefined>;
|
|
93
|
+
/** {@link sessionSummaryOf} 那一读 + 读到它时行上的住客(写原语的应答行要复核住客,`settleSessionRowWrite`)。 */
|
|
94
|
+
private listRowOf;
|
|
95
|
+
/**
|
|
96
|
+
* The list projection's ONE SELECT + row mapper ({@link listSessions} and {@link sessionSummaryOf} both read through it). `runSessionId` scopes the
|
|
97
|
+
* two run-preview subqueries to one session (the single-row read); the list leaves them whole. Placeholders are minted in text order (run scope,
|
|
98
|
+
* then `where`, then the LIMIT), so one builder serves both dialects. `sm.incarnation` is read for the row tag and the answer row's occupant only.
|
|
99
|
+
* The run-preview JOIN: the LATEST run per session (ties broken by task_id so EXACTLY one row joins) + a count rollup; LEFT JOINs keep sessions with
|
|
100
|
+
* no runs (preview NULL, run_count 0) and sessions with no side-table row (title NULL, not pinned).
|
|
101
|
+
*/
|
|
102
|
+
private selectListRows;
|
|
103
|
+
/**
|
|
104
|
+
* 会话行格写原语 —— 用户标题 / 置顶的唯一写口(规则与判序:`src/session-row.ts` 头注「写原语」;事务与语句:`session-row-sql.ts`)。应答行在提交 / 回滚之后
|
|
105
|
+
* 由列表行读另读(含新 `rowTag`)、复核仍是判过的那一位住客。只写列表面上的行(委派分区行 ⇒ `absent`);`updated_at` 不动(改名不是活动)。
|
|
106
|
+
*/
|
|
107
|
+
writeSessionRowCells(sessionId: string, cells: SessionRowPatch, how: ClientSessionRowWrite): Promise<SessionRowWriteOutcome<SessionSummary>>;
|
|
108
|
+
writeSessionRowCells(sessionId: string, cells: SessionRowPatch, how: ReplaySessionRowWrite): Promise<ReplaySessionRowWriteOutcome<SessionSummary>>;
|
|
109
|
+
/** Cheap pre-LLM probe: "none" = session_meta row not yet REGISTERED (createRun fires before the Runner's session acquire — the titler retries),
|
|
110
|
+
* "untitled" / "titled" as read (no side-table row = untitled). */
|
|
111
|
+
probeTitle(sessionId: string): Promise<"none" | "untitled" | "titled">;
|
|
112
|
+
/** The auto-titler's write: lands ONLY when title IS NULL (a concurrent/prior title — the user's included — wins; writer order: `src/session-row.ts`).
|
|
113
|
+
* Returns whether this call set it. No session row ⇒ `false`. */
|
|
114
|
+
setTitleIfNull(sessionId: string, title: string): Promise<boolean>;
|
|
115
|
+
/** 换源通告的基线读(一行 PK 读;列的三态读法见 `instruction-source-notice.ts`)。旁表行不在 ⇒ 读作「没记过」。语句期限 {@link BASELINE_IO_DEADLINE_MS}(过期 ⇒ 查询自己拒、连接销毁)。 */
|
|
116
|
+
readInstructionSource(sessionId: string): Promise<InstructionSourceBaseline>;
|
|
117
|
+
/**
|
|
118
|
+
* CAS 覆写基线(期望值 = 调用方读到的那一份;不符 ⇒ 0 行、不抛 —— 契约见 `InstructionSourceStore`)。会话行不在 ⇒ 不写。**不**碰 `updated_at`。
|
|
119
|
+
* 期限:事务里每一句带语句期限 {@link BASELINE_IO_DEADLINE_MS} —— 被行锁卡住的写由查询自己在期限内拒、连接销毁(事务随连接关闭回滚,池里不留卡住的连接);
|
|
120
|
+
* 调用方的 `withinIoDeadline` 同一只数,是它那一侧的放手。
|
|
121
|
+
*/
|
|
122
|
+
recordInstructionSource(sessionId: string, expected: InstructionSourceBaseline, source: string | null): Promise<void>;
|
|
123
|
+
/** 会话行的位置与项目那一格(域律 `src/session-workspace.ts`,列属主 `session-workspace-cell.ts`)。一行 PK 读,按文本投影、恰好解一次;行不在 / 格为 NULL ⇒ `undefined`。 */
|
|
124
|
+
readSessionWorkspace(sessionId: string): Promise<SessionWorkspace | undefined>;
|
|
125
|
+
/**
|
|
126
|
+
* 整格覆写(最后一帧胜;唯一调用方 = 腿的记录座)。**住客围栏**:只写化身仍是 `incarnation` 的那一行(锁根行时按化身收窄)—— 行不在、或同 id 已换了住客 ⇒
|
|
127
|
+
* 不写、不抛。**不**碰 `updated_at`。
|
|
128
|
+
*/
|
|
129
|
+
recordSessionWorkspace(sessionId: string, incarnation: string, workspace: SessionWorkspace): Promise<void>;
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=session-store-sql.d.ts.map
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import { escapeLike } from "./sql-escape.js";
|
|
2
|
+
import { jsonColumnText } from "./sql-json-column.js";
|
|
3
|
+
import { pgSafeJsonStringify } from "./pg-safe-json.js";
|
|
4
|
+
import { SESSION_EXT_TABLE, ensureSessionExtRowStatement } from "./session-ext-table.js";
|
|
5
|
+
import { decodeSessionWorkspaceCell, encodeSessionWorkspaceCell } from "./session-workspace-cell.js";
|
|
6
|
+
import { createSqlExecutionPlacementSeat, markMirrorStatement, readCellOn, readClaimOn, writeCellOn } from "./execution-placement-sql.js";
|
|
7
|
+
import { SessionActiveRunError, judgeImportWrite } from "../execution-placement.js";
|
|
8
|
+
import { BASELINE_IO_DEADLINE_MS, decodeInstructionSource, encodeInstructionSource } from "../instruction-source-notice.js";
|
|
9
|
+
import { incarnationFromCell } from "../session-incarnation.js";
|
|
10
|
+
import { sessionRowTagKey } from "../session-row.js";
|
|
11
|
+
import { writeSessionRowCellsSql } from "./session-row-sql.js";
|
|
12
|
+
function binder(dialect) {
|
|
13
|
+
const values = [];
|
|
14
|
+
return {
|
|
15
|
+
values,
|
|
16
|
+
p: (v) => {
|
|
17
|
+
values.push(v);
|
|
18
|
+
return dialect === "pg" ? `$${values.length}` : "?";
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
const rowsOnTxn = (txn) => async (sql, values) => (await txn.query(sql, [...values])).rows;
|
|
23
|
+
export function createSqlSessionWriteHooks(db) {
|
|
24
|
+
const dialect = db.dialect;
|
|
25
|
+
return {
|
|
26
|
+
async beforeCommit(txn, write) {
|
|
27
|
+
const rows = rowsOnTxn(txn);
|
|
28
|
+
const root = write.rootSessionId;
|
|
29
|
+
switch (write.op) {
|
|
30
|
+
case "import": {
|
|
31
|
+
const verdict = await judgeImportWrite(root, { claim: () => readClaimOn(rows, dialect, root), cell: () => readCellOn(rows, dialect, root) });
|
|
32
|
+
if (verdict !== "proceed")
|
|
33
|
+
return verdict;
|
|
34
|
+
await writeCellOn(txn, dialect, root, markMirrorStatement(dialect, root));
|
|
35
|
+
return "proceed";
|
|
36
|
+
}
|
|
37
|
+
case "delete":
|
|
38
|
+
case "retention-delete": {
|
|
39
|
+
const claim = await readClaimOn(rows, dialect, root);
|
|
40
|
+
return claim === null ? "proceed" : { refuse: new SessionActiveRunError(root, claim) };
|
|
41
|
+
}
|
|
42
|
+
default: {
|
|
43
|
+
const unknownOp = write.op;
|
|
44
|
+
throw new Error(`session write hooks: no write ${JSON.stringify(unknownOp)}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
keyedFork: {
|
|
49
|
+
async record(txn, fork) {
|
|
50
|
+
const ensure = ensureSessionExtRowStatement(dialect, fork.rootSessionId);
|
|
51
|
+
await txn.query(ensure.text, [...ensure.values]);
|
|
52
|
+
const b = binder(dialect);
|
|
53
|
+
const res = await txn.query(`UPDATE ${SESSION_EXT_TABLE} SET fork_key = ${b.p(fork.forkKey)} WHERE session_id = ${b.p(fork.rootSessionId)}`, b.values);
|
|
54
|
+
if (res.affected !== 1)
|
|
55
|
+
throw new Error(`fork of session ${fork.rootSessionId}: the fork key write matched no row of a row this transaction just created — engine/driver anomaly, refusing to guess`);
|
|
56
|
+
},
|
|
57
|
+
async targetOf(sessionId) {
|
|
58
|
+
const b = binder(dialect);
|
|
59
|
+
const row = (await db.query(`SELECT sm.owner, se.fork_key FROM session_meta sm LEFT JOIN ${SESSION_EXT_TABLE} se ON se.session_id = sm.session_id WHERE sm.session_id = ${b.p(sessionId)}`, b.values)).rows[0];
|
|
60
|
+
if (row === undefined)
|
|
61
|
+
return undefined;
|
|
62
|
+
return { owner: row.owner === null || row.owner === undefined ? null : String(row.owner), forkKey: row.fork_key === null || row.fork_key === undefined ? null : String(row.fork_key) };
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
const BASELINE_STATEMENT = { deadlineMs: BASELINE_IO_DEADLINE_MS };
|
|
68
|
+
async function withLockedSessionExt(db, sessionId, lock, write) {
|
|
69
|
+
const conn = await db.connect();
|
|
70
|
+
const query = (sql, params) => conn.query(sql, params, lock.statement);
|
|
71
|
+
try {
|
|
72
|
+
await conn.begin();
|
|
73
|
+
const b = binder(db.dialect);
|
|
74
|
+
const locked = await query(`SELECT session_id FROM session_meta WHERE session_id = ${b.p(sessionId)}${lock.where === undefined ? "" : ` AND ${lock.where(b.p)}`} FOR UPDATE`, b.values);
|
|
75
|
+
if (locked.rows.length === 0) {
|
|
76
|
+
await conn.rollback();
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
const ensure = ensureSessionExtRowStatement(db.dialect, sessionId);
|
|
80
|
+
await query(ensure.text, [...ensure.values]);
|
|
81
|
+
const wrote = await write(query);
|
|
82
|
+
await conn.commit();
|
|
83
|
+
return wrote;
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
await conn.rollback().catch(() => undefined);
|
|
87
|
+
throw err;
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
conn.release();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const pinnedOf = (dialect, v) => (dialect === "pg" ? v === true : Number(v) === 1);
|
|
94
|
+
export class SqlSessionStore {
|
|
95
|
+
db;
|
|
96
|
+
seat;
|
|
97
|
+
retention;
|
|
98
|
+
placements;
|
|
99
|
+
durability;
|
|
100
|
+
executionPlacement;
|
|
101
|
+
constructor(db, seatOn) {
|
|
102
|
+
this.db = db;
|
|
103
|
+
this.seat = seatOn(createSqlSessionWriteHooks(db));
|
|
104
|
+
this.retention = this.seat.retention;
|
|
105
|
+
this.placements = this.seat.placements;
|
|
106
|
+
this.durability = this.seat.durability;
|
|
107
|
+
this.executionPlacement = createSqlExecutionPlacementSeat(db);
|
|
108
|
+
}
|
|
109
|
+
acquire(...a) {
|
|
110
|
+
return this.seat.acquire(...a);
|
|
111
|
+
}
|
|
112
|
+
register(sessionId, owner) {
|
|
113
|
+
return this.seat.register(sessionId, owner);
|
|
114
|
+
}
|
|
115
|
+
placementOf(sessionId) {
|
|
116
|
+
return this.seat.placementOf(sessionId);
|
|
117
|
+
}
|
|
118
|
+
listPlaced(...a) {
|
|
119
|
+
return this.seat.listPlaced(...a);
|
|
120
|
+
}
|
|
121
|
+
headOf(sessionId) {
|
|
122
|
+
return this.seat.headOf(sessionId);
|
|
123
|
+
}
|
|
124
|
+
touch(sessionId) {
|
|
125
|
+
return this.seat.touch(sessionId);
|
|
126
|
+
}
|
|
127
|
+
release(sessionId) {
|
|
128
|
+
return this.seat.release(sessionId);
|
|
129
|
+
}
|
|
130
|
+
forget(sessionId) {
|
|
131
|
+
this.seat.forget(sessionId);
|
|
132
|
+
}
|
|
133
|
+
fork(...a) {
|
|
134
|
+
return this.seat.fork(...a);
|
|
135
|
+
}
|
|
136
|
+
forkTo(...a) {
|
|
137
|
+
return this.seat.forkTo(...a);
|
|
138
|
+
}
|
|
139
|
+
exportEntries(sessionId) {
|
|
140
|
+
return this.seat.exportEntries(sessionId);
|
|
141
|
+
}
|
|
142
|
+
listEntryIds(sessionId) {
|
|
143
|
+
return this.seat.listEntryIds(sessionId);
|
|
144
|
+
}
|
|
145
|
+
exportEntriesStream(...a) {
|
|
146
|
+
return this.seat.exportEntriesStream(...a);
|
|
147
|
+
}
|
|
148
|
+
importEntries(sessionId, owner, entries) {
|
|
149
|
+
return this.seat.importEntries(sessionId, owner, entries);
|
|
150
|
+
}
|
|
151
|
+
replaceEntries(sessionId, owner, entries) {
|
|
152
|
+
return this.seat.replaceEntries(sessionId, owner, entries);
|
|
153
|
+
}
|
|
154
|
+
beginImportStaging(...a) {
|
|
155
|
+
return this.seat.beginImportStaging(...a);
|
|
156
|
+
}
|
|
157
|
+
readStagedEntries(stagingId) {
|
|
158
|
+
return this.seat.readStagedEntries(stagingId);
|
|
159
|
+
}
|
|
160
|
+
sweepStagingSessions() {
|
|
161
|
+
return this.seat.sweepStagingSessions();
|
|
162
|
+
}
|
|
163
|
+
deleteSession(sessionId, owner) {
|
|
164
|
+
return this.seat.deleteSession(sessionId, owner);
|
|
165
|
+
}
|
|
166
|
+
get size() {
|
|
167
|
+
return this.seat.size;
|
|
168
|
+
}
|
|
169
|
+
dispose() {
|
|
170
|
+
this.seat.dispose();
|
|
171
|
+
}
|
|
172
|
+
async listSessions(opts) {
|
|
173
|
+
return (await this.listSessionRows(opts)).map((a) => a.row);
|
|
174
|
+
}
|
|
175
|
+
async listSessionRows(opts) {
|
|
176
|
+
const dialect = this.db.dialect;
|
|
177
|
+
return this.selectListRows({
|
|
178
|
+
limit: opts.limit,
|
|
179
|
+
where: (p) => {
|
|
180
|
+
const where = ["sm.placement_kind IS NULL"];
|
|
181
|
+
if (opts.owner !== undefined)
|
|
182
|
+
where.push(opts.includeUnowned ? `(sm.owner = ${p(opts.owner)} OR sm.owner IS NULL)` : `sm.owner = ${p(opts.owner)}`);
|
|
183
|
+
if (opts.q) {
|
|
184
|
+
const escape = dialect === "pg" ? "'\\'" : "'\\\\'";
|
|
185
|
+
where.push(`EXISTS (SELECT 1 FROM task_run tr WHERE tr.session_id = sm.session_id AND LOWER(tr.objective_preview) LIKE LOWER(${p(`%${escapeLike(opts.q)}%`)}) ESCAPE ${escape})`);
|
|
186
|
+
}
|
|
187
|
+
if (opts.cursor) {
|
|
188
|
+
const at = new Date(opts.cursor.lastActivityAt);
|
|
189
|
+
where.push(`(sm.updated_at < ${p(at)} OR (sm.updated_at = ${p(at)} AND sm.session_id < ${p(opts.cursor.sessionId)}))`);
|
|
190
|
+
}
|
|
191
|
+
return where;
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
async sessionSummaryOf(sessionId) {
|
|
196
|
+
return (await this.listRowOf(sessionId))?.row;
|
|
197
|
+
}
|
|
198
|
+
async listRowOf(sessionId) {
|
|
199
|
+
return (await this.selectListRows({ limit: 1, runSessionId: sessionId, where: (p) => ["sm.placement_kind IS NULL", `sm.session_id = ${p(sessionId)}`] }))[0];
|
|
200
|
+
}
|
|
201
|
+
async selectListRows(q) {
|
|
202
|
+
const dialect = this.db.dialect;
|
|
203
|
+
const b = binder(dialect);
|
|
204
|
+
const runWhere = () => (q.runSessionId !== undefined ? ` WHERE session_id = ${b.p(q.runSessionId)}` : "");
|
|
205
|
+
const runCount = `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 `;
|
|
206
|
+
const latest = dialect === "pg"
|
|
207
|
+
? "LEFT JOIN (SELECT DISTINCT ON (session_id) session_id, objective_preview, status, task_id, result ->> 'runId' AS last_task_run_id FROM task_run" +
|
|
208
|
+
runWhere() +
|
|
209
|
+
" ORDER BY session_id, created_at DESC, task_id DESC) latest ON latest.session_id = sm.session_id "
|
|
210
|
+
: "LEFT JOIN (SELECT session_id, objective_preview, status, task_id, last_task_run_id FROM (" +
|
|
211
|
+
" SELECT session_id, objective_preview, status, task_id, JSON_UNQUOTE(JSON_EXTRACT(result, '$.runId')) AS last_task_run_id, " +
|
|
212
|
+
" ROW_NUMBER() OVER (PARTITION BY session_id ORDER BY created_at DESC, task_id DESC) AS rn FROM task_run" +
|
|
213
|
+
runWhere() +
|
|
214
|
+
" ) lr WHERE rn = 1) latest ON latest.session_id = sm.session_id ";
|
|
215
|
+
const where = q.where(b.p);
|
|
216
|
+
const sql = "SELECT sm.session_id, sm.owner, sm.incarnation, sm.updated_at AS last_activity, sm.created_at AS first_activity, se.title, se.pinned, " +
|
|
217
|
+
" 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 " +
|
|
218
|
+
"FROM session_meta sm " +
|
|
219
|
+
`LEFT JOIN ${SESSION_EXT_TABLE} se ON se.session_id = sm.session_id ` +
|
|
220
|
+
runCount +
|
|
221
|
+
latest +
|
|
222
|
+
(where.length ? "WHERE " + where.join(" AND ") + " " : "") +
|
|
223
|
+
`ORDER BY sm.updated_at DESC, sm.session_id DESC LIMIT ${b.p(q.limit)}`;
|
|
224
|
+
const rows = (await this.db.query(sql, b.values)).rows;
|
|
225
|
+
return rows.map((r) => {
|
|
226
|
+
const runId = r.last_task_run_id;
|
|
227
|
+
const sessionId = String(r.session_id);
|
|
228
|
+
const owner = r.owner ?? null;
|
|
229
|
+
const incarnation = incarnationFromCell(sessionId, r.incarnation, "absent");
|
|
230
|
+
const title = r.title ?? null;
|
|
231
|
+
const pinned = pinnedOf(dialect, r.pinned);
|
|
232
|
+
const row = {
|
|
233
|
+
sessionId,
|
|
234
|
+
owner,
|
|
235
|
+
lastActivityAt: r.last_activity instanceof Date ? r.last_activity.toISOString() : String(r.last_activity),
|
|
236
|
+
firstActivityAt: r.first_activity instanceof Date ? r.first_activity.toISOString() : String(r.first_activity),
|
|
237
|
+
runCount: Number(r.run_count),
|
|
238
|
+
objectivePreview: r.objective_preview ?? null,
|
|
239
|
+
lastStatus: r.status == null ? "" : String(r.status),
|
|
240
|
+
lastRunId: r.last_run_id ?? null,
|
|
241
|
+
...(typeof runId === "string" && runId !== "" ? { lastTaskRunId: runId } : {}),
|
|
242
|
+
title,
|
|
243
|
+
pinned,
|
|
244
|
+
...sessionRowTagKey({ sessionId, owner, incarnation, cells: { title, pinned } }),
|
|
245
|
+
};
|
|
246
|
+
return { row, occupant: { owner, incarnation } };
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
async writeSessionRowCells(sessionId, cells, how) {
|
|
250
|
+
return writeSessionRowCellsSql(this.db, sessionId, cells, how, (id) => this.listRowOf(id));
|
|
251
|
+
}
|
|
252
|
+
async probeTitle(sessionId) {
|
|
253
|
+
const b = binder(this.db.dialect);
|
|
254
|
+
const row = (await this.db.query(`SELECT se.title FROM session_meta sm LEFT JOIN ${SESSION_EXT_TABLE} se ON se.session_id = sm.session_id WHERE sm.session_id = ${b.p(sessionId)}`, b.values)).rows[0];
|
|
255
|
+
if (row === undefined)
|
|
256
|
+
return "none";
|
|
257
|
+
return row.title === null || row.title === undefined ? "untitled" : "titled";
|
|
258
|
+
}
|
|
259
|
+
async setTitleIfNull(sessionId, title) {
|
|
260
|
+
return withLockedSessionExt(this.db, sessionId, {}, async (query) => {
|
|
261
|
+
const b = binder(this.db.dialect);
|
|
262
|
+
return (await query(`UPDATE ${SESSION_EXT_TABLE} SET title = ${b.p(title)} WHERE session_id = ${b.p(sessionId)} AND title IS NULL`, b.values)).affected > 0;
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
async readInstructionSource(sessionId) {
|
|
266
|
+
const b = binder(this.db.dialect);
|
|
267
|
+
const conn = await this.db.connect();
|
|
268
|
+
try {
|
|
269
|
+
const row = (await conn.query(`SELECT instruction_source FROM ${SESSION_EXT_TABLE} WHERE session_id = ${b.p(sessionId)}`, b.values, BASELINE_STATEMENT)).rows[0];
|
|
270
|
+
return decodeInstructionSource(row?.instruction_source);
|
|
271
|
+
}
|
|
272
|
+
finally {
|
|
273
|
+
conn.release();
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
async recordInstructionSource(sessionId, expected, source) {
|
|
277
|
+
const next = encodeInstructionSource(source);
|
|
278
|
+
await withLockedSessionExt(this.db, sessionId, { statement: BASELINE_STATEMENT }, async (query) => {
|
|
279
|
+
const b = binder(this.db.dialect);
|
|
280
|
+
const text = `UPDATE ${SESSION_EXT_TABLE} SET instruction_source = ${b.p(next)} WHERE session_id = ${b.p(sessionId)} AND ${expected.recorded ? `instruction_source = ${b.p(encodeInstructionSource(expected.source))}` : "instruction_source IS NULL"}`;
|
|
281
|
+
return (await query(text, b.values)).affected > 0;
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
async readSessionWorkspace(sessionId) {
|
|
285
|
+
const b = binder(this.db.dialect);
|
|
286
|
+
const row = (await this.db.query(`SELECT ${jsonColumnText(this.db.dialect, "workspace")} FROM ${SESSION_EXT_TABLE} WHERE session_id = ${b.p(sessionId)}`, b.values)).rows[0];
|
|
287
|
+
return row === undefined ? undefined : decodeSessionWorkspaceCell(row.workspace, sessionId);
|
|
288
|
+
}
|
|
289
|
+
async recordSessionWorkspace(sessionId, incarnation, workspace) {
|
|
290
|
+
const dialect = this.db.dialect;
|
|
291
|
+
const cell = encodeSessionWorkspaceCell(workspace, dialect === "pg" ? pgSafeJsonStringify : JSON.stringify, sessionId);
|
|
292
|
+
await withLockedSessionExt(this.db, sessionId, { where: (p) => `incarnation = ${p(incarnation)}` }, async (query) => {
|
|
293
|
+
const b = binder(dialect);
|
|
294
|
+
return (await query(`UPDATE ${SESSION_EXT_TABLE} SET workspace = ${b.p(cell)}${dialect === "pg" ? "::jsonb" : ""} WHERE session_id = ${b.p(sessionId)}`, b.values)).affected > 0;
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
//# sourceMappingURL=session-store-sql.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { SessionWorkspace } from "../session-workspace.js";
|
|
2
2
|
import { type JsonColumnReader } from "./sql-json-column.js";
|
|
3
|
-
/** 列定义的唯一属主(
|
|
4
|
-
export declare const
|
|
3
|
+
/** 列定义的唯一属主(旁表建表句内插它,`session-ext-table.ts`)。 */
|
|
4
|
+
export declare const SESSION_WORKSPACE_COLUMN: {
|
|
5
5
|
readonly name: "workspace";
|
|
6
6
|
readonly mysql: "workspace JSON NULL";
|
|
7
7
|
readonly pg: "workspace JSONB";
|
|
@@ -2,7 +2,7 @@ import { PROJECT_ID_REGEX } from "@sema-agent/core";
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
4
4
|
import { assertJsonColumnWritable, createZodColumnReader, decodeJsonColumn, isJsonColumnBodyProblem } from "./sql-json-column.js";
|
|
5
|
-
export const
|
|
5
|
+
export const SESSION_WORKSPACE_COLUMN = {
|
|
6
6
|
name: "workspace",
|
|
7
7
|
mysql: "workspace JSON NULL",
|
|
8
8
|
pg: "workspace JSONB",
|
|
@@ -16,7 +16,7 @@ const SESSION_WORKSPACE_SHAPE = z.object({
|
|
|
16
16
|
});
|
|
17
17
|
export const readSessionWorkspaceCell = createZodColumnReader(SESSION_WORKSPACE_SHAPE);
|
|
18
18
|
export const SESSION_WORKSPACE_DEGRADED = "server.session-workspace.record-degraded";
|
|
19
|
-
const where = (sessionId) => `
|
|
19
|
+
const where = (sessionId) => `session_ext.workspace for session ${sessionId}`;
|
|
20
20
|
export function encodeSessionWorkspaceCell(workspace, encode, sessionId) {
|
|
21
21
|
const text = encode(workspace);
|
|
22
22
|
assertJsonColumnWritable(text, readSessionWorkspaceCell, where(sessionId));
|
|
@@ -131,6 +131,16 @@ export interface SqlSharedMemoryStoreOptions {
|
|
|
131
131
|
* This module keeps no copy of the pattern (nor of the `__` ban): the protocol table's namespace separator is core's to spell.
|
|
132
132
|
*/
|
|
133
133
|
export declare function assertStoreId(storeId: string): void;
|
|
134
|
+
/**
|
|
135
|
+
* MINT-side org scope key admission. The rule is core's own judge ({@link isOrgScopeKey}): `org:` plus one segment spelled by core's
|
|
136
|
+
* `formatOrgScope`. This module keeps no copy of the grammar, and no width bound of its own — core's segment bound is far inside the
|
|
137
|
+
* `scope_key` column.
|
|
138
|
+
*
|
|
139
|
+
* Called only where a key ENTERS a durable row: {@link SqlSharedMemoryStore.putScopeState} and {@link SqlSharedMemoryStore.putStore}.
|
|
140
|
+
* The other write methods take the key as a FENCE against the owner already registered for the store id ({@link ownedExactly}: a byte-for-byte
|
|
141
|
+
* match, loud on a miss), so they never judge its shape: a row stored under a key this judge now refuses stays addressable — it can still be
|
|
142
|
+
* written to and deleted, which is the way out for an operator who must re-home it under a key core accepts.
|
|
143
|
+
*/
|
|
134
144
|
export declare function assertScopeKey(scopeKey: string): void;
|
|
135
145
|
/**
|
|
136
146
|
* WRITE-side document-path admission: core's own judge ({@link isSharedMemoryDocumentPath} — the SAME predicate core's listing
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { isSharedMemoryDocumentPath, isSharedMemoryStoreId, SHARED_MEMORY_READ_CAP_BYTES, SharedMemoryStoreError, } from "@sema-agent/core";
|
|
1
|
+
import { isOrgScopeKey, isSharedMemoryDocumentPath, isSharedMemoryStoreId, SHARED_MEMORY_READ_CAP_BYTES, SharedMemoryStoreError, } from "@sema-agent/core";
|
|
2
2
|
import { mysqlDriver, pgDriver } from "./sql-driver.js";
|
|
3
3
|
import { isDupKeyError } from "./sql-errors.js";
|
|
4
4
|
const isDupKey = isDupKeyError;
|
|
5
5
|
export const SHARED_MEMORY_SCOPE_TABLE = "shared_memory_scope";
|
|
6
6
|
export const SHARED_MEMORY_STORE_TABLE = "shared_memory_store";
|
|
7
7
|
export const SHARED_MEMORY_ENTRY_TABLE = "shared_memory_entry";
|
|
8
|
-
const ORG_SCOPE_RE = /^org:\S{1,185}$/;
|
|
9
8
|
const PATH_MAX_CHARS = 512;
|
|
10
9
|
const DESCRIPTION_MAX_CHARS = 512;
|
|
11
10
|
const DEFAULT_MAX_LIST_LIMIT = 500;
|
|
@@ -15,10 +14,14 @@ export function assertStoreId(storeId) {
|
|
|
15
14
|
}
|
|
16
15
|
}
|
|
17
16
|
export function assertScopeKey(scopeKey) {
|
|
18
|
-
if (!
|
|
19
|
-
throw new Error(`shared-memory scope key ${JSON.stringify(scopeKey)} is not usable: it
|
|
17
|
+
if (!isOrgScopeKey(scopeKey)) {
|
|
18
|
+
throw new Error(`shared-memory scope key ${JSON.stringify(scopeKey)} is not usable: core's org scope key rule refuses it (spell the org with core's formatOrgScope: "org:" plus the name's percent-encoded segment)`);
|
|
20
19
|
}
|
|
21
20
|
}
|
|
21
|
+
function ownedExactly(rows, scopeKey) {
|
|
22
|
+
const row = rows[0];
|
|
23
|
+
return row !== undefined && String(row.scope_key) === scopeKey;
|
|
24
|
+
}
|
|
22
25
|
export function assertDocumentPath(path) {
|
|
23
26
|
const refuse = (why) => {
|
|
24
27
|
throw new Error(`shared-memory document path ${JSON.stringify(path)} is not usable: ${why}`);
|
|
@@ -267,7 +270,6 @@ export class SqlSharedMemoryStore {
|
|
|
267
270
|
}
|
|
268
271
|
async putDocument(record) {
|
|
269
272
|
assertStoreId(record.storeId);
|
|
270
|
-
assertScopeKey(record.scopeKey);
|
|
271
273
|
assertDocumentPath(record.path);
|
|
272
274
|
const sizeBytes = Buffer.byteLength(record.content, "utf8");
|
|
273
275
|
if (sizeBytes > SHARED_MEMORY_READ_CAP_BYTES) {
|
|
@@ -278,8 +280,8 @@ export class SqlSharedMemoryStore {
|
|
|
278
280
|
const conn = await this.db.connect();
|
|
279
281
|
try {
|
|
280
282
|
await conn.begin();
|
|
281
|
-
const registered = await conn.query(this.q(`SELECT store_id FROM ${SHARED_MEMORY_STORE_TABLE} WHERE store_id = ? AND scope_key = ? FOR UPDATE`, `SELECT store_id FROM ${SHARED_MEMORY_STORE_TABLE} WHERE store_id = $1 AND scope_key = $2 FOR UPDATE`), [record.storeId, record.scopeKey]);
|
|
282
|
-
if (registered.rows.
|
|
283
|
+
const registered = await conn.query(this.q(`SELECT store_id, scope_key FROM ${SHARED_MEMORY_STORE_TABLE} WHERE store_id = ? AND scope_key = ? FOR UPDATE`, `SELECT store_id, scope_key FROM ${SHARED_MEMORY_STORE_TABLE} WHERE store_id = $1 AND scope_key = $2 FOR UPDATE`), [record.storeId, record.scopeKey]);
|
|
284
|
+
if (!ownedExactly(registered.rows, record.scopeKey)) {
|
|
283
285
|
unregistered = true;
|
|
284
286
|
await conn.rollback();
|
|
285
287
|
}
|
|
@@ -303,14 +305,13 @@ export class SqlSharedMemoryStore {
|
|
|
303
305
|
}
|
|
304
306
|
async deleteDocument(storeId, scopeKey, path) {
|
|
305
307
|
assertStoreId(storeId);
|
|
306
|
-
assertScopeKey(scopeKey);
|
|
307
308
|
let unregistered = false;
|
|
308
309
|
let deleted = false;
|
|
309
310
|
const conn = await this.db.connect();
|
|
310
311
|
try {
|
|
311
312
|
await conn.begin();
|
|
312
|
-
const registered = await conn.query(this.q(`SELECT store_id FROM ${SHARED_MEMORY_STORE_TABLE} WHERE store_id = ? AND scope_key = ? FOR UPDATE`, `SELECT store_id FROM ${SHARED_MEMORY_STORE_TABLE} WHERE store_id = $1 AND scope_key = $2 FOR UPDATE`), [storeId, scopeKey]);
|
|
313
|
-
if (registered.rows
|
|
313
|
+
const registered = await conn.query(this.q(`SELECT store_id, scope_key FROM ${SHARED_MEMORY_STORE_TABLE} WHERE store_id = ? AND scope_key = ? FOR UPDATE`, `SELECT store_id, scope_key FROM ${SHARED_MEMORY_STORE_TABLE} WHERE store_id = $1 AND scope_key = $2 FOR UPDATE`), [storeId, scopeKey]);
|
|
314
|
+
if (!ownedExactly(registered.rows, scopeKey)) {
|
|
314
315
|
unregistered = true;
|
|
315
316
|
await conn.rollback();
|
|
316
317
|
}
|
|
@@ -334,13 +335,12 @@ export class SqlSharedMemoryStore {
|
|
|
334
335
|
}
|
|
335
336
|
async deleteStore(storeId, scopeKey) {
|
|
336
337
|
assertStoreId(storeId);
|
|
337
|
-
assertScopeKey(scopeKey);
|
|
338
338
|
let notOwned = false;
|
|
339
339
|
const conn = await this.db.connect();
|
|
340
340
|
try {
|
|
341
341
|
await conn.begin();
|
|
342
|
-
const owned = await conn.query(this.q(`SELECT store_id FROM ${SHARED_MEMORY_STORE_TABLE} WHERE store_id = ? AND scope_key = ? FOR UPDATE`, `SELECT store_id FROM ${SHARED_MEMORY_STORE_TABLE} WHERE store_id = $1 AND scope_key = $2 FOR UPDATE`), [storeId, scopeKey]);
|
|
343
|
-
if (owned.rows
|
|
342
|
+
const owned = await conn.query(this.q(`SELECT store_id, scope_key FROM ${SHARED_MEMORY_STORE_TABLE} WHERE store_id = ? AND scope_key = ? FOR UPDATE`, `SELECT store_id, scope_key FROM ${SHARED_MEMORY_STORE_TABLE} WHERE store_id = $1 AND scope_key = $2 FOR UPDATE`), [storeId, scopeKey]);
|
|
343
|
+
if (!ownedExactly(owned.rows, scopeKey)) {
|
|
344
344
|
notOwned = true;
|
|
345
345
|
await conn.rollback();
|
|
346
346
|
}
|
|
@@ -58,7 +58,7 @@ export async function ensureSnapshotBucket(store, waitMs, logger, clock, role =
|
|
|
58
58
|
export async function ensureSnapshotBuckets(config, logger) {
|
|
59
59
|
const wanted = [
|
|
60
60
|
["session", config.snapshotBlobStore],
|
|
61
|
-
["workspace", config.remoteExec
|
|
61
|
+
["workspace", config.remoteExec.provider === "k8s" ? config.remoteExec.s3Snapshot : undefined],
|
|
62
62
|
];
|
|
63
63
|
for (const [role, store] of wanted) {
|
|
64
64
|
if (store)
|
|
@@ -21,9 +21,13 @@
|
|
|
21
21
|
* ⚠️ The dup-key predicate ITSELF is NOT store-specific and no longer lives at the call sites:
|
|
22
22
|
* `sql-errors.ts` owns it ([ref] P1-①; 7+ hand-rolled copies had drifted into three different
|
|
23
23
|
* recognition sets, so the same driver error got opposite verdicts in different stores).
|
|
24
|
-
* - per-query timeouts: mysql2 takes `{sql, timeout}` while node-pg takes a `QueryConfig`
|
|
25
|
-
* `query_timeout`. The counter family (write-behind-counter.ts) needs those and keeps its own
|
|
26
|
-
* exec; this seam stays on the plain (sql, params) form.
|
|
24
|
+
* - per-query timeouts on the POOL form: mysql2 takes `{sql, timeout}` while node-pg takes a `QueryConfig`
|
|
25
|
+
* with `query_timeout`. The counter family (write-behind-counter.ts) needs those and keeps its own
|
|
26
|
+
* exec; the pool form of this seam stays on the plain (sql, params) form.
|
|
27
|
+
* A LENT connection ({@link SqlTxConn.query}) takes one optional per-statement deadline
|
|
28
|
+
* ({@link SqlStatementOptions}): past it the statement rejects ({@link SqlStatementDeadlineExceeded}) and the lease
|
|
29
|
+
* is destroyed first (its session / transaction state is unknown — it never goes back to the pool; the server
|
|
30
|
+
* rolls the open transaction back when the connection closes). Same rule on both dialects, one implementation.
|
|
27
31
|
*
|
|
28
32
|
* ─────────────────────────────────────────────────────────────────────────────────────────────────
|
|
29
33
|
* @contract txn.read-semantics — 本仓「事务里的读看到什么」的**唯一**成文处([ref] / [ref] / [ref])
|
|
@@ -127,8 +131,20 @@ export interface SqlResult {
|
|
|
127
131
|
export interface SqlExec {
|
|
128
132
|
query(sql: string, params?: unknown[]): Promise<SqlResult>;
|
|
129
133
|
}
|
|
134
|
+
/** Per-statement options of a lent connection's {@link SqlTxConn.query}. */
|
|
135
|
+
export interface SqlStatementOptions {
|
|
136
|
+
/** The statement rejects ({@link SqlStatementDeadlineExceeded}) if it has not settled within this many ms; the lease is destroyed first. */
|
|
137
|
+
readonly deadlineMs?: number;
|
|
138
|
+
}
|
|
139
|
+
/** A lent connection's statement did not settle within its deadline: the connection was destroyed (never pooled again), the statement rejected. Typed so callers triage it without reading the message. */
|
|
140
|
+
export declare class SqlStatementDeadlineExceeded extends Error {
|
|
141
|
+
readonly deadlineMs: number;
|
|
142
|
+
constructor(deadlineMs: number);
|
|
143
|
+
}
|
|
130
144
|
/** A pooled connection with transaction control (released by the caller in a `finally`). */
|
|
131
145
|
export interface SqlTxConn extends SqlExec {
|
|
146
|
+
/** {@link SqlExec.query}, with an optional per-statement deadline (see the file header). */
|
|
147
|
+
query(sql: string, params?: unknown[], opts?: SqlStatementOptions): Promise<SqlResult>;
|
|
132
148
|
/**
|
|
133
149
|
* The ONE transaction verb ([ref]). What its reads see is NOT decided here — it is decided once per
|
|
134
150
|
* pooled connection at initialization and reported by {@link SqlDriver.facts}. Read the
|
|
@@ -200,7 +216,7 @@ export interface SqlEngineFacts {
|
|
|
200
216
|
* ({@link PACKET_CAP_UNREAD_LANDING_BYTES}), recorded through `recordFailOpen("server.sql.packet-cap-unread")` (a per-hit
|
|
201
217
|
* count and one warn line naming the reason). Never `null`: on these engines the wall exists, only its size may be unread.
|
|
202
218
|
* · **`null`** — PostgreSQL: the extended protocol has no statement-size wall.
|
|
203
|
-
* Its final judge: the SQL snapshot-blob cap (`
|
|
219
|
+
* Its final judge: the SQL snapshot-blob cap (`stores-sql/blob-index-sql.ts` `SqlBlobIndex.put`, the SQL-kept arm), which borrows the connection
|
|
204
220
|
* it will write on through {@link connectJudged}, reads this off it ({@link sqlConnectionFacts}), judges, and writes on that
|
|
205
221
|
* same connection — and when it refuses on a connection opened before the DBA raised the wall, that stale connection is
|
|
206
222
|
* replaced and the write judged once more ({@link connectJudged}). On {@link SqlDriver.facts} the value is the most recently
|
|
@@ -305,7 +321,7 @@ export interface SqlWallsReading extends SqlWalls {
|
|
|
305
321
|
}
|
|
306
322
|
/**
|
|
307
323
|
* 一只 SQL 池的**引擎读口**:方言(SQL 文本轴,装配事实,恒在)+ 此刻的引擎事实(`null` = 没建立)。墙的方言只从这里来 —— 与事实同出一只驱动
|
|
308
|
-
* ({@link SqlDriver} 结构上就是它;`facts` 是活的,池重连后随之换)。HTTP 面的读口(`ServiceDeps.sqlEngine`)与店的写前帽(`
|
|
324
|
+
* ({@link SqlDriver} 结构上就是它;`facts` 是活的,池重连后随之换)。HTTP 面的读口(`ServiceDeps.sqlEngine`)与店的写前帽(`SqlBlobIndex`)各读本池的这一只。
|
|
309
325
|
*/
|
|
310
326
|
export type SqlEngineReading = Pick<SqlDriver, "dialect" | "facts">;
|
|
311
327
|
/**
|
|
@@ -317,7 +333,7 @@ export type SqlEngineReading = Pick<SqlDriver, "dialect" | "facts">;
|
|
|
317
333
|
export declare const SQL_FACTS_ABSENT_WALLS: SqlWallsReading;
|
|
318
334
|
/**
|
|
319
335
|
* 🔴 **此刻一份写入要装进的墙 —— 唯一判官**。两个读者同调它、不各读 `facts`:提交受理口(`http/request-guards.ts` `refuseSubmitBody`)与 SQL 快照 blob 的缺省帽
|
|
320
|
-
* (`
|
|
336
|
+
* (`stores-sql/blob-index-sql.ts` `sqlBlobCapOf`:店的写前判,读的是**将要执行写入的那条连接**的读口 —— 方言 + 它自己那次初始化的事实;受理口读本池驱动的读口)。输入是一只引擎读口({@link SqlEngineReading}:方言与事实同出),一种读法:
|
|
321
337
|
* · 读口缺席 = 没有 SQL 后端 ⇒ 没有墙(「忘了给」读不成这一臂:后端交出了 SQL 池而没给读口 ⇒ `createHttpServer` 起服拒,见 `ServiceDeps.sqlEngine`);
|
|
322
338
|
* · 事实在场 ⇒ 事实里的两位,出处逐墙取事实里记的那一份({@link SqlEngineFacts.wallSources}:读到的 `reported`,读失败退文档缺省的 `assumed`);
|
|
323
339
|
* · 事实缺席(`null`)⇒ 按读口的方言:PG 没有墙(扩展协议没有这两堵墙 —— 方言事实,不随引擎事实缺席);MySQL 协议 ⇒ {@link SQL_FACTS_ABSENT_WALLS}(两堵都 `assumed`;
|
|
@@ -373,7 +389,7 @@ export interface SqlDriver extends SqlExec {
|
|
|
373
389
|
}
|
|
374
390
|
/**
|
|
375
391
|
* 一条**借出的**连接自己的引擎事实 —— 它的初始化回读到的那一份(包墙 `maxPacketBytes` 是这条连接的会话值)。判一条语句装不装得下的地方
|
|
376
|
-
* 只读这里:借出连接 → 读它的墙 → 判 → 在同一条连接上执行(`
|
|
392
|
+
* 只读这里:借出连接 → 读它的墙 → 判 → 在同一条连接上执行(`stores-sql/blob-index-sql.ts` `SqlBlobIndex.put`)。
|
|
377
393
|
* 只认本模块的 `mysqlDriver` / `pgDriver` 的 `connect()` 借出的连接;别的连接(测试替身、包装体)没有经过初始化,墙没人读过 ⇒ **响亮抛**,不猜一个数。
|
|
378
394
|
*/
|
|
379
395
|
export declare function sqlConnectionFacts(conn: SqlTxConn): SqlEngineFacts;
|