@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
|
@@ -12,19 +12,15 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { type AcquiredSession, type PlacedSessionRow, type SessionHead, type SessionPlacementRecord, type SessionStore, type SessionTreeEntry } from "@sema-agent/core";
|
|
14
14
|
import type { Pool } from "mysql2/promise";
|
|
15
|
-
import { type InstructionSourceBaseline } from "../instruction-source-notice.js";
|
|
16
|
-
import type { SessionWorkspace } from "../session-workspace.js";
|
|
17
15
|
import type { RetentionDeclaration } from "@sema-agent/core";
|
|
18
|
-
import type {
|
|
19
|
-
import { type
|
|
20
|
-
import { type PlacementAcquireOpts } from "./session-placement.js";
|
|
16
|
+
import type { SessionWriteHooks } from "./session-write-hooks.js";
|
|
17
|
+
import { type PlacementAcquireOpts } from "../plugins/session-placement.js";
|
|
21
18
|
import { type StagingHandle } from "../session-sync-kernel.js";
|
|
22
|
-
import { type ForkPlacement, type ForkTarget, type ForkToOutcome } from "
|
|
23
|
-
import { type ExecutionPlacementSeat } from "../execution-placement.js";
|
|
19
|
+
import { type ForkPlacement, type ForkTarget, type ForkToOutcome } from "../plugins/session-fork.js";
|
|
24
20
|
export declare class TiDBSessionStore implements SessionStore {
|
|
25
21
|
private readonly pool;
|
|
26
22
|
/** 托管留存声明 = 装配交给它的词(后端按扫车道开不开给;不给 ⇒ `"none"`)。读法逐字见
|
|
27
|
-
* `retention-
|
|
23
|
+
* `retention-declaration.ts` 的 {@link MANAGED_RETENTION}。 */
|
|
28
24
|
readonly retention: RetentionDeclaration;
|
|
29
25
|
/** [ref] C18(core 5.48.0 [ref]):placement **声明**。六条义务的实装点逐条见
|
|
30
26
|
* {@link SESSION_PLACEMENTS} 顶注与本类的 acquire / listSessions / release / listPlaced /
|
|
@@ -39,15 +35,11 @@ export declare class TiDBSessionStore implements SessionStore {
|
|
|
39
35
|
readonly durability: "durable";
|
|
40
36
|
/** In-flight acquisitions keyed by id, so concurrent acquire(sameId) in one process share one. */
|
|
41
37
|
private readonly pending;
|
|
42
|
-
/**
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
*/
|
|
46
|
-
readonly executionPlacement: ExecutionPlacementSeat;
|
|
47
|
-
/** 本店的池包成的 SqlDriver:执行席与会话行格写原语的事务都经它借连接(连接初始化钉住事务读语义,`sql-driver.ts`)。 */
|
|
48
|
-
private readonly db;
|
|
49
|
-
constructor(pool: Pool, opts?: {
|
|
38
|
+
/** 部署的事务内钩(组合座交入;导入 / 删除 / fork 的写事务锁住根行之后、写之前调,`session-write-hooks.ts`)。 */
|
|
39
|
+
private readonly hooks;
|
|
40
|
+
constructor(pool: Pool, opts: {
|
|
50
41
|
readonly retention?: RetentionDeclaration;
|
|
42
|
+
readonly hooks: SessionWriteHooks;
|
|
51
43
|
});
|
|
52
44
|
acquire(sessionId?: string, opts?: PlacementAcquireOpts): Promise<AcquiredSession>;
|
|
53
45
|
private load;
|
|
@@ -111,88 +103,6 @@ export declare class TiDBSessionStore implements SessionStore {
|
|
|
111
103
|
/** 真删除一只 placed 会话的两张表(条件删门在 `placement_kind IS NOT NULL`;事务内 meta 先删=提交点,
|
|
112
104
|
* 与 {@link deleteSession} 同序)。任何 I/O 失败上浮成 `SessionError("storage")`,绝不吞。 */
|
|
113
105
|
private deletePlacedSession;
|
|
114
|
-
/**
|
|
115
|
-
* E16 — list the caller's sessions (CC /resume picker), keyset-paginated newest-first by last activity.
|
|
116
|
-
* Session IDENTITY/enumeration comes from `session_meta` (owner-filtered on the service's OWN owner column);
|
|
117
|
-
* a LEFT JOIN onto the `task_run` runs ledger supplies run-level PREVIEW (latest run's objective/status,
|
|
118
|
-
* run count) but never gates which sessions exist — a session with zero runs still lists (preview null/0).
|
|
119
|
-
* Keyset on `(updated_at, session_id)`; `updated_at` is the session's last-activity marker (touch on every
|
|
120
|
-
* run/append). The shipped {@link SessionSummary} shape is preserved (additive): `lastActivityAt` =
|
|
121
|
-
* `session_meta.updated_at`, `firstActivityAt` = `session_meta.created_at`.
|
|
122
|
-
*/
|
|
123
|
-
listSessions(opts: {
|
|
124
|
-
owner?: string;
|
|
125
|
-
includeUnowned?: boolean;
|
|
126
|
-
cursor?: {
|
|
127
|
-
lastActivityAt: string;
|
|
128
|
-
sessionId: string;
|
|
129
|
-
};
|
|
130
|
-
limit: number;
|
|
131
|
-
q?: string;
|
|
132
|
-
}): Promise<SessionSummary[]>;
|
|
133
|
-
/** {@link listSessions} with each row's occupant (owner + incarnation) from the same SELECT — `OwnerAwareSessionStore.listSessionRows`; the incarnation never enters the row. */
|
|
134
|
-
listSessionRows(opts: {
|
|
135
|
-
owner?: string;
|
|
136
|
-
includeUnowned?: boolean;
|
|
137
|
-
cursor?: {
|
|
138
|
-
lastActivityAt: string;
|
|
139
|
-
sessionId: string;
|
|
140
|
-
};
|
|
141
|
-
limit: number;
|
|
142
|
-
q?: string;
|
|
143
|
-
}): Promise<Array<SessionRowAnswer<SessionSummary>>>;
|
|
144
|
-
/** ONE session's list row — {@link listSessions}' projection (same SELECT, same mapper) scoped to one id; `undefined` = not a
|
|
145
|
-
* row of the list face (no such session / placed). The run-preview subqueries are scoped to the same id (an index read on
|
|
146
|
-
* `task_run.session_id`, not the whole-ledger aggregate the list pays). */
|
|
147
|
-
sessionSummaryOf(sessionId: string): Promise<SessionSummary | undefined>;
|
|
148
|
-
/** {@link sessionSummaryOf} 那一读 + 读到它时行上的住客(写原语的应答行要复核住客,`settleSessionRowWrite`)。 */
|
|
149
|
-
private listRowOf;
|
|
150
|
-
/**
|
|
151
|
-
* 会话行格写原语 —— 用户标题 / 置顶的唯一写口(规则与判序:`src/session-row.ts` 头注「写原语」;事务与语句:`session-row-sql.ts`,PG 孪生同一只实现)。
|
|
152
|
-
* 一只经 `SqlDriver.connect()` 的事务锁住根行、交 core 判官判行期望(属主 + 化身)→ 写者身份(客户端写在镜像上 ⇒ `mirror`)→ 标签 → 写;
|
|
153
|
-
* 应答行在提交 / 回滚之后另读(列表行同一只投影,含新 `rowTag`)、复核仍是判过的那一位住客。只写列表面上的行(委派分区行 ⇒ `absent`);
|
|
154
|
-
* `updated_at` 不动(改名不是活动)。重载按入口分:`replay` 的结局只有 `written` / `absent`。
|
|
155
|
-
*/
|
|
156
|
-
writeSessionRowCells(sessionId: string, cells: SessionRowPatch, how: ClientSessionRowWrite): Promise<SessionRowWriteOutcome<SessionSummary>>;
|
|
157
|
-
writeSessionRowCells(sessionId: string, cells: SessionRowPatch, how: ReplaySessionRowWrite): Promise<ReplaySessionRowWriteOutcome<SessionSummary>>;
|
|
158
|
-
/**
|
|
159
|
-
* The list projection's ONE SELECT + row mapper ({@link listSessions} and {@link sessionSummaryOf} both read through it).
|
|
160
|
-
* `runSessionId` scopes the two run-preview subqueries to one session (the single-row read); the list leaves them whole.
|
|
161
|
-
* Placeholder order follows the text: subquery params, then `where` params, then the LIMIT.
|
|
162
|
-
* `sm.incarnation` is read for the row tag only (`rowTag`, `sessionRowTagKey` — present-iff the row has a token) and the answer row's occupant
|
|
163
|
-
* check; it never enters the list row itself.
|
|
164
|
-
*/
|
|
165
|
-
private selectListRows;
|
|
166
|
-
/** Cheap pre-LLM probe: "none" = session_meta row not yet REGISTERED (createRun fires
|
|
167
|
-
* before the Runner's session acquire — the titler retries), "untitled" / "titled" as read. */
|
|
168
|
-
probeTitle(sessionId: string): Promise<"none" | "untitled" | "titled">;
|
|
169
|
-
/** The auto-titler's write: lands ONLY when title IS NULL (a concurrent/prior title — the user's included — wins;
|
|
170
|
-
* writer order: `src/session-row.ts`). Returns whether this call set it. */
|
|
171
|
-
setTitleIfNull(sessionId: string, title: string): Promise<boolean>;
|
|
172
|
-
/**
|
|
173
|
-
* [ref] —— 换源通告的基线读(一行 PK SELECT;列的三态读法见 `tidb-pool.ts` 的列注)。行不在 ⇒ 读作「没记过」
|
|
174
|
-
* (loader 跑在 acquire 之后,行恒在;不在 = 被带外删了,没有下一 run 可比)。
|
|
175
|
-
*/
|
|
176
|
-
readInstructionSource(sessionId: string): Promise<InstructionSourceBaseline>;
|
|
177
|
-
/**
|
|
178
|
-
* [ref] —— CAS 覆写基线(期望值 = 调用方读到的那一份;不符 ⇒ 0 行、不抛 —— 契约见 `InstructionSourceStore`)。
|
|
179
|
-
* **不**碰 `updated_at`(那是会话活动时刻,留存腿按它判龄;记基线不是一次会话活动)。
|
|
180
|
-
* 两条基线查询都带每查询超时(`BASELINE_IO_DEADLINE_MS`,mysql2 的 `{sql, timeout}` 形):被行锁卡住的写由查询自己
|
|
181
|
-
* 在期限内拒。⚠️ mysql2 的超时是客户端侧的 —— 拒之后那条连接仍被挂着的命令占到服务端结束(S-9 同形,如实披露)。
|
|
182
|
-
*/
|
|
183
|
-
recordInstructionSource(sessionId: string, expected: InstructionSourceBaseline, source: string | null): Promise<void>;
|
|
184
|
-
/**
|
|
185
|
-
* 会话行的位置与项目那一格(`session_meta.workspace`;域律 `src/session-workspace.ts`,列属主 `session-workspace-cell.ts`)。
|
|
186
|
-
* 一行 PK 读,按文本投影、恰好解一次;行不在 / 格为 NULL ⇒ `undefined`;格读不出 ⇒ 读作没有记录并计一次(F 类,见列属主头注)。
|
|
187
|
-
*/
|
|
188
|
-
readSessionWorkspace(sessionId: string): Promise<SessionWorkspace | undefined>;
|
|
189
|
-
/**
|
|
190
|
-
* 整格覆写(最后一帧胜;唯一调用方 = 腿的记录座,它在腿的流循环里一笔一笔等写完)。**住客围栏**:只写化身仍是 `incarnation` 的那一行 ——
|
|
191
|
-
* 行不在、或同 id 已换了住客 ⇒ 0 行、不抛(那一行不是这条腿的)。**不**碰 `updated_at`(那是会话活动时刻,留存腿按它判龄;记下站在哪里不是一次会话活动)。
|
|
192
|
-
* 走店的一般查询面(与腿循环里的账本追加同一形,不另设每查询的客户端超时):mysql2 的客户端超时只是不再等,服务端照把那条 UPDATE 跑完 ——
|
|
193
|
-
* 一笔「放弃等待却仍会落地」的写正是这条腿离开之后还能盖掉下一条腿的那一形。卡住的上界归服务端(锁等待超时)与部署的池策略。
|
|
194
|
-
*/
|
|
195
|
-
recordSessionWorkspace(sessionId: string, incarnation: string, workspace: SessionWorkspace): Promise<void>;
|
|
196
106
|
/**
|
|
197
107
|
* E17 — fork a session: copy its WHOLE durable history to a NEW uuidv7 session owned by `owner`, mirroring
|
|
198
108
|
* core's `PgSessionRepo.fork`. Entry ids / parent links are per-session and copy VERBATIM; only `seq` is
|
|
@@ -210,11 +120,12 @@ export declare class TiDBSessionStore implements SessionStore {
|
|
|
210
120
|
}): Promise<string | null>;
|
|
211
121
|
/**
|
|
212
122
|
* **fork 续写的店契约**(三店同一只私有体;core `fork` 是它的投影):把源会话的整份历史拷进调用方给的目标 id。
|
|
213
|
-
* 1. **原子目标创建**:事务首句 `INSERT INTO session_meta (
|
|
214
|
-
*
|
|
123
|
+
* 1. **原子目标创建**:事务首句 `INSERT INTO session_meta (…) VALUES (:target, …)`,主键即排他(meta 先,随后事务内钩 `fork` —— 部署在同一事务里记下 fork 键
|
|
124
|
+
* (部署格,旁表)—— 再拷事件、COMMIT);
|
|
125
|
+
* 2. **赢家分类**:撞上已在的目标 ⇒ 输方整笔回滚,经钩读目标那一行的属主与 fork 键(一次读)、分类({@link classifyForkTarget});读不到(赢方在分类前回滚或被删)
|
|
215
126
|
* ⇒ 整笔重跑一次,再撞而仍读不到 ⇒ {@link ForkTargetUnsettledError}(不猜);
|
|
216
|
-
* 3. 单事务 ⇒
|
|
217
|
-
* 源不在 ⇒ `null`(先于目标判:源被删之后的同键重试答「源不在」)。新行 = 新住客(化身新铸)
|
|
127
|
+
* 3. 单事务 ⇒ 无中间态;fork 键只由这一钩写。
|
|
128
|
+
* 源不在 ⇒ `null`(先于目标判:源被删之后的同键重试答「源不在」)。新行 = 新住客(化身新铸),部署格全缺省(执行格 absent)。
|
|
218
129
|
*/
|
|
219
130
|
forkTo(sourceId: string, owner: string | null, target: ForkTarget): Promise<ForkToOutcome | null>;
|
|
220
131
|
/** {@link forkTo} 的一次尝试:`"unsettled"` = 撞上目标而回滚之后读不到它(交给外层重跑一次)。 */
|
|
@@ -266,33 +177,32 @@ export declare class TiDBSessionStore implements SessionStore {
|
|
|
266
177
|
* re-implement any of those checks in SQL ([[core-service-boundary]]; the same discipline fork's hand-SQL
|
|
267
178
|
* was warned about). A violation throws `SessionError("invalid_session")` and NOTHING is written.
|
|
268
179
|
* The write: re-seq dense from 0, entry_id/parent_id/payload/ts verbatim, in ONE txn (COMMIT is the sole commit point), written
|
|
269
|
-
* **lock-or-create, meta first
|
|
270
|
-
*
|
|
271
|
-
*
|
|
180
|
+
* **lock-or-create, meta first**:the `session_meta` row is occupied first (an existing row ⇒ refused before anything else about it is read), locked,
|
|
181
|
+
* then the deployment's transaction hook `import` runs on THIS connection (its run claim on the root ⇒ refused, nothing written; its cells for an
|
|
182
|
+
* imported row), then the entries. The leaf is the replayed leaf (core's `leafIdAfterEntry`), matching core's import.
|
|
272
183
|
*/
|
|
273
184
|
importEntries(sessionId: string, owner: string | null, entries: SessionTreeEntry[]): Promise<void>;
|
|
274
185
|
/**
|
|
275
|
-
* 2c session-sync (§7/§8) — IDEMPOTENT REPLACE:
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
*
|
|
281
|
-
*
|
|
282
|
-
* the write logic
|
|
186
|
+
* 2c session-sync (§7/§8) — IDEMPOTENT REPLACE: rewrite the conversation log of `sessionId` with `entries` verbatim under `owner`, ALL in ONE txn.
|
|
187
|
+
* Unlike {@link importEntries} (fresh-only: an existing session at the destination is refused), replace is safe to run OVER an existing
|
|
188
|
+
* session — the §7 fast-forward path and a §8 retry-to-completion both land on it. The validate gate runs FIRST (fail-closed, before any write),
|
|
189
|
+
* then the row: an absent id is created (a fresh occupant, like {@link importEntries}); an existing row is rewritten IN PLACE only when it is the
|
|
190
|
+
* importer's (`owner <=> ?`, defense-in-depth — a wrong-owner caller can never replace another tenant's session: nothing is written) and not a
|
|
191
|
+
* delegation transcript — the same occupant keeps its incarnation and its deployment cells (the side table row is not touched by the store), its
|
|
192
|
+
* created time is the replace's (the log is new). Then the SAME shared write body importEntries uses (the hook, then the events swapped) —
|
|
193
|
+
* ONE source of the write logic. COMMIT is the sole commit point: a mid-replace crash rolls the whole swap back.
|
|
283
194
|
*/
|
|
284
195
|
replaceEntries(sessionId: string, owner: string | null, entries: SessionTreeEntry[]): Promise<void>;
|
|
285
196
|
/**
|
|
286
197
|
* Shared write half of {@link importEntries} / {@link replaceEntries} (ONE source of the write logic, no drift), in the global lock
|
|
287
|
-
* order (根 `session_meta` 先于 `task_
|
|
288
|
-
* ① session_meta FIRST — `create` occupies the row (`ON DUPLICATE KEY UPDATE
|
|
289
|
-
*
|
|
290
|
-
*
|
|
291
|
-
*
|
|
292
|
-
* ②
|
|
293
|
-
*
|
|
294
|
-
* ③
|
|
295
|
-
* ④ the validated entries re-seq dense from 0 (entry_id/parent_id/type/ts verbatim); `replace` drops the old log first.
|
|
198
|
+
* order (根 `session_meta` 先于 `task_*`、部署格与 `session_event`):
|
|
199
|
+
* ① session_meta FIRST — `create` occupies the row (`ON DUPLICATE KEY UPDATE leaf_seq = leaf_seq`: an existing row is left as it is) and then
|
|
200
|
+
* locks it and judges it is the one this call occupied (化身 = 本次铸的;a row that existed before ⇒ refused before anything else about it is
|
|
201
|
+
* read — no hook on someone else's row); `replace` rewrites the locked row in place (owner-guarded, not placed — 0 rows = another owner's row,
|
|
202
|
+
* refused, nothing written). owner = the RE-STAMPED authenticated principal (never the bundle's);
|
|
203
|
+
* ② the deployment's transaction hook `import` on THIS connection after the row lock (`session-write-hooks.ts`):its run claim on the root ⇒
|
|
204
|
+
* refused; its cells for an imported row (a writer here ⇒ refused; otherwise the row is a mirror —「写者在别处」);
|
|
205
|
+
* ③ the validated entries re-seq dense from 0 (entry_id/parent_id/type/ts verbatim); `replace` drops the old log first.
|
|
296
206
|
* Runs inside the caller's open transaction (`conn`) so the caller owns begin/commit/rollback (COMMIT is the sole commit point).
|
|
297
207
|
* `validated` is the OUTPUT of {@link validateEntriesForImport} (the caller runs the gate before opening the txn).
|
|
298
208
|
*/
|
|
@@ -1,38 +1,43 @@
|
|
|
1
1
|
import { StoredSession, SessionError, uuidv7, validateEntriesForImport, leafIdAfterEntry, admitSessionPlacement } from "@sema-agent/core";
|
|
2
2
|
import { TiDBSessionStorage } from "./tidb-session-storage.js";
|
|
3
3
|
import { decodeSessionEventPayload } from "./session-event-payload.js";
|
|
4
|
-
import { jsonColumnText } from "
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { decodeSessionWorkspaceCell, encodeSessionWorkspaceCell } from "./session-workspace-cell.js";
|
|
8
|
-
import { MANAGED_RETENTION, UNMANAGED_RETENTION } from "./retention-store-sql.js";
|
|
9
|
-
import { carriedRowCells, headFromRow, incarnationFromCell, mintSessionIncarnation, sessionFaceFromRow } from "../session-incarnation.js";
|
|
10
|
-
import { sessionRowTagKey } from "../session-row.js";
|
|
11
|
-
import { writeSessionRowCellsSql } from "./session-row-sql.js";
|
|
4
|
+
import { jsonColumnText } from "../plugins/sql-json-column.js";
|
|
5
|
+
import { MANAGED_RETENTION, UNMANAGED_RETENTION } from "../plugins/retention-declaration.js";
|
|
6
|
+
import { headFromRow, mintSessionIncarnation, sessionFaceFromRow } from "../session-incarnation.js";
|
|
12
7
|
import { contentForkRelation, fastForwardSharedContentDiverged } from "../session-sync-content.js";
|
|
13
|
-
import { placementCapabilityOf, LIST_PLACED_BATCH, PLACEMENT_SELECT_COLUMNS, placedRowFromRow, placementFromRow, placementInsertValues, SESSION_DURABILITY, SESSION_PLACEMENTS, } from "
|
|
8
|
+
import { placementCapabilityOf, LIST_PLACED_BATCH, PLACEMENT_SELECT_COLUMNS, placedRowFromRow, placementFromRow, placementInsertValues, SESSION_DURABILITY, SESSION_PLACEMENTS, } from "../plugins/session-placement.js";
|
|
14
9
|
import { classifySyncRelationshipByIds, SyncConflictError, StagedCommitOwnerChangedError, stagingIdFor, STAGING_ID_MARKER, STAGING_GC_GRACE_MS, } from "../session-sync-kernel.js";
|
|
15
|
-
import { assertFirstOwnerWritable, forkOwnerOf, isSessionOwnerValue, placedSessionOwnerOf } from "
|
|
10
|
+
import { assertFirstOwnerWritable, forkOwnerOf, isSessionOwnerValue, placedSessionOwnerOf } from "../plugins/session-ownership.js";
|
|
16
11
|
import { selectForkRows } from "@sema-agent/core";
|
|
17
|
-
import { FORK_LOG_ROW_SHAPE, ForkTargetUnsettledError, classifyForkTarget, forkByFreshId, forkPointOf } from "
|
|
18
|
-
import { isMysqlDupKeyError } from "
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
12
|
+
import { FORK_LOG_ROW_SHAPE, ForkTargetUnsettledError, classifyForkTarget, forkByFreshId, forkPointOf } from "../plugins/session-fork.js";
|
|
13
|
+
import { isMysqlDupKeyError } from "../plugins/sql-errors.js";
|
|
14
|
+
const statementsOn = (conn) => ({
|
|
15
|
+
async query(sql, params) {
|
|
16
|
+
const [res] = await conn.query(sql, params ?? []);
|
|
17
|
+
return Array.isArray(res) ? { rows: res, affected: 0 } : { rows: [], affected: res.affectedRows };
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
class HookRefusal {
|
|
21
|
+
refuse;
|
|
22
|
+
constructor(refuse) {
|
|
23
|
+
this.refuse = refuse;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function settleHookVerdict(verdict) {
|
|
27
|
+
if (verdict !== "proceed")
|
|
28
|
+
throw new HookRefusal(verdict.refuse);
|
|
29
|
+
}
|
|
23
30
|
export class TiDBSessionStore {
|
|
24
31
|
pool;
|
|
25
32
|
retention;
|
|
26
33
|
placements = SESSION_PLACEMENTS;
|
|
27
34
|
durability = SESSION_DURABILITY;
|
|
28
35
|
pending = new Map();
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
constructor(pool, opts = {}) {
|
|
36
|
+
hooks;
|
|
37
|
+
constructor(pool, opts) {
|
|
32
38
|
this.pool = pool;
|
|
33
39
|
this.retention = opts.retention ?? UNMANAGED_RETENTION;
|
|
34
|
-
this.
|
|
35
|
-
this.executionPlacement = createSqlExecutionPlacementSeat(this.db);
|
|
40
|
+
this.hooks = opts.hooks;
|
|
36
41
|
}
|
|
37
42
|
async acquire(sessionId, opts) {
|
|
38
43
|
if (!sessionId) {
|
|
@@ -83,7 +88,7 @@ export class TiDBSessionStore {
|
|
|
83
88
|
const now = new Date();
|
|
84
89
|
await this.pool.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation, " +
|
|
85
90
|
"placement_kind, placement_scope, placement_parent_session_id, placement_root_session_id, placement_handle, placed_at) " +
|
|
86
|
-
"VALUES (?,?,?,?,NULL,0,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE
|
|
91
|
+
"VALUES (?,?,?,?,NULL,0,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE leaf_seq = leaf_seq", [sessionId, owner, now, now, mintSessionIncarnation(), ...placementInsertValues(placement, now)]);
|
|
87
92
|
}
|
|
88
93
|
async placementOf(sessionId) {
|
|
89
94
|
const [rows] = await this.pool.query(`SELECT ${PLACEMENT_SELECT_COLUMNS} FROM session_meta WHERE session_id = ?`, [sessionId]);
|
|
@@ -151,104 +156,6 @@ export class TiDBSessionStore {
|
|
|
151
156
|
conn.release();
|
|
152
157
|
}
|
|
153
158
|
}
|
|
154
|
-
async listSessions(opts) {
|
|
155
|
-
return (await this.listSessionRows(opts)).map((a) => a.row);
|
|
156
|
-
}
|
|
157
|
-
async listSessionRows(opts) {
|
|
158
|
-
const params = [];
|
|
159
|
-
const where = ["sm.placement_kind IS NULL"];
|
|
160
|
-
if (opts.owner !== undefined) {
|
|
161
|
-
where.push(opts.includeUnowned ? "(sm.owner = ? OR sm.owner IS NULL)" : "sm.owner = ?");
|
|
162
|
-
params.push(opts.owner);
|
|
163
|
-
}
|
|
164
|
-
if (opts.q) {
|
|
165
|
-
where.push("EXISTS (SELECT 1 FROM task_run tr WHERE tr.session_id = sm.session_id AND LOWER(tr.objective_preview) LIKE LOWER(?) ESCAPE '\\\\')");
|
|
166
|
-
params.push(`%${escapeLike(opts.q)}%`);
|
|
167
|
-
}
|
|
168
|
-
if (opts.cursor) {
|
|
169
|
-
where.push("(sm.updated_at < ? OR (sm.updated_at = ? AND sm.session_id < ?))");
|
|
170
|
-
params.push(new Date(opts.cursor.lastActivityAt), new Date(opts.cursor.lastActivityAt), opts.cursor.sessionId);
|
|
171
|
-
}
|
|
172
|
-
return this.selectListRows({ where, params, limit: opts.limit });
|
|
173
|
-
}
|
|
174
|
-
async sessionSummaryOf(sessionId) {
|
|
175
|
-
return (await this.listRowOf(sessionId))?.row;
|
|
176
|
-
}
|
|
177
|
-
async listRowOf(sessionId) {
|
|
178
|
-
return (await this.selectListRows({ where: ["sm.placement_kind IS NULL", "sm.session_id = ?"], params: [sessionId], limit: 1, runSessionId: sessionId }))[0];
|
|
179
|
-
}
|
|
180
|
-
async writeSessionRowCells(sessionId, cells, how) {
|
|
181
|
-
return writeSessionRowCellsSql(this.db, sessionId, cells, how, (id) => this.listRowOf(id));
|
|
182
|
-
}
|
|
183
|
-
async selectListRows(q) {
|
|
184
|
-
const runWhere = q.runSessionId !== undefined ? " WHERE session_id = ?" : "";
|
|
185
|
-
const runParams = q.runSessionId !== undefined ? [q.runSessionId, q.runSessionId] : [];
|
|
186
|
-
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, " +
|
|
187
|
-
" 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 " +
|
|
188
|
-
"FROM session_meta sm " +
|
|
189
|
-
"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 " +
|
|
190
|
-
"LEFT JOIN (SELECT session_id, objective_preview, status, task_id, last_task_run_id FROM (" +
|
|
191
|
-
" SELECT session_id, objective_preview, status, task_id, JSON_UNQUOTE(JSON_EXTRACT(result, '$.runId')) AS last_task_run_id, " +
|
|
192
|
-
" ROW_NUMBER() OVER (PARTITION BY session_id ORDER BY created_at DESC, task_id DESC) AS rn FROM task_run" + runWhere +
|
|
193
|
-
" ) lr WHERE rn = 1) latest ON latest.session_id = sm.session_id " +
|
|
194
|
-
(q.where.length ? "WHERE " + q.where.join(" AND ") + " " : "") +
|
|
195
|
-
"ORDER BY sm.updated_at DESC, sm.session_id DESC LIMIT ?";
|
|
196
|
-
const [rows] = await this.pool.query(sql, [...runParams, ...q.params, q.limit]);
|
|
197
|
-
return rows.map((r) => {
|
|
198
|
-
const runId = r.last_task_run_id;
|
|
199
|
-
const sessionId = String(r.session_id);
|
|
200
|
-
const owner = r.owner ?? null;
|
|
201
|
-
const incarnation = incarnationFromCell(sessionId, r.incarnation, "absent");
|
|
202
|
-
const title = r.title ?? null;
|
|
203
|
-
const pinned = Number(r.pinned) === 1;
|
|
204
|
-
const row = {
|
|
205
|
-
sessionId,
|
|
206
|
-
owner,
|
|
207
|
-
lastActivityAt: r.last_activity instanceof Date ? r.last_activity.toISOString() : String(r.last_activity),
|
|
208
|
-
firstActivityAt: r.first_activity instanceof Date ? r.first_activity.toISOString() : String(r.first_activity),
|
|
209
|
-
runCount: Number(r.run_count),
|
|
210
|
-
objectivePreview: r.objective_preview ?? null,
|
|
211
|
-
lastStatus: r.status == null ? "" : String(r.status),
|
|
212
|
-
lastRunId: r.last_run_id ?? null,
|
|
213
|
-
...(typeof runId === "string" && runId !== "" ? { lastTaskRunId: runId } : {}),
|
|
214
|
-
title,
|
|
215
|
-
pinned,
|
|
216
|
-
...sessionRowTagKey({ sessionId, owner, incarnation, cells: { title, pinned } }),
|
|
217
|
-
};
|
|
218
|
-
return { row, occupant: { owner, incarnation } };
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
async probeTitle(sessionId) {
|
|
222
|
-
const [rows] = await this.pool.query("SELECT title FROM session_meta WHERE session_id = ?", [sessionId]);
|
|
223
|
-
if (!rows[0])
|
|
224
|
-
return "none";
|
|
225
|
-
return rows[0].title == null ? "untitled" : "titled";
|
|
226
|
-
}
|
|
227
|
-
async setTitleIfNull(sessionId, title) {
|
|
228
|
-
const [r] = await this.pool.query("UPDATE session_meta SET title = ? WHERE session_id = ? AND title IS NULL", [title, sessionId]);
|
|
229
|
-
return r.affectedRows > 0;
|
|
230
|
-
}
|
|
231
|
-
async readInstructionSource(sessionId) {
|
|
232
|
-
const [rows] = await this.pool.query({ sql: "SELECT instruction_source FROM session_meta WHERE session_id = ?", timeout: BASELINE_IO_DEADLINE_MS }, [sessionId]);
|
|
233
|
-
return decodeInstructionSource(rows[0]?.instruction_source);
|
|
234
|
-
}
|
|
235
|
-
async recordInstructionSource(sessionId, expected, source) {
|
|
236
|
-
const next = encodeInstructionSource(source);
|
|
237
|
-
if (expected.recorded) {
|
|
238
|
-
await this.pool.query({ sql: "UPDATE session_meta SET instruction_source = ? WHERE session_id = ? AND instruction_source = ?", timeout: BASELINE_IO_DEADLINE_MS }, [next, sessionId, encodeInstructionSource(expected.source)]);
|
|
239
|
-
}
|
|
240
|
-
else {
|
|
241
|
-
await this.pool.query({ sql: "UPDATE session_meta SET instruction_source = ? WHERE session_id = ? AND instruction_source IS NULL", timeout: BASELINE_IO_DEADLINE_MS }, [next, sessionId]);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
async readSessionWorkspace(sessionId) {
|
|
245
|
-
const [rows] = await this.pool.query(`SELECT ${jsonColumnText("mysql", "workspace")} FROM session_meta WHERE session_id = ?`, [sessionId]);
|
|
246
|
-
const row = rows[0];
|
|
247
|
-
return row === undefined ? undefined : decodeSessionWorkspaceCell(row.workspace, sessionId);
|
|
248
|
-
}
|
|
249
|
-
async recordSessionWorkspace(sessionId, incarnation, workspace) {
|
|
250
|
-
await this.pool.query("UPDATE session_meta SET workspace = ? WHERE session_id = ? AND incarnation = ?", [encodeSessionWorkspaceCell(workspace, JSON.stringify, sessionId), sessionId, incarnation]);
|
|
251
|
-
}
|
|
252
159
|
async fork(sourceId, owner, opts) {
|
|
253
160
|
return forkByFreshId((s, o, t) => this.forkTo(s, o, t), sourceId, owner, opts);
|
|
254
161
|
}
|
|
@@ -278,8 +185,8 @@ export class TiDBSessionStore {
|
|
|
278
185
|
try {
|
|
279
186
|
await conn.beginTransaction();
|
|
280
187
|
try {
|
|
281
|
-
await conn.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation,
|
|
282
|
-
"placement_kind, placement_scope, placement_parent_session_id, placement_root_session_id, placement_handle, placed_at) VALUES (
|
|
188
|
+
await conn.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation, " +
|
|
189
|
+
"placement_kind, placement_scope, placement_parent_session_id, placement_root_session_id, placement_handle, placed_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)", [target.id, forkOwner, now, now, forkLeafId, eventRows.length, mintSessionIncarnation(), ...placementInsertValues(target.placement, now)]);
|
|
283
190
|
}
|
|
284
191
|
catch (err) {
|
|
285
192
|
if (!isMysqlDupKeyError(err))
|
|
@@ -287,6 +194,8 @@ export class TiDBSessionStore {
|
|
|
287
194
|
collided = true;
|
|
288
195
|
}
|
|
289
196
|
if (!collided) {
|
|
197
|
+
if (target.key !== undefined)
|
|
198
|
+
await this.hooks.keyedFork.record(statementsOn(conn), { rootSessionId: target.id, forkKey: target.key });
|
|
290
199
|
for (let seq = 0; seq < eventRows.length; seq++) {
|
|
291
200
|
const r = eventRows[seq];
|
|
292
201
|
const payload = String(r.payload);
|
|
@@ -303,14 +212,15 @@ export class TiDBSessionStore {
|
|
|
303
212
|
}
|
|
304
213
|
catch {
|
|
305
214
|
}
|
|
215
|
+
if (err instanceof HookRefusal)
|
|
216
|
+
throw err.refuse;
|
|
306
217
|
throw err instanceof SessionError ? err : new SessionError("storage", `fork of session ${sourceId} failed`, err instanceof Error ? err : undefined);
|
|
307
218
|
}
|
|
308
219
|
finally {
|
|
309
220
|
conn.release();
|
|
310
221
|
}
|
|
311
|
-
const
|
|
312
|
-
|
|
313
|
-
return row === undefined ? "unsettled" : classifyForkTarget(target, { owner: row.owner ?? null, forkKey: row.fork_key ?? null }, forkOwner);
|
|
222
|
+
const existing = await this.hooks.keyedFork.targetOf(target.id);
|
|
223
|
+
return existing === undefined ? "unsettled" : classifyForkTarget(target, existing, forkOwner);
|
|
314
224
|
}
|
|
315
225
|
async exportEntries(sessionId) {
|
|
316
226
|
const [metaRows] = await this.pool.query("SELECT 1 FROM session_meta WHERE session_id = ?", [sessionId]);
|
|
@@ -350,7 +260,7 @@ export class TiDBSessionStore {
|
|
|
350
260
|
const conn = await this.pool.getConnection();
|
|
351
261
|
try {
|
|
352
262
|
await conn.beginTransaction();
|
|
353
|
-
await this.writeImportedLog(conn, sessionId, owner, validated,
|
|
263
|
+
await this.writeImportedLog(conn, sessionId, owner, validated, "create");
|
|
354
264
|
await conn.commit();
|
|
355
265
|
}
|
|
356
266
|
catch (err) {
|
|
@@ -359,7 +269,9 @@ export class TiDBSessionStore {
|
|
|
359
269
|
}
|
|
360
270
|
catch {
|
|
361
271
|
}
|
|
362
|
-
if (err instanceof
|
|
272
|
+
if (err instanceof HookRefusal)
|
|
273
|
+
throw err.refuse;
|
|
274
|
+
if (err instanceof SessionError)
|
|
363
275
|
throw err;
|
|
364
276
|
throw new SessionError("storage", `import of session ${sessionId} failed`, err instanceof Error ? err : undefined);
|
|
365
277
|
}
|
|
@@ -373,21 +285,13 @@ export class TiDBSessionStore {
|
|
|
373
285
|
const conn = await this.pool.getConnection();
|
|
374
286
|
try {
|
|
375
287
|
await conn.beginTransaction();
|
|
376
|
-
const [
|
|
377
|
-
const prior =
|
|
288
|
+
const [lockedRows] = await conn.query("SELECT placement_kind FROM session_meta WHERE session_id = ? FOR UPDATE", [sessionId]);
|
|
289
|
+
const prior = lockedRows[0];
|
|
378
290
|
if (prior?.placement_kind != null) {
|
|
379
291
|
await conn.rollback();
|
|
380
292
|
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`);
|
|
381
293
|
}
|
|
382
|
-
|
|
383
|
-
const swapped = purged.affectedRows === 1 ? prior : undefined;
|
|
384
|
-
const cells = carriedRowCells(sessionId, swapped === undefined ? undefined : { incarnation: swapped.incarnation, forkKey: swapped.fork_key });
|
|
385
|
-
const priorCell = swapped === undefined ? undefined : placementCellFromRow(sessionId, swapped);
|
|
386
|
-
if (priorCell?.role === "writer") {
|
|
387
|
-
await conn.rollback();
|
|
388
|
-
throw new SessionWriterPresentError(sessionId, priorCell.lane);
|
|
389
|
-
}
|
|
390
|
-
await this.writeImportedLog(conn, sessionId, owner, validated, cells, "replace");
|
|
294
|
+
await this.writeImportedLog(conn, sessionId, owner, validated, prior === undefined ? "create" : "replace");
|
|
391
295
|
await conn.commit();
|
|
392
296
|
}
|
|
393
297
|
catch (err) {
|
|
@@ -396,7 +300,9 @@ export class TiDBSessionStore {
|
|
|
396
300
|
}
|
|
397
301
|
catch {
|
|
398
302
|
}
|
|
399
|
-
if (err instanceof
|
|
303
|
+
if (err instanceof HookRefusal)
|
|
304
|
+
throw err.refuse;
|
|
305
|
+
if (err instanceof SessionError)
|
|
400
306
|
throw err;
|
|
401
307
|
throw new SessionError("storage", `replace of session ${sessionId} failed`, err instanceof Error ? err : undefined);
|
|
402
308
|
}
|
|
@@ -404,23 +310,26 @@ export class TiDBSessionStore {
|
|
|
404
310
|
conn.release();
|
|
405
311
|
}
|
|
406
312
|
}
|
|
407
|
-
async writeImportedLog(conn, sessionId, owner, validated,
|
|
313
|
+
async writeImportedLog(conn, sessionId, owner, validated, mode) {
|
|
408
314
|
let leafId = null;
|
|
409
315
|
for (const e of validated)
|
|
410
316
|
leafId = leafIdAfterEntry(e);
|
|
411
317
|
const now = new Date();
|
|
412
|
-
const imported = importedRowCells(cells.forkKey);
|
|
413
|
-
await conn.query(`INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation, ${imported.columns}) VALUES (?,?,?,?,?,?,?,?,?)` +
|
|
414
|
-
(mode === "create" ? " ON DUPLICATE KEY UPDATE session_id = session_id" : ""), [sessionId, owner, now, now, leafId, validated.length, cells.incarnation, ...imported.values]);
|
|
415
318
|
if (mode === "create") {
|
|
319
|
+
const minted = mintSessionIncarnation();
|
|
320
|
+
await conn.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation) VALUES (?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE leaf_seq = leaf_seq", [sessionId, owner, now, now, leafId, validated.length, minted]);
|
|
416
321
|
const [held] = await conn.query("SELECT incarnation FROM session_meta WHERE session_id = ? FOR UPDATE", [sessionId]);
|
|
417
|
-
if (String(held[0]?.incarnation) !==
|
|
322
|
+
if (String(held[0]?.incarnation) !== minted) {
|
|
418
323
|
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`));
|
|
419
324
|
}
|
|
420
325
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
326
|
+
else {
|
|
327
|
+
const [rewritten] = await conn.query("UPDATE session_meta SET owner = ?, created_at = ?, updated_at = ?, leaf_id = ?, leaf_seq = ? WHERE session_id = ? AND owner <=> ? AND placement_kind IS NULL", [owner, now, now, leafId, validated.length, sessionId, owner]);
|
|
328
|
+
if (rewritten.affectedRows !== 1) {
|
|
329
|
+
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`));
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
settleHookVerdict(await this.hooks.beforeCommit(statementsOn(conn), { op: "import", rootSessionId: sessionId }));
|
|
424
333
|
if (mode === "replace")
|
|
425
334
|
await conn.query("DELETE FROM session_event WHERE session_id = ?", [sessionId]);
|
|
426
335
|
for (let seq = 0; seq < validated.length; seq++) {
|
|
@@ -429,7 +338,7 @@ export class TiDBSessionStore {
|
|
|
429
338
|
}
|
|
430
339
|
}
|
|
431
340
|
beginImportStaging(realSessionId, token) {
|
|
432
|
-
return new TiDBStagingHandle(this.pool, realSessionId, stagingIdFor(realSessionId, token));
|
|
341
|
+
return new TiDBStagingHandle(this.pool, this.hooks, realSessionId, stagingIdFor(realSessionId, token));
|
|
433
342
|
}
|
|
434
343
|
async assertNotPlaced(sessionId, what) {
|
|
435
344
|
const placed = await this.placementOf(sessionId);
|
|
@@ -457,11 +366,9 @@ export class TiDBSessionStore {
|
|
|
457
366
|
const conn = await this.pool.getConnection();
|
|
458
367
|
try {
|
|
459
368
|
await conn.beginTransaction();
|
|
460
|
-
const
|
|
461
|
-
if (
|
|
462
|
-
await
|
|
463
|
-
throw new SessionActiveRunError(sessionId, claim.activeTaskId);
|
|
464
|
-
}
|
|
369
|
+
const [locked] = await conn.query("SELECT session_id FROM session_meta WHERE session_id = ? FOR UPDATE", [sessionId]);
|
|
370
|
+
if (locked.length > 0)
|
|
371
|
+
settleHookVerdict(await this.hooks.beforeCommit(statementsOn(conn), { op: "delete", rootSessionId: sessionId }));
|
|
465
372
|
const [metaRes] = await conn.query("DELETE FROM session_meta WHERE session_id = ? AND owner <=> ?", [sessionId, owner]);
|
|
466
373
|
if (metaRes.affectedRows > 0)
|
|
467
374
|
await conn.query("DELETE FROM session_event WHERE session_id = ?", [sessionId]);
|
|
@@ -474,6 +381,8 @@ export class TiDBSessionStore {
|
|
|
474
381
|
}
|
|
475
382
|
catch {
|
|
476
383
|
}
|
|
384
|
+
if (err instanceof HookRefusal)
|
|
385
|
+
throw err.refuse;
|
|
477
386
|
throw err;
|
|
478
387
|
}
|
|
479
388
|
finally {
|
|
@@ -489,12 +398,14 @@ export class TiDBSessionStore {
|
|
|
489
398
|
}
|
|
490
399
|
class TiDBStagingHandle {
|
|
491
400
|
pool;
|
|
401
|
+
hooks;
|
|
492
402
|
realSessionId;
|
|
493
403
|
stagingId;
|
|
494
404
|
nextSeq = 0;
|
|
495
405
|
seen = new Set();
|
|
496
|
-
constructor(pool, realSessionId, stagingId) {
|
|
406
|
+
constructor(pool, hooks, realSessionId, stagingId) {
|
|
497
407
|
this.pool = pool;
|
|
408
|
+
this.hooks = hooks;
|
|
498
409
|
this.realSessionId = realSessionId;
|
|
499
410
|
this.stagingId = stagingId;
|
|
500
411
|
}
|
|
@@ -533,18 +444,14 @@ class TiDBStagingHandle {
|
|
|
533
444
|
}
|
|
534
445
|
}
|
|
535
446
|
else {
|
|
536
|
-
await conn.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation) VALUES (?,?,?,?,NULL,0,?) ON DUPLICATE KEY UPDATE
|
|
447
|
+
await conn.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq, incarnation) VALUES (?,?,?,?,NULL,0,?) ON DUPLICATE KEY UPDATE leaf_seq = leaf_seq", [this.realSessionId, owner, now, now, minted]);
|
|
537
448
|
const [claimed] = await conn.query("SELECT incarnation FROM session_meta WHERE session_id = ? FOR UPDATE", [this.realSessionId]);
|
|
538
449
|
if (String(claimed[0]?.incarnation) !== minted) {
|
|
539
450
|
await conn.rollback();
|
|
540
451
|
throw new StagedCommitOwnerChangedError(this.realSessionId);
|
|
541
452
|
}
|
|
542
453
|
}
|
|
543
|
-
|
|
544
|
-
if (claim.activeTaskId !== null) {
|
|
545
|
-
await conn.rollback();
|
|
546
|
-
throw new SessionActiveRunError(this.realSessionId, claim.activeTaskId);
|
|
547
|
-
}
|
|
454
|
+
settleHookVerdict(await this.hooks.beforeCommit(statementsOn(conn), { op: "import", rootSessionId: this.realSessionId }));
|
|
548
455
|
const [stagedRows] = await conn.query("SELECT entry_id FROM session_event WHERE session_id = ? ORDER BY seq ASC FOR UPDATE", [this.stagingId]);
|
|
549
456
|
const [dstRows] = await conn.query("SELECT entry_id FROM session_event WHERE session_id = ? ORDER BY seq ASC FOR UPDATE", [this.realSessionId]);
|
|
550
457
|
const [dstMeta] = await conn.query("SELECT placement_kind FROM session_meta WHERE session_id = ?", [this.realSessionId]);
|
|
@@ -579,16 +486,7 @@ class TiDBStagingHandle {
|
|
|
579
486
|
let leafId = null;
|
|
580
487
|
if (lastRow[0])
|
|
581
488
|
leafId = leafIdAfterEntry(decodeSessionEventPayload(lastRow[0].payload, this.realSessionId, "last staged row"));
|
|
582
|
-
|
|
583
|
-
if (settle.affectedRows !== 1) {
|
|
584
|
-
const locked = readPlacementRowStatement("mysql", this.realSessionId, { lock: true });
|
|
585
|
-
const [lockedRows] = await conn.query(locked.text, [...locked.values]);
|
|
586
|
-
const cell = lockedRows[0] === undefined ? undefined : placementCellFromRow(this.realSessionId, lockedRows[0]);
|
|
587
|
-
await conn.rollback();
|
|
588
|
-
if (cell?.role === "writer")
|
|
589
|
-
throw new SessionWriterPresentError(this.realSessionId, cell.lane);
|
|
590
|
-
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`);
|
|
591
|
-
}
|
|
489
|
+
await conn.query("UPDATE session_meta SET owner = ?, updated_at = ?, leaf_id = ?, leaf_seq = ? WHERE session_id = ?", [owner, now, leafId, srcIds.length, this.realSessionId]);
|
|
592
490
|
const [afterMeta] = await conn.query("SELECT placement_kind, owner, incarnation FROM session_meta WHERE session_id = ?", [this.realSessionId]);
|
|
593
491
|
if (afterMeta[0]?.placement_kind != null) {
|
|
594
492
|
await conn.rollback();
|
|
@@ -603,7 +501,9 @@ class TiDBStagingHandle {
|
|
|
603
501
|
await conn.rollback();
|
|
604
502
|
}
|
|
605
503
|
catch { }
|
|
606
|
-
if (err instanceof
|
|
504
|
+
if (err instanceof HookRefusal)
|
|
505
|
+
throw err.refuse;
|
|
506
|
+
if (err instanceof SyncConflictError || err instanceof SessionError)
|
|
607
507
|
throw err;
|
|
608
508
|
throw new SessionError("storage", `staged commit for ${this.realSessionId} failed`, err instanceof Error ? err : undefined);
|
|
609
509
|
}
|
|
@@ -31,7 +31,7 @@ export declare function assertToolResultProvenanceSchema(query: (sql: string) =>
|
|
|
31
31
|
export declare class SqlToolResultStore implements ToolResultStore {
|
|
32
32
|
protected readonly db: SqlDriver;
|
|
33
33
|
/** 托管留存声明 = 装配交给它的词(后端按扫车道开不开给;不给 ⇒ `"none"`)。读法与「为什么 SQL 店答 managed 而三方法的实现体在
|
|
34
|
-
* `retention-store-sql.ts`」逐字见 {@link MANAGED_RETENTION}(扫车道开着时本店的 `tool_result` 行由那只聚合店按期清:随会话树删 + 孤儿腿)。 */
|
|
34
|
+
* `retention-store-sql.ts`」逐字见 `plugins/retention-declaration.ts` 的 {@link MANAGED_RETENTION}(扫车道开着时本店的 `tool_result` 行由那只聚合店按期清:随会话树删 + 孤儿腿)。 */
|
|
35
35
|
readonly retention: RetentionDeclaration;
|
|
36
36
|
constructor(db: SqlDriver, opts?: {
|
|
37
37
|
readonly retention?: RetentionDeclaration;
|