@sema-agent/server 7.149.0 → 7.151.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 +307 -9
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/USAGE.md +92 -40
- package/deploy/sema-up/README.md +7 -1
- package/deploy/sema-up/chart/Chart.yaml +1 -1
- package/deploy/sema-up/chart/templates/NOTES.txt +5 -0
- package/deploy/sema-up/chart/templates/_helpers.tpl +15 -16
- package/deploy/sema-up/chart/templates/server-v4pro.yaml +5 -3
- package/deploy/sema-up/chart/templates/server.yaml +5 -3
- package/deploy/sema-up/chart/values-full-single.yaml +3 -1
- package/deploy/sema-up/chart/values.yaml +8 -1
- package/deploy/sema-up/cluster-up.sh +1 -0
- package/deploy/sema-up/kube-up.sh +1 -1
- package/deploy/sema-up/lib/flagshape.sh +1 -1
- package/deploy/sema-up/lib/helm-values-cluster.py +2 -0
- package/deploy/sema-up/lib/helm-values-kube.py +2 -0
- package/dist/adoption/import.js +3 -3
- package/dist/approval-ask-machine.d.ts +1 -1
- package/dist/approval-card.d.ts +3 -3
- package/dist/approval-gate-currency.d.ts +2 -2
- package/dist/audit.d.ts +1 -1
- package/dist/audit.js +2 -2
- package/dist/auth-bridge.d.ts +20 -0
- package/dist/auth-bridge.js +9 -0
- package/dist/background-session-reap.d.ts +2 -2
- package/dist/bake-runner/main.d.ts +4 -2
- package/dist/bind-host.d.ts +2 -3
- package/dist/bind-host.js +2 -4
- package/dist/boot/budget-tracing.js +1 -1
- package/dist/boot/deferred-sandbox-path-env.d.ts +5 -3
- package/dist/boot/deferred-sandbox-path-env.js +1 -2
- package/dist/boot/engine-lease.d.ts +5 -0
- package/dist/boot/engine-lease.js +1 -0
- package/dist/boot/execution-env.d.ts +8 -14
- package/dist/boot/execution-env.js +54 -17
- package/dist/boot/execution-lane.js +32 -5
- package/dist/boot/late-binds.d.ts +8 -4
- package/dist/boot/late-binds.js +7 -1
- package/dist/boot/memory-consolidation.d.ts +3 -2
- package/dist/boot/memory-consolidation.js +6 -6
- package/dist/boot/parked-revive-gate.d.ts +5 -5
- package/dist/boot/parked-revive-gate.js +2 -3
- package/dist/boot/reapers.d.ts +5 -0
- package/dist/boot/reapers.js +12 -1
- package/dist/boot/resolve-spec.d.ts +7 -8
- package/dist/boot/resolve-spec.js +80 -35
- package/dist/boot/retention-lane.d.ts +1 -1
- package/dist/boot/runner-deps.d.ts +9 -2
- package/dist/boot/runner-deps.js +3 -3
- package/dist/boot/session-age-out.d.ts +36 -0
- package/dist/boot/session-age-out.js +101 -0
- package/dist/boot/session-faces.d.ts +5 -14
- package/dist/boot/session-faces.js +28 -24
- package/dist/boot/stage-01-config.js +2 -0
- package/dist/boot/stage-03-store-backend.js +2 -2
- package/dist/boot/stage-05-execution-env.d.ts +5 -3
- package/dist/boot/stage-05-execution-env.js +2 -2
- package/dist/boot/stage-06-runners.d.ts +7 -4
- package/dist/boot/stage-06-runners.js +16 -8
- package/dist/boot/stage-07-capability-layer.d.ts +7 -4
- package/dist/boot/stage-07-capability-layer.js +3 -2
- package/dist/boot/stage-08-reapers.d.ts +7 -4
- package/dist/boot/stage-08-reapers.js +4 -2
- package/dist/boot/stage-09-leader.d.ts +11 -5
- package/dist/boot/stage-09-leader.js +12 -3
- package/dist/boot/stage-10-http-server.d.ts +44 -6
- package/dist/boot/stage-10-http-server.js +12 -10
- package/dist/boot/web-admin.d.ts +1 -1
- package/dist/boot/web-admin.js +3 -1
- package/dist/boot/workflow-orchestration.d.ts +7 -2
- package/dist/boot/workflow-orchestration.js +24 -7
- package/dist/bounded-rotation.d.ts +29 -0
- package/dist/bounded-rotation.js +24 -0
- package/dist/capability-facts.d.ts +84 -66
- package/dist/capability-facts.js +58 -41
- package/dist/center-credential.d.ts +25 -6
- package/dist/center-credential.js +25 -5
- package/dist/config-catalog.d.ts +26 -7
- package/dist/config-catalog.js +15 -12
- package/dist/config-invariants.d.ts +2 -1
- package/dist/config-invariants.js +4 -4
- package/dist/config-types.d.ts +22 -15
- package/dist/config.d.ts +51 -2
- package/dist/config.js +35 -24
- package/dist/deployment-governance.d.ts +7 -28
- package/dist/deployment-governance.js +1 -14
- package/dist/deployment-shape.d.ts +9 -1
- package/dist/deployment-shape.js +2 -1
- package/dist/device-protocol.d.ts +5 -4
- package/dist/device-protocol.js +1 -1
- package/dist/device-ws-hub.d.ts +1 -0
- package/dist/device-ws-hub.js +66 -29
- package/dist/device-ws-protocol.d.ts +79 -12
- package/dist/device-ws-protocol.js +64 -26
- package/dist/engine-identity.d.ts +23 -5
- package/dist/engine-identity.js +13 -0
- package/dist/entry-mode-clamp-notice.d.ts +2 -1
- package/dist/execution-env-refusal.d.ts +23 -0
- package/dist/execution-env-refusal.js +5 -0
- package/dist/execution-lane-caps.d.ts +3 -1
- package/dist/field-conflict.d.ts +3 -3
- package/dist/file-history-capture.d.ts +11 -4
- package/dist/file-history-capture.js +9 -6
- package/dist/file-history-disclosure.d.ts +7 -5
- package/dist/file-history-disclosure.js +7 -5
- package/dist/fleet/drain-entry.d.ts +4 -0
- package/dist/fleet/engine-signal.d.ts +14 -1
- package/dist/fleet/engine-signal.js +5 -1
- package/dist/fleet/fleet-reconciler.d.ts +1 -1
- package/dist/fleet/serving-lease.d.ts +18 -3
- package/dist/fleet/serving-lease.js +81 -35
- package/dist/fleet-client.d.ts +13 -11
- package/dist/fleet-client.js +53 -33
- package/dist/history-anchor.js +1 -1
- package/dist/hitl-delivery.d.ts +1 -3
- package/dist/hitl-delivery.js +1 -1
- package/dist/hooks/hook-runner.js +2 -2
- package/dist/host-lsp-manager.d.ts +2 -2
- package/dist/http/active-run-conflict.d.ts +3 -7
- package/dist/http/active-run-conflict.js +2 -2
- package/dist/http/admission.js +19 -4
- package/dist/http/approval-sweeps.js +7 -6
- package/dist/http/caller-trust.d.ts +2 -2
- package/dist/http/caller-trust.js +3 -3
- package/dist/http/checkpoint-ctx.d.ts +20 -63
- package/dist/http/checkpoint-ctx.js +2 -24
- package/dist/http/claim-heartbeat.d.ts +4 -4
- package/dist/http/claim-heartbeat.js +3 -3
- package/dist/http/dispatch.d.ts +2 -1
- package/dist/http/dispatch.js +36 -29
- package/dist/http/durable-run-leg.js +17 -17
- package/dist/http/health-self-report.d.ts +9 -1
- package/dist/http/health-self-report.js +2 -0
- package/dist/http/owner-access.d.ts +264 -20
- package/dist/http/owner-access.js +215 -5
- package/dist/http/readiness.d.ts +4 -2
- package/dist/http/resume-ctx-read.js +38 -1
- package/dist/http/resume-legs.d.ts +2 -1
- package/dist/http/resume-legs.js +273 -89
- package/dist/http/route-ctx.d.ts +41 -19
- package/dist/http/route-table.d.ts +1 -1
- package/dist/http/routes/a2a-serve.js +15 -14
- package/dist/http/routes/admin-memory-optout.d.ts +1 -1
- package/dist/http/routes/agents-roster.js +2 -3
- package/dist/http/routes/approvals-assistant.d.ts +3 -2
- package/dist/http/routes/approvals-assistant.js +12 -12
- package/dist/http/routes/background.d.ts +3 -3
- package/dist/http/routes/capabilities.js +10 -6
- package/dist/http/routes/config-catalog.d.ts +1 -1
- package/dist/http/routes/fleet.js +10 -8
- package/dist/http/routes/mcp-reconnect.js +2 -2
- package/dist/http/routes/memory-origin.d.ts +1 -1
- package/dist/http/routes/notify-wake.js +2 -3
- package/dist/http/routes/projects.d.ts +4 -2
- package/dist/http/routes/projects.js +7 -6
- package/dist/http/routes/retention-ops.d.ts +1 -1
- package/dist/http/routes/run-permission-mode.d.ts +1 -1
- package/dist/http/routes/run-permission-mode.js +2 -2
- package/dist/http/routes/runs.d.ts +1 -1
- package/dist/http/routes/runs.js +125 -66
- package/dist/http/routes/session-memory-sync.js +4 -5
- package/dist/http/routes/session-sync.d.ts +4 -3
- package/dist/http/routes/session-sync.js +174 -83
- package/dist/http/routes/sessions-list.js +13 -7
- package/dist/http/routes/sessions.d.ts +1 -1
- package/dist/http/routes/sessions.js +26 -48
- package/dist/http/routes/tasks.js +38 -23
- package/dist/http/routes/trace-usage.js +14 -14
- package/dist/http/routes/workflows.d.ts +1 -0
- package/dist/http/routes/workflows.js +20 -38
- package/dist/http/server.d.ts +28 -30
- package/dist/http/server.js +7 -3
- package/dist/http/stored-body-replay.d.ts +3 -24
- package/dist/http/stored-body-replay.js +2 -12
- package/dist/http/submit-ctx.d.ts +1 -2
- package/dist/http/submit-ctx.js +8 -6
- package/dist/http/submit-replay.d.ts +15 -2
- package/dist/http/submit-replay.js +10 -4
- package/dist/http/wire-types.d.ts +19 -6
- package/dist/identity-source.d.ts +20 -3
- package/dist/identity-source.js +14 -0
- package/dist/index.d.ts +4 -0
- package/dist/lane-home-probe.d.ts +3 -7
- package/dist/lane-home-probe.js +0 -1
- package/dist/leader/endpoint.js +9 -8
- package/dist/leader/leader.d.ts +2 -1
- package/dist/leg-facts.d.ts +19 -5
- package/dist/leg-facts.js +3 -1
- package/dist/mcp-authorization-face.d.ts +18 -14
- package/dist/mcp-authorization-face.js +3 -2
- package/dist/memory-scope.d.ts +5 -4
- package/dist/memory-scope.js +4 -4
- package/dist/memory-store-refusal.d.ts +1 -1
- package/dist/memory-sync-client.d.ts +30 -2
- package/dist/memory-sync-client.js +16 -9
- package/dist/observability/corrupt-read-seat.d.ts +1 -1
- package/dist/observability/err-text.d.ts +7 -0
- package/dist/observability/err-text.js +5 -0
- package/dist/observability/fail-open.d.ts +12 -8
- package/dist/observability/fail-open.js +12 -8
- package/dist/observability/logger.d.ts +7 -0
- package/dist/observability/logger.js +3 -1
- package/dist/observability/metrics.js +1 -0
- package/dist/observability/run-terminal.d.ts +1 -1
- package/dist/orchestration/workflow-notify-journal.d.ts +18 -1
- package/dist/orchestration/workflow-notify-journal.js +12 -1
- package/dist/orchestration/workflow-park-decision.d.ts +70 -18
- package/dist/orchestration/workflow-park-decision.js +49 -10
- package/dist/orchestration/workflow-park-holder.d.ts +65 -0
- package/dist/orchestration/workflow-park-holder.js +98 -0
- package/dist/orchestration/workflow-record-fence.d.ts +36 -0
- package/dist/orchestration/workflow-record-fence.js +42 -0
- package/dist/owner-faces.d.ts +28 -4
- package/dist/owner-faces.js +2 -0
- package/dist/parked-decide.d.ts +104 -53
- package/dist/parked-decide.js +153 -117
- package/dist/peer-directory.d.ts +2 -2
- package/dist/peer-inbound-face.d.ts +2 -6
- package/dist/peer-inbound-face.js +0 -3
- package/dist/peer-seat.d.ts +1 -1
- package/dist/peer-seat.js +52 -59
- package/dist/permission-mode-admission.d.ts +41 -5
- package/dist/permission-mode-admission.js +29 -3
- package/dist/plugins/adoption-log-sql.d.ts +3 -1
- package/dist/plugins/adoption-log-sql.js +7 -1
- package/dist/plugins/approval-ask-store-file.d.ts +10 -2
- package/dist/plugins/approval-ask-store-file.js +8 -0
- package/dist/plugins/approval-ask-store-memory.d.ts +6 -1
- package/dist/plugins/approval-ask-store-memory.js +28 -9
- package/dist/plugins/approval-ask-store-sql.d.ts +43 -2
- package/dist/plugins/approval-ask-store-sql.js +34 -5
- package/dist/plugins/approval-exemption-store.js +3 -0
- package/dist/plugins/approval-nonce-store.js +2 -0
- package/dist/plugins/breaker-state-sql.js +2 -0
- package/dist/plugins/caching-session-store.d.ts +9 -15
- package/dist/plugins/caching-session-store.js +6 -0
- package/dist/plugins/device-store-sql.js +6 -0
- package/dist/plugins/error-origin.d.ts +18 -0
- package/dist/plugins/error-origin.js +10 -0
- package/dist/plugins/exec-path-law.d.ts +34 -0
- package/dist/plugins/exec-path-law.js +13 -0
- package/dist/plugins/execution-placement-sql.js +6 -6
- package/dist/plugins/file-run-store.js +1 -1
- package/dist/plugins/image-bake-store-sql.js +6 -0
- package/dist/plugins/image-index-sql.js +4 -0
- package/dist/plugins/landed-attendants.d.ts +212 -0
- package/dist/plugins/landed-attendants.js +161 -0
- package/dist/plugins/leader-run-store-sql.js +4 -0
- package/dist/plugins/local-checkpoint-store.d.ts +4 -1
- package/dist/plugins/local-checkpoint-store.js +3 -0
- package/dist/plugins/local-session-live-reference.d.ts +40 -0
- package/dist/plugins/local-session-live-reference.js +39 -0
- package/dist/plugins/local-session-store.d.ts +71 -33
- package/dist/plugins/local-session-store.js +129 -27
- package/dist/plugins/memory-engine-pg.js +4 -0
- package/dist/plugins/memory-engine-sql-apply.js +5 -2
- package/dist/plugins/memory-engine-tidb.js +4 -0
- package/dist/plugins/memory-optout-grant-store-sql.js +4 -0
- package/dist/plugins/memory-run-store.js +1 -1
- package/dist/plugins/memory-sync-store-pg.js +5 -0
- package/dist/plugins/memory-sync-store-tidb.js +5 -0
- package/dist/plugins/one-time-migrations.d.ts +80 -61
- package/dist/plugins/one-time-migrations.js +241 -104
- package/dist/plugins/org-delivery-quota-store.js +4 -1
- package/dist/plugins/outcome-ledger-sql.js +2 -0
- package/dist/plugins/permission-rule-store-sql.js +11 -4
- package/dist/plugins/pg-cost-quota.js +4 -0
- package/dist/plugins/pg-pool.js +28 -1
- package/dist/plugins/pg-rate-limiter.js +2 -0
- package/dist/plugins/posix-shell-fs.d.ts +6 -1
- package/dist/plugins/posix-shell-fs.js +8 -3
- package/dist/plugins/remote-env-adb.d.ts +19 -15
- package/dist/plugins/remote-env-adb.js +44 -17
- package/dist/plugins/remote-env-connect-seat.d.ts +105 -0
- package/dist/plugins/remote-env-connect-seat.js +72 -0
- package/dist/plugins/remote-env-device.d.ts +22 -5
- package/dist/plugins/remote-env-device.js +114 -14
- package/dist/plugins/remote-env-e2b.d.ts +18 -7
- package/dist/plugins/remote-env-e2b.js +53 -40
- package/dist/plugins/remote-env-file-error.js +6 -2
- package/dist/plugins/remote-env-host.d.ts +18 -1
- package/dist/plugins/remote-env-host.js +48 -47
- package/dist/plugins/remote-env-k8s.d.ts +41 -39
- package/dist/plugins/remote-env-k8s.js +56 -32
- package/dist/plugins/remote-env-local-docker.d.ts +13 -8
- package/dist/plugins/remote-env-local-docker.js +37 -43
- package/dist/plugins/remote-env-ssh.d.ts +18 -12
- package/dist/plugins/remote-env-ssh.js +64 -62
- package/dist/plugins/remote-shell.d.ts +101 -2
- package/dist/plugins/remote-shell.js +221 -3
- package/dist/plugins/replica-registration.d.ts +35 -0
- package/dist/plugins/replica-registration.js +48 -0
- package/dist/plugins/resume-anchor-store-sql.d.ts +11 -2
- package/dist/plugins/resume-anchor-store-sql.js +12 -5
- package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
- package/dist/plugins/retention-lane-store-sql.js +4 -0
- package/dist/plugins/retention-sql.d.ts +20 -2
- package/dist/plugins/retention-sql.js +11 -0
- package/dist/plugins/run-store-sql.d.ts +22 -3
- package/dist/plugins/run-store-sql.js +33 -9
- package/dist/plugins/scenario-prefix-switch-m42.d.ts +6 -12
- package/dist/plugins/scenario-prefix-switch-m42.js +7 -5
- package/dist/plugins/schema-consistency.d.ts +9 -5
- package/dist/plugins/schema-consistency.js +48 -18
- package/dist/plugins/schema-drift-sentence.js +1 -1
- package/dist/plugins/send-file-ledger.js +3 -0
- package/dist/plugins/serving-requirement.d.ts +68 -0
- package/dist/plugins/serving-requirement.js +60 -0
- package/dist/plugins/session-birth.d.ts +6 -2
- package/dist/plugins/session-capture-record-store-sql.js +2 -0
- package/dist/plugins/session-ext-table.d.ts +14 -1
- package/dist/plugins/session-ext-table.js +5 -1
- package/dist/plugins/session-row-sql.d.ts +49 -2
- package/dist/plugins/session-row-sql.js +56 -13
- package/dist/plugins/session-store-sql.d.ts +34 -18
- package/dist/plugins/session-store-sql.js +98 -17
- package/dist/plugins/shared-memory-store-sql.d.ts +1 -0
- package/dist/plugins/shared-memory-store-sql.js +6 -2
- package/dist/plugins/sql-driver.d.ts +2 -1
- package/dist/plugins/sql-driver.js +18 -3
- package/dist/plugins/sql-errors.d.ts +11 -6
- package/dist/plugins/sql-errors.js +5 -6
- package/dist/plugins/stop-then-start-sentence.d.ts +48 -3
- package/dist/plugins/stop-then-start-sentence.js +48 -0
- package/dist/plugins/stop-then-start.d.ts +109 -0
- package/dist/plugins/stop-then-start.js +104 -0
- package/dist/plugins/store-backend.d.ts +40 -13
- package/dist/plugins/store-backend.js +169 -27
- package/dist/plugins/store-boundary.d.ts +57 -0
- package/dist/plugins/store-boundary.js +139 -0
- package/dist/plugins/store-contracts.d.ts +29 -2
- package/dist/plugins/store-contracts.js +3 -0
- package/dist/plugins/store-fault-sentence-m53.d.ts +7 -0
- package/dist/plugins/store-fault-sentence-m53.js +25 -0
- package/dist/plugins/store-fault.d.ts +26 -7
- package/dist/plugins/store-fault.js +18 -2
- package/dist/plugins/task-attachment-store.js +9 -0
- package/dist/plugins/task-list-store-sql.d.ts +2 -2
- package/dist/plugins/task-list-store-sql.js +10 -8
- package/dist/plugins/tidb-cost-quota.js +4 -0
- package/dist/plugins/tidb-pool.js +6 -1
- package/dist/plugins/tidb-rate-limiter.js +4 -0
- package/dist/plugins/user-input-ledger-sql.js +4 -0
- package/dist/plugins/workflow-run-store-sql.d.ts +28 -10
- package/dist/plugins/workflow-run-store-sql.js +64 -4
- package/dist/plugins/workspace-fill.d.ts +4 -7
- package/dist/plugins/workspace-fill.js +2 -1
- package/dist/plugins/write-behind-counter.js +5 -0
- package/dist/process-instance.d.ts +3 -0
- package/dist/process-instance.js +7 -0
- package/dist/project-context.d.ts +64 -0
- package/dist/project-context.js +66 -0
- package/dist/project-registry.d.ts +46 -0
- package/dist/project-registry.js +44 -0
- package/dist/question.d.ts +9 -21
- package/dist/question.js +28 -19
- package/dist/request-key-closure.js +1 -1
- package/dist/request-value-closure.d.ts +4 -0
- package/dist/request-value-closure.js +10 -0
- package/dist/run-acceptance.d.ts +3 -2
- package/dist/run-acceptance.js +4 -6
- package/dist/run-cancel-card.d.ts +1 -1
- package/dist/run-cancel-card.js +2 -1
- package/dist/run-local.js +1 -0
- package/dist/runs.d.ts +37 -8
- package/dist/runs.js +53 -12
- package/dist/runtime-governance.d.ts +5 -4
- package/dist/security.d.ts +31 -33
- package/dist/security.js +41 -7
- package/dist/server-secret-env.d.ts +12 -10
- package/dist/server-secret-env.js +24 -15
- package/dist/session-agent.d.ts +1 -10
- package/dist/session-agent.js +0 -6
- package/dist/session-bindings.d.ts +82 -0
- package/dist/session-bindings.js +18 -0
- package/dist/session-children-retire.d.ts +2 -2
- package/dist/session-children-retire.js +2 -6
- package/dist/session-deletion.d.ts +16 -1
- package/dist/session-error-wire.d.ts +3 -3
- package/dist/session-error-wire.js +3 -3
- package/dist/session-incarnation.d.ts +28 -4
- package/dist/session-incarnation.js +14 -2
- package/dist/session-kind.d.ts +57 -0
- package/dist/session-kind.js +22 -0
- package/dist/session-notify-door.d.ts +4 -4
- package/dist/session-notify-door.js +5 -6
- package/dist/session-row.d.ts +21 -1
- package/dist/session-row.js +7 -1
- package/dist/session-rules-seat.d.ts +4 -3
- package/dist/session-rules-seat.js +3 -11
- package/dist/session-sync-kernel.d.ts +24 -14
- package/dist/session-sync-kernel.js +3 -6
- package/dist/session-sync.d.ts +4 -98
- package/dist/session-sync.js +4 -74
- package/dist/session-titler.d.ts +10 -3
- package/dist/session-titler.js +19 -6
- package/dist/ssh-host-key.d.ts +5 -0
- package/dist/ssh-host-key.js +3 -0
- package/dist/stores-sql/checkpoint-store-sql.d.ts +21 -2
- package/dist/stores-sql/checkpoint-store-sql.js +106 -18
- package/dist/stores-sql/file-history-store-sql.d.ts +52 -2
- package/dist/stores-sql/file-history-store-sql.js +108 -70
- package/dist/stores-sql/pg-session-storage.d.ts +34 -7
- package/dist/stores-sql/pg-session-storage.js +37 -13
- package/dist/stores-sql/retention-store-sql.d.ts +2 -10
- package/dist/stores-sql/retention-store-sql.js +2 -10
- package/dist/stores-sql/session-policy-store-sql.d.ts +30 -7
- package/dist/stores-sql/session-policy-store-sql.js +61 -44
- package/dist/stores-sql/session-write-hooks.d.ts +11 -0
- package/dist/stores-sql/tidb-session-store.d.ts +58 -7
- package/dist/stores-sql/tidb-session-store.js +37 -13
- package/dist/submission-exit.d.ts +3 -2
- package/dist/submission-exit.js +9 -1
- package/dist/task-cwd.d.ts +6 -5
- package/dist/task-settings.d.ts +19 -5
- package/dist/task-settings.js +13 -7
- package/dist/terminal.d.ts +33 -2
- package/dist/terminal.js +139 -9
- package/dist/tool-approval.d.ts +147 -12
- package/dist/tool-approval.js +206 -109
- package/dist/trace/engine-notice-wire.d.ts +14 -0
- package/dist/trace/engine-notice-wire.js +18 -1
- package/dist/trace/project.d.ts +4 -3
- package/dist/trace/project.js +3 -2
- package/dist/trace/projection-table.d.ts +33 -2
- package/dist/trace/self-minted-wire.d.ts +422 -26
- package/dist/trace/self-minted-wire.js +149 -23
- package/dist/trace/tool-card.d.ts +3 -0
- package/dist/trace/tool-card.js +24 -0
- package/dist/trace/tool-output-media.d.ts +2 -1
- package/dist/unattended-permission-mode.d.ts +1 -1
- package/dist/web-admin-client/apply-effective.d.ts +1 -1
- package/dist/web-admin-client/effective-keys.d.ts +1 -1
- package/dist/web-admin-client/skills-mcp.d.ts +1 -1
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +120 -41
- package/package.json +3 -3
- package/wire-changes/server@7.150.0.json +335 -0
- package/wire-changes/server@7.151.0.json +762 -0
- package/dist/http/session-inherit-env.d.ts +0 -36
- package/dist/http/session-inherit-env.js +0 -6
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { parkedApprovalCard } from "../approval-content-kind.js";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import { CHECKPOINT_FACET_SQL, CHECKPOINT_SWEEP_ORDER_SQL, CheckpointIndexRefusedError, checkpointSweepAfterSql, hostSessionOf, judgeCheckpointIndexCells } from "../plugins/checkpoint-index-columns.js";
|
|
4
|
-
import { CheckpointError, validatePendingSteer, appendPendingSteer, winnerFromOutcome, summarizeCheckpoint, checkpointRevOf, judgeCheckpointRow, CONTRACT_KIT_ENGINE_VERSION, ExecutionOutcomeConflictError, executionRecordDisposition, executionVerdict, } from "@sema-agent/core";
|
|
4
|
+
import { CheckpointError, validatePendingSteer, appendPendingSteer, winnerFromOutcome, summarizeCheckpoint, checkpointRevOf, judgeCheckpointRow, CONTRACT_KIT_ENGINE_VERSION, ExecutionOutcomeConflictError, executionRecordDisposition, executionVerdict, readCheckpointSessionQuery, selectSessionPage, } from "@sema-agent/core";
|
|
5
5
|
import { parkRowFacts, skillCardFacts } from "../approval-card.js";
|
|
6
|
-
import { assertJsonColumnWritable, createObjectColumnReader, decodeJsonColumn, decodeNullableJsonColumn, foldJsonColumn, isJsonColumnBodyProblem, jsonColumnText, jsonColumnTextSha256, readJsonArrayColumn, readJsonColumnTextSha256, readNonNullJsonValueColumn, StoreCorruptError, } from "../plugins/sql-json-column.js";
|
|
6
|
+
import { assertJsonColumnWritable, createObjectColumnReader, decodeJsonColumn, decodeNullableJsonColumn, foldJsonColumn, isJsonColumnBodyProblem, isJsonObject, jsonColumnText, jsonColumnTextSha256, readJsonArrayColumn, readJsonColumnTextSha256, readNonNullJsonValueColumn, StoreCorruptError, } from "../plugins/sql-json-column.js";
|
|
7
7
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
8
8
|
import { durableRowIdentity, withoutCheckpointToken } from "../checkpoint-identity.js";
|
|
9
9
|
import { mysqlDriver, pgDriver, dialectProtocolJsonEncoder } from "../plugins/sql-driver.js";
|
|
@@ -242,6 +242,12 @@ export function readPendingSteerQueueColumn(queueRaw) {
|
|
|
242
242
|
}
|
|
243
243
|
const STEER_CAS_ATTEMPTS = 8;
|
|
244
244
|
const EXECUTION_RECORD_CAS_ATTEMPTS = 8;
|
|
245
|
+
const STORED_ROW_DAMAGE = {
|
|
246
|
+
body: "its stored body is not a checkpoint object",
|
|
247
|
+
decision: "its stored decision record cannot be read",
|
|
248
|
+
execution: "its stored execution record cannot be read",
|
|
249
|
+
filing: "its stored body does not name the session the row is filed under",
|
|
250
|
+
};
|
|
245
251
|
export class SqlCheckpointStore {
|
|
246
252
|
db;
|
|
247
253
|
logger;
|
|
@@ -337,21 +343,62 @@ export class SqlCheckpointStore {
|
|
|
337
343
|
throw rowTooLargeOrSelf(this.db.dialect, e, `the checkpoint row for session ${JSON.stringify(cp.sessionId)}`);
|
|
338
344
|
}
|
|
339
345
|
}
|
|
346
|
+
storedCheckpointColumns() {
|
|
347
|
+
const d = this.db.dialect;
|
|
348
|
+
return (`status, ${jsonColumnText(d, "checkpoint")}, ${jsonColumnText(d, "outcome")}, reopen_reason, rev, ` +
|
|
349
|
+
`${jsonColumnText(d, "pending_steer_queue")}, ${jsonColumnText(d, "execution_outcome")}, execution_at_ms`);
|
|
350
|
+
}
|
|
340
351
|
async get(token) {
|
|
341
|
-
const { rows } = await this.db.query(
|
|
342
|
-
`${jsonColumnText("mysql", "pending_steer_queue")}, ${jsonColumnText("mysql", "execution_outcome")}, execution_at_ms FROM checkpoint WHERE token = ?`, `SELECT status, ${jsonColumnText("pg", "checkpoint")}, ${jsonColumnText("pg", "outcome")}, reopen_reason, rev, ` +
|
|
343
|
-
`${jsonColumnText("pg", "pending_steer_queue")}, ${jsonColumnText("pg", "execution_outcome")}, execution_at_ms FROM checkpoint WHERE token = $1`), [token]);
|
|
352
|
+
const { rows } = await this.db.query(`SELECT ${this.storedCheckpointColumns()} FROM checkpoint WHERE token = ${this.ph(1)}`, [token]);
|
|
344
353
|
const r = rows[0];
|
|
345
354
|
if (!r)
|
|
346
355
|
return null;
|
|
347
|
-
const
|
|
356
|
+
const read = this.readStoredCheckpoint(r, token);
|
|
357
|
+
if (read.kind === "damaged")
|
|
358
|
+
throw read.error;
|
|
359
|
+
const cp = read.checkpoint;
|
|
360
|
+
this.logger?.info?.("checkpoint_get", {
|
|
361
|
+
tokenFp: tokenFingerprint(token),
|
|
362
|
+
status: cp.status,
|
|
363
|
+
hasWorkspaceHandle: !!cp.state?.workspaceHandle,
|
|
364
|
+
snapshotId: cp.state?.workspaceHandle?.snapshotId,
|
|
365
|
+
});
|
|
366
|
+
return cp;
|
|
367
|
+
}
|
|
368
|
+
readStoredCheckpoint(r, token) {
|
|
369
|
+
const where = `for token ${tokenFingerprint(token)}`;
|
|
370
|
+
let cp;
|
|
371
|
+
try {
|
|
372
|
+
cp = readCheckpointBodyOrRefuse(r.checkpoint, where);
|
|
373
|
+
}
|
|
374
|
+
catch (e) {
|
|
375
|
+
if (e instanceof StoreCorruptError)
|
|
376
|
+
return { kind: "damaged", member: "body", error: e };
|
|
377
|
+
throw e;
|
|
378
|
+
}
|
|
348
379
|
cp.status = r.status;
|
|
349
|
-
|
|
380
|
+
let outcomeRaw;
|
|
381
|
+
try {
|
|
382
|
+
outcomeRaw = refusing("a decision record nobody can read", () => decodeOutcomeColumn(r.outcome, where));
|
|
383
|
+
}
|
|
384
|
+
catch (e) {
|
|
385
|
+
if (e instanceof StoreCorruptError)
|
|
386
|
+
return { kind: "damaged", member: "decision", error: e, body: cp };
|
|
387
|
+
throw e;
|
|
388
|
+
}
|
|
350
389
|
if (outcomeRaw)
|
|
351
390
|
cp.resolvedOutcome = winnerFromOutcome(outcomeRaw);
|
|
352
391
|
cp.reopenReason = r.reopen_reason ?? undefined;
|
|
353
392
|
cp.rev = r.rev == null ? 0 : Number(r.rev);
|
|
354
|
-
|
|
393
|
+
let executionOutcome;
|
|
394
|
+
try {
|
|
395
|
+
executionOutcome = refusing("an execution record nobody can read", () => decodeExecutionOutcomeColumn(r.execution_outcome, where));
|
|
396
|
+
}
|
|
397
|
+
catch (e) {
|
|
398
|
+
if (e instanceof StoreCorruptError)
|
|
399
|
+
return { kind: "damaged", member: "execution", error: e, body: cp };
|
|
400
|
+
throw e;
|
|
401
|
+
}
|
|
355
402
|
if (executionOutcome !== undefined) {
|
|
356
403
|
cp.executionOutcome = executionOutcome;
|
|
357
404
|
if (r.execution_at_ms != null)
|
|
@@ -362,17 +409,58 @@ export class SqlCheckpointStore {
|
|
|
362
409
|
recordFailOpen("server.sql-json.checkpoint.pending-steer-queue-lossy");
|
|
363
410
|
this.logger?.info?.("checkpoint_pending_steer_unreadable", { tokenFp: tokenFingerprint(token) });
|
|
364
411
|
}
|
|
365
|
-
if (cp.state)
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
412
|
+
if (isJsonObject(cp.state) && steer.state.pendingSteerQueue !== undefined)
|
|
413
|
+
cp.state.pendingSteerQueue = steer.state.pendingSteerQueue;
|
|
414
|
+
return { kind: "row", checkpoint: cp };
|
|
415
|
+
}
|
|
416
|
+
async listBySession(query) {
|
|
417
|
+
const read = readCheckpointSessionQuery(query);
|
|
418
|
+
const { rows } = await this.db.query(`SELECT token, scope, session_id, created_at_ms, ${this.storedCheckpointColumns()} FROM checkpoint WHERE session_id = ${this.ph(1)}`, [read.sessionId]);
|
|
419
|
+
const tokens = rows.map((r) => String(r.token));
|
|
420
|
+
const withoutTokens = (sentence) => tokens.reduce((out, t) => withoutCheckpointToken(out, t), sentence);
|
|
421
|
+
const damaged = new Map();
|
|
422
|
+
const candidates = [];
|
|
423
|
+
rows.forEach((r, i) => {
|
|
424
|
+
const token = tokens[i];
|
|
425
|
+
const got = this.readStoredCheckpoint(r, token);
|
|
426
|
+
const body = got.kind === "row" ? got.checkpoint : got.member === "body" ? undefined : got.body;
|
|
427
|
+
const filedHere = body !== undefined && body.sessionId === String(r.session_id);
|
|
428
|
+
if (got.kind === "row" && filedHere) {
|
|
429
|
+
candidates.push([token, body]);
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
damaged.set(token, { problem: STORED_ROW_DAMAGE[got.kind === "damaged" ? got.member : "filing"], r, counted: filedHere });
|
|
433
|
+
if (filedHere) {
|
|
434
|
+
candidates.push([token, body]);
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
const filing = { token: token, sessionId: String(r.session_id), scope: String(r.scope), status: String(r.status), createdAt: Number(r.created_at_ms) };
|
|
438
|
+
candidates.push([token, filing]);
|
|
374
439
|
});
|
|
375
|
-
|
|
440
|
+
const page = selectSessionPage(candidates, read, (cp) => cp, (sentence) => recordFailOpen("server.approvals.park-row-blob-unreadable", withoutTokens(sentence)));
|
|
441
|
+
if (damaged.size === 0)
|
|
442
|
+
return page;
|
|
443
|
+
return {
|
|
444
|
+
...page,
|
|
445
|
+
rows: page.rows.map((row) => {
|
|
446
|
+
const token = row.kind === "row" ? row.checkpoint.token : row.token;
|
|
447
|
+
const hit = damaged.get(token);
|
|
448
|
+
if (hit === undefined)
|
|
449
|
+
return row;
|
|
450
|
+
if (hit.counted && row.kind === "row")
|
|
451
|
+
recordFailOpen("server.approvals.park-row-blob-unreadable", `session listing, session ${JSON.stringify(String(hit.r.session_id))}: ${hit.problem} — listed as unreadable`);
|
|
452
|
+
const createdAt = Number(hit.r.created_at_ms);
|
|
453
|
+
return {
|
|
454
|
+
kind: "unreadable",
|
|
455
|
+
token: token,
|
|
456
|
+
scope: String(hit.r.scope),
|
|
457
|
+
sessionId: String(hit.r.session_id),
|
|
458
|
+
status: String(hit.r.status),
|
|
459
|
+
createdAt: Number.isFinite(createdAt) ? createdAt : null,
|
|
460
|
+
problem: hit.problem,
|
|
461
|
+
};
|
|
462
|
+
}),
|
|
463
|
+
};
|
|
376
464
|
}
|
|
377
465
|
async resolve(token, scope, outcome, expect) {
|
|
378
466
|
const params = [this.json(outcome, "checkpoint outcome"), Date.now(), token, scope];
|
|
@@ -1,15 +1,26 @@
|
|
|
1
1
|
import type { Pool as MySqlPool } from "mysql2/promise";
|
|
2
2
|
import type { Pool as PgPool } from "pg";
|
|
3
|
-
import { type FileHistoryRetentionPolicy, type ExecutionEnv, type FileHistoryStore, type FileHistoryResult, type FileHistoryTrackResult, type FileHistoryRestoreResult, type FileHistoryExport, type FileHistoryExportResult } from "@sema-agent/core";
|
|
3
|
+
import { type FileHistoryRetentionPolicy, type ExecutionEnv, type FileHistoryStore, type FileHistoryError, type FileHistoryResult, type FileHistoryTrackResult, type FileHistoryRestoreResult, type FileHistoryExport, type FileHistoryExportResult } from "@sema-agent/core";
|
|
4
4
|
import type { BlobBackend } from "../plugins/blob-backend.js";
|
|
5
5
|
import { SqlBlobIndex } from "./blob-index-sql.js";
|
|
6
6
|
import type { ServiceBlobExchange } from "../plugins/store-backend.js";
|
|
7
|
-
import { type SqlDriver, type SqlDialect } from "../plugins/sql-driver.js";
|
|
7
|
+
import { type SqlDriver, type SqlTxConn, type SqlDialect } from "../plugins/sql-driver.js";
|
|
8
8
|
/** The file-history blob domain's SQL table (this store's blob index + the A7 assert). */
|
|
9
9
|
export declare const FILE_HISTORY_BLOB_TABLE = "file_history_blob";
|
|
10
10
|
/** MinIO object-key prefix for this domain (snapshot epoch used "blobs/"; a separate prefix keeps the
|
|
11
11
|
* two domains' objects disjoint so neither sweep can see the other's keys). */
|
|
12
12
|
export declare const FILE_HISTORY_BLOB_KEY_PREFIX = "file-history/";
|
|
13
|
+
/**
|
|
14
|
+
* {@link SqlFileHistoryStore.prepareImport} 的答:门 / 取值校验的拒(`ok: false`,店码),或这一份信封落进目标要做什么 —— `same`(目标已是这一张图:不写)/
|
|
15
|
+
* `publish`(目标空,或 `replace` 下目标是另一张图:发布步写)。发布步({@link SqlFileHistoryStore.publishImportOn})在调用方的事务里按它做。
|
|
16
|
+
*/
|
|
17
|
+
export type FileHistoryImportPlan = {
|
|
18
|
+
readonly ok: false;
|
|
19
|
+
readonly error: FileHistoryError;
|
|
20
|
+
} | {
|
|
21
|
+
readonly ok: true;
|
|
22
|
+
readonly mode: "same" | "publish";
|
|
23
|
+
};
|
|
13
24
|
/**
|
|
14
25
|
* [ref] twin 保留条款(core 7.0.0 [ref] §片2 的 SQL 孪生)—— 构造期旋钮,键形与 core 内置后端
|
|
15
26
|
* `FileFileHistoryStoreOptions.retention` / `FileStorageBackendOptions.fileHistory.retention` **同域同名**。
|
|
@@ -89,6 +100,13 @@ export declare class SqlFileHistoryStore implements FileHistoryStore {
|
|
|
89
100
|
* The lock order itself stays single: sentinels first (sorted), then the scope's rows.
|
|
90
101
|
*/
|
|
91
102
|
private lockScope;
|
|
103
|
+
/**
|
|
104
|
+
* The ensure-and-lock of {@link lockScope} **without** its restart — the statement source a transaction someone else opened runs (the
|
|
105
|
+
* landed section: its first statements lock the session's root row and judge the occupant, so a restart here would drop that lock and
|
|
106
|
+
* that judgement). A lost arbitration (a deadlock victim, or {@link ScopeSentinelVanishedError}, a `SqlArbitrationLostError`) propagates;
|
|
107
|
+
* the transaction's owner restarts from ITS top. Order: sentinels sorted, then the scope's rows (the one lock order).
|
|
108
|
+
*/
|
|
109
|
+
lockScopeOn(conn: SqlTxConn, scopes: readonly string[]): Promise<void>;
|
|
92
110
|
/** Returns the UPDATE's affected count — 0 = the record is gone (annulled) or was already observed;
|
|
93
111
|
* the caller disambiguates by re-reading (codex R1-high: answering minted:false after the baseline
|
|
94
112
|
* vanished would let a later touch mint already-modified bytes as pristine). */
|
|
@@ -164,6 +182,12 @@ export declare class SqlFileHistoryStore implements FileHistoryStore {
|
|
|
164
182
|
* collected by the async grace-window sweep (never a synchronous external-store delete on the purge
|
|
165
183
|
* path). Also the overwrite-dst session-sync wipe seam (import requires an EMPTY target graph). */
|
|
166
184
|
deleteBySession(scope: string): Promise<number>;
|
|
185
|
+
/** The whole-scope removal as a statement source on the caller's transaction ({@link lockScopeOn} + the six tables, sentinel last):
|
|
186
|
+
* {@link deleteBySession}'s body, and the landed section's wipe (one transaction with the session's root-row lock). Returns rows removed. */
|
|
187
|
+
deleteScopeOn(conn: SqlTxConn, scope: string): Promise<number>;
|
|
188
|
+
/** Delete one scope's rows from `tables` (caller holds the scope's sentinel lock). 第六表 `file_history_reaped_entry`(spent-entry 台账)随 scope 一起走:
|
|
189
|
+
* E21 是删除权,台账不是例外。 */
|
|
190
|
+
private deleteScopeRowsOn;
|
|
167
191
|
/** Async byte-GC: blobs referenced by NO version row (any scope), past the created_at grace window.
|
|
168
192
|
* Domain-isolated by construction (this store's blob table only — [ref] D1). Reaper-driven. */
|
|
169
193
|
sweepOrphanBlobs(): Promise<number>;
|
|
@@ -190,6 +214,32 @@ export declare class SqlFileHistoryStore implements FileHistoryStore {
|
|
|
190
214
|
*/
|
|
191
215
|
private verifySourceBlobs;
|
|
192
216
|
importHistory(scope: string, data: FileHistoryExport, srcGetBlob: (hash: string) => Promise<Uint8Array | undefined>): Promise<FileHistoryResult>;
|
|
217
|
+
/**
|
|
218
|
+
* 导入接住的一只故障的**唯一**折法(本店的图事务、准备步与落定段的发布步同读):core 7.55.0 —— `unsupported_format` 只说坏信封(或它的形驱动的活),
|
|
219
|
+
* 门之后撞上的故障(库拒了一份好图 / 重来用尽 / 连接断)是 `adopt_failed`,带接住的那只故障判得出的 `permanence`(判不出 ⇒ 不带)。
|
|
220
|
+
*/
|
|
221
|
+
importFailureOf(err: unknown): FileHistoryError;
|
|
222
|
+
/**
|
|
223
|
+
* 导入的**事务外半**(`importHistory` 与落定段的重放同经它):门(core 的共享校验 + 本店的 mode 值域)→ 预读目标图(读不铸哨兵)判同图 / 异图 → 取值校验信封引用的每一枚 blob
|
|
224
|
+
* (首推 / 替换:校验过的字节续租进 blob 后端,发布前的孤儿清扫不收走它们;同图:只校验不回写)。`replace`(consented overwrite-dst 的落定重放)⇒ 异图不拒,按首推准备。
|
|
225
|
+
* 任何拒 ⇒ `ok: false`(店码);一个字节没发布。
|
|
226
|
+
*/
|
|
227
|
+
prepareImport(scope: string, data: FileHistoryExport, srcGetBlob: (hash: string) => Promise<Uint8Array | undefined>, opts: {
|
|
228
|
+
readonly replace: boolean;
|
|
229
|
+
}): Promise<FileHistoryImportPlan>;
|
|
230
|
+
/**
|
|
231
|
+
* 导入的**发布步**,一只语句源跑在调用方的事务里(`importHistory` 自己的图事务 / 落定段):`same` ⇒ 不写;`publish` ⇒ scope 锁({@link lockScopeOn},不自己重开事务)→
|
|
232
|
+
* 事务内空目标复判(非破坏性目标律的 TOCTOU 关)→ 空 ⇒ 整图一步插入;已是这张图 ⇒ 不写;另一张图 ⇒ `replace` 下先删这一 scope 的图行(五表,哨兵留着 —— 本事务持着它)
|
|
233
|
+
* 再插(删与插同一事务:替换不会把目标留成空),否则 `conflict`、一个字节没写。
|
|
234
|
+
*/
|
|
235
|
+
publishImportOn(conn: SqlTxConn, scope: string, data: FileHistoryExport, plan: Extract<FileHistoryImportPlan, {
|
|
236
|
+
ok: true;
|
|
237
|
+
}>, opts: {
|
|
238
|
+
readonly replace: boolean;
|
|
239
|
+
}): Promise<FileHistoryResult>;
|
|
240
|
+
/** The publish step's body under a held scope lock ({@link importHistory}: {@link lockScope} in the store's own transaction · {@link publishImportOn}:
|
|
241
|
+
* {@link lockScopeOn} in the landed section). */
|
|
242
|
+
private publishImportLockedOn;
|
|
193
243
|
}
|
|
194
244
|
/** MySQL-protocol (TiDB) binding. */
|
|
195
245
|
export declare class TiDBFileHistoryStore extends SqlFileHistoryStore {
|
|
@@ -2,7 +2,7 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { captureFileBackupViaEnv, applyFileRestoreViaEnv, fileHistoryModeProvenDifferent, worldStillMatchesMint, annulProofFailure, boundaryPublishVerdict, fileHistoryDescribeTrackKey, fileHistoryTrackKeyOf, fileHistoryResolveTrackKey, fileHistoryExportDigest, validateFileHistoryExport, resolveFileHistoryRetention, fileHistoryBoundariesToKeep, } from "@sema-agent/core";
|
|
3
3
|
import { BlobTooLargeError, SqlBlobIndex } from "./blob-index-sql.js";
|
|
4
4
|
import { mysqlDriver, pgDriver } from "../plugins/sql-driver.js";
|
|
5
|
-
import { isMissingColumnError, retryOnDeadlock } from "../plugins/sql-errors.js";
|
|
5
|
+
import { isMissingColumnError, retryOnDeadlock, SqlArbitrationLostError } from "../plugins/sql-errors.js";
|
|
6
6
|
import { probedMissingColumnRefusal } from "../plugins/schema-drift-sentence.js";
|
|
7
7
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
8
8
|
import { foldCaughtFailure } from "../file-history-disclosure.js";
|
|
@@ -12,8 +12,9 @@ const sha256hex = (s) => createHash("sha256").update(s).digest("hex");
|
|
|
12
12
|
const truthy = (v) => v === true || v === 1 || v === "1";
|
|
13
13
|
class StagedRefGoneError extends Error {
|
|
14
14
|
}
|
|
15
|
-
class ScopeSentinelVanishedError extends
|
|
15
|
+
class ScopeSentinelVanishedError extends SqlArbitrationLostError {
|
|
16
16
|
}
|
|
17
|
+
const FILE_HISTORY_SCOPED_TABLES = ["file_history_boundary_file", "file_history_boundary", "file_history_reaped_entry", "file_history_version", "file_history_tracked", "file_history_scope"];
|
|
17
18
|
function reapedEntryRefusal(entryId) {
|
|
18
19
|
return {
|
|
19
20
|
ok: false,
|
|
@@ -67,14 +68,17 @@ export class SqlFileHistoryStore {
|
|
|
67
68
|
await conn.begin();
|
|
68
69
|
}
|
|
69
70
|
restart = true;
|
|
70
|
-
|
|
71
|
-
await conn.query(this.q("INSERT INTO file_history_scope (scope, created_at_ms) VALUES (?,?) ON DUPLICATE KEY UPDATE scope = scope", "INSERT INTO file_history_scope (scope, created_at_ms) VALUES ($1,$2) ON CONFLICT (scope) DO NOTHING"), [scope, Date.now()]);
|
|
72
|
-
const r = await conn.query(this.q("SELECT scope FROM file_history_scope WHERE scope = ? FOR UPDATE", "SELECT scope FROM file_history_scope WHERE scope = $1 FOR UPDATE"), [scope]);
|
|
73
|
-
if (!r.rows[0])
|
|
74
|
-
throw new ScopeSentinelVanishedError(`could not acquire the scope write lock for "${scope}" (sentinel row kept vanishing)`);
|
|
75
|
-
}
|
|
71
|
+
await this.lockScopeOn(conn, ordered);
|
|
76
72
|
}, (err) => err instanceof ScopeSentinelVanishedError);
|
|
77
73
|
}
|
|
74
|
+
async lockScopeOn(conn, scopes) {
|
|
75
|
+
for (const scope of [...new Set(scopes)].sort()) {
|
|
76
|
+
await conn.query(this.q("INSERT INTO file_history_scope (scope, created_at_ms) VALUES (?,?) ON DUPLICATE KEY UPDATE scope = scope", "INSERT INTO file_history_scope (scope, created_at_ms) VALUES ($1,$2) ON CONFLICT (scope) DO NOTHING"), [scope, Date.now()]);
|
|
77
|
+
const r = await conn.query(this.q("SELECT scope FROM file_history_scope WHERE scope = ? FOR UPDATE", "SELECT scope FROM file_history_scope WHERE scope = $1 FOR UPDATE"), [scope]);
|
|
78
|
+
if (!r.rows[0])
|
|
79
|
+
throw new ScopeSentinelVanishedError(`could not acquire the scope write lock for "${scope}" (sentinel row kept vanishing)`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
78
82
|
async markObserved(scope, keyHash) {
|
|
79
83
|
const r = await this.db.query(this.q("UPDATE file_history_tracked SET observed_later = 1 WHERE scope = ? AND key_hash = ? AND observed_later = 0", "UPDATE file_history_tracked SET observed_later = TRUE WHERE scope = $1 AND key_hash = $2 AND observed_later = FALSE"), [scope, keyHash]);
|
|
80
84
|
return r.affected;
|
|
@@ -134,7 +138,7 @@ export class SqlFileHistoryStore {
|
|
|
134
138
|
return { ok: false, error: { code: "track_failed", message: `first-touch record for ${absPath} kept racing a concurrent retraction — retry` } };
|
|
135
139
|
}
|
|
136
140
|
catch (err) {
|
|
137
|
-
const f = foldCaughtFailure(err);
|
|
141
|
+
const f = foldCaughtFailure(err, "sql-file-history-store.trackEdit");
|
|
138
142
|
return { ok: false, error: { code: "track_failed", message: `first-touch record for ${absPath} could not be persisted: ${f.text}`, ...f.fault } };
|
|
139
143
|
}
|
|
140
144
|
}
|
|
@@ -201,7 +205,7 @@ export class SqlFileHistoryStore {
|
|
|
201
205
|
return { ok: false, error: { code: "conflict", message: `first-touch record for ${absPath} was replaced while this retraction was being proven` } };
|
|
202
206
|
}
|
|
203
207
|
catch (err) {
|
|
204
|
-
const f = foldCaughtFailure(err);
|
|
208
|
+
const f = foldCaughtFailure(err, "sql-file-history-store.annulTrack");
|
|
205
209
|
return { ok: false, error: { code: "track_failed", message: `annulling the first-touch record for ${absPath} failed: ${f.text}`, ...f.fault } };
|
|
206
210
|
}
|
|
207
211
|
}
|
|
@@ -309,7 +313,7 @@ export class SqlFileHistoryStore {
|
|
|
309
313
|
}
|
|
310
314
|
}
|
|
311
315
|
catch (err) {
|
|
312
|
-
const f = foldCaughtFailure(err);
|
|
316
|
+
const f = foldCaughtFailure(err, "sql-file-history-store.snapshot");
|
|
313
317
|
return { ok: false, error: { code: "snapshot_failed", message: f.text, ...f.fault } };
|
|
314
318
|
}
|
|
315
319
|
this.activeAttempts.add(lease);
|
|
@@ -412,7 +416,7 @@ export class SqlFileHistoryStore {
|
|
|
412
416
|
catch (err) {
|
|
413
417
|
await conn.rollback().catch(() => undefined);
|
|
414
418
|
this.spentAttempts.add(lease);
|
|
415
|
-
const f = foldCaughtFailure(err);
|
|
419
|
+
const f = foldCaughtFailure(err, "sql-file-history-store.snapshot");
|
|
416
420
|
return { ok: false, error: { code: "snapshot_failed", message: `boundary for entry "${entryId}" could not be committed: ${f.text}`, ...f.fault } };
|
|
417
421
|
}
|
|
418
422
|
finally {
|
|
@@ -423,7 +427,7 @@ export class SqlFileHistoryStore {
|
|
|
423
427
|
}
|
|
424
428
|
catch (err) {
|
|
425
429
|
this.spentAttempts.add(lease);
|
|
426
|
-
const f = foldCaughtFailure(err);
|
|
430
|
+
const f = foldCaughtFailure(err, "sql-file-history-store.snapshot");
|
|
427
431
|
return { ok: false, error: { code: "snapshot_failed", message: f.text, ...f.fault } };
|
|
428
432
|
}
|
|
429
433
|
finally {
|
|
@@ -491,7 +495,7 @@ export class SqlFileHistoryStore {
|
|
|
491
495
|
});
|
|
492
496
|
}
|
|
493
497
|
catch (err) {
|
|
494
|
-
applied = { outcome: "failed", reason: foldCaughtFailure(err).text };
|
|
498
|
+
applied = { outcome: "failed", reason: foldCaughtFailure(err, "sql-file-history-store.restore").text };
|
|
495
499
|
}
|
|
496
500
|
if (applied.outcome === "applied")
|
|
497
501
|
ledger.filesChanged.push(t.trackKey);
|
|
@@ -508,7 +512,7 @@ export class SqlFileHistoryStore {
|
|
|
508
512
|
}
|
|
509
513
|
catch (err) {
|
|
510
514
|
const touched = empty.filesChanged.length > 0 || empty.identicalSkipped.length > 0 || empty.refused.length > 0 || empty.failed.length > 0;
|
|
511
|
-
const f = foldCaughtFailure(err);
|
|
515
|
+
const f = foldCaughtFailure(err, "sql-file-history-store.restore");
|
|
512
516
|
return { outcome: touched ? "partial" : "failed", ...empty, error: { code: "restore_failed", message: f.text, ...f.fault } };
|
|
513
517
|
}
|
|
514
518
|
}
|
|
@@ -578,11 +582,7 @@ export class SqlFileHistoryStore {
|
|
|
578
582
|
try {
|
|
579
583
|
await conn.begin();
|
|
580
584
|
await this.lockScope(conn, scope);
|
|
581
|
-
|
|
582
|
-
for (const table of ["file_history_boundary_file", "file_history_boundary", "file_history_reaped_entry", "file_history_version", "file_history_tracked", "file_history_scope"]) {
|
|
583
|
-
const r = await conn.query(this.q(`DELETE FROM ${table} WHERE scope = ?`, `DELETE FROM ${table} WHERE scope = $1`), [scope]);
|
|
584
|
-
total += r.affected;
|
|
585
|
-
}
|
|
585
|
+
const total = await this.deleteScopeRowsOn(conn, scope, FILE_HISTORY_SCOPED_TABLES);
|
|
586
586
|
await conn.commit();
|
|
587
587
|
return total;
|
|
588
588
|
}
|
|
@@ -594,6 +594,18 @@ export class SqlFileHistoryStore {
|
|
|
594
594
|
conn.release();
|
|
595
595
|
}
|
|
596
596
|
}
|
|
597
|
+
async deleteScopeOn(conn, scope) {
|
|
598
|
+
await this.lockScopeOn(conn, [scope]);
|
|
599
|
+
return this.deleteScopeRowsOn(conn, scope, FILE_HISTORY_SCOPED_TABLES);
|
|
600
|
+
}
|
|
601
|
+
async deleteScopeRowsOn(conn, scope, tables) {
|
|
602
|
+
let total = 0;
|
|
603
|
+
for (const table of tables) {
|
|
604
|
+
const r = await conn.query(this.q(`DELETE FROM ${table} WHERE scope = ?`, `DELETE FROM ${table} WHERE scope = $1`), [scope]);
|
|
605
|
+
total += r.affected;
|
|
606
|
+
}
|
|
607
|
+
return total;
|
|
608
|
+
}
|
|
597
609
|
async sweepOrphanBlobs() {
|
|
598
610
|
const { rows } = await this.db.query("SELECT blob_hash FROM file_history_blob WHERE blob_hash NOT IN (SELECT blob_hash FROM file_history_version WHERE blob_hash IS NOT NULL)");
|
|
599
611
|
const orphans = rows.map((r) => String(r.blob_hash));
|
|
@@ -612,7 +624,7 @@ export class SqlFileHistoryStore {
|
|
|
612
624
|
catch (err) {
|
|
613
625
|
if (err instanceof BlobTooLargeError)
|
|
614
626
|
return { ok: false, error: { code: "adopt_failed", message: err.message, permanence: "terminal", blobCap: { wallSource: err.cap.source } } };
|
|
615
|
-
const f = foldCaughtFailure(err);
|
|
627
|
+
const f = foldCaughtFailure(err, "sql-file-history-store.putExchangedBlob");
|
|
616
628
|
return { ok: false, error: { code: "snapshot_failed", message: `blob ${hash} write/upload failed: ${f.text}`, ...f.fault } };
|
|
617
629
|
}
|
|
618
630
|
}
|
|
@@ -650,7 +662,7 @@ export class SqlFileHistoryStore {
|
|
|
650
662
|
}
|
|
651
663
|
}
|
|
652
664
|
catch (err) {
|
|
653
|
-
const f = foldCaughtFailure(err);
|
|
665
|
+
const f = foldCaughtFailure(err, "sql-file-history-store.adoptScope");
|
|
654
666
|
return { ok: false, error: { code: "adopt_failed", message: `adoptScope failed: ${f.text}`, ...f.fault } };
|
|
655
667
|
}
|
|
656
668
|
}
|
|
@@ -707,10 +719,10 @@ export class SqlFileHistoryStore {
|
|
|
707
719
|
const conn = await this.db.connect();
|
|
708
720
|
try {
|
|
709
721
|
await conn.begin();
|
|
710
|
-
await this.
|
|
711
|
-
const
|
|
722
|
+
const held = await conn.query(this.q("SELECT scope FROM file_history_scope WHERE scope = ? FOR UPDATE", "SELECT scope FROM file_history_scope WHERE scope = $1 FOR UPDATE"), [scope]);
|
|
723
|
+
const history = held.rows[0] === undefined ? null : await this.exportOf(conn, scope, true);
|
|
712
724
|
await conn.commit();
|
|
713
|
-
return { ok: true, history
|
|
725
|
+
return { ok: true, history };
|
|
714
726
|
}
|
|
715
727
|
catch (err) {
|
|
716
728
|
await conn.rollback().catch(() => undefined);
|
|
@@ -732,7 +744,7 @@ export class SqlFileHistoryStore {
|
|
|
732
744
|
bytes = await srcGetBlob(hash);
|
|
733
745
|
}
|
|
734
746
|
catch (err) {
|
|
735
|
-
const f = foldCaughtFailure(err);
|
|
747
|
+
const f = foldCaughtFailure(err, "sql-file-history-store.verifySourceBlobs");
|
|
736
748
|
return { ok: false, error: { code: "adopt_failed", message: `source blob ${hash} fetch failed: ${f.text}`, ...f.fault } };
|
|
737
749
|
}
|
|
738
750
|
if (bytes === undefined)
|
|
@@ -745,6 +757,40 @@ export class SqlFileHistoryStore {
|
|
|
745
757
|
return { ok: true };
|
|
746
758
|
}
|
|
747
759
|
async importHistory(scope, data, srcGetBlob) {
|
|
760
|
+
const plan = await this.prepareImport(scope, data, srcGetBlob, { replace: false });
|
|
761
|
+
if (!plan.ok)
|
|
762
|
+
return plan;
|
|
763
|
+
if (plan.mode === "same")
|
|
764
|
+
return { ok: true };
|
|
765
|
+
try {
|
|
766
|
+
const conn = await this.db.connect();
|
|
767
|
+
try {
|
|
768
|
+
await conn.begin();
|
|
769
|
+
await this.lockScope(conn, scope);
|
|
770
|
+
const out = await this.publishImportLockedOn(conn, scope, data, { replace: false });
|
|
771
|
+
if (out.ok)
|
|
772
|
+
await conn.commit();
|
|
773
|
+
else
|
|
774
|
+
await conn.rollback();
|
|
775
|
+
return out;
|
|
776
|
+
}
|
|
777
|
+
catch (err) {
|
|
778
|
+
await conn.rollback().catch(() => undefined);
|
|
779
|
+
throw err;
|
|
780
|
+
}
|
|
781
|
+
finally {
|
|
782
|
+
conn.release();
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
catch (err) {
|
|
786
|
+
return { ok: false, error: this.importFailureOf(err) };
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
importFailureOf(err) {
|
|
790
|
+
const f = foldCaughtFailure(err, "sql-file-history-store.importHistory");
|
|
791
|
+
return { code: "adopt_failed", message: `importHistory failed: ${f.text}`, ...f.fault };
|
|
792
|
+
}
|
|
793
|
+
async prepareImport(scope, data, srcGetBlob, opts) {
|
|
748
794
|
try {
|
|
749
795
|
const shaped = validateFileHistoryExport(data);
|
|
750
796
|
if (!shaped.ok)
|
|
@@ -762,59 +808,51 @@ export class SqlFileHistoryStore {
|
|
|
762
808
|
return exported;
|
|
763
809
|
const existing = exported.history;
|
|
764
810
|
const sameGraph = existing !== null && fileHistoryExportDigest(existing) === fileHistoryExportDigest(data);
|
|
765
|
-
if (existing !== null && !sameGraph) {
|
|
811
|
+
if (existing !== null && !sameGraph && !opts.replace) {
|
|
766
812
|
return { ok: false, error: { code: "conflict", message: `importHistory target "${scope}" already holds a DIFFERENT history — a first import requires an empty target and a retry is legal only for the identical envelope (atomic visibility prevents half-graphs; this law prevents publishing the wrong complete graph over locally-grown history)` } };
|
|
767
813
|
}
|
|
768
814
|
const verified = await this.verifySourceBlobs(data, srcGetBlob, sameGraph ? undefined : (hash, bytes) => this.blobs.put(hash, bytes));
|
|
769
815
|
if (!verified.ok)
|
|
770
816
|
return verified;
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
for (const t of data.tracked) {
|
|
788
|
-
const keyHash = sha256hex(t.relOrAbsPath);
|
|
789
|
-
await conn.query(this.q("INSERT INTO file_history_tracked (scope, key_hash, track_key, key_kind, key_family, base_available, observed_later, created_at_ms) VALUES (?,?,?,?,?,?,0,?)", "INSERT INTO file_history_tracked (scope, key_hash, track_key, key_kind, key_family, base_available, observed_later, created_at_ms) VALUES ($1,$2,$3,$4,$5,$6,FALSE,$7)"), [scope, keyHash, t.relOrAbsPath, t.keyKind, t.keyFamily, this.bool(t.baseAvailable), now]);
|
|
790
|
-
for (const v of t.versions) {
|
|
791
|
-
await conn.query(this.q("INSERT INTO file_history_version (scope, key_hash, version, blob_hash, mode, parent_ident, backup_time) VALUES (?,?,?,?,?,?,?)", "INSERT INTO file_history_version (scope, key_hash, version, blob_hash, mode, parent_ident, backup_time) VALUES ($1,$2,$3,$4,$5,$6,$7)"), [scope, keyHash, v.version, v.blobHash, v.mode ?? null, v.parentIdent ?? null, v.backupTime]);
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
const seqBase = await conn.query(this.q("SELECT COALESCE(MAX(publish_seq), 0) AS s FROM file_history_boundary WHERE scope = ?", "SELECT COALESCE(MAX(publish_seq), 0) AS s FROM file_history_boundary WHERE scope = $1"), [scope]);
|
|
795
|
-
let publishSeq = Number(seqBase.rows[0].s);
|
|
796
|
-
for (const b of data.boundaries) {
|
|
797
|
-
publishSeq += 1;
|
|
798
|
-
await conn.query(this.q("INSERT INTO file_history_boundary (scope, entry_id, published_at_ms, publish_seq) VALUES (?,?,?,?)", "INSERT INTO file_history_boundary (scope, entry_id, published_at_ms, publish_seq) VALUES ($1,$2,$3,$4)"), [scope, b.entryId, now, publishSeq]);
|
|
799
|
-
for (const f of b.files) {
|
|
800
|
-
await conn.query(this.q("INSERT INTO file_history_boundary_file (scope, entry_id, key_hash, version) VALUES (?,?,?,?)", "INSERT INTO file_history_boundary_file (scope, entry_id, key_hash, version) VALUES ($1,$2,$3,$4)"), [scope, b.entryId, sha256hex(f.relOrAbsPath), f.version]);
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
await conn.commit();
|
|
817
|
+
return { ok: true, mode: sameGraph ? "same" : "publish" };
|
|
818
|
+
}
|
|
819
|
+
catch (err) {
|
|
820
|
+
return { ok: false, error: this.importFailureOf(err) };
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
async publishImportOn(conn, scope, data, plan, opts) {
|
|
824
|
+
if (plan.mode === "same")
|
|
825
|
+
return { ok: true };
|
|
826
|
+
await this.lockScopeOn(conn, [scope]);
|
|
827
|
+
return this.publishImportLockedOn(conn, scope, data, opts);
|
|
828
|
+
}
|
|
829
|
+
async publishImportLockedOn(conn, scope, data, opts) {
|
|
830
|
+
if (!(await this.scopeEmpty(conn, scope, true))) {
|
|
831
|
+
const held = await this.exportOf(conn, scope, true);
|
|
832
|
+
if (held !== null && fileHistoryExportDigest(held) === fileHistoryExportDigest(data))
|
|
804
833
|
return { ok: true };
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
834
|
+
if (!opts.replace)
|
|
835
|
+
return { ok: false, error: { code: "conflict", message: `importHistory target "${scope}" gained a DIFFERENT history while this import was fetching blobs — nothing was published` } };
|
|
836
|
+
await this.deleteScopeRowsOn(conn, scope, FILE_HISTORY_SCOPED_TABLES.filter((t) => t !== "file_history_scope"));
|
|
837
|
+
}
|
|
838
|
+
const now = Date.now();
|
|
839
|
+
for (const t of data.tracked) {
|
|
840
|
+
const keyHash = sha256hex(t.relOrAbsPath);
|
|
841
|
+
await conn.query(this.q("INSERT INTO file_history_tracked (scope, key_hash, track_key, key_kind, key_family, base_available, observed_later, created_at_ms) VALUES (?,?,?,?,?,?,0,?)", "INSERT INTO file_history_tracked (scope, key_hash, track_key, key_kind, key_family, base_available, observed_later, created_at_ms) VALUES ($1,$2,$3,$4,$5,$6,FALSE,$7)"), [scope, keyHash, t.relOrAbsPath, t.keyKind, t.keyFamily, this.bool(t.baseAvailable), now]);
|
|
842
|
+
for (const v of t.versions) {
|
|
843
|
+
await conn.query(this.q("INSERT INTO file_history_version (scope, key_hash, version, blob_hash, mode, parent_ident, backup_time) VALUES (?,?,?,?,?,?,?)", "INSERT INTO file_history_version (scope, key_hash, version, blob_hash, mode, parent_ident, backup_time) VALUES ($1,$2,$3,$4,$5,$6,$7)"), [scope, keyHash, v.version, v.blobHash, v.mode ?? null, v.parentIdent ?? null, v.backupTime]);
|
|
812
844
|
}
|
|
813
845
|
}
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
846
|
+
const seqBase = await conn.query(this.q("SELECT COALESCE(MAX(publish_seq), 0) AS s FROM file_history_boundary WHERE scope = ?", "SELECT COALESCE(MAX(publish_seq), 0) AS s FROM file_history_boundary WHERE scope = $1"), [scope]);
|
|
847
|
+
let publishSeq = Number(seqBase.rows[0].s);
|
|
848
|
+
for (const b of data.boundaries) {
|
|
849
|
+
publishSeq += 1;
|
|
850
|
+
await conn.query(this.q("INSERT INTO file_history_boundary (scope, entry_id, published_at_ms, publish_seq) VALUES (?,?,?,?)", "INSERT INTO file_history_boundary (scope, entry_id, published_at_ms, publish_seq) VALUES ($1,$2,$3,$4)"), [scope, b.entryId, now, publishSeq]);
|
|
851
|
+
for (const f of b.files) {
|
|
852
|
+
await conn.query(this.q("INSERT INTO file_history_boundary_file (scope, entry_id, key_hash, version) VALUES (?,?,?,?)", "INSERT INTO file_history_boundary_file (scope, entry_id, key_hash, version) VALUES ($1,$2,$3,$4)"), [scope, b.entryId, sha256hex(f.relOrAbsPath), f.version]);
|
|
853
|
+
}
|
|
817
854
|
}
|
|
855
|
+
return { ok: true };
|
|
818
856
|
}
|
|
819
857
|
}
|
|
820
858
|
export class TiDBFileHistoryStore extends SqlFileHistoryStore {
|