@sema-agent/server 7.150.0 → 7.152.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 +451 -0
- package/README.md +15 -3
- package/README.zh-CN.md +10 -2
- package/USAGE.md +144 -87
- package/deploy/sema-up/README.md +11 -4
- 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 +7 -6
- package/deploy/sema-up/kube-up.sh +6 -6
- package/deploy/sema-up/lib/flagshape.sh +36 -8
- package/deploy/sema-up/lib/helm-values-cluster.py +2 -0
- package/deploy/sema-up/lib/helm-values-kube.py +2 -0
- package/deploy/sema-up/machines.example.yaml +3 -1
- package/deploy/sema-up/sema-up.sh +22 -16
- package/dist/adoption/import.js +3 -3
- package/dist/approval-ask-audit-store.d.ts +1 -1
- package/dist/approval-ask-machine.d.ts +4 -10
- package/dist/approval-ask-retention.d.ts +1 -1
- package/dist/approval-card.d.ts +12 -14
- package/dist/approval-gate-currency.d.ts +9 -9
- package/dist/approval-gate-currency.js +3 -4
- package/dist/approval-hmac.d.ts +17 -1
- package/dist/approval-hmac.js +7 -1
- package/dist/approval-queue-kind.d.ts +1 -1
- package/dist/approval.d.ts +2 -2
- package/dist/approval.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 +1 -1
- package/dist/bake-runner/main.d.ts +4 -2
- package/dist/bind-host.d.ts +3 -6
- package/dist/bind-host.js +4 -7
- package/dist/boot/budget-tracing.js +1 -1
- package/dist/boot/coordinators.d.ts +1 -1
- package/dist/boot/coordinators.js +9 -10
- package/dist/boot/device-lane.js +2 -0
- package/dist/boot/engine-lease.d.ts +5 -0
- package/dist/boot/engine-lease.js +1 -0
- package/dist/boot/execution-env.d.ts +9 -14
- package/dist/boot/execution-env.js +85 -22
- 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/leader.d.ts +31 -30
- package/dist/boot/leader.js +38 -28
- package/dist/boot/leg-path-adjudication.d.ts +40 -0
- package/dist/boot/leg-path-adjudication.js +18 -0
- package/dist/boot/memory-consolidation.d.ts +3 -2
- package/dist/boot/memory-consolidation.js +6 -6
- package/dist/boot/parked-revive-gate.js +3 -15
- package/dist/boot/reapers.d.ts +6 -1
- package/dist/boot/reapers.js +13 -2
- package/dist/boot/resolve-spec.d.ts +3 -3
- package/dist/boot/resolve-spec.js +43 -48
- package/dist/boot/runner-deps.d.ts +46 -35
- package/dist/boot/runner-deps.js +94 -91
- 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 +9 -5
- package/dist/boot/shutdown.d.ts +6 -0
- package/dist/boot/shutdown.js +10 -4
- package/dist/boot/stage-01-config.js +2 -0
- package/dist/boot/stage-02-web-admin-pull.js +4 -5
- package/dist/boot/stage-05-execution-env.d.ts +5 -3
- package/dist/boot/stage-05-execution-env.js +3 -2
- package/dist/boot/stage-06-runners.d.ts +9 -4
- package/dist/boot/stage-06-runners.js +24 -9
- package/dist/boot/stage-07-capability-layer.d.ts +8 -4
- package/dist/boot/stage-07-capability-layer.js +3 -2
- package/dist/boot/stage-08-reapers.d.ts +8 -4
- package/dist/boot/stage-08-reapers.js +4 -2
- package/dist/boot/stage-09-leader.d.ts +12 -5
- package/dist/boot/stage-09-leader.js +14 -5
- package/dist/boot/stage-10-http-server.d.ts +19 -5
- package/dist/boot/stage-10-http-server.js +14 -21
- package/dist/boot/stores.js +7 -4
- package/dist/boot/web-admin.js +5 -8
- 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/capabilities/approvals-push.d.ts +3 -3
- package/dist/capabilities/scenarios.d.ts +28 -13
- package/dist/capabilities/scenarios.js +2 -2
- package/dist/capability-facts.d.ts +43 -32
- package/dist/capability-facts.js +45 -51
- package/dist/card-rule-offers.d.ts +32 -0
- package/dist/card-rule-offers.js +130 -0
- 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 +26 -22
- package/dist/config-dir-trust.js +4 -2
- package/dist/config-invariants.d.ts +20 -8
- package/dist/config-invariants.js +55 -12
- package/dist/config-lkg.d.ts +5 -19
- package/dist/config-lkg.js +8 -62
- package/dist/config-provider.d.ts +1 -1
- package/dist/config-root-migration.d.ts +2 -2
- package/dist/config-types.d.ts +38 -42
- package/dist/config.d.ts +58 -7
- package/dist/config.js +90 -40
- package/dist/control-socket.d.ts +51 -0
- package/dist/control-socket.js +115 -0
- package/dist/deployment-governance.d.ts +2 -1
- 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 +7 -0
- package/dist/device-ws-hub.js +10 -3
- package/dist/device-ws-protocol.d.ts +63 -10
- package/dist/device-ws-protocol.js +56 -25
- package/dist/elicitation.js +2 -2
- package/dist/engine-identity.d.ts +19 -7
- package/dist/engine-identity.js +10 -2
- package/dist/entry-mode-clamp-notice.d.ts +2 -1
- package/dist/entry-problems.d.ts +40 -3
- package/dist/entry-problems.js +3 -1
- package/dist/execution-env-refusal.d.ts +29 -0
- package/dist/execution-env-refusal.js +8 -0
- package/dist/execution-lane-caps.d.ts +3 -1
- package/dist/field-conflict.d.ts +1 -1
- 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 +12 -29
- package/dist/fleet/engine-signal.js +1 -7
- package/dist/fleet-client.js +2 -5
- package/dist/gate-settlement.d.ts +1 -8
- package/dist/gate-settlement.js +0 -4
- package/dist/history-anchor.js +1 -1
- package/dist/hitl-delivery.d.ts +2 -3
- package/dist/hitl-delivery.js +0 -2
- package/dist/hooks/hook-llm.js +4 -2
- package/dist/hooks/hook-runner.d.ts +3 -2
- package/dist/hooks/hook-runner.js +18 -3
- package/dist/host-decision.d.ts +11 -11
- package/dist/host-lsp-manager.d.ts +2 -2
- package/dist/http/active-run-conflict.d.ts +35 -2
- package/dist/http/active-run-conflict.js +48 -19
- package/dist/http/admission.js +17 -2
- package/dist/http/approval-sweeps.d.ts +1 -1
- package/dist/http/approval-sweeps.js +10 -7
- package/dist/http/checkpoint-ctx.d.ts +4 -0
- package/dist/http/checkpoint-ctx.js +2 -0
- package/dist/http/dispatch.d.ts +8 -3
- package/dist/http/dispatch.js +59 -32
- package/dist/http/durable-run-leg.js +16 -17
- package/dist/http/health-reader.d.ts +16 -9
- package/dist/http/health-reader.js +2 -1
- package/dist/http/health-self-report.d.ts +9 -1
- package/dist/http/health-self-report.js +3 -1
- package/dist/http/owner-access.d.ts +33 -14
- package/dist/http/owner-access.js +40 -10
- package/dist/http/principal-gate.d.ts +49 -21
- package/dist/http/principal-gate.js +61 -11
- package/dist/http/request-channel.d.ts +23 -0
- package/dist/http/request-channel.js +9 -0
- package/dist/http/request-guards.js +17 -2
- package/dist/http/resume-ctx-read.js +42 -3
- package/dist/http/resume-legs.d.ts +4 -3
- package/dist/http/resume-legs.js +367 -117
- package/dist/http/route-ctx.d.ts +91 -15
- package/dist/http/route-ctx.js +17 -0
- package/dist/http/route-table.d.ts +5 -1
- package/dist/http/route-table.js +4 -0
- package/dist/http/routes/admin-config-refresh.js +3 -3
- package/dist/http/routes/admin-drain.d.ts +3 -0
- package/dist/http/routes/admin-drain.js +7 -7
- package/dist/http/routes/admin-memory-optout.d.ts +1 -1
- package/dist/http/routes/admin-memory-optout.js +3 -3
- package/dist/http/routes/adoption.js +3 -5
- package/dist/http/routes/agents-roster.js +2 -3
- package/dist/http/routes/approvals-assistant.d.ts +2 -1
- package/dist/http/routes/approvals-assistant.js +99 -51
- package/dist/http/routes/background.d.ts +3 -3
- package/dist/http/routes/capabilities.d.ts +4 -0
- package/dist/http/routes/capabilities.js +38 -10
- package/dist/http/routes/config-catalog.js +2 -2
- package/dist/http/routes/devices.js +1 -1
- package/dist/http/routes/diagnostics.d.ts +3 -3
- package/dist/http/routes/diagnostics.js +19 -7
- package/dist/http/routes/fleet.js +10 -8
- package/dist/http/routes/images.js +10 -10
- package/dist/http/routes/memory-bundle.js +3 -3
- package/dist/http/routes/memory-compliance.js +3 -3
- package/dist/http/routes/memory-consolidation.js +2 -2
- package/dist/http/routes/memory-origin.js +3 -3
- package/dist/http/routes/memory-policy.js +2 -2
- package/dist/http/routes/observability.js +3 -3
- package/dist/http/routes/projects.d.ts +3 -2
- package/dist/http/routes/projects.js +4 -5
- package/dist/http/routes/retention-ops.js +3 -3
- package/dist/http/routes/rules.js +5 -5
- package/dist/http/routes/run-permission-mode.d.ts +2 -1
- package/dist/http/routes/run-permission-mode.js +2 -2
- package/dist/http/routes/runs.d.ts +4 -9
- package/dist/http/routes/runs.js +171 -65
- package/dist/http/routes/session-sync.d.ts +22 -9
- package/dist/http/routes/session-sync.js +204 -107
- package/dist/http/routes/sessions-list.js +13 -7
- package/dist/http/routes/sessions.js +30 -15
- package/dist/http/routes/tasks.js +46 -41
- package/dist/http/routes/trace-usage.js +2 -2
- package/dist/http/routes/workflows.js +1 -1
- package/dist/http/server.d.ts +30 -30
- package/dist/http/server.js +16 -4
- package/dist/http/submit-ctx.d.ts +1 -1
- package/dist/http/submit-ctx.js +7 -4
- package/dist/http/wire-types.d.ts +14 -4
- package/dist/human-facing-args.d.ts +33 -0
- package/dist/human-facing-args.js +33 -0
- package/dist/identity-source.d.ts +20 -3
- package/dist/identity-source.js +14 -0
- package/dist/index.d.ts +5 -1
- package/dist/k8s-api-credentials.d.ts +29 -0
- package/dist/k8s-api-credentials.js +25 -0
- package/dist/lane-home-probe.d.ts +3 -7
- package/dist/lane-home-probe.js +2 -2
- package/dist/leader/diffup.d.ts +19 -0
- package/dist/leader/diffup.js +7 -0
- package/dist/leader/leader.d.ts +116 -11
- package/dist/leader/leader.js +69 -5
- package/dist/leader/repair-wire.js +2 -2
- package/dist/leader/run-entry.d.ts +2 -1
- package/dist/leader/run-entry.js +1 -1
- package/dist/leader/wire.d.ts +42 -132
- package/dist/leader/wire.js +81 -67
- package/dist/leg-facts.d.ts +13 -5
- package/dist/leg-facts.js +2 -1
- package/dist/memory-scope.d.ts +23 -11
- package/dist/memory-scope.js +18 -6
- package/dist/memory-sync-client.d.ts +30 -2
- package/dist/memory-sync-client.js +16 -9
- package/dist/model-entry-headers.d.ts +9 -10
- package/dist/model-entry-headers.js +0 -4
- package/dist/model-entry-refusal.d.ts +7 -4
- package/dist/model-entry-refusal.js +11 -5
- package/dist/model-plane-face.d.ts +1 -1
- package/dist/model-plane-face.js +3 -1
- package/dist/model-route-endpoint.d.ts +13 -7
- package/dist/model-route-endpoint.js +14 -11
- package/dist/model-select.d.ts +22 -0
- package/dist/model-select.js +9 -0
- package/dist/model-unreachable-notice.d.ts +11 -0
- package/dist/model-unreachable-notice.js +4 -4
- package/dist/object-store-buckets.d.ts +640 -6
- package/dist/object-store-buckets.js +166 -0
- package/dist/observability/err-text.d.ts +7 -0
- package/dist/observability/err-text.js +5 -0
- package/dist/observability/fail-open.d.ts +25 -17
- package/dist/observability/fail-open.js +25 -17
- package/dist/observability/logger.js +3 -1
- package/dist/observability/metrics.js +1 -0
- package/dist/operator-roster.d.ts +68 -0
- package/dist/operator-roster.js +32 -0
- 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/outbound-dispatcher.d.ts +71 -8
- package/dist/outbound-dispatcher.js +48 -7
- package/dist/owner-faces.d.ts +2 -2
- package/dist/parked-decide.d.ts +106 -55
- 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/permission-mode-admission.d.ts +74 -5
- package/dist/permission-mode-admission.js +56 -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 +14 -2
- package/dist/plugins/approval-ask-store-file.js +11 -0
- package/dist/plugins/approval-ask-store-memory.d.ts +10 -1
- package/dist/plugins/approval-ask-store-memory.js +35 -10
- package/dist/plugins/approval-ask-store-sql.d.ts +69 -4
- package/dist/plugins/approval-ask-store-sql.js +45 -5
- package/dist/plugins/approval-exemption-store.js +3 -0
- package/dist/plugins/approval-nonce-store.js +2 -0
- package/dist/plugins/blob-backend.d.ts +83 -7
- package/dist/plugins/blob-backend.js +57 -6
- 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/checkpoint-index-columns.d.ts +20 -16
- package/dist/plugins/checkpoint-index-columns.js +3 -6
- package/dist/plugins/checkpoint-store-face.d.ts +119 -61
- package/dist/plugins/checkpoint-store-face.js +134 -55
- 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/execution-placement-sql.js +6 -6
- 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 +13 -3
- package/dist/plugins/local-checkpoint-store.js +38 -49
- 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-sync-store-pg.js +5 -0
- package/dist/plugins/memory-sync-store-tidb.js +5 -0
- package/dist/plugins/one-time-migrations.d.ts +104 -80
- package/dist/plugins/one-time-migrations.js +226 -132
- 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 +35 -10
- 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 +4 -1
- package/dist/plugins/remote-env-adb.d.ts +19 -15
- package/dist/plugins/remote-env-adb.js +41 -15
- 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 +106 -8
- package/dist/plugins/remote-env-e2b.d.ts +15 -6
- package/dist/plugins/remote-env-e2b.js +42 -32
- package/dist/plugins/remote-env-file-error.js +6 -2
- package/dist/plugins/remote-env-host.d.ts +46 -10
- package/dist/plugins/remote-env-host.js +124 -52
- package/dist/plugins/remote-env-k8s.d.ts +109 -41
- package/dist/plugins/remote-env-k8s.js +160 -54
- package/dist/plugins/remote-env-local-docker.d.ts +13 -8
- package/dist/plugins/remote-env-local-docker.js +34 -41
- package/dist/plugins/remote-env-ssh.d.ts +18 -12
- package/dist/plugins/remote-env-ssh.js +60 -59
- package/dist/plugins/remote-shell.d.ts +98 -1
- package/dist/plugins/remote-shell.js +218 -0
- package/dist/plugins/replica-registration.d.ts +1 -1
- package/dist/plugins/replica-registration.js +1 -1
- 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.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 +21 -2
- package/dist/plugins/run-store-sql.js +31 -7
- package/dist/plugins/s3-presign.d.ts +23 -0
- package/dist/plugins/s3-presign.js +37 -3
- package/dist/plugins/scenario-prefix-switch-m42.d.ts +1 -1
- package/dist/plugins/scenario-prefix-switch-m42.js +2 -2
- package/dist/plugins/scheduler-support.d.ts +2 -2
- package/dist/plugins/scheduler-support.js +0 -3
- package/dist/plugins/schema-consistency.d.ts +4 -2
- package/dist/plugins/schema-consistency.js +19 -2
- package/dist/plugins/schema-indexes.js +2 -0
- package/dist/plugins/send-file-ledger.d.ts +2 -2
- package/dist/plugins/send-file-ledger.js +5 -2
- package/dist/plugins/send-user-file.d.ts +3 -4
- package/dist/plugins/send-user-file.js +56 -52
- package/dist/plugins/serving-requirement.d.ts +26 -7
- package/dist/plugins/serving-requirement.js +11 -4
- package/dist/plugins/session-birth.d.ts +6 -2
- package/dist/plugins/session-blob-claim-token.d.ts +15 -0
- package/dist/plugins/session-blob-claim-token.js +20 -0
- package/dist/plugins/session-blob-namespace.d.ts +46 -0
- package/dist/plugins/session-blob-namespace.js +116 -0
- 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/snapshot-bucket-presence.d.ts +25 -2
- package/dist/plugins/snapshot-bucket-presence.js +8 -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/sql-json-column.d.ts +1 -1
- package/dist/plugins/stop-then-start-sentence.d.ts +10 -4
- package/dist/plugins/stop-then-start-sentence.js +14 -10
- 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 +84 -15
- package/dist/plugins/store-backend.js +236 -72
- package/dist/plugins/store-boundary.d.ts +57 -0
- package/dist/plugins/store-boundary.js +139 -0
- package/dist/plugins/store-contracts.d.ts +17 -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.d.ts +2 -1
- package/dist/plugins/task-attachment-store.js +14 -1
- 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 +21 -25
- 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-custody.d.ts +4 -3
- 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/project-context.d.ts +0 -2
- package/dist/project-context.js +4 -8
- package/dist/project-registry.d.ts +46 -0
- package/dist/project-registry.js +44 -0
- package/dist/question.d.ts +2 -1
- package/dist/question.js +5 -13
- package/dist/request-form.d.ts +6 -1
- package/dist/request-form.js +4 -1
- 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/rule-issue-reading.d.ts +1 -1
- package/dist/rules-consent.d.ts +1 -1
- package/dist/run-acceptance.d.ts +3 -2
- package/dist/run-acceptance.js +4 -6
- package/dist/run-local.js +9 -9
- package/dist/runs.d.ts +54 -16
- package/dist/runs.js +67 -13
- package/dist/runtime-governance.d.ts +5 -4
- package/dist/security.d.ts +30 -32
- package/dist/security.js +41 -7
- package/dist/sema-config-dir.d.ts +2 -1
- package/dist/server-secret-env.d.ts +12 -10
- package/dist/server-secret-env.js +24 -15
- 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 +10 -0
- package/dist/session-incarnation.js +10 -1
- package/dist/session-kind.d.ts +57 -0
- package/dist/session-kind.js +22 -0
- package/dist/session-row.d.ts +21 -1
- package/dist/session-row.js +7 -1
- package/dist/session-sync-kernel.d.ts +24 -14
- package/dist/session-sync-kernel.js +3 -6
- package/dist/session-sync.d.ts +25 -103
- package/dist/session-sync.js +14 -76
- 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/blob-index-sql.d.ts +30 -9
- package/dist/stores-sql/blob-index-sql.js +40 -21
- package/dist/stores-sql/checkpoint-store-sql.d.ts +54 -27
- package/dist/stores-sql/checkpoint-store-sql.js +70 -146
- package/dist/stores-sql/file-history-store-sql.d.ts +113 -15
- package/dist/stores-sql/file-history-store-sql.js +205 -95
- 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 +1 -9
- package/dist/stores-sql/retention-store-sql.js +5 -19
- 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 +2 -2
- package/dist/task-settings.d.ts +7 -5
- package/dist/task-settings.js +3 -3
- package/dist/terminal.d.ts +33 -2
- package/dist/terminal.js +139 -9
- package/dist/tool-approval.d.ts +465 -507
- package/dist/tool-approval.js +606 -1222
- package/dist/trace/core-keyset-guard.d.ts +1 -1
- package/dist/trace/ledger-events.d.ts +3 -3
- package/dist/trace/project.d.ts +2 -3
- package/dist/trace/redact.js +3 -2
- package/dist/trace/self-minted-wire.d.ts +571 -178
- package/dist/trace/self-minted-wire.js +189 -56
- package/dist/web-admin-client/apply-effective.js +2 -2
- package/dist/web-admin-client/read-warnings.d.ts +2 -2
- package/dist/web-admin-client/read-warnings.js +1 -1
- package/dist/web-admin-client/types.d.ts +1 -1
- package/dist/workspace-space.d.ts +10 -4
- package/dist/workspace-space.js +4 -1
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +79 -28
- package/package.json +7 -3
- package/scripts/embed-minimal.mjs +3 -3
- package/wire-changes/server@7.151.0.json +762 -0
- package/wire-changes/server@7.152.0.json +900 -0
- package/dist/config-d-migration.d.ts +0 -37
- package/dist/config-d-migration.js +0 -60
- package/dist/http/session-inherit-env.d.ts +0 -36
- package/dist/http/session-inherit-env.js +0 -6
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { once } from "node:events";
|
|
2
2
|
import { sessionLogDigest, sessionLogDigestsComparable, validateFileHistoryExport } from "@sema-agent/core";
|
|
3
3
|
import { StringDecoder } from "node:string_decoder";
|
|
4
|
-
import { FileHistoryUnreadableError,
|
|
4
|
+
import { FileHistoryUnreadableError, NO_VISIBLE_BLOBS, exportSessionManifest } from "../../session-sync.js";
|
|
5
|
+
import { fileHistoryImportFace } from "../../plugins/landed-attendants.js";
|
|
5
6
|
import * as facts from "../../capability-facts.js";
|
|
6
7
|
import { classifySyncRelationshipByIds, createStagedLogFeed, MalformedImportEntryError, newStagingToken, readImportAnchors, SyncConflictError } from "../../session-sync-kernel.js";
|
|
7
8
|
import { isUuidShape } from "../../security.js";
|
|
@@ -9,7 +10,7 @@ import { sendJson, sendError, sendSessionError, sendSessionPolicyError, logReque
|
|
|
9
10
|
import { StampedSessionArchiveError } from "../../session-error-wire.js";
|
|
10
11
|
import { buildDisplaySafeUrl } from "../../trace/redact.js";
|
|
11
12
|
import { storeFaultClassOf } from "../../plugins/store-fault.js";
|
|
12
|
-
import { enter, readSessionAgent, readSyncGate, refuse } from "../owner-access.js";
|
|
13
|
+
import { enter, readSessionAgent, readSyncGate, recheckSyncGate, refuse } from "../owner-access.js";
|
|
13
14
|
import { listFilterOf, ownerKeyOf, sameKey } from "../../owner-faces.js";
|
|
14
15
|
const SYNC_BLOB_MAX_BYTES = 64 * 1024 * 1024;
|
|
15
16
|
const SYNC_NDJSON_LINE_MAX_BYTES = 8 * 1024 * 1024;
|
|
@@ -17,12 +18,18 @@ import { SHA256_HEX_RE } from "../../digest-form.js";
|
|
|
17
18
|
import { assertNever } from "../route-ctx.js";
|
|
18
19
|
import { readSessionRulesRecords, describeSessionRulesRecordProblem } from "../../session-policy-wire.js";
|
|
19
20
|
import { readSessionPinned, readSessionTitle } from "../../session-row.js";
|
|
21
|
+
import { incarnationLogPrefix, SessionOccupantChangedError } from "../../session-incarnation.js";
|
|
20
22
|
import { defaultTaskRegistry } from "@sema-agent/core";
|
|
21
23
|
import { SessionActiveRunError, SessionForkInProgressError, SessionWriterPresentError } from "../../execution-placement.js";
|
|
22
24
|
import { probeSessionChildren, sessionTenantScope } from "../../session-children-retire.js";
|
|
23
25
|
import { liveChildrenRefusal } from "./background.js";
|
|
24
|
-
export function
|
|
25
|
-
return
|
|
26
|
+
export function landingOf(gate, scope, sessionId) {
|
|
27
|
+
return gate.occupant !== undefined
|
|
28
|
+
? { sessionId, owner: gate.occupant.owner, occupied: true }
|
|
29
|
+
: { sessionId, owner: scope.fleetWide ? null : scope.gateOwner, occupied: false };
|
|
30
|
+
}
|
|
31
|
+
export function importLeaseKey(landing) {
|
|
32
|
+
return JSON.stringify([landing.owner, landing.sessionId]);
|
|
26
33
|
}
|
|
27
34
|
export function createSessionSyncLocal() {
|
|
28
35
|
const pendingStagings = new Map();
|
|
@@ -106,6 +113,7 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
106
113
|
}
|
|
107
114
|
const gateFace = gate.face;
|
|
108
115
|
const owner = gateFace === undefined ? undefined : gateFace.owner;
|
|
116
|
+
const landing = landingOf(gate, scope, sessionId);
|
|
109
117
|
const sendSessionAbsent = (reason) => refuse(syncEntry, res, reason);
|
|
110
118
|
const pullGateOk = () => {
|
|
111
119
|
if (!gate.mayPull) {
|
|
@@ -121,6 +129,33 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
121
129
|
}
|
|
122
130
|
return true;
|
|
123
131
|
};
|
|
132
|
+
const stillAdmitted = async (port) => {
|
|
133
|
+
const admitted = port === "pull" ? gate.face : gate.occupant;
|
|
134
|
+
return admitted === undefined || (await recheckSyncGate(sessionStorage, sessionId, admitted, port));
|
|
135
|
+
};
|
|
136
|
+
const pullAnswer = async (send) => {
|
|
137
|
+
let same;
|
|
138
|
+
try {
|
|
139
|
+
same = await stillAdmitted("pull");
|
|
140
|
+
}
|
|
141
|
+
catch (e) {
|
|
142
|
+
if (sendPullUnreadable(req, res, deps.logger, e))
|
|
143
|
+
return;
|
|
144
|
+
throw e;
|
|
145
|
+
}
|
|
146
|
+
if (!same) {
|
|
147
|
+
sendSessionAbsent("missing");
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
send();
|
|
151
|
+
};
|
|
152
|
+
const pushAnswer = async (send) => {
|
|
153
|
+
if (!(await stillAdmitted("push"))) {
|
|
154
|
+
sendError(res, 409, "conflict", "sync conflict");
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
send();
|
|
158
|
+
};
|
|
124
159
|
const replaySessionRow = async (row, face) => {
|
|
125
160
|
if (Object.keys(row).length === 0)
|
|
126
161
|
return true;
|
|
@@ -166,27 +201,46 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
166
201
|
}
|
|
167
202
|
};
|
|
168
203
|
const canReplayHistory = fileHistoryImportFace(historyStore) !== undefined;
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
204
|
+
const landAttendants = async (attendants, landed, plan) => {
|
|
205
|
+
if (plan.wipe && !(await attendants.wipe(sessionId, landed)).landed)
|
|
206
|
+
return { kind: "gone", leg: "wipe" };
|
|
207
|
+
if (plan.fileHistory !== null && canReplayHistory) {
|
|
208
|
+
const h = await attendants.replayHistory(sessionId, landed, plan.fileHistory, plan.getBlob, { allowReplace: plan.allowReplace });
|
|
209
|
+
if (!h.landed)
|
|
210
|
+
return { kind: "gone", leg: "file_history" };
|
|
211
|
+
if (!h.value.ok)
|
|
212
|
+
return { kind: "history_failed", failure: h.value };
|
|
213
|
+
}
|
|
214
|
+
if (plan.policy.length > 0 && !(await attendants.replayPolicy(sessionId, landed, plan.policy, plan.operator)).landed)
|
|
215
|
+
return { kind: "gone", leg: "policy" };
|
|
216
|
+
if (plan.anchors.length > 0 && !(await attendants.putAnchors(sessionId, landed, plan.anchors, plan.owner)).landed)
|
|
217
|
+
return { kind: "gone", leg: "anchors" };
|
|
218
|
+
if (!(await replaySessionRow(plan.row, landed)))
|
|
219
|
+
return { kind: "gone", leg: "row" };
|
|
220
|
+
return { kind: "landed" };
|
|
221
|
+
};
|
|
222
|
+
const sendLandedOccupantGone = (arm, leg, landed) => {
|
|
223
|
+
deps.logger?.warn?.("session_sync_landed_occupant_gone", { sessionId, arm, leg, landed: incarnationLogPrefix(landed.incarnation) });
|
|
224
|
+
deps.metrics?.inc("session_sync_landed_occupant_gone_total", { arm, leg }, 1);
|
|
225
|
+
sendError(res, 409, "conflict", "sync conflict");
|
|
172
226
|
};
|
|
173
227
|
if (match.decl.id === "session-sync-manifest") {
|
|
174
228
|
if (!pullGateOk())
|
|
175
229
|
return;
|
|
176
|
-
let
|
|
230
|
+
let manifest;
|
|
177
231
|
try {
|
|
178
|
-
|
|
232
|
+
manifest = await exportSessionManifest(sessionId, backend, historyStore);
|
|
179
233
|
}
|
|
180
234
|
catch (e) {
|
|
181
235
|
if (sendSessionPolicyError(res, e) || sendPullUnreadable(req, res, deps.logger, e))
|
|
182
236
|
return;
|
|
183
237
|
throw e;
|
|
184
238
|
}
|
|
185
|
-
if (
|
|
239
|
+
if (manifest === null) {
|
|
186
240
|
sendSessionAbsent("missing");
|
|
187
241
|
return;
|
|
188
242
|
}
|
|
189
|
-
sendJson(res, 200, { manifest
|
|
243
|
+
await pullAnswer(() => sendJson(res, 200, { manifest }));
|
|
190
244
|
return;
|
|
191
245
|
}
|
|
192
246
|
if (match.decl.id === "session-sync-entries") {
|
|
@@ -202,9 +256,11 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
202
256
|
}
|
|
203
257
|
afterSeq = n;
|
|
204
258
|
}
|
|
259
|
+
const recheck = async () => { if (!(await stillAdmitted("pull")))
|
|
260
|
+
throw new SessionOccupantChangedError(sessionId); };
|
|
205
261
|
let it;
|
|
206
262
|
try {
|
|
207
|
-
it = await sessionStorage.exportEntriesStream(sessionId, afterSeq === undefined ?
|
|
263
|
+
it = await sessionStorage.exportEntriesStream(sessionId, { ...(afterSeq === undefined ? {} : { afterSeq }), recheck });
|
|
208
264
|
}
|
|
209
265
|
catch (e) {
|
|
210
266
|
if (sendPullUnreadable(req, res, deps.logger, e))
|
|
@@ -215,6 +271,19 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
215
271
|
sendSessionAbsent("missing");
|
|
216
272
|
return;
|
|
217
273
|
}
|
|
274
|
+
let openSame;
|
|
275
|
+
try {
|
|
276
|
+
openSame = await stillAdmitted("pull");
|
|
277
|
+
}
|
|
278
|
+
catch (e) {
|
|
279
|
+
if (sendPullUnreadable(req, res, deps.logger, e))
|
|
280
|
+
return;
|
|
281
|
+
throw e;
|
|
282
|
+
}
|
|
283
|
+
if (!openSame) {
|
|
284
|
+
sendSessionAbsent("missing");
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
218
287
|
res.writeHead(200, { "content-type": "application/x-ndjson", "transfer-encoding": "chunked" });
|
|
219
288
|
const write = async (line) => { if (!res.write(line))
|
|
220
289
|
await once(res, "drain"); };
|
|
@@ -229,15 +298,18 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
229
298
|
res.end();
|
|
230
299
|
}
|
|
231
300
|
catch (e) {
|
|
232
|
-
|
|
301
|
+
if (e instanceof SessionOccupantChangedError)
|
|
302
|
+
deps.logger?.info?.("session_sync_pull_occupant_changed", { sessionId, count });
|
|
303
|
+
else
|
|
304
|
+
deps.logger?.error?.("sync entries stream failed", { sessionId, count, error: msg(e) });
|
|
233
305
|
res.end();
|
|
234
306
|
}
|
|
235
307
|
return;
|
|
236
308
|
}
|
|
237
309
|
if (match.decl.id === "session-sync-history-blob") {
|
|
238
|
-
const
|
|
239
|
-
if (!
|
|
240
|
-
sendSessionSyncUnavailable(res,
|
|
310
|
+
const faces = facts.sessionSyncFileHistory(deps);
|
|
311
|
+
if (!faces.available) {
|
|
312
|
+
sendSessionSyncUnavailable(res, faces.why);
|
|
241
313
|
return;
|
|
242
314
|
}
|
|
243
315
|
if (!pullGateOk())
|
|
@@ -251,45 +323,45 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
251
323
|
sendError(res, 400, "request.id_invalid", "blob hash must be 64 lowercase-hex chars");
|
|
252
324
|
return;
|
|
253
325
|
}
|
|
254
|
-
|
|
255
|
-
const exchange = blobs.blobExchange;
|
|
256
|
-
let envelope;
|
|
326
|
+
let visible;
|
|
257
327
|
try {
|
|
258
|
-
|
|
328
|
+
visible = await faces.blobs.visible(landing, [hash]);
|
|
259
329
|
}
|
|
260
330
|
catch (e) {
|
|
261
331
|
if (sendPullUnreadable(req, res, deps.logger, e))
|
|
262
332
|
return;
|
|
263
333
|
throw e;
|
|
264
334
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
sendError(res, 404, "not_found.blob", "blob not found");
|
|
335
|
+
if (!visible.hashes.has(hash)) {
|
|
336
|
+
await pullAnswer(() => sendError(res, 404, "not_found.blob", "blob not found"));
|
|
268
337
|
return;
|
|
269
338
|
}
|
|
270
339
|
let bytes;
|
|
271
340
|
try {
|
|
272
|
-
bytes = await
|
|
341
|
+
bytes = await visible.get(hash);
|
|
273
342
|
}
|
|
274
343
|
catch (e) {
|
|
275
344
|
if (sendPullUnreadable(req, res, deps.logger, e))
|
|
276
345
|
return;
|
|
277
346
|
throw e;
|
|
278
347
|
}
|
|
279
|
-
|
|
280
|
-
|
|
348
|
+
const found = bytes;
|
|
349
|
+
if (!found) {
|
|
350
|
+
await pullAnswer(() => sendError(res, 404, "not_found.blob", "blob not found"));
|
|
281
351
|
return;
|
|
282
352
|
}
|
|
283
|
-
|
|
284
|
-
|
|
353
|
+
await pullAnswer(() => {
|
|
354
|
+
res.writeHead(200, { "content-type": "application/octet-stream" });
|
|
355
|
+
res.end(Buffer.from(found));
|
|
356
|
+
});
|
|
285
357
|
return;
|
|
286
358
|
}
|
|
287
359
|
if (match.decl.id === "session-sync-blob-put") {
|
|
288
360
|
if (rateLimited(req, res))
|
|
289
361
|
return;
|
|
290
|
-
const
|
|
291
|
-
if (!
|
|
292
|
-
sendSessionSyncUnavailable(res,
|
|
362
|
+
const faces = facts.sessionSyncFileHistory(deps);
|
|
363
|
+
if (!faces.available) {
|
|
364
|
+
sendSessionSyncUnavailable(res, faces.why);
|
|
293
365
|
return;
|
|
294
366
|
}
|
|
295
367
|
if (!pushGateOk())
|
|
@@ -300,7 +372,7 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
300
372
|
return;
|
|
301
373
|
}
|
|
302
374
|
const body = await readRawBody(req, SYNC_BLOB_MAX_BYTES);
|
|
303
|
-
const put = await blobs.
|
|
375
|
+
const put = await faces.blobs.put(landing, hash, new Uint8Array(body));
|
|
304
376
|
if (!put.ok) {
|
|
305
377
|
if (put.error.blobCap !== undefined) {
|
|
306
378
|
sendError(res, 413, "blob_too_large_for_sql", put.error.message, { wallSource: put.error.blobCap.wallSource });
|
|
@@ -386,10 +458,14 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
386
458
|
const entryIds = body.entryIds;
|
|
387
459
|
const policy = policyRead.records;
|
|
388
460
|
const anchors = anchorsRead.anchors;
|
|
389
|
-
const dstIds = (await sessionStorage.listEntryIds?.(sessionId)) ?? null;
|
|
461
|
+
const dstIds = gate.occupant === undefined ? null : ((await sessionStorage.listEntryIds?.(sessionId)) ?? null);
|
|
390
462
|
const relA = classifySyncRelationshipByIds(entryIds, dstIds);
|
|
391
463
|
const refusalA = await firstPhaseRefusal(sessionStorage, sessionId, relA.relation, resolution === "overwrite-dst");
|
|
392
464
|
if (refusalA !== undefined) {
|
|
465
|
+
if (!(await stillAdmitted("push"))) {
|
|
466
|
+
sendError(res, 409, "conflict", "sync conflict");
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
393
469
|
if (refusalA.kind === "stamped") {
|
|
394
470
|
if (sendSessionError(res, refusalA.error, "import"))
|
|
395
471
|
return;
|
|
@@ -398,15 +474,21 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
398
474
|
sendError(res, 409, "conflict", "sync conflict", { relation: relA });
|
|
399
475
|
return;
|
|
400
476
|
}
|
|
477
|
+
let visible = NO_VISIBLE_BLOBS;
|
|
401
478
|
if (fileHistory !== null) {
|
|
402
479
|
const distinct = new Set();
|
|
403
480
|
for (const t of fileHistory.tracked)
|
|
404
481
|
for (const v of t.versions)
|
|
405
482
|
if (v.blobHash !== null)
|
|
406
483
|
distinct.add(v.blobHash);
|
|
407
|
-
if (
|
|
408
|
-
|
|
409
|
-
|
|
484
|
+
if (distinct.size > 0) {
|
|
485
|
+
const faces = facts.sessionSyncFileHistory(deps);
|
|
486
|
+
if (faces.available)
|
|
487
|
+
visible = await faces.blobs.visible(landing, [...distinct]);
|
|
488
|
+
if ([...distinct].some((h) => !visible.hashes.has(h))) {
|
|
489
|
+
await pushAnswer(() => sendMissingBlob(res));
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
410
492
|
}
|
|
411
493
|
}
|
|
412
494
|
let payloadVerified = false;
|
|
@@ -432,10 +514,10 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
432
514
|
case "mirror":
|
|
433
515
|
break;
|
|
434
516
|
case "writer":
|
|
435
|
-
sendSessionWriter(res);
|
|
517
|
+
await pushAnswer(() => sendSessionWriter(res));
|
|
436
518
|
return;
|
|
437
519
|
case "forking":
|
|
438
|
-
sendError(res, 409, "conflict.fork_in_progress", new SessionForkInProgressError(sessionId).message);
|
|
520
|
+
await pushAnswer(() => sendError(res, 409, "conflict.fork_in_progress", new SessionForkInProgressError(sessionId).message));
|
|
439
521
|
return;
|
|
440
522
|
default: {
|
|
441
523
|
const unknownRole = placed.cell;
|
|
@@ -443,12 +525,13 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
443
525
|
}
|
|
444
526
|
}
|
|
445
527
|
}
|
|
446
|
-
const activeNow = await activityOf(gateFace);
|
|
528
|
+
const activeNow = gate.occupant === undefined ? undefined : await activityOf(gateFace);
|
|
447
529
|
if (activeNow !== undefined) {
|
|
448
|
-
sendActivity(activeNow, "open");
|
|
530
|
+
await pushAnswer(() => sendActivity(activeNow, "open"));
|
|
449
531
|
return;
|
|
450
532
|
}
|
|
451
533
|
if (relA.relation === "identical" && !digestSaysDiffer) {
|
|
534
|
+
const attendantsA = backend.landedAttendants();
|
|
452
535
|
const seat = sessionStorage.executionPlacement;
|
|
453
536
|
if (seat !== undefined) {
|
|
454
537
|
if (gateFace === undefined) {
|
|
@@ -474,38 +557,41 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
474
557
|
}
|
|
475
558
|
}
|
|
476
559
|
}
|
|
477
|
-
if (
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
560
|
+
if (gateFace === undefined) {
|
|
561
|
+
sendError(res, 409, "conflict", "sync conflict");
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
let landedA;
|
|
565
|
+
try {
|
|
566
|
+
landedA = await landAttendants(attendantsA, gateFace, { wipe: false, fileHistory, getBlob: visible.get, allowReplace: resolution === "overwrite-dst", policy, operator: operatorOk, anchors, owner: importingPrincipal, row });
|
|
567
|
+
}
|
|
568
|
+
catch (e) {
|
|
569
|
+
if (sendSessionPolicyError(res, e))
|
|
481
570
|
return;
|
|
482
|
-
|
|
571
|
+
throw e;
|
|
483
572
|
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
573
|
+
switch (landedA.kind) {
|
|
574
|
+
case "landed":
|
|
575
|
+
break;
|
|
576
|
+
case "gone":
|
|
577
|
+
sendLandedOccupantGone("identical", landedA.leg, gateFace);
|
|
578
|
+
return;
|
|
579
|
+
case "history_failed":
|
|
580
|
+
sendFileHistoryReplayFailure(res, landedA.failure);
|
|
581
|
+
return;
|
|
582
|
+
default: {
|
|
583
|
+
const unhandled = landedA;
|
|
584
|
+
throw new Error(`session sync: unknown landing answer ${JSON.stringify(unhandled)}`);
|
|
492
585
|
}
|
|
493
586
|
}
|
|
494
|
-
|
|
495
|
-
for (const a of anchors)
|
|
496
|
-
await deps.resumeAnchorStore.put(sessionId, a.eventId, a.entryId, importingPrincipal);
|
|
497
|
-
if (!(await replaySessionRow(row, gateFace))) {
|
|
498
|
-
sendError(res, 409, "conflict", "sync conflict");
|
|
499
|
-
return;
|
|
500
|
-
}
|
|
501
|
-
sendJson(res, 200, {
|
|
587
|
+
await pushAnswer(() => sendJson(res, 200, {
|
|
502
588
|
relation: "identical",
|
|
503
589
|
basis: payloadVerified ? "entry-ids+digest" : "entry-ids",
|
|
504
590
|
payloadVerified,
|
|
505
|
-
});
|
|
591
|
+
}));
|
|
506
592
|
return;
|
|
507
593
|
}
|
|
508
|
-
const leaseKey = importLeaseKey(
|
|
594
|
+
const leaseKey = importLeaseKey(landing);
|
|
509
595
|
const staleSecRaw = deps.config.syncImportLeaseStaleSec;
|
|
510
596
|
const staleSec = typeof staleSecRaw === "number" && Number.isFinite(staleSecRaw) ? staleSecRaw : 600;
|
|
511
597
|
const staleMs = staleSec * 1000;
|
|
@@ -514,8 +600,12 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
514
600
|
const heldLease = importLeases.get(leaseKey);
|
|
515
601
|
if (heldLease) {
|
|
516
602
|
const retryAfterSec = staleMs > 0 ? Math.max(1, Math.ceil((staleMs - (Date.now() - heldLease.at)) / 1000)) : undefined;
|
|
517
|
-
sendError(res, 409, "import_in_flight", "an import is already in flight for this session"
|
|
518
|
-
+ (retryAfterSec !== undefined ? ` (a stalled holder is taken over after ${staleSec}s of inactivity; retry in ~${retryAfterSec}s)` : ""), retryAfterSec !== undefined ? { retryAfterSec } : undefined);
|
|
603
|
+
await pushAnswer(() => sendError(res, 409, "import_in_flight", "an import is already in flight for this session"
|
|
604
|
+
+ (retryAfterSec !== undefined ? ` (a stalled holder is taken over after ${staleSec}s of inactivity; retry in ~${retryAfterSec}s)` : ""), retryAfterSec !== undefined ? { retryAfterSec } : undefined));
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
if (!(await stillAdmitted("push"))) {
|
|
608
|
+
sendError(res, 409, "conflict", "sync conflict");
|
|
519
609
|
return;
|
|
520
610
|
}
|
|
521
611
|
const token = newStagingToken();
|
|
@@ -524,7 +614,7 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
524
614
|
importLeases.set(leaseKey, { at: Date.now(), stagingId, sessionId });
|
|
525
615
|
pendingStagings.set(stagingId, {
|
|
526
616
|
handle, realSessionId: sessionId, owner: importingPrincipal, ...(resolution ? { resolution } : {}), operatorOk,
|
|
527
|
-
fileHistory, policy, anchors, row, declaredEntryIds: entryIds, openedBy: ownerKeyOf(scope.gateOwner),
|
|
617
|
+
fileHistory, policy, anchors, row, declaredEntryIds: entryIds, openedBy: ownerKeyOf(scope.gateOwner), expectedOccupant: gateFace, leaseKey, visible,
|
|
528
618
|
});
|
|
529
619
|
sendJson(res, 200, { stagingId, relation: relA.relation });
|
|
530
620
|
return;
|
|
@@ -610,19 +700,22 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
610
700
|
return;
|
|
611
701
|
throw e;
|
|
612
702
|
}
|
|
613
|
-
let
|
|
703
|
+
let gateAtCommit;
|
|
614
704
|
let activeAtCommit;
|
|
705
|
+
let activeStillAdmitted = true;
|
|
615
706
|
try {
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
activeAtCommit =
|
|
707
|
+
gateAtCommit = await judgeSessionGate();
|
|
708
|
+
const atCommit = gateAtCommit.occupant;
|
|
709
|
+
activeAtCommit = gateAtCommit.mayPush && atCommit !== undefined ? await activityOf(gateAtCommit.face) : undefined;
|
|
710
|
+
if (activeAtCommit !== undefined && atCommit !== undefined)
|
|
711
|
+
activeStillAdmitted = await recheckSyncGate(sessionStorage, sessionId, atCommit, "push");
|
|
619
712
|
}
|
|
620
713
|
catch (e) {
|
|
621
714
|
await pending.handle.abort().catch(() => undefined);
|
|
622
715
|
cleanupStaging(stagingId);
|
|
623
716
|
throw e;
|
|
624
717
|
}
|
|
625
|
-
if (!
|
|
718
|
+
if (!gateAtCommit.mayPush) {
|
|
626
719
|
await pending.handle.abort().catch(() => undefined);
|
|
627
720
|
cleanupStaging(stagingId);
|
|
628
721
|
sendSessionAbsent("not_visible");
|
|
@@ -631,37 +724,44 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
631
724
|
if (activeAtCommit !== undefined) {
|
|
632
725
|
await pending.handle.abort().catch(() => undefined);
|
|
633
726
|
cleanupStaging(stagingId);
|
|
727
|
+
if (!activeStillAdmitted) {
|
|
728
|
+
sendError(res, 409, "conflict", "sync conflict");
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
634
731
|
sendActivity(activeAtCommit, "streamed");
|
|
635
732
|
return;
|
|
636
733
|
}
|
|
637
734
|
try {
|
|
638
|
-
const
|
|
639
|
-
|
|
640
|
-
await wipeSessionAttendants({ fileHistoryStore: historyStore, sessionPolicyStore: deps.sessionPolicyStore, resumeAnchorStore: deps.resumeAnchorStore }, sessionId);
|
|
641
|
-
}
|
|
642
|
-
if (pending.fileHistory !== null && canReplayHistory) {
|
|
643
|
-
const failed = await replayHistory(pending.fileHistory, pending.resolution === "overwrite-dst");
|
|
644
|
-
if (failed !== undefined) {
|
|
645
|
-
sendFileHistoryReplayFailure(res, failed);
|
|
646
|
-
cleanupStaging(stagingId);
|
|
647
|
-
return;
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
const policyStore = deps.sessionPolicyStore;
|
|
651
|
-
if (policyStore)
|
|
652
|
-
await replayPolicyRecords(policyStore, sessionId, pending.policy, pending.operatorOk);
|
|
653
|
-
const anchorStore = deps.resumeAnchorStore;
|
|
654
|
-
if (anchorStore)
|
|
655
|
-
for (const a of pending.anchors)
|
|
656
|
-
await anchorStore.put(sessionId, a.eventId, a.entryId, pending.owner);
|
|
657
|
-
const rowLanded = await replaySessionRow(pending.row, committed.face);
|
|
735
|
+
const attendantsB = backend.landedAttendants();
|
|
736
|
+
const committed = await pending.handle.commit(pending.owner, { expectedOccupant: pending.expectedOccupant, ...(pending.resolution ? { resolution: pending.resolution } : {}) });
|
|
658
737
|
sessionStorage.evict?.(sessionId);
|
|
738
|
+
const landedB = await landAttendants(attendantsB, committed.face, {
|
|
739
|
+
wipe: pending.resolution === "overwrite-dst" && (committed.relation === "fork" || committed.relation === "stale"),
|
|
740
|
+
fileHistory: pending.fileHistory,
|
|
741
|
+
getBlob: pending.visible.get,
|
|
742
|
+
allowReplace: pending.resolution === "overwrite-dst",
|
|
743
|
+
policy: pending.policy,
|
|
744
|
+
operator: pending.operatorOk,
|
|
745
|
+
anchors: pending.anchors,
|
|
746
|
+
owner: pending.owner,
|
|
747
|
+
row: pending.row,
|
|
748
|
+
});
|
|
659
749
|
cleanupStaging(stagingId);
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
750
|
+
switch (landedB.kind) {
|
|
751
|
+
case "landed":
|
|
752
|
+
sendJson(res, 200, { relation: committed.relation });
|
|
753
|
+
return;
|
|
754
|
+
case "gone":
|
|
755
|
+
sendLandedOccupantGone("commit", landedB.leg, committed.face);
|
|
756
|
+
return;
|
|
757
|
+
case "history_failed":
|
|
758
|
+
sendFileHistoryReplayFailure(res, landedB.failure);
|
|
759
|
+
return;
|
|
760
|
+
default: {
|
|
761
|
+
const unhandled = landedB;
|
|
762
|
+
throw new Error(`session sync: unknown landing answer ${JSON.stringify(unhandled)}`);
|
|
763
|
+
}
|
|
663
764
|
}
|
|
664
|
-
sendJson(res, 200, { relation: committed.relation });
|
|
665
765
|
}
|
|
666
766
|
catch (e) {
|
|
667
767
|
await pending.handle.abort().catch(() => undefined);
|
|
@@ -700,9 +800,17 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
700
800
|
sendError(res, 400, "request.field_invalid", "entryIds must be an array of strings");
|
|
701
801
|
return;
|
|
702
802
|
}
|
|
703
|
-
const dst = await sessionStorage.exportEntries(sessionId);
|
|
803
|
+
const dst = gate.occupant === undefined ? null : await sessionStorage.exportEntries(sessionId);
|
|
704
804
|
const relation = classifySyncRelationshipByIds(body.entryIds, dst ? dst.map((e) => e.id) : null);
|
|
805
|
+
if (gate.occupant === undefined) {
|
|
806
|
+
sendJson(res, 200, relation);
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
705
809
|
const refusal = await firstPhaseRefusal(sessionStorage, sessionId, relation.relation, false);
|
|
810
|
+
if (!(await stillAdmitted("push"))) {
|
|
811
|
+
sendError(res, 409, "conflict", "sync conflict");
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
706
814
|
if (refusal?.kind === "stamped") {
|
|
707
815
|
if (sendSessionError(res, refusal.error, "import"))
|
|
708
816
|
return;
|
|
@@ -771,14 +879,6 @@ function sendFileHistoryReplayFailure(res, failure) {
|
|
|
771
879
|
const { code, permanence } = failure.error;
|
|
772
880
|
sendError(res, 422, code, `file history import failed (${code})`, permanence === undefined ? undefined : { permanence });
|
|
773
881
|
}
|
|
774
|
-
async function firstMissingBlob(blobFaces, referenced) {
|
|
775
|
-
if (referenced.length === 0)
|
|
776
|
-
return undefined;
|
|
777
|
-
if (!blobFaces.available)
|
|
778
|
-
return referenced[0];
|
|
779
|
-
const present = await blobFaces.blobExchange.has(referenced);
|
|
780
|
-
return referenced.find((h) => !present.has(h));
|
|
781
|
-
}
|
|
782
882
|
function sendSessionSyncUnavailable(res, why) {
|
|
783
883
|
switch (why) {
|
|
784
884
|
case "store_backend_absent":
|
|
@@ -791,9 +891,6 @@ function sendSessionSyncUnavailable(res, why) {
|
|
|
791
891
|
case "session_import_staging_absent":
|
|
792
892
|
sendError(res, 501, "capability.session_store_required", "staged session import requires a durable session store");
|
|
793
893
|
return;
|
|
794
|
-
case "file_history_store_absent":
|
|
795
|
-
sendError(res, 501, "capability.snapshot_store_required", "session sync is not available on this deployment: no file-history store is wired, so there is no file history to export or import");
|
|
796
|
-
return;
|
|
797
894
|
case "policy_purge_absent":
|
|
798
895
|
sendError(res, 501, "capability.policy_purge_required", "session sync is not available on this deployment: the session-policy store has no deleteBySession purge verb, so an overwrite-dst import could not wipe the destination's stale policy rows. Every in-tree policy store carries it since core 1.423 — upgrade the policy store");
|
|
799
896
|
return;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { isUuidV7 } from "../../security.js";
|
|
2
2
|
import { sendJson, sendError } from "../send.js";
|
|
3
3
|
import { sendUnauthorized } from "../principal-gate.js";
|
|
4
|
-
import { routeListFilter } from "../owner-access.js";
|
|
4
|
+
import { listOwnerOf, listVisibleSessions, routeListFilter } from "../owner-access.js";
|
|
5
5
|
import { filterFingerprint } from "../cursor-fingerprint.js";
|
|
6
|
+
import { SESSION_KINDS, isSessionKind } from "../../session-kind.js";
|
|
6
7
|
import { sessionTempoOfRunStatus } from "../../plugins/store-contracts.js";
|
|
7
8
|
import { assertNever } from "../route-ctx.js";
|
|
8
9
|
import { PRINCIPAL_MAX_BYTES } from "../../principal-shape.js";
|
|
@@ -21,8 +22,7 @@ export async function handleSessionsList(req, res, match, ctx) {
|
|
|
21
22
|
sendError(res, 501, "capability.session_store_required", "session list requires a durable session store (DB_BACKEND=mysql|pg|local)");
|
|
22
23
|
return;
|
|
23
24
|
}
|
|
24
|
-
|
|
25
|
-
await handleSessionList(res, lister, new URLSearchParams(match.search), gateOwner, includeUnowned);
|
|
25
|
+
await handleSessionList(res, listing.lister, new URLSearchParams(match.search), { fleetWide, gateOwner, includeUnowned });
|
|
26
26
|
return;
|
|
27
27
|
}
|
|
28
28
|
case "sendfile-links": {
|
|
@@ -109,18 +109,24 @@ function decodeSessionCursor(s, expectF) {
|
|
|
109
109
|
return undefined;
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
|
-
async function handleSessionList(res, lister, query,
|
|
113
|
-
const
|
|
112
|
+
async function handleSessionList(res, lister, query, filter) {
|
|
113
|
+
const kindRaw = query.get("sessionKind");
|
|
114
|
+
if (kindRaw !== null && !isSessionKind(kindRaw)) {
|
|
115
|
+
sendError(res, 400, "request.query_invalid", `sessionKind must be one of ${SESSION_KINDS.map((w) => JSON.stringify(w)).join(" | ")} when present (absent = the default view: top-level sessions only)`);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const sessionKind = kindRaw ?? undefined;
|
|
119
|
+
const owner = listOwnerOf(filter, query.get("owner") ?? undefined);
|
|
114
120
|
const limit = Math.min(100, Math.max(1, Number(query.get("limit") ?? 50) || 50));
|
|
115
121
|
const qRaw = (query.get("q") ?? "").trim();
|
|
116
122
|
const q = qRaw ? qRaw.slice(0, 256) : undefined;
|
|
117
|
-
const f = filterFingerprint([owner, includeUnowned, q]);
|
|
123
|
+
const f = filterFingerprint(sessionKind === undefined ? [owner, filter.includeUnowned, q] : [owner, filter.includeUnowned, q, sessionKind]);
|
|
118
124
|
const cursor = decodeSessionCursor(query.get("cursor"), f);
|
|
119
125
|
if (cursor === "mismatch") {
|
|
120
126
|
sendError(res, 400, "request.cursor_filter_mismatch", "cursor was issued under a different filter (owner/q) — restart from the first page without a cursor");
|
|
121
127
|
return;
|
|
122
128
|
}
|
|
123
|
-
const rows = await lister
|
|
129
|
+
const rows = await listVisibleSessions(lister, filter, query.get("owner") ?? undefined, { ...(cursor ? { cursor } : {}), limit: limit + 1, ...(q ? { q } : {}), ...(sessionKind !== undefined ? { sessionKind } : {}) });
|
|
124
130
|
const hasMore = rows.length > limit;
|
|
125
131
|
const page = hasMore ? rows.slice(0, limit) : rows;
|
|
126
132
|
const last = page[page.length - 1];
|