@sema-agent/server 7.132.0 → 7.134.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 +353 -0
- package/README.md +8 -9
- package/README.zh-CN.md +7 -6
- package/USAGE.md +301 -64
- package/deploy/sema-up/README.md +35 -1
- package/deploy/sema-up/chart/Chart.yaml +1 -1
- package/deploy/sema-up/chart/templates/NOTES.txt +15 -0
- package/deploy/sema-up/chart/templates/_helpers.tpl +129 -0
- package/deploy/sema-up/chart/templates/sealed-key.yaml +24 -0
- package/deploy/sema-up/chart/templates/server-v4pro.yaml +19 -2
- package/deploy/sema-up/chart/templates/server.yaml +19 -2
- package/deploy/sema-up/chart/values-full-single.yaml +5 -1
- package/deploy/sema-up/chart/values.yaml +34 -0
- package/deploy/sema-up/stack/compose.yaml +3 -0
- package/deploy/sema-up/stack/sema-stack-cluster.yaml +4 -2
- package/dist/adoption/plan.d.ts +3 -2
- package/dist/adoption/plan.js +7 -5
- package/dist/adoption/runner.js +3 -10
- package/dist/adoption/sql.d.ts +3 -2
- package/dist/adoption/sql.js +3 -5
- package/dist/adoption/wire.d.ts +4 -1
- package/dist/adoption/wire.js +1 -0
- package/dist/approval-ask-audit-store.d.ts +32 -14
- package/dist/approval-ask-audit-store.js +272 -74
- package/dist/approval-ask-machine.d.ts +15 -0
- package/dist/approval-ask-machine.js +6 -0
- package/dist/approval-ask-retention.d.ts +136 -0
- package/dist/approval-ask-retention.js +102 -0
- package/dist/approval-card.d.ts +83 -23
- package/dist/approval-card.js +45 -15
- package/dist/approval-content-kind.d.ts +5 -2
- package/dist/approval-content-kind.js +16 -3
- package/dist/approval-deny-reasons.d.ts +1 -1
- package/dist/approval-gate-currency.d.ts +72 -0
- package/dist/approval-gate-currency.js +94 -0
- package/dist/approval.d.ts +15 -0
- package/dist/approval.js +55 -17
- package/dist/audit.d.ts +27 -10
- package/dist/audit.js +12 -16
- package/dist/bake-runner/main.js +3 -2
- package/dist/boot/coordinators.d.ts +6 -1
- package/dist/boot/coordinators.js +6 -3
- package/dist/boot/deferred-sandbox-path-env.js +1 -1
- package/dist/boot/execution-env.js +9 -7
- package/dist/boot/execution-lane.js +11 -0
- package/dist/boot/leader.d.ts +2 -0
- package/dist/boot/leader.js +4 -1
- package/dist/boot/memory-boundary.js +2 -2
- package/dist/boot/memory-consolidation.d.ts +5 -4
- package/dist/boot/memory-consolidation.js +7 -6
- package/dist/boot/org-delivery.d.ts +28 -0
- package/dist/boot/org-delivery.js +20 -0
- package/dist/boot/read-deny-anchor-audit.js +2 -2
- package/dist/boot/reapers.d.ts +3 -0
- package/dist/boot/reapers.js +21 -1
- package/dist/boot/resolve-spec.d.ts +3 -0
- package/dist/boot/resolve-spec.js +60 -81
- package/dist/boot/runner-deps.d.ts +18 -5
- package/dist/boot/runner-deps.js +10 -0
- package/dist/boot/session-faces.d.ts +1 -0
- package/dist/boot/session-faces.js +4 -2
- package/dist/boot/stage-02-web-admin-pull.js +4 -1
- package/dist/boot/stage-06-runners.d.ts +6 -3
- package/dist/boot/stage-06-runners.js +20 -4
- package/dist/boot/stage-07-capability-layer.d.ts +4 -1
- package/dist/boot/stage-07-capability-layer.js +24 -3
- package/dist/boot/stage-08-reapers.d.ts +4 -1
- package/dist/boot/stage-08-reapers.js +2 -2
- package/dist/boot/stage-09-leader.d.ts +5 -1
- package/dist/boot/stage-09-leader.js +2 -2
- package/dist/boot/stage-10-http-server.d.ts +5 -1
- package/dist/boot/stage-10-http-server.js +6 -6
- package/dist/boot/stores.d.ts +8 -0
- package/dist/boot/stores.js +10 -14
- package/dist/boot/web-admin.d.ts +10 -0
- package/dist/boot/web-admin.js +162 -65
- package/dist/boot/workflow-orchestration.d.ts +2 -0
- package/dist/boot/workflow-orchestration.js +2 -1
- package/dist/brain.js +8 -3
- package/dist/breaker-key.d.ts +6 -0
- package/dist/breaker-key.js +29 -0
- package/dist/budget.js +5 -1
- package/dist/caller-text-echo.d.ts +11 -6
- package/dist/caller-text-echo.js +10 -12
- package/dist/canonical-json.d.ts +2 -41
- package/dist/canonical-json.js +2 -13
- package/dist/capabilities/center-plugins.d.ts +7 -0
- package/dist/capabilities/center-plugins.js +1 -1
- package/dist/capabilities/composite-members.d.ts +1 -1
- package/dist/capabilities/composite-members.js +1 -1
- package/dist/capabilities/execution-lane.js +2 -2
- package/dist/capabilities/hands-lane.d.ts +21 -0
- package/dist/capabilities/hands-lane.js +10 -0
- package/dist/capabilities/mcp-probe.d.ts +36 -4
- package/dist/capabilities/mcp-probe.js +29 -6
- package/dist/capabilities/scenarios.d.ts +63 -7
- package/dist/capabilities/scenarios.js +39 -7
- package/dist/capability-facts.d.ts +36 -4
- package/dist/capability-facts.js +30 -4
- package/dist/center-config-tier.d.ts +69 -0
- package/dist/center-config-tier.js +42 -0
- package/dist/checkpoint-ctx-seal-m28.d.ts +42 -0
- package/dist/checkpoint-ctx-seal-m28.js +19 -0
- package/dist/checkpoint-identity.d.ts +16 -0
- package/dist/checkpoint-identity.js +19 -0
- package/dist/config-catalog.d.ts +17 -10
- package/dist/config-catalog.js +43 -28
- package/dist/config-d-migration.d.ts +3 -1
- package/dist/config-d-migration.js +2 -2
- package/dist/config-dir-trust.d.ts +114 -0
- package/dist/config-dir-trust.js +388 -0
- package/dist/config-invariants.d.ts +18 -5
- package/dist/config-invariants.js +35 -3
- package/dist/config-provider.d.ts +6 -1
- package/dist/config-provider.js +6 -2
- package/dist/config-types.d.ts +71 -14
- package/dist/config-types.js +8 -0
- package/dist/config.d.ts +13 -0
- package/dist/config.js +121 -59
- package/dist/delegated-session-owner.d.ts +185 -0
- package/dist/delegated-session-owner.js +119 -0
- package/dist/device-protocol.d.ts +5 -3
- package/dist/device-protocol.js +1 -1
- package/dist/device-ws-hub.js +21 -2
- package/dist/device-ws-protocol.d.ts +70 -6
- package/dist/device-ws-protocol.js +50 -23
- package/dist/elicitation.js +3 -2
- package/dist/entry-problems.d.ts +34 -0
- package/dist/entry-problems.js +14 -0
- package/dist/execution-lane-caps.d.ts +49 -10
- package/dist/execution-lane-caps.js +43 -4
- package/dist/field-conflict.d.ts +44 -0
- package/dist/field-conflict.js +21 -0
- package/dist/file-history-disclosure.d.ts +34 -5
- package/dist/file-history-disclosure.js +2 -2
- package/dist/fleet/fleet-bus.d.ts +84 -6
- package/dist/fleet/fleet-bus.js +42 -9
- package/dist/hooks/classic-stations.d.ts +10 -5
- package/dist/hooks/classic-stations.js +4 -4
- package/dist/hooks/command-spawn.d.ts +57 -0
- package/dist/hooks/command-spawn.js +139 -0
- package/dist/hooks/hook-llm.js +2 -2
- package/dist/hooks/hook-runner.d.ts +127 -25
- package/dist/hooks/hook-runner.js +668 -287
- package/dist/hooks/hook-source.d.ts +44 -0
- package/dist/hooks/hook-source.js +40 -0
- package/dist/http/active-run-conflict.d.ts +5 -13
- package/dist/http/active-run-conflict.js +17 -27
- package/dist/http/admission.js +111 -60
- package/dist/http/approval-sweeps.js +58 -19
- package/dist/http/caller-trust.d.ts +77 -0
- package/dist/http/caller-trust.js +143 -0
- package/dist/http/checkpoint-ctx.d.ts +173 -18
- package/dist/http/checkpoint-ctx.js +171 -12
- package/dist/http/claimed-run.d.ts +4 -3
- package/dist/http/device-placement.d.ts +2 -4
- package/dist/http/device-placement.js +2 -1
- package/dist/http/dispatch.js +13 -7
- package/dist/http/durable-run-leg.d.ts +4 -32
- package/dist/http/durable-run-leg.js +37 -12
- package/dist/http/idempotency.d.ts +2 -2
- package/dist/http/idempotency.js +2 -2
- package/dist/http/owner-access.d.ts +56 -0
- package/dist/http/owner-access.js +42 -0
- package/dist/http/principal-gate.d.ts +78 -27
- package/dist/http/principal-gate.js +64 -14
- package/dist/http/readiness.d.ts +11 -5
- package/dist/http/readiness.js +10 -3
- package/dist/http/refusal-body.d.ts +12 -0
- package/dist/http/refusal-body.js +11 -0
- package/dist/http/request-guards.d.ts +16 -32
- package/dist/http/request-guards.js +31 -30
- package/dist/http/resume-ctx-read.d.ts +50 -6
- package/dist/http/resume-ctx-read.js +33 -7
- package/dist/http/resume-legs.d.ts +5 -5
- package/dist/http/resume-legs.js +163 -91
- package/dist/http/retired-service-deps.d.ts +43 -0
- package/dist/http/retired-service-deps.js +18 -0
- package/dist/http/route-ctx.d.ts +44 -37
- package/dist/http/route-ctx.js +4 -0
- package/dist/http/route-table.js +2 -0
- package/dist/http/routes/a2a-serve.js +3 -4
- package/dist/http/routes/admin-config-refresh.js +1 -1
- package/dist/http/routes/admin-drain.js +1 -1
- package/dist/http/routes/admin-memory-optout.js +5 -1
- package/dist/http/routes/adoption.js +2 -9
- package/dist/http/routes/agents-roster.js +2 -3
- package/dist/http/routes/approvals-assistant.js +91 -56
- package/dist/http/routes/background.js +3 -2
- package/dist/http/routes/capabilities.js +20 -4
- package/dist/http/routes/config-catalog.d.ts +3 -3
- package/dist/http/routes/config-catalog.js +1 -1
- package/dist/http/routes/devices.js +3 -17
- package/dist/http/routes/diagnostics.d.ts +3 -3
- package/dist/http/routes/diagnostics.js +6 -2
- package/dist/http/routes/fleet.js +4 -4
- package/dist/http/routes/images.js +6 -7
- package/dist/http/routes/leader.js +1 -8
- package/dist/http/routes/mcp-reconnect.d.ts +1 -1
- package/dist/http/routes/mcp-reconnect.js +3 -2
- package/dist/http/routes/memory-bundle.js +1 -1
- package/dist/http/routes/memory-compliance.js +1 -1
- package/dist/http/routes/memory-consolidation.js +1 -1
- package/dist/http/routes/memory-origin.d.ts +1 -1
- package/dist/http/routes/memory-origin.js +1 -1
- package/dist/http/routes/memory-policy.d.ts +1 -1
- package/dist/http/routes/memory-policy.js +3 -2
- package/dist/http/routes/notify-wake.js +8 -21
- package/dist/http/routes/observability.js +2 -2
- package/dist/http/routes/principal-inbox.d.ts +39 -0
- package/dist/http/routes/principal-inbox.js +76 -0
- package/dist/http/routes/retention-ops.js +2 -9
- package/dist/http/routes/rules.js +9 -22
- package/dist/http/routes/runs.d.ts +5 -0
- package/dist/http/routes/runs.js +63 -115
- package/dist/http/routes/session-sync.d.ts +3 -2
- package/dist/http/routes/session-sync.js +53 -48
- package/dist/http/routes/sessions-list.d.ts +1 -1
- package/dist/http/routes/sessions-list.js +3 -4
- package/dist/http/routes/sessions.js +33 -43
- package/dist/http/routes/side-query.js +6 -4
- package/dist/http/routes/tasks.js +71 -30
- package/dist/http/routes/trace-usage.js +7 -9
- package/dist/http/routes/workflows.d.ts +351 -0
- package/dist/http/routes/workflows.js +153 -119
- package/dist/http/run-declared-families.d.ts +16 -0
- package/dist/http/run-declared-families.js +12 -0
- package/dist/http/send.d.ts +24 -3
- package/dist/http/send.js +14 -2
- package/dist/http/server.d.ts +23 -18
- package/dist/http/server.js +20 -15
- package/dist/http/stored-body-replay.js +17 -4
- package/dist/http/submit-ctx.d.ts +4 -3
- package/dist/http/submit-ctx.js +23 -6
- package/dist/http/submit-replay.d.ts +4 -3
- package/dist/http/submit-replay.js +5 -4
- package/dist/http/wire-types.d.ts +17 -10
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -1
- package/dist/lane-home-probe.d.ts +78 -9
- package/dist/lane-home-probe.js +78 -12
- package/dist/leader/endpoint.d.ts +4 -1
- package/dist/leader/endpoint.js +11 -10
- package/dist/leader/wire.d.ts +4 -0
- package/dist/leader/wire.js +8 -7
- package/dist/limit-refusal.d.ts +17 -5
- package/dist/limit-refusal.js +4 -1
- package/dist/main.js +1 -1
- package/dist/mcp-assertion-header.d.ts +31 -10
- package/dist/mcp-assertion-header.js +10 -3
- package/dist/memory-origin-fence.js +2 -2
- package/dist/memory-project-layer.d.ts +37 -0
- package/dist/memory-project-layer.js +46 -0
- package/dist/memory-scope.d.ts +23 -16
- package/dist/memory-scope.js +24 -8
- package/dist/memory-sync-client.d.ts +27 -13
- package/dist/memory-sync-client.js +107 -18
- package/dist/model-compat.d.ts +1 -1
- package/dist/model-entry-refusal.d.ts +9 -1
- package/dist/model-entry-refusal.js +6 -1
- package/dist/model-plane-face.d.ts +51 -0
- package/dist/model-plane-face.js +24 -0
- package/dist/model-provider.d.ts +5 -0
- package/dist/model-provider.js +4 -2
- package/dist/model-route-endpoint.d.ts +8 -11
- package/dist/model-route-endpoint.js +4 -7
- package/dist/model-route-env.d.ts +22 -0
- package/dist/model-route-env.js +2 -0
- package/dist/model-route-gap.d.ts +60 -30
- package/dist/model-route-gap.js +21 -15
- package/dist/observability/corrupt-read-seat.d.ts +7 -0
- package/dist/observability/corrupt-read-seat.js +9 -0
- package/dist/observability/err-text.d.ts +6 -0
- package/dist/observability/err-text.js +18 -4
- package/dist/observability/fail-open.d.ts +35 -9
- package/dist/observability/fail-open.js +35 -9
- package/dist/observability/metrics.js +1 -0
- package/dist/observability/prompt-manifest.d.ts +22 -25
- package/dist/observability/prompt-manifest.js +54 -50
- package/dist/observability/tool-trace.js +9 -10
- package/dist/operator-ask.d.ts +10 -0
- package/dist/operator-ask.js +3 -0
- package/dist/orchestration/workflow-agent-session-index.d.ts +12 -12
- package/dist/orchestration/workflow-agent-session-index.js +9 -13
- package/dist/orchestration/workflow-completion-inbox.d.ts +42 -15
- package/dist/orchestration/workflow-completion-inbox.js +13 -0
- package/dist/orchestration/workflow-notify-journal.d.ts +13 -32
- package/dist/orchestration/workflow-notify-journal.js +27 -34
- package/dist/orchestration/workflow-park-decision.d.ts +89 -0
- package/dist/orchestration/workflow-park-decision.js +119 -0
- package/dist/org-delivery-gate.d.ts +81 -0
- package/dist/org-delivery-gate.js +59 -0
- package/dist/panel-tool-names.d.ts +23 -0
- package/dist/panel-tool-names.js +8 -0
- package/dist/parked-decide.d.ts +43 -11
- package/dist/parked-decide.js +100 -51
- package/dist/parked-revive-surface.d.ts +9 -2
- package/dist/parked-revive-surface.js +11 -8
- package/dist/permission-rule-list.d.ts +1 -1
- package/dist/permission-rule-list.js +2 -2
- package/dist/permission-rule-vocab.d.ts +1 -23
- package/dist/permission-rule-vocab.js +0 -13
- package/dist/plugins/approval-ask-store-file.d.ts +16 -1
- package/dist/plugins/approval-ask-store-file.js +21 -0
- package/dist/plugins/approval-ask-store-memory.d.ts +18 -1
- package/dist/plugins/approval-ask-store-memory.js +34 -2
- package/dist/plugins/approval-ask-store-sql.d.ts +25 -0
- package/dist/plugins/approval-ask-store-sql.js +37 -1
- package/dist/plugins/approval-payload-fingerprint.d.ts +14 -6
- package/dist/plugins/approval-payload-fingerprint.js +1 -1
- package/dist/plugins/blob-backend.d.ts +38 -25
- package/dist/plugins/blob-backend.js +29 -19
- package/dist/plugins/breaker-state-sql.d.ts +2 -2
- package/dist/plugins/breaker-state-sql.js +4 -4
- package/dist/plugins/caching-session-store.d.ts +4 -1
- package/dist/plugins/caching-session-store.js +7 -3
- package/dist/plugins/checkpoint-store-sql.d.ts +36 -1
- package/dist/plugins/checkpoint-store-sql.js +65 -3
- package/dist/plugins/file-history-store-sql.d.ts +5 -2
- package/dist/plugins/file-history-store-sql.js +3 -1
- package/dist/plugins/file-run-store.d.ts +5 -13
- package/dist/plugins/file-run-store.js +9 -0
- package/dist/plugins/fork-routing-session-store.d.ts +9 -1
- package/dist/plugins/fork-routing-session-store.js +12 -1
- package/dist/plugins/host-platform.d.ts +2 -0
- package/dist/plugins/host-platform.js +2 -1
- package/dist/plugins/local-checkpoint-store.d.ts +15 -2
- package/dist/plugins/local-checkpoint-store.js +32 -1
- package/dist/plugins/local-session-store.d.ts +13 -10
- package/dist/plugins/local-session-store.js +23 -14
- package/dist/plugins/local-session-window.d.ts +45 -0
- package/dist/plugins/local-session-window.js +38 -0
- package/dist/plugins/mailbox-recipient-lifecycle.d.ts +3 -2
- package/dist/plugins/mailbox-store-sql.d.ts +30 -48
- package/dist/plugins/mailbox-store-sql.js +77 -52
- package/dist/plugins/memory-embedder-fingerprint.js +2 -1
- package/dist/plugins/memory-run-store.d.ts +5 -13
- package/dist/plugins/memory-run-store.js +8 -0
- package/dist/plugins/one-time-migrations.d.ts +121 -5
- package/dist/plugins/one-time-migrations.js +332 -8
- package/dist/plugins/org-delivery-quota-store.d.ts +123 -0
- package/dist/plugins/org-delivery-quota-store.js +228 -0
- package/dist/plugins/permission-rule-store-file.js +10 -6
- package/dist/plugins/permission-rule-store-sql.js +8 -8
- package/dist/plugins/pg-pool.js +39 -7
- package/dist/plugins/pg-session-storage.d.ts +18 -5
- package/dist/plugins/pg-session-storage.js +68 -23
- package/dist/plugins/remote-env-adb.d.ts +6 -12
- package/dist/plugins/remote-env-adb.js +12 -2
- package/dist/plugins/remote-env-device.d.ts +14 -7
- package/dist/plugins/remote-env-device.js +23 -15
- package/dist/plugins/remote-env-k8s.d.ts +8 -12
- package/dist/plugins/remote-env-k8s.js +23 -5
- package/dist/plugins/remote-env-local-docker.d.ts +10 -15
- package/dist/plugins/remote-env-local-docker.js +12 -9
- package/dist/plugins/remote-env-ssh.d.ts +10 -16
- package/dist/plugins/remote-env-ssh.js +17 -11
- package/dist/plugins/run-store-sql.d.ts +16 -14
- package/dist/plugins/run-store-sql.js +22 -2
- package/dist/plugins/schema-indexes.js +2 -0
- package/dist/plugins/send-user-file.js +2 -2
- package/dist/plugins/session-epoch-anchor.d.ts +32 -6
- package/dist/plugins/session-epoch-anchor.js +2 -3
- package/dist/plugins/session-ownership.d.ts +11 -0
- package/dist/plugins/session-ownership.js +11 -0
- package/dist/plugins/session-placement.d.ts +12 -14
- package/dist/plugins/session-placement.js +2 -15
- package/dist/plugins/session-store.d.ts +18 -8
- package/dist/plugins/session-store.js +15 -4
- package/dist/plugins/sql-driver.d.ts +74 -11
- package/dist/plugins/sql-driver.js +210 -12
- package/dist/plugins/sql-errors.d.ts +23 -0
- package/dist/plugins/sql-errors.js +21 -0
- package/dist/plugins/store-backend.d.ts +36 -1
- package/dist/plugins/store-backend.js +40 -14
- package/dist/plugins/store-contracts.d.ts +42 -0
- package/dist/plugins/store-contracts.js +1 -0
- package/dist/plugins/tidb-pool.d.ts +6 -1
- package/dist/plugins/tidb-pool.js +44 -10
- package/dist/plugins/tidb-session-storage.d.ts +3 -2
- package/dist/plugins/tidb-session-storage.js +2 -2
- package/dist/plugins/tidb-session-store.d.ts +25 -5
- package/dist/plugins/tidb-session-store.js +67 -21
- package/dist/plugins/workflow-journal-store-sql.d.ts +73 -19
- package/dist/plugins/workflow-journal-store-sql.js +19 -6
- package/dist/plugins/workflow-run-store-sql.d.ts +30 -8
- package/dist/plugins/workflow-run-store-sql.js +50 -9
- package/dist/posture-source.d.ts +18 -10
- package/dist/posture-source.js +4 -1
- package/dist/project-identity.d.ts +45 -6
- package/dist/project-identity.js +162 -8
- package/dist/project-layer-key.d.ts +28 -0
- package/dist/project-layer-key.js +29 -0
- package/dist/question.js +3 -2
- package/dist/read-deny-entry.d.ts +25 -0
- package/dist/read-deny-entry.js +12 -0
- package/dist/read-face-posture.d.ts +37 -11
- package/dist/read-face-posture.js +32 -3
- package/dist/request-key-closure.d.ts +7 -0
- package/dist/request-key-closure.js +9 -1
- package/dist/request-tool-axes.d.ts +12 -0
- package/dist/request-tool-axes.js +20 -0
- package/dist/request-value-closure.d.ts +5 -0
- package/dist/request-value-closure.js +11 -3
- package/dist/rule-entry-list.d.ts +26 -2
- package/dist/rule-entry-list.js +72 -12
- package/dist/rules-consent.js +3 -4
- package/dist/run-acceptance.d.ts +19 -3
- package/dist/run-acceptance.js +13 -1
- package/dist/run-local.js +37 -6
- package/dist/runs.d.ts +12 -10
- package/dist/runs.js +25 -6
- package/dist/runtime-caps-resolver.d.ts +16 -6
- package/dist/runtime-caps-resolver.js +18 -5
- package/dist/sandbox-pkg-source.d.ts +19 -1
- package/dist/sandbox-pkg-source.js +29 -7
- package/dist/sealed-key.d.ts +32 -0
- package/dist/sealed-key.js +55 -1
- package/dist/security.d.ts +95 -15
- package/dist/security.js +52 -9
- package/dist/session-error-wire.d.ts +31 -7
- package/dist/session-error-wire.js +9 -2
- package/dist/session-sync-kernel.d.ts +37 -10
- package/dist/session-sync-kernel.js +10 -0
- package/dist/session-titled-frame.d.ts +33 -0
- package/dist/session-titled-frame.js +4 -0
- package/dist/session-titler.d.ts +9 -1
- package/dist/session-titler.js +15 -4
- package/dist/spec-fields.js +21 -2
- package/dist/sql-engine-posture.d.ts +3 -1
- package/dist/task-a2a.js +4 -3
- package/dist/task-cwd.d.ts +1 -1
- package/dist/task-mcp.d.ts +5 -5
- package/dist/task-mcp.js +27 -74
- package/dist/task-settings.d.ts +93 -88
- package/dist/task-settings.js +71 -63
- package/dist/text-bidi.d.ts +0 -4
- package/dist/text-bidi.js +1 -3
- package/dist/tighten-only-seats.d.ts +150 -0
- package/dist/tighten-only-seats.js +196 -0
- package/dist/tool-approval.d.ts +451 -136
- package/dist/tool-approval.js +733 -256
- package/dist/tool-rule-names.d.ts +6 -0
- package/dist/trace/core-keyset-guard.d.ts +108 -25
- package/dist/trace/exit-seat.d.ts +40 -0
- package/dist/trace/exit-seat.js +130 -0
- package/dist/trace/ledger-events.d.ts +6 -3
- package/dist/trace/project.d.ts +98 -13
- package/dist/trace/project.js +57 -32
- package/dist/trace/projection-table.d.ts +100 -0
- package/dist/trace/projection-table.js +130 -0
- package/dist/trace/redact.d.ts +57 -0
- package/dist/trace/redact.js +962 -168
- package/dist/trace/self-minted-wire.d.ts +349 -1
- package/dist/trace/self-minted-wire.js +118 -1
- package/dist/trace/wire-projection-faces.d.ts +304 -2
- package/dist/trace/wire-projection-faces.js +160 -13
- package/dist/url-refusal.js +2 -0
- package/dist/web-admin-client/apply-effective.d.ts +12 -0
- package/dist/web-admin-client/apply-effective.js +56 -5
- package/dist/web-admin-client/facade.d.ts +1 -1
- package/dist/web-admin-client/facade.js +1 -1
- package/dist/web-admin-client/read-face.js +5 -9
- package/dist/web-admin-client/skills-mcp.d.ts +1 -1
- package/dist/web-admin-client/skills-mcp.js +6 -8
- package/dist/web-admin-client/types.d.ts +1 -1
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +57 -9
- package/package.json +8 -4
- package/scripts/drain-check-local.mjs +34 -2
- package/scripts/drain-check.mysql.sql +20 -2
- package/scripts/drain-check.pg.sql +20 -2
- package/dist/mcp-tool-faces.d.ts +0 -69
- package/dist/mcp-tool-faces.js +0 -106
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import { isUuidV7, legacyUnownedAmnestyOpen } from "../../security.js";
|
|
2
2
|
import { sendJson, sendError } from "../send.js";
|
|
3
|
-
import { sendUnauthorized } from "../principal-gate.js";
|
|
3
|
+
import { fleetViewOf, sendUnauthorized } from "../principal-gate.js";
|
|
4
4
|
import { filterFingerprint } from "../cursor-fingerprint.js";
|
|
5
5
|
import { sessionTempoOfRunStatus } from "../../plugins/store-contracts.js";
|
|
6
6
|
import { assertNever } from "../route-ctx.js";
|
|
7
7
|
import * as facts from "../../capability-facts.js";
|
|
8
8
|
export async function handleSessionsList(req, res, match, ctx) {
|
|
9
9
|
const { deps } = ctx;
|
|
10
|
-
const { isFleetWide, sessionOwnerScope } = ctx.helpers;
|
|
11
10
|
switch (match.decl.id) {
|
|
12
11
|
case "sessions-list": {
|
|
13
|
-
const { fleetWide, gateOwner } =
|
|
12
|
+
const { fleetWide, gateOwner } = fleetViewOf(req, deps, "read");
|
|
14
13
|
if (!fleetWide && gateOwner === null) {
|
|
15
14
|
sendUnauthorized(res, "identity");
|
|
16
15
|
return;
|
|
@@ -26,7 +25,7 @@ export async function handleSessionsList(req, res, match, ctx) {
|
|
|
26
25
|
return;
|
|
27
26
|
}
|
|
28
27
|
case "sendfile-links": {
|
|
29
|
-
const { fleetWide, gateOwner } =
|
|
28
|
+
const { fleetWide, gateOwner } = fleetViewOf(req, deps, "read");
|
|
30
29
|
if (!fleetWide && gateOwner === null) {
|
|
31
30
|
sendUnauthorized(res, "identity");
|
|
32
31
|
return;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SessionError, materializeA2aTools, materializeMcpTools, sanitizePathComponent } from "@sema-agent/core";
|
|
2
2
|
import { a2aForScenario, mcpForScenario } from "../../web-admin-client/facade.js";
|
|
3
|
-
import { HttpError, isUuidShape, legacyUnownedSessionOk } from "../../security.js";
|
|
3
|
+
import { HttpError, isUuidShape, legacyUnownedSessionOk, sessionOwnerCellOf } from "../../security.js";
|
|
4
4
|
import { windowMessages, truncateMessageBlobs, redactSessionViewForRead } from "../../audit.js";
|
|
5
5
|
import { redactSecrets } from "../../trace/redact.js";
|
|
6
6
|
import { recordFailOpen } from "../../observability/fail-open.js";
|
|
@@ -9,18 +9,19 @@ import { sentMemoryComplianceFailure } from "../memory-compliance-failure-reply.
|
|
|
9
9
|
import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, MAX_SETTINGS_ENV_VARS, MAX_SETTINGS_ENV_KEY_CHARS, MAX_SETTINGS_ENV_VALUE_CHARS } from "../../task-settings.js";
|
|
10
10
|
import { readWritableSessionPermissionRules } from "../../session-policy-wire.js";
|
|
11
11
|
import { sendJson, sendError, sendSessionError, sendSessionPolicyError, sseHeaders, httpErrorBody } from "../send.js";
|
|
12
|
-
import { gatedPrincipal, explicitOperatorOk, sessionOwnerReadable, sendPrincipalRefusal, sendUnauthorized } from "../principal-gate.js";
|
|
12
|
+
import { fleetViewOf, gatedPrincipal, explicitOperatorOk, sessionCallerOf, sessionOwnerReadable, sendPrincipalRefusal, sendUnauthorized } from "../principal-gate.js";
|
|
13
|
+
import { runRowReadableOnSessionFace } from "../owner-access.js";
|
|
13
14
|
import { assertNever, SESSION_NOT_FOUND_MESSAGE } from "../route-ctx.js";
|
|
14
15
|
import * as facts from "../../capability-facts.js";
|
|
15
16
|
import { rosterAgentId } from "./agents-roster.js";
|
|
16
17
|
import { projectSessionListRow } from "./sessions-list.js";
|
|
17
18
|
import { readSessionRowPatch } from "../../session-row.js";
|
|
19
|
+
import { buildBoundedToolNames } from "../../panel-tool-names.js";
|
|
18
20
|
const MCP_STATUS_TTL_MS = 5_000;
|
|
19
21
|
const A2A_STATUS_TTL_MS = 5_000;
|
|
20
22
|
const A2A_MATERIALIZE_TIMEOUT_MS = 10_000;
|
|
21
23
|
const MEMORY_STATUS_TTL_MS = 5_000;
|
|
22
24
|
const LAST_LEG_LEDGER_TIMEOUT_MS = 2_000;
|
|
23
|
-
const A2A_PANEL_MAX_TOOL_NAMES = 200;
|
|
24
25
|
export function createSessionsLocal() {
|
|
25
26
|
return {
|
|
26
27
|
sseConnections: 0,
|
|
@@ -30,11 +31,11 @@ export function createSessionsLocal() {
|
|
|
30
31
|
memoryStatusCache: new Map(),
|
|
31
32
|
};
|
|
32
33
|
}
|
|
33
|
-
async function lastLegMcpFor(deps, sessionId,
|
|
34
|
+
async function lastLegMcpFor(deps, sessionId, caller) {
|
|
34
35
|
let timer;
|
|
35
36
|
try {
|
|
36
37
|
return await Promise.race([
|
|
37
|
-
readLastLegMcp(deps, sessionId,
|
|
38
|
+
readLastLegMcp(deps, sessionId, caller),
|
|
38
39
|
new Promise((r) => { timer = setTimeout(() => { recordFailOpen("server.mcp-panel.last-leg-unreadable", `ledger read exceeded ${LAST_LEG_LEDGER_TIMEOUT_MS}ms`); r(undefined); }, LAST_LEG_LEDGER_TIMEOUT_MS); }),
|
|
39
40
|
]);
|
|
40
41
|
}
|
|
@@ -47,14 +48,14 @@ async function lastLegMcpFor(deps, sessionId, principal) {
|
|
|
47
48
|
clearTimeout(timer);
|
|
48
49
|
}
|
|
49
50
|
}
|
|
50
|
-
async function readLastLegMcp(deps, sessionId,
|
|
51
|
+
async function readLastLegMcp(deps, sessionId, caller) {
|
|
51
52
|
const runStore = deps.runStore;
|
|
52
53
|
if (!runStore || sessionId === null)
|
|
53
54
|
return undefined;
|
|
54
55
|
const latest = await runStore.latestRunForSession(sessionId);
|
|
55
56
|
if (latest === undefined)
|
|
56
57
|
return undefined;
|
|
57
|
-
if (!
|
|
58
|
+
if (!runRowReadableOnSessionFace(deps, caller, latest.owner, "owner_or_operator"))
|
|
58
59
|
return undefined;
|
|
59
60
|
const events = await runStore.getEvents(latest.taskId, 0);
|
|
60
61
|
for (let i = events.length - 1; i >= 0; i--) {
|
|
@@ -70,7 +71,7 @@ async function readLastLegMcp(deps, sessionId, principal) {
|
|
|
70
71
|
}
|
|
71
72
|
export async function handleSessions(req, res, match, ctx) {
|
|
72
73
|
const { deps } = ctx;
|
|
73
|
-
const { readJson, safeDecode, sessionOwnerOk
|
|
74
|
+
const { readJson, safeDecode, sessionOwnerOk } = ctx.helpers;
|
|
74
75
|
const readOwnedSessionView = async (sessionAudit, sessionId, readOpts) => {
|
|
75
76
|
const ownerOf = deps.sessionStorage?.ownerOf?.bind(deps.sessionStorage);
|
|
76
77
|
if (ownerOf !== undefined) {
|
|
@@ -79,7 +80,7 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
79
80
|
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
80
81
|
return undefined;
|
|
81
82
|
}
|
|
82
|
-
if (!sessionOwnerOk(req, res, owner))
|
|
83
|
+
if (!sessionOwnerOk(req, res, await sessionOwnerCellOf(deps.sessionStorage, sessionId, owner)))
|
|
83
84
|
return undefined;
|
|
84
85
|
}
|
|
85
86
|
let audit;
|
|
@@ -95,7 +96,7 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
95
96
|
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
96
97
|
return undefined;
|
|
97
98
|
}
|
|
98
|
-
if (!sessionOwnerOk(req, res, audit.owner))
|
|
99
|
+
if (!sessionOwnerOk(req, res, await sessionOwnerCellOf(deps.sessionStorage, sessionId, audit.owner)))
|
|
99
100
|
return undefined;
|
|
100
101
|
return audit;
|
|
101
102
|
};
|
|
@@ -138,12 +139,13 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
138
139
|
}
|
|
139
140
|
{
|
|
140
141
|
if (match.decl.id === "session-mcp") {
|
|
141
|
-
const
|
|
142
|
+
const caller = sessionCallerOf(req, deps);
|
|
143
|
+
const principal = caller.principal;
|
|
142
144
|
if (deps.config.requirePrincipal && !principal) {
|
|
143
145
|
sendPrincipalRefusal(res, req, deps.config);
|
|
144
146
|
return;
|
|
145
147
|
}
|
|
146
|
-
const lastLegMcp = await lastLegMcpFor(deps, safeDecode(match.params.sessionId),
|
|
148
|
+
const lastLegMcp = await lastLegMcpFor(deps, safeDecode(match.params.sessionId), caller);
|
|
147
149
|
const ledgerHalf = lastLegMcp === undefined ? {} : { lastLegMcp };
|
|
148
150
|
let scenarioName;
|
|
149
151
|
try {
|
|
@@ -233,13 +235,7 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
233
235
|
...(s.agentName ? { agentName: s.agentName } : {}),
|
|
234
236
|
...(s.agentDescription ? { agentDescription: s.agentDescription } : {}),
|
|
235
237
|
...(s.endpoint ? { endpoint: redactSecrets(String(s.endpoint)).slice(0, 2048) } : {}),
|
|
236
|
-
...(s.toolNames
|
|
237
|
-
? {
|
|
238
|
-
toolCount: s.toolNames.length,
|
|
239
|
-
toolNames: s.toolNames.slice(0, A2A_PANEL_MAX_TOOL_NAMES),
|
|
240
|
-
...(s.toolNames.length > A2A_PANEL_MAX_TOOL_NAMES ? { toolNamesTruncated: true } : {}),
|
|
241
|
-
}
|
|
242
|
-
: {}),
|
|
238
|
+
...(s.toolNames ? { toolCount: s.toolNames.length, ...buildBoundedToolNames(s.toolNames) } : {}),
|
|
243
239
|
...(s.error ? { error: redactSecrets(String(s.error)).slice(0, 200) } : {}),
|
|
244
240
|
})),
|
|
245
241
|
};
|
|
@@ -277,7 +273,7 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
277
273
|
return;
|
|
278
274
|
}
|
|
279
275
|
owner = head.owner;
|
|
280
|
-
if (!sessionOwnerOk(req, res, owner ?? null))
|
|
276
|
+
if (!sessionOwnerOk(req, res, await sessionOwnerCellOf(deps.sessionStorage, headSession, owner ?? null)))
|
|
281
277
|
return;
|
|
282
278
|
leafId = head.leafId;
|
|
283
279
|
}
|
|
@@ -287,7 +283,7 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
287
283
|
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
288
284
|
return;
|
|
289
285
|
}
|
|
290
|
-
if (!sessionOwnerOk(req, res, owner ?? null))
|
|
286
|
+
if (!sessionOwnerOk(req, res, await sessionOwnerCellOf(deps.sessionStorage, headSession, owner ?? null)))
|
|
291
287
|
return;
|
|
292
288
|
leafId = await getLeafId(headSession);
|
|
293
289
|
}
|
|
@@ -344,13 +340,13 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
344
340
|
return;
|
|
345
341
|
}
|
|
346
342
|
sse.sseOwnerLookups++;
|
|
347
|
-
const
|
|
348
|
-
void
|
|
343
|
+
const cellP = ownerOf(evSession).then((o) => (o === undefined ? undefined : sessionOwnerCellOf(evFact.sessionStorage, evSession, o)));
|
|
344
|
+
void cellP.catch(() => undefined).then(() => { sse.sseOwnerLookups--; });
|
|
349
345
|
let stallTimer;
|
|
350
|
-
let
|
|
346
|
+
let cell;
|
|
351
347
|
try {
|
|
352
|
-
|
|
353
|
-
|
|
348
|
+
cell = await Promise.race([
|
|
349
|
+
cellP,
|
|
354
350
|
new Promise((r) => { stallTimer = setTimeout(() => r("__stall__"), stallBound); stallTimer.unref?.(); }),
|
|
355
351
|
]);
|
|
356
352
|
}
|
|
@@ -360,21 +356,22 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
360
356
|
}
|
|
361
357
|
if (closed)
|
|
362
358
|
return;
|
|
363
|
-
if (
|
|
359
|
+
if (cell === "__stall__") {
|
|
364
360
|
cleanup();
|
|
365
361
|
res.setHeader("retry-after", "5");
|
|
366
362
|
sendError(res, 503, "state.sse_owner_lookup_stalled", "session-events owner lookup stalled — retry");
|
|
367
363
|
return;
|
|
368
364
|
}
|
|
369
|
-
if (
|
|
365
|
+
if (cell === undefined) {
|
|
370
366
|
cleanup();
|
|
371
367
|
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
372
368
|
return;
|
|
373
369
|
}
|
|
374
|
-
if (!sessionOwnerOk(req, res,
|
|
370
|
+
if (!sessionOwnerOk(req, res, cell)) {
|
|
375
371
|
cleanup();
|
|
376
372
|
return;
|
|
377
373
|
}
|
|
374
|
+
const owner = cell.owner;
|
|
378
375
|
const q = new URLSearchParams(match.search);
|
|
379
376
|
const lastLeafId = q.get("lastLeafId");
|
|
380
377
|
let first = true;
|
|
@@ -678,7 +675,7 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
678
675
|
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
679
676
|
return;
|
|
680
677
|
}
|
|
681
|
-
if (!sessionOwnerOk(req, res, face.owner))
|
|
678
|
+
if (!sessionOwnerOk(req, res, await sessionOwnerCellOf(store, sessionId, face.owner)))
|
|
682
679
|
return;
|
|
683
680
|
const read = readSessionRowPatch(await readJson(req));
|
|
684
681
|
if ("problem" in read) {
|
|
@@ -702,7 +699,7 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
702
699
|
}
|
|
703
700
|
const fork = forkFact.sessionStorage.fork.bind(forkFact.sessionStorage);
|
|
704
701
|
const ownerOf = forkFact.sessionStorage.ownerOf.bind(forkFact.sessionStorage);
|
|
705
|
-
const scope =
|
|
702
|
+
const scope = fleetViewOf(req, deps, "write");
|
|
706
703
|
if (!scope.fleetWide && scope.gateOwner === null) {
|
|
707
704
|
sendUnauthorized(res, "identity");
|
|
708
705
|
return;
|
|
@@ -717,7 +714,7 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
717
714
|
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
718
715
|
return;
|
|
719
716
|
}
|
|
720
|
-
if (!scope.fleetWide && owner !== scope.gateOwner && !legacyUnownedSessionOk(deps.config.requirePrincipal,
|
|
717
|
+
if (!scope.fleetWide && owner !== scope.gateOwner && !legacyUnownedSessionOk(deps.config.requirePrincipal, sessionCallerOf(req, deps), await sessionOwnerCellOf(forkFact.sessionStorage, sourceId, owner))) {
|
|
721
718
|
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
722
719
|
return;
|
|
723
720
|
}
|
|
@@ -740,7 +737,7 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
740
737
|
}
|
|
741
738
|
const purge = deleteFact.purge;
|
|
742
739
|
const ownerOf = deleteFact.sessionStorage.ownerOf.bind(deleteFact.sessionStorage);
|
|
743
|
-
const scope =
|
|
740
|
+
const scope = fleetViewOf(req, deps, "write");
|
|
744
741
|
if (!scope.fleetWide && scope.gateOwner === null) {
|
|
745
742
|
sendUnauthorized(res, "identity");
|
|
746
743
|
return;
|
|
@@ -755,7 +752,7 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
755
752
|
sendJson(res, 200, { deleted: false });
|
|
756
753
|
return;
|
|
757
754
|
}
|
|
758
|
-
if (!scope.fleetWide && owner !== scope.gateOwner && !legacyUnownedSessionOk(deps.config.requirePrincipal,
|
|
755
|
+
if (!scope.fleetWide && owner !== scope.gateOwner && !legacyUnownedSessionOk(deps.config.requirePrincipal, sessionCallerOf(req, deps), await sessionOwnerCellOf(deleteFact.sessionStorage, sessionId, owner))) {
|
|
759
756
|
sendJson(res, 200, { deleted: false });
|
|
760
757
|
return;
|
|
761
758
|
}
|
|
@@ -805,7 +802,7 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
805
802
|
sendError(res, 409, "conflict.session_ownerless", "this session has no recorded owner, so its rules can be neither read nor changed through this route; rules already stored for it keep applying to the runs of the principal they were stored under. Ways out: on the local backend, during the upgrade window, the first task submitted with a principal on a session written by an earlier version claims it for that principal (this route then serves its owner); or delete the session (DELETE /v1/sessions/:id), which removes its rules with it");
|
|
806
803
|
return;
|
|
807
804
|
}
|
|
808
|
-
const operator = explicitOperatorOk(principal, deps.config
|
|
805
|
+
const operator = explicitOperatorOk(principal, deps.config);
|
|
809
806
|
if (!operator && owner !== (principal ?? null)) {
|
|
810
807
|
sendError(res, 404, "not_found.session", SESSION_NOT_FOUND_MESSAGE);
|
|
811
808
|
return;
|
|
@@ -822,14 +819,7 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
822
819
|
}
|
|
823
820
|
return;
|
|
824
821
|
}
|
|
825
|
-
|
|
826
|
-
try {
|
|
827
|
-
body = (await readJson(req));
|
|
828
|
-
}
|
|
829
|
-
catch {
|
|
830
|
-
sendError(res, 400, "request.invalid_json", "invalid JSON body");
|
|
831
|
-
return;
|
|
832
|
-
}
|
|
822
|
+
const body = (await readJson(req));
|
|
833
823
|
const read = readWritableSessionPermissionRules(body.rules);
|
|
834
824
|
if (read.refusal !== undefined) {
|
|
835
825
|
sendSessionPolicyError(res, read.refusal);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { MODEL_ROLES, expandTiers, isThinkingLevel } from "@sema-agent/core";
|
|
2
2
|
import { HttpError } from "../../security.js";
|
|
3
|
+
import { echoCallerText } from "../../caller-text-echo.js";
|
|
3
4
|
import { isAtMentionable, matchCatalogModel, refusedCatalogEntry, selectableCatalog } from "../../model-select.js";
|
|
4
|
-
import { modelUnavailableMessage } from "../../model-entry-refusal.js";
|
|
5
|
+
import { echoModelRef, modelUnavailableMessage } from "../../model-entry-refusal.js";
|
|
5
6
|
import { redactSecrets } from "../../trace/redact.js";
|
|
6
7
|
import { admitProviderEcho, redactErrorMessage } from "../../observability/run-terminal.js";
|
|
7
8
|
import { sendJson, sendError, sendHttpError, msg } from "../send.js";
|
|
@@ -12,6 +13,7 @@ export function isCorePreflightValidationError(message) {
|
|
|
12
13
|
return CORE_PREFLIGHT_VALIDATION_PREFIXES.some((prefix) => message.startsWith(prefix));
|
|
13
14
|
}
|
|
14
15
|
export const CORE_PREFLIGHT_VALIDATION_ANCHORS = CORE_PREFLIGHT_VALIDATION_PREFIXES;
|
|
16
|
+
const MODEL_ROLE_ECHO_CHARS = 80;
|
|
15
17
|
function isModelRole(value) {
|
|
16
18
|
return typeof value === "string" && MODEL_ROLES.includes(value);
|
|
17
19
|
}
|
|
@@ -62,7 +64,7 @@ export async function handleSideQuery(req, res, match, ctx) {
|
|
|
62
64
|
return;
|
|
63
65
|
}
|
|
64
66
|
if (body.modelRole !== undefined && !isModelRole(body.modelRole)) {
|
|
65
|
-
const shown = (typeof body.modelRole === "string" ? JSON.stringify(body.modelRole
|
|
67
|
+
const shown = (typeof body.modelRole === "string" ? JSON.stringify(echoCallerText(body.modelRole, { maxChars: MODEL_ROLE_ECHO_CHARS })) : `of type ${body.modelRole === null ? "null" : Array.isArray(body.modelRole) ? "array" : typeof body.modelRole}`);
|
|
66
68
|
sendError(res, 400, "request.field_invalid", `invalid modelRole ${shown} — must be a string, one of ${MODEL_ROLES.join(" / ")} (the engine's role vocabulary); a value outside it is refused before any model call`);
|
|
67
69
|
return;
|
|
68
70
|
}
|
|
@@ -70,12 +72,12 @@ export async function handleSideQuery(req, res, match, ctx) {
|
|
|
70
72
|
const catalog = selectableCatalog(deps.config.models ?? {}, deps.config.tiers, deps.config.modelRefusals);
|
|
71
73
|
const unavailable = matchCatalogModel(body.model, catalog, deps.config.modelRefusals) === undefined ? refusedCatalogEntry(body.model, deps.config.modelRefusals) : undefined;
|
|
72
74
|
if (unavailable !== undefined) {
|
|
73
|
-
const message = modelUnavailableMessage("model", body.model
|
|
75
|
+
const message = modelUnavailableMessage("model", body.model, unavailable.name, unavailable.refusal);
|
|
74
76
|
sendError(res, 400, "request.model_unavailable", message);
|
|
75
77
|
return;
|
|
76
78
|
}
|
|
77
79
|
if (!isAtMentionable(body.model, catalog, deps.config.atMention, deps.config.modelRefusals)) {
|
|
78
|
-
sendError(res, 400, "request.model_not_allowed", `model is not @-mentionable on this deployment: "${body.model
|
|
80
|
+
sendError(res, 400, "request.model_not_allowed", `model is not @-mentionable on this deployment: "${echoModelRef(body.model)}" — which models can be @-mentioned is configured by the operator (models.atMention)`);
|
|
79
81
|
return;
|
|
80
82
|
}
|
|
81
83
|
}
|
|
@@ -18,10 +18,10 @@ import { contextUsageEventData, turnEndEventData, toolStartEventData, toolEndEve
|
|
|
18
18
|
import { cascadeConfig, runMeta } from "../run-meta.js";
|
|
19
19
|
import { idemCallerOf, inflightKeyOf, submissionIdemOf } from "../idempotency.js";
|
|
20
20
|
import { replayOfExistingRun } from "../submit-replay.js";
|
|
21
|
-
import { sendJson, sendError, sseHeaders } from "../send.js";
|
|
21
|
+
import { httpErrorCode, sendJson, sendError, sseHeaders } from "../send.js";
|
|
22
22
|
import { collectSsePreamble } from "../sse-log.js";
|
|
23
23
|
import { buildApprovalPreamble, buildApprovalPreambleSseFrames } from "../../approval-card.js";
|
|
24
|
-
import { createApprovalCardEmitter, resolveApprovalLeg, resolveStreamApprovalGate } from "../../tool-approval.js";
|
|
24
|
+
import { approvalPreambleReplays, createApprovalCardEmitter, declaredApprovalFrameFamilies, resolveApprovalLeg, resolveStreamApprovalGate } from "../../tool-approval.js";
|
|
25
25
|
import { buildActiveRunConflict, isActiveRunConflictBody, toDoneFrameResult } from "../active-run-conflict.js";
|
|
26
26
|
import { commitPlacement, devicePlacementErrorBody } from "../device-placement.js";
|
|
27
27
|
import { commitSessionInheritEnv } from "../session-inherit-env.js";
|
|
@@ -34,6 +34,9 @@ import { persistSubmitCtx } from "../submit-ctx.js";
|
|
|
34
34
|
import { serviceVersion } from "../../service-version.js";
|
|
35
35
|
import { withModelReachScope } from "../../model-reach-scope.js";
|
|
36
36
|
import { createInstructionSelectionLeg } from "../../instruction-selection.js";
|
|
37
|
+
import { fieldConflictFacts } from "../../field-conflict.js";
|
|
38
|
+
import { refusalBody } from "../refusal-body.js";
|
|
39
|
+
import { heldWorkflowParkDecisions } from "../../parked-decide.js";
|
|
37
40
|
const DISCONNECT_PARK_GRACE_MS = 10_000;
|
|
38
41
|
function sseData(res, payload) {
|
|
39
42
|
const anchor = payload.eventId;
|
|
@@ -60,8 +63,8 @@ function sendSubmitResult(res, resp) {
|
|
|
60
63
|
: raw;
|
|
61
64
|
sendJson(res, 200, body);
|
|
62
65
|
}
|
|
63
|
-
function writeMetaFrame(res, taskId, sessionId) {
|
|
64
|
-
res.write(`event: meta\ndata: ${JSON.stringify({ type: "meta", taskId, ...(sessionId ? { sessionId } : {}) })}\n\n`);
|
|
66
|
+
function writeMetaFrame(res, taskId, sessionId, warnings) {
|
|
67
|
+
res.write(`event: meta\ndata: ${JSON.stringify({ type: "meta", taskId, ...(sessionId ? { sessionId } : {}), ...(warnings !== undefined ? { warnings } : {}) })}\n\n`);
|
|
65
68
|
}
|
|
66
69
|
function refusalDoneResult(body) {
|
|
67
70
|
if (isActiveRunConflictBody(body))
|
|
@@ -147,14 +150,26 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
147
150
|
const prepared = await prepareSpec(req, res, keyedBody, undefined, { liveStream });
|
|
148
151
|
if (!prepared)
|
|
149
152
|
return;
|
|
153
|
+
const declaredFamilies = declaredApprovalFrameFamilies(prepared.body.settings);
|
|
154
|
+
const approvalLegInput = (streamApprovalOn) => {
|
|
155
|
+
const legWalltimeMs = typeof prepared.spec.limits?.maxWalltimeMs === "number" ? prepared.spec.limits.maxWalltimeMs : undefined;
|
|
156
|
+
return {
|
|
157
|
+
streamApprovalOn,
|
|
158
|
+
windowMs: deps.config.streamApproval?.windowMs,
|
|
159
|
+
windowMarginMs: deps.config.streamAskWindowMarginMs,
|
|
160
|
+
...(legWalltimeMs !== undefined ? { legWalltimeMs } : {}),
|
|
161
|
+
declaredFamilies,
|
|
162
|
+
};
|
|
163
|
+
};
|
|
164
|
+
const assembleApprovalLeg = (streamApprovalOn) => resolveApprovalLeg({ ...approvalLegInput(streamApprovalOn), nowMonotonicMs: performance.now() });
|
|
150
165
|
const principal = prepared.auth?.principal;
|
|
151
|
-
const wiringOperatorFace = explicitOperatorOk(gatedPrincipal(req, deps.config), deps.config
|
|
166
|
+
const wiringOperatorFace = explicitOperatorOk(gatedPrincipal(req, deps.config), deps.config);
|
|
152
167
|
let wiringReceipt;
|
|
153
168
|
const instructionLeg = createInstructionSelectionLeg();
|
|
154
169
|
const withRunReceipt = (out) => wiringReceipt !== undefined ? { ...out, wiringManifest: wiringReceipt } : out;
|
|
155
170
|
if (liveStream) {
|
|
156
171
|
if (prepared.verify || prepared.cascade) {
|
|
157
|
-
sendError(res, 400, "request.field_conflict", "verify / cascade are not supported on /v1/tasks/stream (both are multi-attempt, not a single stream) — use /v1/tasks or /v1/runs");
|
|
172
|
+
sendError(res, 400, "request.field_conflict", "verify / cascade are not supported on /v1/tasks/stream (both are multi-attempt, not a single stream) — use /v1/tasks or /v1/runs", fieldConflictFacts("cascade_verify", prepared.verify ? (prepared.cascade ? ["verify", "cascade"] : ["verify"]) : ["cascade"]));
|
|
158
173
|
return;
|
|
159
174
|
}
|
|
160
175
|
const earlyDurableTid = facts.durableRuns(deps).available && prepared.spec.sessionId ? uuidv7() : undefined;
|
|
@@ -169,7 +184,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
169
184
|
reqState.streamDetached = detachOnDisconnect;
|
|
170
185
|
sseHeaders(res, earlyDurableTid ? { "x-task-id": earlyDurableTid } : undefined);
|
|
171
186
|
if (earlyDurableTid)
|
|
172
|
-
writeMetaFrame(res, earlyDurableTid, prepared.spec.sessionId);
|
|
187
|
+
writeMetaFrame(res, earlyDurableTid, prepared.spec.sessionId, prepared.warnings);
|
|
173
188
|
const ac = createRunAbort();
|
|
174
189
|
let closed = false;
|
|
175
190
|
let detachLogged = false;
|
|
@@ -212,17 +227,29 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
212
227
|
toolApprovalEnabled: Boolean(deps.toolApproval),
|
|
213
228
|
streamApprovalEnabled: deps.config.streamApproval?.enabled === true,
|
|
214
229
|
backend: deps.backend,
|
|
215
|
-
|
|
230
|
+
checkpointStore: deps.checkpointStore,
|
|
216
231
|
});
|
|
217
|
-
const replaySessionId = replayGate.active ? prepared.spec.sessionId : undefined;
|
|
232
|
+
const replaySessionId = approvalPreambleReplays(approvalLegInput(replayGate.active)) ? prepared.spec.sessionId : undefined;
|
|
233
|
+
const presentedOnConnection = new Set();
|
|
234
|
+
const presentedCompleted = new Set();
|
|
235
|
+
const admitPresentedComplete = (askId) => {
|
|
236
|
+
if (!presentedOnConnection.has(askId))
|
|
237
|
+
return true;
|
|
238
|
+
if (presentedCompleted.has(askId))
|
|
239
|
+
return false;
|
|
240
|
+
presentedCompleted.add(askId);
|
|
241
|
+
return true;
|
|
242
|
+
};
|
|
218
243
|
if (replaySessionId) {
|
|
219
244
|
const replayOwner = gatedPrincipal(req, deps.config) ?? null;
|
|
220
245
|
const askStore = replayGate.active ? replayGate.askStore : undefined;
|
|
246
|
+
let presentedAskIds = [];
|
|
221
247
|
const preambleFrames = await collectSsePreamble(async (signal) => {
|
|
222
248
|
if (!askStore)
|
|
223
249
|
return [];
|
|
224
250
|
const rows = await askStore.listPendingBySession(replaySessionId, replayOwner, signal);
|
|
225
251
|
const { frames, skipped, dropped } = buildApprovalPreamble(rows, Date.now(), deps.config.streamApproval.replayMax);
|
|
252
|
+
presentedAskIds = frames.map((f) => f.askId);
|
|
226
253
|
if (skipped > 0)
|
|
227
254
|
deps.logger?.warn?.("approval_preamble_row_skipped", { sessionId: replaySessionId, skipped, face: "tasks.stream" });
|
|
228
255
|
if (dropped > 0)
|
|
@@ -232,6 +259,16 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
232
259
|
if (!res.writableEnded && !res.destroyed) {
|
|
233
260
|
for (const f of preambleFrames)
|
|
234
261
|
res.write(`event: ${f.event}\ndata: ${JSON.stringify(f.data)}\n\n`);
|
|
262
|
+
if (deps.toolApproval && preambleFrames.length > 0) {
|
|
263
|
+
for (const askId of presentedAskIds)
|
|
264
|
+
presentedOnConnection.add(askId);
|
|
265
|
+
const unwatch = deps.toolApproval.watchPresentedCards(presentedAskIds, (frame) => {
|
|
266
|
+
if (res.writableEnded || res.destroyed || !admitPresentedComplete(frame.askId))
|
|
267
|
+
return;
|
|
268
|
+
res.write(`event: ${frame.type}\ndata: ${JSON.stringify(frame)}\n\n`);
|
|
269
|
+
});
|
|
270
|
+
res.once("close", unwatch);
|
|
271
|
+
}
|
|
235
272
|
}
|
|
236
273
|
}
|
|
237
274
|
const hb = setInterval(() => {
|
|
@@ -378,7 +415,9 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
378
415
|
const tailContentMode = prepared.spec.forwardSubagentEvents === true ? "on" : "progress_only";
|
|
379
416
|
const childReasoning = createReasoningSegments();
|
|
380
417
|
const topReasoning = createReasoningSegments();
|
|
418
|
+
const heldDecisions = prepared.spec.sessionId !== undefined ? await heldWorkflowParkDecisions(deps, prepared.spec.sessionId) : {};
|
|
381
419
|
const fwdInternals = {
|
|
420
|
+
...heldDecisions,
|
|
382
421
|
onForwardEvent: (e) => {
|
|
383
422
|
fleetPub?.onForwardEvent(e);
|
|
384
423
|
ledgerSink?.onForwardEvent(e);
|
|
@@ -457,6 +496,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
457
496
|
liveStreamRef = liveStream;
|
|
458
497
|
if (durableTaskId)
|
|
459
498
|
steerableRuns.set(durableTaskId, liveStream);
|
|
499
|
+
prepared.acceptance.attachStream({ reachable: () => !closed && !res.writableEnded && !res.destroyed, write: (frame) => sseData(res, { ...frame }) });
|
|
460
500
|
for await (const ev of liveStream) {
|
|
461
501
|
if (closed)
|
|
462
502
|
break;
|
|
@@ -635,18 +675,11 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
635
675
|
toolApprovalEnabled: Boolean(deps.toolApproval),
|
|
636
676
|
streamApprovalEnabled: deps.config.streamApproval?.enabled === true,
|
|
637
677
|
backend: deps.backend,
|
|
638
|
-
|
|
678
|
+
checkpointStore: deps.checkpointStore,
|
|
639
679
|
}).active;
|
|
640
|
-
const
|
|
641
|
-
const approvalLeg = resolveApprovalLeg({
|
|
642
|
-
streamApprovalOn,
|
|
643
|
-
windowMs: deps.config.streamApproval?.windowMs,
|
|
644
|
-
windowMarginMs: deps.config.streamAskWindowMarginMs,
|
|
645
|
-
...(legWalltimeMs !== undefined ? { legWalltimeMs } : {}),
|
|
646
|
-
nowMonotonicMs: performance.now(),
|
|
647
|
-
});
|
|
680
|
+
const approvalLeg = assembleApprovalLeg(streamApprovalOn);
|
|
648
681
|
const legDeadlineMonotonic = approvalLeg.legDeadlineMonotonic;
|
|
649
|
-
const
|
|
682
|
+
const unattended = approvalLeg.unattended;
|
|
650
683
|
const emitCard = createApprovalCardEmitter({
|
|
651
684
|
...(ledgerSink
|
|
652
685
|
? {
|
|
@@ -659,6 +692,8 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
659
692
|
writeLive: (frame) => {
|
|
660
693
|
if (res.writableEnded || res.destroyed)
|
|
661
694
|
throw new Error("live stream ended");
|
|
695
|
+
if (frame.type === "approval_complete" && !admitPresentedComplete(frame.askId))
|
|
696
|
+
return;
|
|
662
697
|
emitAsk(frame);
|
|
663
698
|
},
|
|
664
699
|
});
|
|
@@ -666,11 +701,10 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
666
701
|
if (!res.writableEnded && !res.destroyed)
|
|
667
702
|
emitAsk(frame);
|
|
668
703
|
};
|
|
669
|
-
const approvalCtx = deps.toolApproval &&
|
|
704
|
+
const approvalCtx = deps.toolApproval && unattended === undefined
|
|
670
705
|
? {
|
|
671
706
|
taskId: askTaskId,
|
|
672
707
|
owner: askOwner,
|
|
673
|
-
emit: emitApproval,
|
|
674
708
|
abortSignal: ac.signal,
|
|
675
709
|
...(prepared.spec.sessionId ? { sessionId: prepared.spec.sessionId } : {}),
|
|
676
710
|
...(streamApprovalOn
|
|
@@ -680,13 +714,13 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
680
714
|
legKey: "",
|
|
681
715
|
...(legDeadlineMonotonic !== undefined ? { legDeadlineMonotonic } : {}),
|
|
682
716
|
}
|
|
683
|
-
: {}),
|
|
717
|
+
: { emit: emitApproval }),
|
|
684
718
|
}
|
|
685
719
|
: undefined;
|
|
686
720
|
if (deps.toolApproval) {
|
|
687
721
|
const toolApproval = deps.toolApproval;
|
|
688
|
-
prepared.spec.onAsk =
|
|
689
|
-
? toolApproval.boundUnattendedAsk({ owner: askOwner, taskId: askTaskId, ...(prepared.spec.sessionId ? { sessionId: prepared.spec.sessionId } : {}) })
|
|
722
|
+
prepared.spec.onAsk = unattended
|
|
723
|
+
? toolApproval.boundUnattendedAsk({ owner: askOwner, taskId: askTaskId, ...(prepared.spec.sessionId ? { sessionId: prepared.spec.sessionId } : {}) }, unattended)
|
|
690
724
|
: toolApproval.boundAsk({
|
|
691
725
|
owner: askOwner,
|
|
692
726
|
taskId: askTaskId,
|
|
@@ -843,7 +877,13 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
843
877
|
res.end();
|
|
844
878
|
}
|
|
845
879
|
else {
|
|
846
|
-
const withSyncSubmitKeys = (body) =>
|
|
880
|
+
const withSyncSubmitKeys = (body) => {
|
|
881
|
+
const extra = {
|
|
882
|
+
...(prepared.verify || prepared.cascade ? { notice: MULTI_ATTEMPT_NO_EVENT_CHANNEL_NOTICE } : {}),
|
|
883
|
+
...(prepared.warnings !== undefined ? { warnings: prepared.warnings } : {}),
|
|
884
|
+
};
|
|
885
|
+
return Object.keys(extra).length > 0 ? { ...body, ...extra } : body;
|
|
886
|
+
};
|
|
847
887
|
const runSyncSubmitLeg = async () => {
|
|
848
888
|
const syncLegStartedAt = Date.now();
|
|
849
889
|
let durableTaskId;
|
|
@@ -902,10 +942,11 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
902
942
|
let result;
|
|
903
943
|
counters.uncountedBillableInflight++;
|
|
904
944
|
try {
|
|
905
|
-
const
|
|
906
|
-
const
|
|
945
|
+
const heldDecisions = prepared.spec.sessionId !== undefined ? await heldWorkflowParkDecisions(deps, prepared.spec.sessionId) : {};
|
|
946
|
+
const verifyLeg = (v) => runWithVerification(prepared.runner, specWithSignal, v, heldDecisions);
|
|
947
|
+
const cascadeLeg = () => runCascade(prepared.runner, specWithSignal, cascadeConfig(deps.config.cascadeLadder, prepared.spec.limits?.maxCostUsd), heldDecisions);
|
|
907
948
|
const plainLeg = async () => {
|
|
908
|
-
const stream = prepared.runner.runTaskStream(specWithSignal);
|
|
949
|
+
const stream = prepared.runner.runTaskStream(specWithSignal, undefined, heldDecisions);
|
|
909
950
|
for await (const ev of stream) {
|
|
910
951
|
observeSyncLegEvent(ev);
|
|
911
952
|
prepared.acceptance.observe(ev);
|
|
@@ -942,8 +983,8 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
942
983
|
const retryAfterSec = resumeAtPlane.errorCode === "memory.admission_required" && result.retryAfterMs !== undefined
|
|
943
984
|
? Math.max(1, Math.ceil(result.retryAfterMs / 1000))
|
|
944
985
|
: undefined;
|
|
945
|
-
const refusalFacts = zeroWorkRefusalFacts(redactTerminalResult(sealResultForExit(result)).terminal);
|
|
946
|
-
return { status: resumeAtStatus, body: { errorCode: resumeAtPlane.errorCode,
|
|
986
|
+
const refusalFacts = zeroWorkRefusalFacts(redactTerminalResult(sealResultForExit(result)).terminal, prepared.warnings);
|
|
987
|
+
return { status: resumeAtStatus, body: refusalBody({ errorCode: httpErrorCode(resumeAtStatus, resumeAtPlane.errorCode), message: redactErrorMessage(resumeAtPlane.errorMessage) ?? "resume-at failed", facts: { ...(retryAfterSec !== undefined ? { retryAfterSec } : {}), ...refusalFacts } }) };
|
|
947
988
|
}
|
|
948
989
|
const exitResult = await mintSubmitExitResult(result, deps.checkpointStore);
|
|
949
990
|
const syncPlane = outwardPlane(result.terminal);
|
|
@@ -6,16 +6,16 @@ import { sleep } from "../sse-log.js";
|
|
|
6
6
|
import { bindSseLifecycle } from "../sse-lifecycle.js";
|
|
7
7
|
import { sendJson, sendError, sseHeaders, SSE_MAX_STREAM_MS, SSE_HEARTBEAT_IDLE_MS } from "../send.js";
|
|
8
8
|
import { filterFingerprint } from "../cursor-fingerprint.js";
|
|
9
|
-
import {
|
|
9
|
+
import { fleetViewOf, sendUnauthorized } from "../principal-gate.js";
|
|
10
10
|
import { assertNever } from "../route-ctx.js";
|
|
11
11
|
import * as facts from "../../capability-facts.js";
|
|
12
12
|
const TOOL_RESULT_LIMIT_CAP = 262_144;
|
|
13
13
|
const TOOL_RESULT_OFFSET_CAP = 2_147_483_646;
|
|
14
14
|
export async function handleTraceUsage(req, res, match, ctx) {
|
|
15
15
|
const { deps } = ctx;
|
|
16
|
-
const {
|
|
16
|
+
const { safeDecode } = ctx.helpers;
|
|
17
17
|
if (match.decl.id === "tasks-source-summary") {
|
|
18
|
-
if (!
|
|
18
|
+
if (!fleetViewOf(req, deps, "read").fleetWide) {
|
|
19
19
|
sendUnauthorized(res, "operator");
|
|
20
20
|
return;
|
|
21
21
|
}
|
|
@@ -35,8 +35,7 @@ export async function handleTraceUsage(req, res, match, ctx) {
|
|
|
35
35
|
match.decl.id === "task-trace-stream" ||
|
|
36
36
|
match.decl.id === "task-trace-artifacts" ||
|
|
37
37
|
match.decl.id === "task-tool-result") {
|
|
38
|
-
const fleetWide =
|
|
39
|
-
const gateOwner = fleetWide ? null : (gatedPrincipal(req, deps.config) || null);
|
|
38
|
+
const { fleetWide, gateOwner } = fleetViewOf(req, deps, "read");
|
|
40
39
|
if (!fleetWide && gateOwner === null) {
|
|
41
40
|
sendUnauthorized(res, "identity");
|
|
42
41
|
return;
|
|
@@ -89,9 +88,8 @@ export async function handleTraceUsage(req, res, match, ctx) {
|
|
|
89
88
|
return;
|
|
90
89
|
}
|
|
91
90
|
const runStore = runs.runStore;
|
|
92
|
-
const fleetWide =
|
|
93
|
-
|
|
94
|
-
if (!fleetWide && !jwtOwner) {
|
|
91
|
+
const { fleetWide, gateOwner } = fleetViewOf(req, deps, "read");
|
|
92
|
+
if (!fleetWide && gateOwner === null) {
|
|
95
93
|
sendUnauthorized(res, "identity");
|
|
96
94
|
return;
|
|
97
95
|
}
|
|
@@ -109,7 +107,7 @@ export async function handleTraceUsage(req, res, match, ctx) {
|
|
|
109
107
|
sendError(res, 400, "request.query_invalid", "invalid from/to (ISO-8601 or epoch-ms; from < to)");
|
|
110
108
|
return;
|
|
111
109
|
}
|
|
112
|
-
const owner =
|
|
110
|
+
const owner = gateOwner ?? (q.get("principal") ?? undefined);
|
|
113
111
|
try {
|
|
114
112
|
const scan = await runStore.usageScan(from, to, owner !== undefined ? { owner } : undefined);
|
|
115
113
|
const base = { from: new Date(from).toISOString(), to: new Date(to).toISOString(), ...(scan.truncated ? { truncated: true } : {}) };
|