@sema-agent/server 7.132.0 → 7.133.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 +266 -0
- package/README.md +8 -9
- package/README.zh-CN.md +7 -6
- package/USAGE.md +174 -55
- package/deploy/sema-up/README.md +19 -1
- package/deploy/sema-up/chart/templates/server-v4pro.yaml +5 -2
- package/deploy/sema-up/chart/templates/server.yaml +5 -2
- 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 +31 -13
- 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 +79 -19
- package/dist/approval-card.js +44 -13
- package/dist/approval-deny-reasons.d.ts +1 -1
- package/dist/approval.d.ts +9 -0
- package/dist/approval.js +24 -3
- package/dist/audit.d.ts +27 -10
- package/dist/audit.js +12 -16
- package/dist/bake-runner/main.js +3 -2
- 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.js +2 -0
- 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/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.js +1 -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 +2 -0
- package/dist/boot/stage-06-runners.js +6 -3
- package/dist/boot/stage-07-capability-layer.d.ts +2 -0
- package/dist/boot/stage-07-capability-layer.js +24 -3
- package/dist/boot/stage-08-reapers.d.ts +2 -0
- package/dist/boot/stage-08-reapers.js +2 -2
- package/dist/boot/stage-09-leader.d.ts +3 -0
- package/dist/boot/stage-10-http-server.d.ts +3 -0
- package/dist/boot/stage-10-http-server.js +5 -6
- package/dist/boot/stores.js +2 -2
- package/dist/boot/web-admin.d.ts +4 -0
- package/dist/boot/web-admin.js +151 -63
- 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/caller-text-echo.d.ts +9 -6
- package/dist/caller-text-echo.js +8 -11
- 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 +27 -4
- package/dist/capabilities/scenarios.d.ts +63 -7
- package/dist/capabilities/scenarios.js +39 -7
- package/dist/capability-facts.d.ts +34 -4
- package/dist/capability-facts.js +28 -2
- package/dist/center-config-tier.d.ts +69 -0
- package/dist/center-config-tier.js +42 -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 +43 -0
- package/dist/field-conflict.js +20 -0
- package/dist/fleet/fleet-bus.d.ts +80 -6
- package/dist/fleet/fleet-bus.js +41 -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 +103 -60
- package/dist/http/approval-sweeps.js +55 -18
- package/dist/http/caller-trust.d.ts +77 -0
- package/dist/http/caller-trust.js +143 -0
- package/dist/http/checkpoint-ctx.d.ts +16 -16
- package/dist/http/checkpoint-ctx.js +13 -10
- 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 +10 -4
- package/dist/http/durable-run-leg.d.ts +4 -32
- package/dist/http/durable-run-leg.js +34 -10
- package/dist/http/owner-access.d.ts +56 -0
- package/dist/http/owner-access.js +42 -0
- package/dist/http/principal-gate.d.ts +48 -23
- package/dist/http/principal-gate.js +39 -12
- package/dist/http/readiness.d.ts +11 -5
- package/dist/http/readiness.js +10 -3
- package/dist/http/request-guards.d.ts +16 -23
- package/dist/http/request-guards.js +30 -15
- package/dist/http/resume-ctx-read.d.ts +2 -5
- package/dist/http/resume-ctx-read.js +7 -4
- package/dist/http/resume-legs.d.ts +3 -4
- package/dist/http/resume-legs.js +126 -69
- 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 -28
- 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/approvals-assistant.js +49 -48
- 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.js +5 -1
- 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.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 +5 -18
- package/dist/http/routes/runs.d.ts +5 -0
- package/dist/http/routes/runs.js +33 -106
- package/dist/http/routes/session-sync.js +15 -28
- package/dist/http/routes/sessions.js +30 -40
- package/dist/http/routes/side-query.js +6 -4
- package/dist/http/routes/tasks.js +50 -14
- package/dist/http/routes/workflows.d.ts +351 -0
- package/dist/http/routes/workflows.js +153 -119
- package/dist/http/send.d.ts +24 -3
- package/dist/http/send.js +14 -2
- package/dist/http/server.d.ts +17 -18
- package/dist/http/server.js +15 -13
- package/dist/http/stored-body-replay.js +17 -4
- package/dist/http/submit-ctx.js +4 -3
- package/dist/http/submit-replay.d.ts +4 -3
- package/dist/http/submit-replay.js +5 -4
- package/dist/http/wire-types.d.ts +10 -7
- package/dist/index.d.ts +1 -1
- package/dist/index.js +0 -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/limit-refusal.d.ts +17 -5
- package/dist/limit-refusal.js +4 -1
- package/dist/mcp-assertion-header.d.ts +31 -10
- package/dist/mcp-assertion-header.js +10 -3
- package/dist/mcp-tool-faces.js +1 -3
- 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-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 +3 -17
- package/dist/observability/prompt-manifest.js +45 -42
- package/dist/observability/tool-trace.js +9 -9
- 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 +118 -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 +39 -6
- package/dist/parked-decide.js +98 -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/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/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 +3 -0
- package/dist/plugins/caching-session-store.js +3 -0
- package/dist/plugins/checkpoint-store-sql.d.ts +14 -0
- package/dist/plugins/checkpoint-store-sql.js +20 -2
- 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 +2 -0
- package/dist/plugins/local-checkpoint-store.js +6 -1
- package/dist/plugins/local-session-store.d.ts +1 -1
- package/dist/plugins/local-session-window.d.ts +39 -0
- package/dist/plugins/local-session-window.js +54 -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 +110 -5
- package/dist/plugins/one-time-migrations.js +320 -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/pg-pool.js +39 -7
- package/dist/plugins/pg-session-storage.d.ts +17 -4
- package/dist/plugins/pg-session-storage.js +29 -6
- package/dist/plugins/remote-env-adb.d.ts +6 -12
- package/dist/plugins/remote-env-adb.js +12 -2
- 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-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 +24 -4
- package/dist/plugins/tidb-session-store.js +28 -4
- 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-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 +36 -6
- package/dist/runs.d.ts +10 -9
- package/dist/runs.js +21 -3
- 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/security.d.ts +87 -9
- package/dist/security.js +52 -9
- package/dist/session-error-wire.d.ts +19 -2
- package/dist/session-error-wire.js +5 -1
- 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-cwd.d.ts +1 -1
- package/dist/task-mcp.d.ts +3 -3
- package/dist/task-mcp.js +7 -66
- package/dist/task-settings.d.ts +89 -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 +303 -87
- package/dist/tool-approval.js +623 -239
- package/dist/trace/core-keyset-guard.d.ts +104 -22
- package/dist/trace/exit-seat.d.ts +28 -0
- package/dist/trace/exit-seat.js +76 -0
- package/dist/trace/ledger-events.d.ts +3 -2
- package/dist/trace/project.d.ts +80 -12
- package/dist/trace/project.js +41 -28
- package/dist/trace/projection-table.d.ts +100 -0
- package/dist/trace/projection-table.js +130 -0
- package/dist/trace/redact.d.ts +54 -0
- package/dist/trace/redact.js +778 -152
- package/dist/trace/self-minted-wire.d.ts +345 -1
- package/dist/trace/self-minted-wire.js +117 -1
- package/dist/trace/wire-projection-faces.d.ts +298 -2
- package/dist/trace/wire-projection-faces.js +154 -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.js +4 -5
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +57 -9
- package/package.json +7 -3
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { redactHead } from "../observability/run-terminal.js";
|
|
2
|
+
const errText = (e) => redactHead(e instanceof Error ? e.message : String(e), 2000);
|
|
2
3
|
import { findParkOrigin } from "../parked-decide.js";
|
|
3
4
|
import { decodeCheckpointScope } from "../security.js";
|
|
4
5
|
import { hostDecisionFacts } from "../host-decision.js";
|
|
@@ -8,7 +9,9 @@ import { gatedPrincipal, hasServiceAuth } from "./principal-gate.js";
|
|
|
8
9
|
import { costQuotaRefusalOf } from "./request-guards.js";
|
|
9
10
|
import { admissionRefusalOf, readinessOf } from "./readiness.js";
|
|
10
11
|
import { readResumeCtx } from "./resume-ctx-read.js";
|
|
12
|
+
import { isResumeBlockedByPolicy } from "./checkpoint-ctx.js";
|
|
11
13
|
import { storedBodyReplayRefusal } from "./stored-body-replay.js";
|
|
14
|
+
import { refusalBody } from "./refusal-body.js";
|
|
12
15
|
const BLOCKED_SCALAR_KEYS = ["errorCode", "blockedBy", "field", "reason", "count", "cap", "index"];
|
|
13
16
|
function blockedScalars(body) {
|
|
14
17
|
const out = {};
|
|
@@ -70,7 +73,7 @@ export function createApprovalSweeps(ctx) {
|
|
|
70
73
|
catch (e) {
|
|
71
74
|
deps.metrics?.inc("deny_sweep_list_failed_total");
|
|
72
75
|
deps.logger?.warn?.("deny_sweep_list_failed", {
|
|
73
|
-
err:
|
|
76
|
+
err: errText(e),
|
|
74
77
|
note: "the SLA deny-sweep could not read the expired approvals this tick, so nothing was denied and nothing was skipped; the rows stay suspended and are read again next tick (the terminal backstop still reaps them)",
|
|
75
78
|
});
|
|
76
79
|
return;
|
|
@@ -80,21 +83,24 @@ export function createApprovalSweeps(ctx) {
|
|
|
80
83
|
let token;
|
|
81
84
|
try {
|
|
82
85
|
token = (await cs.findPendingTokenBySession(sessionId, undefined, { gateKinds: APPROVAL_GATE_KINDS })) ?? undefined;
|
|
83
|
-
if (!
|
|
84
|
-
return { token, parked: false };
|
|
86
|
+
if (!token)
|
|
87
|
+
return { token, parked: false, rev: undefined };
|
|
85
88
|
const cp = await cs.get(token);
|
|
86
89
|
if (!cp)
|
|
87
|
-
return { token, parked: false };
|
|
90
|
+
return { token, parked: false, rev: undefined };
|
|
91
|
+
const rev = cp.rev ?? 0;
|
|
92
|
+
if (!parkSources)
|
|
93
|
+
return { token, parked: false, rev };
|
|
88
94
|
const origin = await findParkOrigin({
|
|
89
95
|
...(deps.backgroundAgentStore !== undefined ? { agentStore: deps.backgroundAgentStore } : {}),
|
|
90
96
|
...(deps.workflowAgentSessionIndex !== undefined && deps.workflowRunStore !== undefined
|
|
91
97
|
? { workflowIndex: deps.workflowAgentSessionIndex, workflowRunStore: deps.workflowRunStore }
|
|
92
98
|
: {}),
|
|
93
99
|
}, { token, scope: cp.scope, sessionId: cp.sessionId });
|
|
94
|
-
return { token, parked: origin !== undefined };
|
|
100
|
+
return { token, parked: origin !== undefined, rev };
|
|
95
101
|
}
|
|
96
102
|
catch {
|
|
97
|
-
return { token, parked: false };
|
|
103
|
+
return { token, parked: false, rev: undefined };
|
|
98
104
|
}
|
|
99
105
|
};
|
|
100
106
|
const storedBodyUnservable = async (sessionId) => {
|
|
@@ -105,11 +111,32 @@ export function createApprovalSweeps(ctx) {
|
|
|
105
111
|
return undefined;
|
|
106
112
|
return storedBodyReplayRefusal(read.ctx, deps.config);
|
|
107
113
|
};
|
|
114
|
+
const expirePolicyBlockedCard = async (cardToken, observedRev, sessionId, body) => {
|
|
115
|
+
let won;
|
|
116
|
+
try {
|
|
117
|
+
const cp = await cs.get(cardToken);
|
|
118
|
+
if (!cp)
|
|
119
|
+
return;
|
|
120
|
+
if (cp.status !== "pending" || (cp.rev ?? 0) !== observedRev) {
|
|
121
|
+
deps.logger?.warn?.("deny_sweep_policy_blocked_expire_skipped", { sessionId, skipReason: cp.status !== "pending" ? "not_pending" : "card_changed", ...blockedScalars(body) });
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
won = await cs.expire(cardToken, cp.scope);
|
|
125
|
+
}
|
|
126
|
+
catch (e) {
|
|
127
|
+
deps.logger?.warn?.("deny_sweep_policy_blocked_expire_failed", { sessionId, err: errText(e) });
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
blockedWarned.delete(cardToken);
|
|
131
|
+
if (won)
|
|
132
|
+
deps.metrics?.inc("deny_sweep_policy_blocked_expired_total");
|
|
133
|
+
deps.logger?.warn?.("deny_sweep_policy_blocked_expired", { sessionId, won, ...blockedScalars(body) });
|
|
134
|
+
};
|
|
108
135
|
let parkedSkipped = 0;
|
|
109
136
|
const replaySkipped = [];
|
|
110
137
|
const seenCards = new Set();
|
|
111
138
|
for (const { sessionId } of expired) {
|
|
112
|
-
const { token: cardToken, parked } = await cardOf(sessionId);
|
|
139
|
+
const { token: cardToken, parked, rev: cardRev } = await cardOf(sessionId);
|
|
113
140
|
if (cardToken !== undefined)
|
|
114
141
|
seenCards.add(cardToken);
|
|
115
142
|
if (parked) {
|
|
@@ -121,12 +148,22 @@ export function createApprovalSweeps(ctx) {
|
|
|
121
148
|
replaySkipped.push({ sessionId, kind: unservable.kind });
|
|
122
149
|
continue;
|
|
123
150
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
151
|
+
let out;
|
|
152
|
+
try {
|
|
153
|
+
out = await resumeCheckpoint(sessionId, "deny", "approval SLA expired — auto-denied", { decidedBy: "sla_timeout" });
|
|
154
|
+
}
|
|
155
|
+
catch (e) {
|
|
156
|
+
deps.logger?.warn?.("deny_sweep_resume_failed", { sessionId, err: errText(e) });
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (out.status < 400) {
|
|
160
|
+
if (cardToken !== undefined)
|
|
128
161
|
blockedWarned.delete(cardToken);
|
|
129
|
-
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
noteResumeBlocked(cardToken, sessionId, out.status, out.body);
|
|
165
|
+
if (isResumeBlockedByPolicy(out) && cardToken !== undefined && cardRev !== undefined)
|
|
166
|
+
await expirePolicyBlockedCard(cardToken, cardRev, sessionId, out.body);
|
|
130
167
|
}
|
|
131
168
|
for (const token of [...blockedWarned.keys()])
|
|
132
169
|
if (!seenCards.has(token))
|
|
@@ -144,10 +181,10 @@ export function createApprovalSweeps(ctx) {
|
|
|
144
181
|
async function redeemParkedAsk(req) {
|
|
145
182
|
const cs = deps.checkpointStore;
|
|
146
183
|
if (!cs) {
|
|
147
|
-
return { status: 404, body: {
|
|
184
|
+
return { status: 404, body: refusalBody({ errorCode: "not_found.approval", message: "approval not found" }) };
|
|
148
185
|
}
|
|
149
186
|
if (!hasServiceAuth(deps.config) && !deps.config.allowUnauthedWrites) {
|
|
150
|
-
return { status: 503, body: {
|
|
187
|
+
return { status: 503, body: refusalBody({ errorCode: "auth.service_token_required", message: "this worker requires a service auth token (set SERVICE_AUTH_TOKEN) before accepting task submissions" }) };
|
|
151
188
|
}
|
|
152
189
|
const refusal = admissionRefusalOf(readinessOf(deps), deps.servingLease?.(), "hitl-answer");
|
|
153
190
|
if (refusal !== undefined)
|
|
@@ -169,17 +206,17 @@ export function createApprovalSweeps(ctx) {
|
|
|
169
206
|
catch (e) {
|
|
170
207
|
if (!(await mayDiscloseRowError(cs, req.gateToken, { principal: req.principal, explicitOperator: false }))) {
|
|
171
208
|
deps.logger?.warn?.("parked_late_decision_unreadable_row_withheld", { askId: req.askId });
|
|
172
|
-
return { status: 404, body: {
|
|
209
|
+
return { status: 404, body: refusalBody({ errorCode: "not_found.approval", message: "approval not found" }) };
|
|
173
210
|
}
|
|
174
|
-
return { status: 409, body: {
|
|
211
|
+
return { status: 409, body: refusalBody({ errorCode: "conflict.checkpoint_unreadable", message: e instanceof Error ? e.message : String(e) }) };
|
|
175
212
|
}
|
|
176
213
|
if (!cp) {
|
|
177
|
-
return { status: 404, body: {
|
|
214
|
+
return { status: 404, body: refusalBody({ errorCode: "not_found.approval", message: "approval not found" }) };
|
|
178
215
|
}
|
|
179
216
|
const cpPrincipal = decodeCheckpointScope(cp.scope);
|
|
180
217
|
if (cpPrincipal !== undefined && cpPrincipal !== req.principal) {
|
|
181
218
|
deps.logger?.warn?.("parked_late_decision_scope_mismatch", { askId: req.askId });
|
|
182
|
-
return { status: 404, body: {
|
|
219
|
+
return { status: 404, body: refusalBody({ errorCode: "not_found.approval", message: "approval not found" }) };
|
|
183
220
|
}
|
|
184
221
|
const binding = {
|
|
185
222
|
checkpointToken: req.gateToken,
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **调用方信任档** —— 一份请求里「不经人答就生效」的规则表按谁的档准入:全仓唯一一张表({@link callerTrust})、唯一一只准入判({@link requestTrustRefusal})。
|
|
3
|
+
*
|
|
4
|
+
* ## 律
|
|
5
|
+
* 契约(settings-schema `SAFETY_MERGE_SPEC.permissions` 的成员阶梯)按**层的信任档**准入每个成员:`allow` 放宽免问面(`loosen`),要 `global`;`deny` / `ask` 只收紧,
|
|
6
|
+
* 文件层最低档 `local` 就收。wire 上没有层 —— 提交体是合并后的扁平结果,层只在合并端(cli 本地)存在;自报一个层 / 作者位不可验(引擎侧已拒立这种位)。所以本服务
|
|
7
|
+
* 的「层」= **这次请求验过的凭据**:档只由部署形 + 身份来源 + 登录令牌的 scope 角色推,调用方说什么都不改档。
|
|
8
|
+
*
|
|
9
|
+
* · 单用户 turnkey(`requirePrincipal !== true`,本仓 posture 判别式同一只)⇒ `global`:一台机一位超管;
|
|
10
|
+
* · 多租 · 部署的 service 凭据 ⇒ `global`:部署自己的凭据是运维档(契约给「启动方自己的行为」—— `SETTINGS_SOURCE_CONTRACT.flagSettings` —— 的档),持它的前端为自己转发的内容负责;
|
|
11
|
+
* 出示的 Bearer 就是 service 凭据时,哪怕它同时也被鉴权桥验成登录令牌,也按 service 凭据读(与凭据门派 `source` 同序:`dispatch.ts` 先认 service 凭据);
|
|
12
|
+
* · 多租 · 中心登录令牌,scope 角色 ≥ 契约 `domainWriteRole("governance")` ⇒ `global`:能写 scope 治理域(autonomy / commandPolicy / approvalRequire,全组织的免问面)
|
|
13
|
+
* 的角色,放宽自己这一 run 是它的子集;门槛读契约,不手抄角色词;
|
|
14
|
+
* · 多租 · 其余(登录令牌角色低于它 / 不带 / 认不出;直连门 principal 令牌只证身份不证角色;没有验过的凭据)⇒ `project`:契约给非运维档作者的仓控来源的档,
|
|
15
|
+
* 收紧成员照收、放宽成员不收 —— 没有可验的角色就落到这里(fail-closed)。
|
|
16
|
+
*
|
|
17
|
+
* **判的是哪些表**:请求体上不经人答就生效的规则表 —— `settings.permissions` 的每个成员桶(逐 `membersOf("permissions")`),以及请求**按名点用**(`userInvokedSkills`)的
|
|
18
|
+
* 自带技能的两只清单(引擎 `seedSkillGrants`:人点的名就是表态,不出 `Skill` 卡 ⇒ `allowedTools` 是 `allow` 成员的孪生、`disallowedTools` 是 `deny` 的孪生,键 → 成员一张表
|
|
19
|
+
* {@link SKILL_LIST_MEMBER})。没点用的自带技能不判:它的清单要经 `Skill` 卡上的人点头才生效 —— 那正是契约给低于门槛的放宽指定的同意出口。
|
|
20
|
+
* 准入只经契约导出:桶非空且 `!isAtLeast(档, memberAdmission(…).minTrust)` ⇒ 不准入;不按方向开特例(契约的层准入同样先判 floor)。
|
|
21
|
+
* 不准入 ⇒ 受理口 400,体与条目问题同形(`entry-problems.ts`:`field` + `detail.problems` + `total`),原因词 = 契约 `LAYER_ADMISSION_VERDICTS` 的 `below_trust_floor`,
|
|
22
|
+
* 另带 `detail.callerTrust`(这次请求的档)。响亮拒而不是静默丢:契约要求低于门槛的放宽不得静默吞,出路是同意(答卡)或运维的 allow 表。
|
|
23
|
+
*
|
|
24
|
+
* 读者(都读同一次求值,不各算一份):提交受理口(`admission.ts` `prepareSpec`,三提交面共用)与能力位 `settings.permissions.callerTrust`(`routes/capabilities.ts`)。
|
|
25
|
+
* 续跑腿不读本档:存储体在自己的受理口按当时的档准入过。
|
|
26
|
+
*/
|
|
27
|
+
import type { IncomingMessage } from "node:http";
|
|
28
|
+
import { type MinTrust, type SafetyMergeMember, type UserRole } from "@sema-agent/settings-schema";
|
|
29
|
+
import type { SkillSpec } from "@sema-agent/core";
|
|
30
|
+
import type { ServiceConfig } from "../config-types.js";
|
|
31
|
+
import { type EntryProblem } from "../entry-problems.js";
|
|
32
|
+
import type { ParsedTaskSettings } from "../task-settings.js";
|
|
33
|
+
/**
|
|
34
|
+
* 档是**怎么**推出来的(闭集;拒句按它说出路)。加一个词 ⇒ {@link basisClause} 的穷举 switch 编译红。
|
|
35
|
+
* · `single_user` —— 单用户 turnkey;
|
|
36
|
+
* · `service_credential` —— 多租 · 部署的 service 凭据;
|
|
37
|
+
* · `sign_in_role` —— 多租 · 登录令牌带契约阶梯里的角色(够不够档看 `trust`);
|
|
38
|
+
* · `sign_in_role_unrecognized` —— 多租 · 登录令牌不带角色,或角色不在契约阶梯里;
|
|
39
|
+
* · `identity_without_role` —— 多租 · 直连门 principal 令牌(只证身份);
|
|
40
|
+
* · `unverified` —— 多租 · 没有验过的凭据。
|
|
41
|
+
*/
|
|
42
|
+
export type CallerTrustBasis = "single_user" | "service_credential" | "sign_in_role" | "sign_in_role_unrecognized" | "identity_without_role" | "unverified";
|
|
43
|
+
/** 一次求值的读数。`role` 只在 `basis === "sign_in_role"` 时在场(契约阶梯里的词)。 */
|
|
44
|
+
export interface CallerTrust {
|
|
45
|
+
readonly trust: MinTrust;
|
|
46
|
+
readonly basis: CallerTrustBasis;
|
|
47
|
+
readonly role?: UserRole;
|
|
48
|
+
}
|
|
49
|
+
/** 档表(唯一一张)。规则全文见文件头。 */
|
|
50
|
+
export declare function callerTrust(req: IncomingMessage, config: ServiceConfig): CallerTrust;
|
|
51
|
+
/** 请求 `settings.permissions` 的成员桶 —— 按**契约的成员型**取:契约加成员而本仓的请求桶没有它 ⇒ 这里编译红(本仓必须先决定那只桶怎么上 wire)。 */
|
|
52
|
+
type RequestPermissionMembers = Pick<NonNullable<ParsedTaskSettings["permissions"]>, SafetyMergeMember<"permissions">>;
|
|
53
|
+
/** 准入判的输入:解析后的请求桶(`parseTaskSettings` 的读数 = `applyTaskSettings` 投进引擎的那一份)+ 请求自带技能 + 按名点用的技能名(两者受理门已判过形)。 */
|
|
54
|
+
export interface RequestRuleTables {
|
|
55
|
+
readonly permissions: RequestPermissionMembers | undefined;
|
|
56
|
+
readonly skills: readonly Pick<SkillSpec, "name" | "allowedTools" | "disallowedTools">[] | undefined;
|
|
57
|
+
readonly userInvokedSkills: readonly string[] | undefined;
|
|
58
|
+
}
|
|
59
|
+
/** 受理口拒体:句子 + 机读位(原样进 `sendError` 的 facts)。 */
|
|
60
|
+
export interface RequestTrustRefusal {
|
|
61
|
+
readonly message: string;
|
|
62
|
+
readonly facts: {
|
|
63
|
+
readonly field: string;
|
|
64
|
+
readonly detail: {
|
|
65
|
+
readonly problems: readonly EntryProblem[];
|
|
66
|
+
readonly total: number;
|
|
67
|
+
readonly callerTrust: MinTrust;
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* 请求里不经人答就生效的规则表的成员级准入(唯一一只判)。全部准入 ⇒ `undefined`;否则句子说扫描序里**第一张**不准入的表(坐标、档、floor、出路),
|
|
73
|
+
* 机读位把这一遍判下来的**全部**条目一次列全。空表什么也不改,不算。
|
|
74
|
+
*/
|
|
75
|
+
export declare function requestTrustRefusal(tables: RequestRuleTables, reading: CallerTrust): RequestTrustRefusal | undefined;
|
|
76
|
+
export {};
|
|
77
|
+
//# sourceMappingURL=caller-trust.d.ts.map
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { domainWriteRole, isAtLeast, isRole, memberAdmission, membersOf, roleAtLeast, USER_ROLES, } from "@sema-agent/settings-schema";
|
|
2
|
+
import { entryProblem, entryProblemsFacts } from "../entry-problems.js";
|
|
3
|
+
import { ssoVerifiedRole } from "../security.js";
|
|
4
|
+
import { systemFor, verifiedIdentitySource } from "./principal-gate.js";
|
|
5
|
+
export function callerTrust(req, config) {
|
|
6
|
+
if (config.requirePrincipal !== true)
|
|
7
|
+
return { trust: "global", basis: "single_user" };
|
|
8
|
+
const source = verifiedIdentitySource(req, config);
|
|
9
|
+
switch (source) {
|
|
10
|
+
case "service_token":
|
|
11
|
+
return { trust: "global", basis: "service_credential" };
|
|
12
|
+
case "sso": {
|
|
13
|
+
if (systemFor(req, config) !== undefined)
|
|
14
|
+
return { trust: "global", basis: "service_credential" };
|
|
15
|
+
const role = ssoVerifiedRole(req);
|
|
16
|
+
if (role === undefined || !isRole(role))
|
|
17
|
+
return { trust: "project", basis: "sign_in_role_unrecognized" };
|
|
18
|
+
return { trust: roleAtLeast(role, domainWriteRole("governance")) ? "global" : "project", basis: "sign_in_role", role };
|
|
19
|
+
}
|
|
20
|
+
case "direct_door_jwt":
|
|
21
|
+
return { trust: "project", basis: "identity_without_role" };
|
|
22
|
+
case undefined:
|
|
23
|
+
return { trust: "project", basis: "unverified" };
|
|
24
|
+
default: {
|
|
25
|
+
const unknownSource = source;
|
|
26
|
+
throw new Error(`callerTrust: identity source ${String(unknownSource)} has no trust row`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const TRUST_REFUSAL_CODE = "request.field_invalid";
|
|
31
|
+
const BELOW_TRUST_FLOOR = "below_trust_floor";
|
|
32
|
+
const SKILL_LIST_MEMBER = { allowedTools: "allow", disallowedTools: "deny" };
|
|
33
|
+
function effectiveRuleTables(t) {
|
|
34
|
+
const out = [];
|
|
35
|
+
if (t.permissions !== undefined) {
|
|
36
|
+
for (const member of membersOf("permissions")) {
|
|
37
|
+
const entries = t.permissions[member];
|
|
38
|
+
if (entries !== undefined)
|
|
39
|
+
out.push({ member, field: `settings.permissions.${member}`, entries, source: "settings" });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (t.skills !== undefined && t.userInvokedSkills !== undefined) {
|
|
43
|
+
const named = new Set();
|
|
44
|
+
for (const name of t.userInvokedSkills) {
|
|
45
|
+
if (named.has(name))
|
|
46
|
+
continue;
|
|
47
|
+
named.add(name);
|
|
48
|
+
const at = t.skills.findIndex((skill) => skill.name === name);
|
|
49
|
+
const skill = t.skills[at];
|
|
50
|
+
if (skill === undefined)
|
|
51
|
+
continue;
|
|
52
|
+
for (const key of ["allowedTools", "disallowedTools"]) {
|
|
53
|
+
const entries = skill[key];
|
|
54
|
+
if (entries !== undefined)
|
|
55
|
+
out.push({ member: SKILL_LIST_MEMBER[key], field: `skills[${at}].${key}`, entries, source: "invoked_skill" });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
61
|
+
export function requestTrustRefusal(tables, reading) {
|
|
62
|
+
const problems = [];
|
|
63
|
+
let first;
|
|
64
|
+
for (const table of effectiveRuleTables(tables)) {
|
|
65
|
+
if (table.entries.length === 0)
|
|
66
|
+
continue;
|
|
67
|
+
const admission = memberAdmission("permissions", table.member);
|
|
68
|
+
if (isAtLeast(reading.trust, admission.minTrust))
|
|
69
|
+
continue;
|
|
70
|
+
first ??= { table, minTrust: admission.minTrust, loosens: directionLoosens(admission.direction) };
|
|
71
|
+
for (const entry of table.entries)
|
|
72
|
+
problems.push(entryProblem(TRUST_REFUSAL_CODE, table.field, entry, BELOW_TRUST_FLOOR));
|
|
73
|
+
}
|
|
74
|
+
if (first === undefined)
|
|
75
|
+
return undefined;
|
|
76
|
+
const facts = entryProblemsFacts(problems);
|
|
77
|
+
return {
|
|
78
|
+
message: `${first.table.field} needs trust "${first.minTrust}" (${whatItDoes(first.table.source, first.loosens)}) and this request is admitted at "${reading.trust}": ${basisClause(reading)}. Below that trust these rules are not applied — ${outletOf(first.table.source, first.loosens)}.`,
|
|
79
|
+
facts: { field: facts.field, detail: { ...facts.detail, callerTrust: reading.trust } },
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function whatItDoes(source, loosens) {
|
|
83
|
+
const effect = loosens ? "lets matching calls run without a person being asked" : "changes which calls are asked or refused";
|
|
84
|
+
switch (source) {
|
|
85
|
+
case "settings":
|
|
86
|
+
return `it ${effect}`;
|
|
87
|
+
case "invoked_skill":
|
|
88
|
+
return `the request invokes this skill by name, so the list applies with no approval card and ${effect}`;
|
|
89
|
+
default: {
|
|
90
|
+
const unknownSource = source;
|
|
91
|
+
throw new Error(`requestTrustRefusal: rule table source ${String(unknownSource)} unknown`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function outletOf(source, loosens) {
|
|
96
|
+
if (!loosens)
|
|
97
|
+
return "remove them, or ask the operator to set them deployment-wide";
|
|
98
|
+
switch (source) {
|
|
99
|
+
case "settings":
|
|
100
|
+
return "remove them and answer the approval questions as they come, or ask the operator to put them in APPROVAL_GRANT";
|
|
101
|
+
case "invoked_skill":
|
|
102
|
+
return "drop the skill from userInvokedSkills (the model can still activate it behind its approval card), remove the list, or ask the operator to put the rules in APPROVAL_GRANT";
|
|
103
|
+
default: {
|
|
104
|
+
const unknownSource = source;
|
|
105
|
+
throw new Error(`requestTrustRefusal: rule table source ${String(unknownSource)} unknown`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function directionLoosens(direction) {
|
|
110
|
+
switch (direction) {
|
|
111
|
+
case "loosen":
|
|
112
|
+
return true;
|
|
113
|
+
case "tighten":
|
|
114
|
+
return false;
|
|
115
|
+
default: {
|
|
116
|
+
const unknownDirection = direction;
|
|
117
|
+
throw new Error(`requestTrustRefusal: member direction ${String(unknownDirection)} unknown`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function basisClause(reading) {
|
|
122
|
+
const governanceRole = domainWriteRole("governance");
|
|
123
|
+
const reach = `the deployment's service credential, or a registry sign-in token whose role in its active scope is "${governanceRole}" or higher (the role that may write the scope's governance), is admitted at "global"`;
|
|
124
|
+
switch (reading.basis) {
|
|
125
|
+
case "single_user":
|
|
126
|
+
return `this single-user deployment admits every caller at "global"`;
|
|
127
|
+
case "service_credential":
|
|
128
|
+
return `the deployment's service credential is admitted at "global"`;
|
|
129
|
+
case "sign_in_role":
|
|
130
|
+
return `the registry sign-in token's role in its active scope is "${reading.role ?? ""}" — ${reach}; a scope admin can raise the role`;
|
|
131
|
+
case "sign_in_role_unrecognized":
|
|
132
|
+
return `the registry sign-in token carries no role this deployment recognizes (${USER_ROLES.join(" < ")}) — ${reach}`;
|
|
133
|
+
case "identity_without_role":
|
|
134
|
+
return `a direct-door principal token proves who the caller is, not a role — ${reach}`;
|
|
135
|
+
case "unverified":
|
|
136
|
+
return `the request carries no credential this deployment verifies — ${reach}`;
|
|
137
|
+
default: {
|
|
138
|
+
const unknownBasis = reading.basis;
|
|
139
|
+
throw new Error(`requestTrustRefusal: trust basis ${String(unknownBasis)} unknown`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=caller-trust.js.map
|
|
@@ -101,15 +101,9 @@ export declare function permissionModeReadingStated(ctx: CheckpointCtx): boolean
|
|
|
101
101
|
/**
|
|
102
102
|
* 第三问的拒答复 —— 码与落点同 allow 记号那一问(`checkpoint.unsupported_version`,409;卡仍 pending 时体上的 `retriable:true` 由 {@link resumeRefusalWithCard} 追加,本铸点不带)。
|
|
103
103
|
* 句子说原因(更早的版本在每次续跑重钳运维的 AUTONOMY、没把它记进任务;本版只在提交时记一次 ⇒ 续跑会按任务提交时的原词跑)与两条出路。
|
|
104
|
-
* 🔴 形是给冻结门看的:`status: <三位数>, body: {
|
|
104
|
+
* 🔴 形是给冻结门看的:`status: <三位数>, body: refusalBody({ errorCode: "<码>", message: "<整句双引号字面>"`,中间只许有空白。
|
|
105
105
|
*/
|
|
106
|
-
export declare function permissionModeReadingRefusal():
|
|
107
|
-
status: number;
|
|
108
|
-
body: {
|
|
109
|
-
error: string;
|
|
110
|
-
errorCode: string;
|
|
111
|
-
};
|
|
112
|
-
};
|
|
106
|
+
export declare function permissionModeReadingRefusal(): ResumeRefusalAnswer;
|
|
113
107
|
/**
|
|
114
108
|
* 本行能否被按当前读法重放:体里 `allow` 在生效而行上的记号不是当前这一个词 ⇒ **不能**(`true` = 拒)。
|
|
115
109
|
* 记号缺席、是旧词(`"statement"`)或认不得(手改 / 将来的写者)⇒ 同样拒:只有当前这个词是「按当前读法写下」的证据。
|
|
@@ -122,15 +116,9 @@ export declare function allowListReadingEraOf(ctx: CheckpointCtx): AllowListRead
|
|
|
122
116
|
* 拒在认领之前,所以没有 `taskId`(行没抢到就不造键)。句子按这一行是哪一代写的分两句(闭集穷举,加一代 ⇒ 编译红):说原因(这条任务停在哪种读法上、
|
|
123
117
|
* 照当前读法续跑会放宽什么)与出路(作为新任务重提,仍停驻的先取消)。
|
|
124
118
|
*
|
|
125
|
-
* 🔴 形是给冻结门看的(`api-error-text-freeze` 的 resume 家族收集器):`status: <三位数>, body: {
|
|
119
|
+
* 🔴 形是给冻结门看的(`api-error-text-freeze` 的 resume 家族收集器):`status: <三位数>, body: refusalBody({ errorCode: "<码>", message: "<整句双引号字面>"`,中间只许有空白。
|
|
126
120
|
*/
|
|
127
|
-
export declare function allowListReadingRefusal(era: AllowListReadingEra):
|
|
128
|
-
status: number;
|
|
129
|
-
body: {
|
|
130
|
-
error: string;
|
|
131
|
-
errorCode: string;
|
|
132
|
-
};
|
|
133
|
-
};
|
|
121
|
+
export declare function allowListReadingRefusal(era: AllowListReadingEra): ResumeRefusalAnswer;
|
|
134
122
|
/**
|
|
135
123
|
* 一条停驻行的持久化体在本版上**不能**重放时,是哪一条规则拒的(闭集;加一条 ⇒ {@link storedBodyReplayAnswer} 的 switch 编译红)。
|
|
136
124
|
* 答 `checkpoint.unsupported_version` 的两臂随带这一行的写者版本(续跑上下文的 `writerVersion`,缺席 = 本版之前写下的行)—— 那一码的两版本键由它铸({@link buildCheckpointVersionKeys})。
|
|
@@ -172,11 +160,23 @@ export interface ResumeRefusalAnswer {
|
|
|
172
160
|
* `blockedBy` = 内层的码,句子 = 前缀 + 内层的句子,内层的机读事实原样随体。{@link storedBodyReplayAnswer} 的工具名清单那一臂与
|
|
173
161
|
* `resume-legs.ts` 的折叠口都调它 —— 句子与码只在这里写一次(冻结门 `api-error-text-freeze` 的模板骨架表锚这一处)。
|
|
174
162
|
*
|
|
163
|
+
* 🔴 **句尾点名出口**:这一拒不消费卡(卡仍 pending、run 行留停驻),被挡在一张 deny 不掉的卡前面的调用方需要知道怎么收场 ——
|
|
164
|
+
* `POST /v1/runs/:id/cancel` 对停驻的 run 直接结算(CAS 过期该卡再终局 run 行,不跑模型轮)。句子写「若仍停驻」而不是随卡有无换话:有卡 / 无卡(终态 run 的 steer 在铸唤醒卡之前拒)
|
|
165
|
+
* 两种处境除 `retriable` 外逐字同体(同判同答),与另两类存量体拒(allow 读法 / 模式词读法)句子里的出口是同一个端点、同一种写法。
|
|
166
|
+
*
|
|
175
167
|
* 本铸点**不带** `retriable`:那是调用点的事实(有没有一张待批卡仍在等),由 {@link resumeRefusalWithCard} 一处追加 —— 续跑入口(卡仍 pending、run 行留 `suspended`)
|
|
176
168
|
* 带 `retriable:true`,终态 run 的 steer 在铸唤醒卡之前拒(没有卡)不带。键集 = `errorCode` · `error` · `blockedBy` · (`field` · `reason` · 其余 `extra`,有才在)
|
|
177
169
|
* · (`retriable`,卡在才在)。
|
|
178
170
|
*/
|
|
179
171
|
export declare function resumeBlockedByPolicyAnswer(inner: Refusal): ResumeRefusalAnswer;
|
|
172
|
+
/**
|
|
173
|
+
* 一次续跑的答复是不是 {@link resumeBlockedByPolicyAnswer} 铸的那一只(409 `resume_blocked_by_policy`)—— SLA 拒扫腿据此区分「同一份字节在本版永远被拒」与别的 ≥400
|
|
174
|
+
* (后者可能是瞬时的,照旧逐拍重试)。码的写端与读端住在同一个模块,读端不在别处第二次手拼这个字面。
|
|
175
|
+
*/
|
|
176
|
+
export declare function isResumeBlockedByPolicy(answer: {
|
|
177
|
+
readonly status: number;
|
|
178
|
+
readonly body: unknown;
|
|
179
|
+
}): boolean;
|
|
180
180
|
/**
|
|
181
181
|
* 🔴 **三类续跑拒体上 `retriable` 的唯一写点**(本服务重放存量体前的拒、准入策略折叠、引擎拒的续跑):卡仍 pending(`cardPending`)⇒ 体尾追加 `retriable: true`;
|
|
182
182
|
* 否则体原样(键缺席)。这三类拒上规则一条:`retriable` 在场 ⇔ 有一张待批卡仍在等、run 行留 `suspended`(**不是**「原样重试会成功」—— 契约附录 A.1 注那一个意思)。
|
|
@@ -32,10 +32,10 @@ export function permissionModeReadingStated(ctx) {
|
|
|
32
32
|
export function permissionModeReadingRefusal() {
|
|
33
33
|
return {
|
|
34
34
|
status: 409,
|
|
35
|
-
body: {
|
|
36
|
-
error: "this task was suspended by an earlier server version that re-applied this deployment's AUTONOMY on every resume instead of recording it in the task; this version records the operator's plan floor once, when a task is submitted, so continuing would run this task under the permission mode it was submitted with rather than the one it ran under. Nothing was decided and nothing was resumed, and retrying will be refused the same way: submit the work again as a new task, and if this task is still suspended cancel it first (POST /v1/runs/:id/cancel); to let it finish with read-only hands instead, set AUTONOMY=read-only until it does.",
|
|
35
|
+
body: refusalBody({
|
|
37
36
|
errorCode: "checkpoint.unsupported_version",
|
|
38
|
-
|
|
37
|
+
message: "this task was suspended by an earlier server version that re-applied this deployment's AUTONOMY on every resume instead of recording it in the task; this version records the operator's plan floor once, when a task is submitted, so continuing would run this task under the permission mode it was submitted with rather than the one it ran under. Nothing was decided and nothing was resumed, and retrying will be refused the same way: submit the work again as a new task, and if this task is still suspended cancel it first (POST /v1/runs/:id/cancel); to let it finish with read-only hands instead, set AUTONOMY=read-only until it does.",
|
|
38
|
+
}),
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
41
|
export function allowListReadingUnstated(ctx) {
|
|
@@ -53,18 +53,18 @@ export function allowListReadingRefusal(era) {
|
|
|
53
53
|
case "list":
|
|
54
54
|
return {
|
|
55
55
|
status: 409,
|
|
56
|
-
body: {
|
|
57
|
-
error: "this task was suspended by an earlier server version that did not record how it read settings.permissions.allow (7.114.0 and earlier read it as a whitelist: every tool not on the list was refused); this version hands that list to the engine's allow layer, where each entry is a standing yes for the calls it covers, so continuing would give the task more tools than it was submitted with. Nothing was decided and nothing was resumed, and retrying will be refused the same way: submit the work again as a new task, and if this task is still suspended cancel it first (POST /v1/runs/:id/cancel). To allow only certain tools, use the session rules' toolAllow (PUT /v1/sessions/:id/policy) or the request's excludeTools.",
|
|
56
|
+
body: refusalBody({
|
|
58
57
|
errorCode: "checkpoint.unsupported_version",
|
|
59
|
-
|
|
58
|
+
message: "this task was suspended by an earlier server version that did not record how it read settings.permissions.allow (7.114.0 and earlier read it as a whitelist: every tool not on the list was refused); this version hands that list to the engine's allow layer, where each entry is a standing yes for the calls it covers, so continuing would give the task more tools than it was submitted with. Nothing was decided and nothing was resumed, and retrying will be refused the same way: submit the work again as a new task, and if this task is still suspended cancel it first (POST /v1/runs/:id/cancel). To allow only certain tools, use the session rules' toolAllow (PUT /v1/sessions/:id/policy) or the request's excludeTools.",
|
|
59
|
+
}),
|
|
60
60
|
};
|
|
61
61
|
case "statement":
|
|
62
62
|
return {
|
|
63
63
|
status: 409,
|
|
64
|
-
body: {
|
|
65
|
-
error: "this task was suspended by an earlier server version (7.115.0 to 7.116.x) that read settings.permissions.allow as a statement that the named tools are allowed, which answered only the question nobody else had answered and never cleared an ordinary question such as a file write or a shell command; this version hands that list to the engine's allow layer, where an entry also clears those ordinary questions, so continuing would run calls without asking that the task was submitted to ask about. Nothing was decided and nothing was resumed, and retrying will be refused the same way: submit the work again as a new task, and if this task is still suspended cancel it first (POST /v1/runs/:id/cancel).",
|
|
64
|
+
body: refusalBody({
|
|
66
65
|
errorCode: "checkpoint.unsupported_version",
|
|
67
|
-
|
|
66
|
+
message: "this task was suspended by an earlier server version (7.115.0 to 7.116.x) that read settings.permissions.allow as a statement that the named tools are allowed, which answered only the question nobody else had answered and never cleared an ordinary question such as a file write or a shell command; this version hands that list to the engine's allow layer, where an entry also clears those ordinary questions, so continuing would run calls without asking that the task was submitted to ask about. Nothing was decided and nothing was resumed, and retrying will be refused the same way: submit the work again as a new task, and if this task is still suspended cancel it first (POST /v1/runs/:id/cancel).",
|
|
67
|
+
}),
|
|
68
68
|
};
|
|
69
69
|
default: {
|
|
70
70
|
const unhandled = era;
|
|
@@ -78,9 +78,12 @@ export function buildCheckpointVersionKeys(writerVersion, readerVersion) {
|
|
|
78
78
|
export function resumeBlockedByPolicyAnswer(inner) {
|
|
79
79
|
return {
|
|
80
80
|
status: 409,
|
|
81
|
-
body: refusalBody({ errorCode: "resume_blocked_by_policy", message: `resume blocked by an admission policy change: ${inner.message}`, facts: { blockedBy: inner.errorCode, ...inner.facts } }),
|
|
81
|
+
body: refusalBody({ errorCode: "resume_blocked_by_policy", message: `resume blocked by an admission policy change: ${inner.message} — nothing was resumed; if the task is still suspended on its approval, cancel it (POST /v1/runs/:id/cancel) to end it without another model turn`, facts: { blockedBy: inner.errorCode, ...inner.facts } }),
|
|
82
82
|
};
|
|
83
83
|
}
|
|
84
|
+
export function isResumeBlockedByPolicy(answer) {
|
|
85
|
+
return answer.status === 409 && typeof answer.body === "object" && answer.body !== null && answer.body.errorCode === "resume_blocked_by_policy";
|
|
86
|
+
}
|
|
84
87
|
export function resumeRefusalWithCard(answer, cardPending) {
|
|
85
88
|
return cardPending ? { status: answer.status, body: { ...answer.body, retriable: true } } : answer;
|
|
86
89
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 一条提交腿**刚认领**的 run 在起跑之前就收场 ——
|
|
3
|
-
* (`device-placement.ts` `commitPlacement`)
|
|
2
|
+
* 一条提交腿**刚认领**的 run 在起跑之前就收场 —— 撤掉本请求建的东西的**唯一**写法。三个调用点:device 落点拒
|
|
3
|
+
* (`device-placement.ts` `commitPlacement`)、续跑输入写不进(`submit-ctx.ts` `persistSubmitCtx`)与 workflow 决定行没落成
|
|
4
|
+
* (`durable-run-leg.ts` 的 fresh 投递腿;此刻认领可能已被收割归了别人 —— 两步都按这一跑自己的 taskId,碰不到别人的认领)。
|
|
4
5
|
*
|
|
5
6
|
* 撤两步,**各自独立**(一步失败不拦另一步,残余最小):
|
|
6
7
|
* · 终结这条 run 行(`setTerminal` 是按 taskId 的 CAS,首写者赢 ⇒ 重复调用是 no-op)—— 随之释放它的 `task_active` 认领;
|
|
7
8
|
* · 放开它占的提交幂等键(拒绝答复邀请重试;同键重试必须真跑,不是回放这次拒绝 —— `RunStore.releaseIdemKey`)。
|
|
8
|
-
* 撤不掉不致命、但要响:每一步的失败各交给调用方按自己的名字留痕(`onFailure`)。终结没落下 ⇒
|
|
9
|
+
* 撤不掉不致命、但要响:每一步的失败各交给调用方按自己的名字留痕(`onFailure`)。终结没落下 ⇒ 起跑前的心跳随调用腿收场停下(或从没起过),`reapStale`
|
|
9
10
|
* 在 `REAP_RUN_STALE_SEC` 之后把这条行翻 failed 并释放会话;幂等键没放开 ⇒ 同键重提回放这条行的回执(两形都是修前每一次的样子)。
|
|
10
11
|
*/
|
|
11
12
|
import type { RunStore } from "../plugins/store-backend.js";
|
|
@@ -53,6 +53,7 @@ import { type DeviceOwner } from "../device-store.js";
|
|
|
53
53
|
import { type DeviceEnrollment, type DeviceRejection } from "../device-enrollment.js";
|
|
54
54
|
import type { ServiceConfig } from "../config.js";
|
|
55
55
|
import type { RunStore } from "../plugins/store-backend.js";
|
|
56
|
+
import { type RefusalBody } from "./refusal-body.js";
|
|
56
57
|
/**
|
|
57
58
|
* 调用方 device 域的**唯一铸造点**。`undefined` = 判不出域(无 principal,或凭据不带 tenant claim)——
|
|
58
59
|
* 消费点必须响亮拒,禁静默空视图。
|
|
@@ -170,8 +171,5 @@ export declare function placementWriteDeadlineMs(runStaleSec: number | undefined
|
|
|
170
171
|
*/
|
|
171
172
|
export declare function commitPlacement(ctx: PlacementCommitCtx, runStore: RunStore, taskId: string, intent: DevicePlacementIntent | undefined): Promise<DeviceRejection | null>;
|
|
172
173
|
/** 拒绝的**唯一** wire 体铸点(四个消费点各自用自己协议的发送姿势,但体的键集只有这一份)。 */
|
|
173
|
-
export declare function devicePlacementErrorBody(rej: DeviceRejection):
|
|
174
|
-
error: string;
|
|
175
|
-
errorCode: string;
|
|
176
|
-
};
|
|
174
|
+
export declare function devicePlacementErrorBody(rej: DeviceRejection): RefusalBody;
|
|
177
175
|
//# sourceMappingURL=device-placement.d.ts.map
|
|
@@ -3,6 +3,7 @@ import { gatedPrincipal } from "./principal-gate.js";
|
|
|
3
3
|
import { DEVICE_ERROR_CODES, assertDeviceIdShape, } from "../device-store.js";
|
|
4
4
|
import { buildDeviceRejection } from "../device-enrollment.js";
|
|
5
5
|
import { releaseClaimedRun } from "./claimed-run.js";
|
|
6
|
+
import { refusalBody } from "./refusal-body.js";
|
|
6
7
|
export function deviceOwnerDomainOf(req, config) {
|
|
7
8
|
const subject = gatedPrincipal(req, config);
|
|
8
9
|
if (subject === undefined)
|
|
@@ -139,6 +140,6 @@ export async function commitPlacement(ctx, runStore, taskId, intent) {
|
|
|
139
140
|
return rejection;
|
|
140
141
|
}
|
|
141
142
|
export function devicePlacementErrorBody(rej) {
|
|
142
|
-
return {
|
|
143
|
+
return refusalBody({ errorCode: rej.code, message: rej.message });
|
|
143
144
|
}
|
|
144
145
|
//# sourceMappingURL=device-placement.js.map
|
package/dist/http/dispatch.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { setSsoPrincipal, ssoVerifiedPrincipal, setSsoScope, isDestructiveSessionWrite, PRINCIPAL_TOKEN_HEADER, APPROVAL_MAC_HEADER, APPROVAL_MAC_KID_HEADER } from "../security.js";
|
|
2
|
+
import { setSsoPrincipal, ssoVerifiedPrincipal, setSsoScope, setSsoRole, isDestructiveSessionWrite, PRINCIPAL_TOKEN_HEADER, APPROVAL_MAC_HEADER, APPROVAL_MAC_KID_HEADER } from "../security.js";
|
|
3
3
|
import { publicStoreProbeError } from "../store-live-probe.js";
|
|
4
4
|
import { looksLikeJwt } from "@sema-agent/settings-schema/api/auth-bridge";
|
|
5
5
|
import { ROUTE_METHOD_ORDER } from "./route-ctx.js";
|
|
6
6
|
import { READ_CREDENTIAL_ROUTES, PUBLIC_ROUTES, POST_CREDENTIAL_ROUTES, routeLabel, isBillableSubmitPath, billableKindOf, isCredentialGatedRewrite, isMethodDispatchedSubmitPath, lookupRoute, isProbePath, isSubscriptionRequest } from "./route-table.js";
|
|
7
7
|
import { sendJson, sendError, isSseResponse, endSseWithError } from "./send.js";
|
|
8
|
-
import { admissionRefusalOf, readinessOf, healthReadinessKeys, sendReadyz, sendBillableRefusal, billableRefusalOf } from "./readiness.js";
|
|
8
|
+
import { admissionRefusalOf, readinessOf, healthReadinessKeys, sendReadyz, sendBillableRefusal, billableRefusalOf, billableRefusalFrame } from "./readiness.js";
|
|
9
9
|
import { ENGINE_SIGNAL_KEY, buildEngineSignal, fleetMembershipKeys, healthConfigLedgerKeys, healthDrainingKeys, mintEngineSignal, restartActionable } from "../fleet/engine-signal.js";
|
|
10
10
|
import { buildHealthSelfReport } from "./health-self-report.js";
|
|
11
11
|
import { authorized, systemFor, gatedPrincipal, sendPrincipalRefusal, sendUnauthorized, verifiedIdentitySource, hasServiceAuth, devOpenDeployment } from "./principal-gate.js";
|
|
@@ -51,10 +51,10 @@ export function createDispatch(ctx) {
|
|
|
51
51
|
};
|
|
52
52
|
deps.drainState.lastAttachEndedAt = () => lastAttachEndedAt;
|
|
53
53
|
deps.drainState.endSubscriptions = () => {
|
|
54
|
-
const
|
|
54
|
+
const frame = billableRefusalFrame(billableRefusalOf("draining"));
|
|
55
55
|
for (const r of [...openSubscriptions])
|
|
56
56
|
if (isSseResponse(r))
|
|
57
|
-
endSseWithError(r,
|
|
57
|
+
endSseWithError(r, frame);
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
60
|
async function dispatchSegment(rows, req, res, url, search, method, allow, ctx, admit) {
|
|
@@ -159,6 +159,10 @@ export function createDispatch(ctx) {
|
|
|
159
159
|
const credential = deps.centerCredentialState?.();
|
|
160
160
|
return credential ? { centerCredential: credential } : {};
|
|
161
161
|
})(),
|
|
162
|
+
...(() => {
|
|
163
|
+
const identityNotice = deps.centerIdentityNoticeState?.();
|
|
164
|
+
return identityNotice ? { centerIdentityNotice: identityNotice } : {};
|
|
165
|
+
})(),
|
|
162
166
|
...(() => {
|
|
163
167
|
const modelRefusals = deps.modelRefusalsState?.();
|
|
164
168
|
return modelRefusals ? { modelRefusals } : {};
|
|
@@ -214,6 +218,8 @@ export function createDispatch(ctx) {
|
|
|
214
218
|
req.headers[deps.config.principalHeader.toLowerCase()] = v.identity.principal;
|
|
215
219
|
if (v.identity.scope)
|
|
216
220
|
setSsoScope(req, v.identity.scope);
|
|
221
|
+
if (v.identity.role)
|
|
222
|
+
setSsoRole(req, v.identity.role);
|
|
217
223
|
deps.logger?.info?.("auth_bridge_principal_accepted", { principal: v.identity.principal, ...(v.identity.scope ? { scope: v.identity.scope } : {}) });
|
|
218
224
|
}
|
|
219
225
|
}
|