@sema-agent/server 7.148.0 → 7.150.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 +551 -9
- package/README.md +17 -2
- package/README.zh-CN.md +5 -1
- package/USAGE.md +126 -68
- package/dist/adoption/wire.d.ts +3 -3
- package/dist/approval-card.d.ts +33 -33
- package/dist/approval-gate-currency.d.ts +1 -1
- package/dist/approval-leg-origin.d.ts +33 -0
- package/dist/approval-leg-origin.js +4 -0
- package/dist/audit.d.ts +1 -1
- package/dist/audit.js +2 -2
- package/dist/auth-bridge.d.ts +9 -1
- package/dist/auth-bridge.js +4 -4
- package/dist/auth-keys.d.ts +10 -1
- package/dist/auth-keys.js +8 -0
- package/dist/background-session-reap.d.ts +1 -1
- package/dist/boot/deferred-sandbox-path-env.d.ts +5 -3
- package/dist/boot/deferred-sandbox-path-env.js +1 -2
- package/dist/boot/execution-env.js +10 -11
- package/dist/boot/execution-lane.d.ts +6 -4
- package/dist/boot/execution-lane.js +3 -1
- package/dist/boot/org-delivery.d.ts +3 -2
- package/dist/boot/parked-revive-gate.d.ts +5 -5
- package/dist/boot/parked-revive-gate.js +2 -3
- package/dist/boot/resolve-spec.d.ts +4 -5
- package/dist/boot/resolve-spec.js +66 -28
- package/dist/boot/retention-lane.d.ts +1 -1
- package/dist/boot/runner-deps.js +3 -1
- package/dist/boot/session-faces.js +21 -21
- package/dist/boot/stage-01-config.d.ts +1 -0
- package/dist/boot/stage-01-config.js +4 -3
- package/dist/boot/stage-02-web-admin-pull.d.ts +1 -0
- package/dist/boot/stage-02-web-admin-pull.js +10 -2
- package/dist/boot/stage-03-store-backend.d.ts +1 -0
- package/dist/boot/stage-03-store-backend.js +4 -4
- package/dist/boot/stage-04-stores.d.ts +1 -0
- package/dist/boot/stage-05-execution-env.d.ts +1 -0
- package/dist/boot/stage-06-runners.d.ts +2 -1
- package/dist/boot/stage-06-runners.js +1 -2
- package/dist/boot/stage-07-capability-layer.d.ts +2 -1
- package/dist/boot/stage-07-capability-layer.js +1 -1
- package/dist/boot/stage-08-reapers.d.ts +2 -1
- package/dist/boot/stage-09-leader.d.ts +2 -1
- package/dist/boot/stage-10-http-server.d.ts +35 -2
- package/dist/boot/stage-10-http-server.js +9 -4
- package/dist/boot/stores.d.ts +3 -0
- package/dist/boot/stores.js +22 -19
- package/dist/boot/web-admin.d.ts +1 -1
- package/dist/capabilities/tool-defer.d.ts +1 -1
- package/dist/capability-facts.d.ts +70 -60
- package/dist/capability-facts.js +45 -49
- package/dist/card-settle-stored-scope.d.ts +56 -0
- package/dist/card-settle-stored-scope.js +27 -0
- package/dist/card-void.d.ts +7 -12
- package/dist/card-void.js +2 -7
- package/dist/config-catalog.d.ts +12 -0
- package/dist/config-catalog.js +37 -33
- package/dist/config-invariants.d.ts +5 -4
- package/dist/config-invariants.js +4 -4
- package/dist/config-types.d.ts +49 -16
- package/dist/config.d.ts +6 -5
- package/dist/config.js +35 -24
- package/dist/context-window-notice.d.ts +31 -0
- package/dist/context-window-notice.js +28 -0
- package/dist/deployment-governance.d.ts +7 -28
- package/dist/deployment-governance.js +1 -14
- package/dist/deployment-shape.d.ts +20 -11
- package/dist/deployment-shape.js +1 -0
- package/dist/device-ws-hub.d.ts +1 -0
- package/dist/device-ws-hub.js +64 -28
- package/dist/device-ws-protocol.d.ts +20 -6
- package/dist/device-ws-protocol.js +9 -2
- package/dist/engine-identity.d.ts +7 -1
- package/dist/engine-identity.js +5 -0
- package/dist/env-name-allowlist-knobs.d.ts +6 -5
- package/dist/env-name-allowlist-knobs.js +5 -0
- package/dist/execution-lane-caps.d.ts +20 -5
- package/dist/execution-lane-caps.js +18 -4
- package/dist/field-conflict.d.ts +2 -2
- package/dist/fleet/engine-signal.d.ts +14 -1
- package/dist/fleet/engine-signal.js +5 -1
- package/dist/fleet/fleet-reconciler.d.ts +1 -1
- package/dist/fleet/serving-lease.d.ts +18 -3
- package/dist/fleet/serving-lease.js +81 -35
- package/dist/fleet-client.d.ts +13 -11
- package/dist/fleet-client.js +53 -33
- package/dist/history-anchor.js +1 -1
- package/dist/hitl-delivery.d.ts +1 -3
- package/dist/hitl-delivery.js +1 -1
- package/dist/hooks/command-spawn.d.ts +7 -2
- package/dist/hooks/command-spawn.js +13 -6
- package/dist/hooks/hook-runner.d.ts +5 -0
- package/dist/hooks/hook-runner.js +11 -7
- package/dist/http/active-run-conflict.d.ts +3 -7
- package/dist/http/active-run-conflict.js +2 -2
- package/dist/http/admission.js +5 -5
- package/dist/http/approval-sweeps.js +5 -3
- package/dist/http/caller-trust.d.ts +2 -2
- package/dist/http/caller-trust.js +3 -3
- package/dist/http/checkpoint-ctx.d.ts +18 -63
- package/dist/http/checkpoint-ctx.js +1 -24
- package/dist/http/claim-heartbeat.d.ts +6 -6
- package/dist/http/claim-heartbeat.js +6 -6
- package/dist/http/claimed-run.d.ts +16 -9
- package/dist/http/claimed-run.js +8 -0
- package/dist/http/dispatch.js +7 -5
- package/dist/http/durable-run-leg.d.ts +8 -1
- package/dist/http/durable-run-leg.js +34 -15
- package/dist/http/execution-context.d.ts +115 -14
- package/dist/http/execution-context.js +130 -40
- package/dist/http/owner-access.d.ts +246 -21
- package/dist/http/owner-access.js +186 -8
- package/dist/http/principal-gate.d.ts +18 -11
- package/dist/http/principal-gate.js +3 -1
- package/dist/http/readiness.d.ts +4 -2
- package/dist/http/resume-ctx-read.d.ts +14 -3
- package/dist/http/resume-ctx-read.js +24 -3
- package/dist/http/resume-legs.d.ts +35 -3
- package/dist/http/resume-legs.js +262 -106
- package/dist/http/route-ctx.d.ts +29 -11
- package/dist/http/routes/a2a-serve.js +17 -14
- package/dist/http/routes/approvals-assistant.d.ts +3 -2
- package/dist/http/routes/approvals-assistant.js +11 -11
- package/dist/http/routes/capabilities.js +5 -4
- package/dist/http/routes/config-catalog.d.ts +1 -1
- package/dist/http/routes/diagnostics.d.ts +3 -1
- package/dist/http/routes/diagnostics.js +30 -5
- package/dist/http/routes/mcp-reconnect.js +2 -2
- package/dist/http/routes/memory-origin.d.ts +1 -1
- package/dist/http/routes/notify-wake.js +2 -3
- package/dist/http/routes/principal-inbox.d.ts +3 -1
- package/dist/http/routes/principal-inbox.js +3 -2
- package/dist/http/routes/projects.d.ts +1 -0
- package/dist/http/routes/projects.js +5 -3
- package/dist/http/routes/retention-ops.d.ts +1 -1
- package/dist/http/routes/run-permission-mode.d.ts +1 -1
- package/dist/http/routes/run-permission-mode.js +2 -2
- package/dist/http/routes/runs.d.ts +1 -1
- package/dist/http/routes/runs.js +33 -37
- package/dist/http/routes/session-memory-sync.js +4 -5
- package/dist/http/routes/session-sync.d.ts +24 -5
- package/dist/http/routes/session-sync.js +45 -37
- package/dist/http/routes/sessions-list.d.ts +1 -1
- package/dist/http/routes/sessions.d.ts +1 -1
- package/dist/http/routes/sessions.js +18 -44
- package/dist/http/routes/tasks.js +136 -101
- package/dist/http/routes/trace-usage.js +14 -14
- package/dist/http/routes/workflows.d.ts +1 -0
- package/dist/http/routes/workflows.js +20 -38
- package/dist/http/server.d.ts +12 -9
- package/dist/http/server.js +35 -6
- package/dist/http/stored-body-replay.d.ts +3 -24
- package/dist/http/stored-body-replay.js +2 -12
- package/dist/http/submit-ctx.d.ts +2 -2
- package/dist/http/submit-ctx.js +5 -5
- package/dist/http/submit-replay.d.ts +15 -2
- package/dist/http/submit-replay.js +10 -4
- package/dist/http/wire-types.d.ts +6 -3
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -0
- package/dist/leader/endpoint.js +9 -8
- package/dist/leg-facts.d.ts +13 -2
- package/dist/leg-facts.js +2 -0
- package/dist/local-store-declaration.d.ts +54 -0
- package/dist/local-store-declaration.js +59 -0
- package/dist/mcp-assertion-header.d.ts +1 -1
- package/dist/mcp-assertion-header.js +1 -1
- package/dist/mcp-authorization-face.d.ts +18 -14
- package/dist/mcp-authorization-face.js +3 -2
- package/dist/memory-project-layer.js +1 -1
- package/dist/memory-scope.d.ts +4 -4
- package/dist/memory-scope.js +4 -4
- package/dist/memory-store-refusal.d.ts +1 -1
- package/dist/memory-sync-client.js +2 -2
- package/dist/model-context-window.d.ts +50 -11
- package/dist/model-context-window.js +20 -1
- package/dist/model-entry-refusal.d.ts +5 -4
- package/dist/model-entry-refusal.js +17 -3
- package/dist/model-reach-scope.d.ts +2 -1
- package/dist/observability/corrupt-read-seat.d.ts +1 -1
- package/dist/observability/fail-open.d.ts +19 -7
- package/dist/observability/fail-open.js +19 -7
- package/dist/observability/logger.d.ts +7 -0
- package/dist/observability/logger.js +3 -1
- package/dist/observability/metrics.js +1 -1
- package/dist/observability/run-terminal.d.ts +1 -1
- package/dist/observability/run-terminal.js +1 -0
- package/dist/observability/secret-env-scrub.d.ts +1 -1
- package/dist/observability/secret-env-scrub.js +1 -1
- package/dist/org-delivery-gate.d.ts +31 -6
- package/dist/org-delivery-gate.js +7 -4
- package/dist/owner-faces.d.ts +30 -5
- package/dist/owner-faces.js +2 -0
- package/dist/park-row-commit.d.ts +67 -0
- package/dist/park-row-commit.js +76 -0
- package/dist/parked-decide.d.ts +9 -7
- package/dist/parked-decide.js +25 -6
- package/dist/peer-directory.d.ts +1 -1
- package/dist/peer-seat.d.ts +1 -1
- package/dist/peer-seat.js +52 -59
- package/dist/plugins/adoption-log-sql.d.ts +3 -3
- package/dist/plugins/adoption-log-sql.js +19 -19
- package/dist/plugins/approval-ask-store-sql.d.ts +1 -1
- package/dist/plugins/approval-ask-store-sql.js +3 -5
- package/dist/plugins/approval-exemption-skill-cutoff.d.ts +64 -0
- package/dist/plugins/approval-exemption-skill-cutoff.js +122 -0
- package/dist/plugins/approval-exemption-skill-m46.d.ts +0 -6
- package/dist/plugins/approval-exemption-skill-m46.js +6 -10
- package/dist/plugins/approval-exemption-store.d.ts +26 -1
- package/dist/plugins/approval-exemption-store.js +28 -19
- package/dist/plugins/ensure-schema-lock-policy.d.ts +36 -3
- package/dist/plugins/ensure-schema-lock-policy.js +75 -9
- package/dist/plugins/exec-path-law.d.ts +34 -0
- package/dist/plugins/exec-path-law.js +13 -0
- package/dist/plugins/file-run-store.d.ts +22 -9
- package/dist/plugins/file-run-store.js +51 -23
- package/dist/plugins/local-checkpoint-store.d.ts +8 -1
- package/dist/plugins/local-checkpoint-store.js +5 -1
- package/dist/plugins/local-data-root-admission.d.ts +52 -0
- package/dist/plugins/local-data-root-admission.js +100 -0
- package/dist/plugins/local-engine-high-water.d.ts +28 -7
- package/dist/plugins/local-engine-high-water.js +27 -22
- package/dist/plugins/local-session-store.d.ts +134 -49
- package/dist/plugins/local-session-store.js +149 -108
- package/dist/plugins/memory-run-store.d.ts +14 -9
- package/dist/plugins/memory-run-store.js +48 -22
- package/dist/plugins/one-time-migration-marker.d.ts +19 -3
- package/dist/plugins/one-time-migration-marker.js +7 -1
- package/dist/plugins/one-time-migrations.d.ts +110 -59
- package/dist/plugins/one-time-migrations.js +224 -136
- package/dist/plugins/org-delivery-quota-store.d.ts +34 -11
- package/dist/plugins/org-delivery-quota-store.js +29 -14
- package/dist/plugins/permission-rule-store-sql.d.ts +1 -1
- package/dist/plugins/permission-rule-store-sql.js +5 -9
- package/dist/plugins/pg-pool.d.ts +1 -1
- package/dist/plugins/pg-pool.js +14 -12
- package/dist/plugins/posix-shell-fs.js +4 -2
- package/dist/plugins/remote-env-adb.js +3 -2
- package/dist/plugins/remote-env-device.d.ts +3 -2
- package/dist/plugins/remote-env-device.js +8 -6
- package/dist/plugins/remote-env-e2b.d.ts +4 -8
- package/dist/plugins/remote-env-e2b.js +21 -15
- package/dist/plugins/remote-env-host.d.ts +51 -10
- package/dist/plugins/remote-env-host.js +97 -31
- package/dist/plugins/remote-env-k8s.js +7 -4
- package/dist/plugins/remote-env-local-docker.d.ts +6 -3
- package/dist/plugins/remote-env-local-docker.js +11 -25
- package/dist/plugins/remote-env-ssh.js +4 -3
- package/dist/plugins/remote-shell.d.ts +3 -1
- package/dist/plugins/remote-shell.js +3 -3
- package/dist/plugins/replica-registration.d.ts +35 -0
- package/dist/plugins/replica-registration.js +48 -0
- package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
- package/dist/plugins/run-store-sql.d.ts +27 -8
- package/dist/plugins/run-store-sql.js +46 -18
- package/dist/plugins/scenario-prefix-switch-m42.d.ts +8 -12
- package/dist/plugins/scenario-prefix-switch-m42.js +9 -6
- package/dist/plugins/schema-consistency.d.ts +91 -5
- package/dist/plugins/schema-consistency.js +189 -7
- package/dist/plugins/schema-drift-sentence.d.ts +6 -5
- package/dist/plugins/schema-drift-sentence.js +13 -5
- package/dist/plugins/serving-requirement.d.ts +49 -0
- package/dist/plugins/serving-requirement.js +53 -0
- package/dist/plugins/session-placement.d.ts +5 -4
- package/dist/plugins/shell-sentinel-owner-m48.d.ts +22 -0
- package/dist/plugins/shell-sentinel-owner-m48.js +6 -0
- package/dist/plugins/stop-then-start-sentence.d.ts +55 -0
- package/dist/plugins/stop-then-start-sentence.js +51 -0
- package/dist/plugins/store-backend.d.ts +23 -9
- package/dist/plugins/store-backend.js +58 -24
- package/dist/plugins/store-contracts.d.ts +45 -5
- package/dist/plugins/store-contracts.js +19 -0
- package/dist/plugins/tidb-pool.d.ts +11 -6
- package/dist/plugins/tidb-pool.js +22 -12
- package/dist/plugins/workspace-custody.d.ts +44 -0
- package/dist/plugins/workspace-custody.js +47 -0
- package/dist/process-instance.d.ts +3 -0
- package/dist/process-instance.js +7 -0
- package/dist/project-context.d.ts +66 -0
- package/dist/project-context.js +70 -0
- package/dist/project-identity.d.ts +8 -7
- package/dist/project-identity.js +40 -17
- package/dist/project-layer-key.d.ts +13 -2
- package/dist/project-layer-key.js +9 -1
- package/dist/question.d.ts +9 -21
- package/dist/question.js +28 -19
- package/dist/read-deny-floor-notice.d.ts +36 -0
- package/dist/read-deny-floor-notice.js +40 -0
- package/dist/{plain-string-record.d.ts → request-declaration-disposition.d.ts} +9 -23
- package/dist/request-declaration-disposition.js +39 -0
- package/dist/run-cancel-card.d.ts +28 -0
- package/dist/run-cancel-card.js +20 -0
- package/dist/run-cancel-context.d.ts +10 -0
- package/dist/run-cancel-context.js +1 -0
- package/dist/run-local.js +28 -13
- package/dist/runs.d.ts +31 -7
- package/dist/runs.js +34 -16
- package/dist/security.d.ts +3 -3
- package/dist/server-secret-env.d.ts +34 -20
- package/dist/server-secret-env.js +61 -18
- package/dist/session-agent.d.ts +4 -12
- package/dist/session-agent.js +0 -6
- package/dist/session-children-retire.d.ts +6 -3
- package/dist/session-children-retire.js +2 -5
- package/dist/session-incarnation.d.ts +18 -4
- package/dist/session-incarnation.js +4 -1
- package/dist/session-notify-door.d.ts +4 -4
- package/dist/session-notify-door.js +5 -6
- package/dist/session-rules-seat.d.ts +4 -3
- package/dist/session-rules-seat.js +3 -11
- package/dist/session-sync.js +3 -3
- package/dist/session-workspace.d.ts +3 -3
- package/dist/store-admission.d.ts +33 -0
- package/dist/store-admission.js +49 -0
- package/dist/store-posture.d.ts +5 -0
- package/dist/store-posture.js +18 -1
- package/dist/stores-sql/checkpoint-store-sql.d.ts +22 -3
- package/dist/stores-sql/checkpoint-store-sql.js +109 -23
- package/dist/stores-sql/file-history-store-sql.d.ts +1 -1
- package/dist/stores-sql/file-history-store-sql.js +3 -5
- package/dist/stores-sql/retention-store-sql.d.ts +1 -1
- package/dist/stores-sql/tool-result-store-sql.d.ts +1 -1
- package/dist/stores-sql/tool-result-store-sql.js +4 -6
- package/dist/task-a2a.d.ts +3 -8
- package/dist/task-a2a.js +25 -27
- package/dist/task-cwd.d.ts +27 -10
- package/dist/task-cwd.js +3 -0
- package/dist/task-mcp.d.ts +3 -3
- package/dist/task-mcp.js +1 -1
- package/dist/task-settings.d.ts +13 -0
- package/dist/task-settings.js +14 -7
- package/dist/terminal.d.ts +2 -0
- package/dist/terminal.js +21 -6
- package/dist/tool-approval.d.ts +50 -41
- package/dist/tool-approval.js +96 -73
- package/dist/trace/engine-notice-wire.d.ts +38 -21
- package/dist/trace/engine-notice-wire.js +25 -4
- package/dist/trace/ledger-sink.d.ts +3 -1
- package/dist/trace/project.d.ts +8 -5
- package/dist/trace/project.js +11 -9
- package/dist/trace/projection-table.d.ts +33 -2
- package/dist/trace/redact.d.ts +12 -0
- package/dist/trace/redact.js +5 -1
- package/dist/trace/self-minted-wire.d.ts +268 -56
- package/dist/trace/self-minted-wire.js +110 -40
- package/dist/trace/tool-card.d.ts +3 -0
- package/dist/trace/tool-card.js +24 -0
- package/dist/trace/tool-output-media.d.ts +2 -1
- package/dist/unattended-permission-mode.d.ts +1 -1
- package/dist/web-admin-client/apply-effective.d.ts +1 -1
- package/dist/web-admin-client/apply-effective.js +22 -9
- package/dist/web-admin-client/effective-keys.d.ts +1 -1
- package/dist/web-admin-client/skills-mcp.d.ts +1 -1
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +57 -29
- package/docs/error-text-lexicon.json +317 -0
- package/package.json +4 -2
- package/scripts/embed-minimal.mjs +183 -0
- package/wire-changes/server@7.149.0.json +372 -0
- package/wire-changes/server@7.150.0.json +335 -0
- package/dist/plain-string-record.js +0 -70
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* 剥掉的键经一个登记过的 `SecretEnvScrubSite` 留痕(现场按**腿**命名);git 腿再叠 git 自己的两件({@link gitSubprocessEnv})。射程 = **每一条**宿主腿:
|
|
14
14
|
* src 里每一只值引 `node:child_process` 的文件都是登记腿、其中每一次起子进程都显式交组合出的 env(普查门在 `test/secret-env-scrub-host-legs.test.ts`)。
|
|
15
15
|
* 腿的继承词是调用点的事实,按「子进程里跑的是谁选的代码」定:模型命令(host shell,交任务自己的词)/ 仓里的钩子与凭据助手(三条 git 腿)/ 执行仓代码的
|
|
16
|
-
* 语言服务器 / 配方脚本 ⇒ 形状剥 `"scrub"`;请求带来的 settings hooks 命令 ⇒ 严格名单(十二名基础名单,`[names]` 臂;请求的继承词只给工具 shell、对 hook 不作用)
|
|
16
|
+
* 语言服务器 / 配方脚本 ⇒ 形状剥 `"scrub"`;请求带来的 settings hooks 命令 ⇒ 严格名单(十二名基础名单,`[names]` 臂;请求的继承词只给工具 shell、对 hook 不作用)∪ 部署方 `HOOK_ENV_ALLOW` 点名放回、请求的有效环境层作授予叠在继承结果之上(引擎的 `hostChildEnv`);部署方装在宿主上的 CLI(`docker` / `adb`,命令本身
|
|
17
17
|
* 在容器 / 设备里跑、那边的 env 由 `-e` 显式给)⇒ `"all"` —— 只扣本服务自有凭据,CLI 与它的凭据助手 / 插件要读的生态变量照交(形状剥会剥
|
|
18
18
|
* `ADB_VENDOR_KEYS` / `AWS_SECRET_ACCESS_KEY` 一类,实测)。三个继承词只在引擎一处判:
|
|
19
19
|
* · `"all"` = 整份环境 **减** 本进程声明的自有凭据名;`"scrub"`(缺省)= 形状剥 ∪ 该名单;`[names]` = 严格白名单,点到名单中的名 ⇒ 引擎响亮拒
|
|
@@ -29,24 +29,27 @@
|
|
|
29
29
|
* `loadConfig()` 的**声明前提**({@link ownCredentialDeclarationRefusal}:起服表的名没全扣 ⇒ `loadConfig` 抛,拒句点名 {@link declareServerWithheldEnv} —— 「先声明」不再只是入口里的位置契约,
|
|
30
30
|
* 不经入口直接调 `loadConfig` 的进程〔包根嵌入方 / bench / 探针脚本〕得先声明,声明对它们才不是空的)与能力词 `inherit_env.own_credentials_withheld` 在本进程的事实
|
|
31
31
|
* {@link ownCredentialsWithheld}(当前生效配置应声明的名全被扣;活配置经 {@link bindOwnCredentialsConfig} 在 `loadConfig()` 之后绑一次;未绑 / 没扣 ⇒ false)。
|
|
32
|
-
* · **值臂**(core 7.52.0 [ref]):声明是一张表 `{ names, values }`。`values` =
|
|
33
|
-
*
|
|
32
|
+
* · **值臂**(core 7.52.0 [ref]):声明是一张表 `{ names, values }`。`values` = 本进程持有凭据的变量(目录 secret 名 ∪ 模型条目 `apiKeyEnv`,{@link serverCredentialValueSources})
|
|
33
|
+
* 此刻装着的**每一枚**本服务凭据 —— 颗粒按本服务读这只变量的那一只解析器定(目录行的 `credentialForm`:整值总在内,`SERVICE_AUTH_TOKENS` 另加每一枚令牌、
|
|
34
|
+
* `APPROVAL_HMAC_KEYS` 另加每一把钥;{@link credentialsHeldBy})—— 引擎按整串比,所以一枚令牌换了个变量名放着与原变量同扣,部分重叠 / 前后缀不同的值不扣。
|
|
35
|
+
* 逐枚交给引擎的同一只判;引擎判「不是凭据值」的那一枚不扣、名照扣、不拒启、点名那个变量一次。能力词 `inherit_env.own_credential_values_withheld` 的事实 = {@link ownCredentialValuesWithheld}(位真 ⇔ 值臂每一枚都真扣)。
|
|
36
|
+
* 「这个变量是不是本服务的凭据」(名是,或值是)只有一只读法 {@link ownCredentialVariable}(判官 = 引擎此刻的声明):名单型旋钮的解析器与声明增长时的重判都问它。
|
|
34
37
|
* · 调用方显式传入的 env(`cfg.env` / 单次命令的 `env` / 沙箱的点名放回)不是继承,照旧原样叠在组合结果之上 —— 那是适配器自己的凭据通道。
|
|
35
38
|
* 各腿只调 {@link hostSubprocessEnv} / {@link gitSubprocessEnv} —— 不许再有内联拼装、不许再有 `{ ...process.env }`、不许本仓再判任何一个继承词、不许起子进程不交 env
|
|
36
39
|
* (类级门与普查门在 `test/secret-env-scrub-host-legs.test.ts`)。
|
|
37
40
|
*
|
|
38
41
|
* ## 点名放行([ref] / [ref],一只谓词各处用)
|
|
39
42
|
* {@link allowlistedEnv} 是本仓「一张名单点到哪些 env 键,就把它们从源 env **原样**交给沙箱」的唯一实现。调用:沙箱带外 env 的 `E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV` /
|
|
40
|
-
* `K8S_SANDBOX_ENV`(`config.ts`)与名单解析器的「在不在」判。宿主子进程的剥后放回(`LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW`)不经它 —— 那是引擎的严格名单臂(上一段)。
|
|
43
|
+
* `K8S_SANDBOX_ENV`(`config.ts`)与名单解析器的「在不在」判。宿主子进程的剥后放回(`LSP_ENV_ALLOW` / `HOOK_ENV_ALLOW` / `BAKE_ENV_ALLOW`)不经它 —— 那是引擎的严格名单臂(上一段)。
|
|
41
44
|
* host shell 的 `inheritEnv:[names]`(hermetic 形)7.129.0 起由引擎读(与 core `NodeExecutionEnv` 同名选项同义,点到自有凭据名即拒),不经本谓词。
|
|
42
45
|
* 名单型旋钮的**名单**另由 `config.ts` 的同一只解析器 `parseEnvNameAllowlist` 判形(形错 / 点名名单型旋钮 / 点名本服务自有凭据名 ⇒ 拒启;源里没有的名按序号 warn),本函数只管取值。
|
|
43
46
|
* 「点名」在各处同义:点到的键原样带值(空串值照带),密钥形也照过(点名 = 部署方显式承担);源里没有的名不产生条目。
|
|
44
|
-
* 剥后放回形(`LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW`)的放回本身也经引擎:{@link composeHostSubprocessEnv} 把名单交引擎的严格名单臂(`inheritedHostEnv([names])`)叠在
|
|
47
|
+
* 剥后放回形(`LSP_ENV_ALLOW` / `HOOK_ENV_ALLOW` / `BAKE_ENV_ALLOW`)的放回本身也经引擎:{@link composeHostSubprocessEnv} 把名单交引擎的严格名单臂(`inheritedHostEnv([names])`)叠在
|
|
45
48
|
* 继承结果之上 ⇒ 点到本服务自有凭据名 = 引擎在**组合这一刻**抛(语言服务器在构造时组合一次、之后 spawn 用那份静态记录;bake-runner 每次 spawn 组合;
|
|
46
49
|
* 起服时解析器 `parseEnvNameAllowlist` 已用同一只判 `inheritEnvPolicyProblem` 拒过 —— 解析器对五只名单一律拒,点到**解析时已声明**的自有凭据名的沙箱名单因此
|
|
47
50
|
* 到不了 {@link allowlistedEnv} 取值这一步;声明之后增长(中心 / config.d 模型条目的 `apiKeyEnv` 换代并进)时,换代声明对活配置里每只名单此刻交出去的名用同一只判
|
|
48
|
-
* 重判一次 —— 命中 ⇒ 撤回这次增长、候选整拒,已装好的沙箱 env / 语言服务器 env 不变成本服务的凭据,`config.ts` `ownCredentialHitsInForce`)
|
|
49
|
-
*
|
|
51
|
+
* 重判一次 —— 命中 ⇒ 撤回这次增长、候选整拒,已装好的沙箱 env / 语言服务器 env 不变成本服务的凭据,`config.ts` `ownCredentialHitsInForce`)。
|
|
52
|
+
* 解析器与重判问的是「点到的变量是不是本服务的凭据」—— 名是,**或值是**(另一个名下的副本,{@link ownCredentialVariable})—— 都拒;组合这一刻引擎照样扣下值副本(不变量)。本仓不另判、不比值。
|
|
50
53
|
*/
|
|
51
54
|
import { type InheritEnvPolicy, type SecretEnvFinding } from "@sema-agent/core";
|
|
52
55
|
import type { ServiceConfig } from "./config-types.js";
|
|
@@ -61,8 +64,9 @@ export type ModelApiKeyEnvTable = Readonly<Record<string, string>>;
|
|
|
61
64
|
*/
|
|
62
65
|
export declare function serverWithheldEnvNames(modelApiKeyEnv?: ModelApiKeyEnvTable): readonly string[];
|
|
63
66
|
/**
|
|
64
|
-
* 本进程**持有凭据值**的变量名(值臂的来源;core 7.52.0 [ref]):目录 secret 名 ∪ 模型条目 `apiKeyEnv` 的值 ——
|
|
65
|
-
*
|
|
67
|
+
* 本进程**持有凭据值**的变量名(值臂的来源;core 7.52.0 [ref]):目录 secret 名 ∪ 模型条目 `apiKeyEnv` 的值 —— 名单与值臂两臂同一张表派生,只少不在目录里的名单型旋钮名
|
|
68
|
+
* (今天 = bake-runner 进程的 `BAKE_ENV_ALLOW`);目录里的名单型旋钮行是 secret 行,原文按凭据类整值对待(行上 `credentialForm: "whole"`)。每个来源装着几枚凭据
|
|
69
|
+
* 由目录行的颗粒定({@link credentialsHeldBy})。纯函数,排序只为读数稳定。
|
|
66
70
|
*/
|
|
67
71
|
export declare function serverCredentialValueSources(modelApiKeyEnv?: ModelApiKeyEnvTable): readonly string[];
|
|
68
72
|
/**
|
|
@@ -72,15 +76,14 @@ export declare function serverCredentialValueSources(modelApiKeyEnv?: ModelApiKe
|
|
|
72
76
|
* · **名臂**:此前声明过的 ∪ 这张表的 {@link serverWithheldEnvNames}。引擎对坏形名 / 运行时回填名(`PATH` / `HOME` …)响亮抛且声明保持原状 —— 本函数不接住:
|
|
73
77
|
* 起服那次抛 = 拒启;换代那次抛 = 候选整拒(`applyEffective` 抛,与 stage 抛同路)。第一次交表带着名臂与**此前已收下的值**(引擎收过、env 不变 ⇒ 同值照收),
|
|
74
78
|
* 所以这一次抛只可能是名的问题。
|
|
75
|
-
* · **值臂**:{@link serverCredentialValueSources}
|
|
76
|
-
*
|
|
77
|
-
*
|
|
79
|
+
* · **值臂**:{@link serverCredentialValueSources} 的每一个变量此刻装着的每一枚凭据({@link credentialsHeldBy}:整值,列表形另加每一枚),逐枚交给引擎**同一只**判
|
|
80
|
+
* (不另写「什么算凭据值」的第二份判据):引擎收下 ⇒ 记进值臂;
|
|
81
|
+
* 引擎拒(不是凭据值:空白 / 占位词 / 一个字符重复 / 不足 8 字符;或它是每个子进程都会拿到的变量的值)⇒ 这一枚不扣(同一变量的其余几枚照收)、名照扣、**不拒启**(与引擎自己的收值口
|
|
82
|
+
* `holdOwnCredentialValues` 同一姿势:不收 + 说一次),经留痕座结构化点名那个变量一次(它装着几枚被拒都是一行)—— `warn` 行 `own_credential_value_not_withheld { name, reason }`
|
|
78
83
|
* ({@link recordOwnCredentialValueNotWithheld};`reason` 是引擎拒句原文,从不回显值;起服第一步还没有 logger 时当拍经进程默认的 JSON 行日志器写 stderr,不缓冲),能力位 `inherit_env.own_credential_values_withheld`
|
|
79
84
|
* 因此答 false(位真 ⇔ 值臂真扣)。引擎拒一个值时声明保持上一份,所以逐个交表的中间态都是上一份的超集。
|
|
80
|
-
* 🔴
|
|
81
|
-
*
|
|
82
|
-
* 本仓不在引擎外面吞 console、也不拿声明口去抢引擎的「每进程每值说一次」账(下游不改上游座的语义);等 core 给席或让声明口的拒收记作已说。读数与翻面条件:
|
|
83
|
-
* `test/own-credential-value-boot-lines.test.ts`(真主进程;零裸行的期望以 `it.fails` 立着,core 改形那天转红再翻成常规钉)。
|
|
85
|
+
* 🔴 **这一行是唯一的一行**:值是网关密钥(`MODEL_API_KEY` / `ANTHROPIC_API_KEY`)时,引擎在适配器构造口收下同一个值,声明口拒过的值它记作已说、不再说第二遍
|
|
86
|
+
* (core 7.62.0 起;此前那里另有一行引擎的裸 `console.warn`)。读数:`test/own-credential-value-boot-lines.test.ts`(真主进程,零裸文本行)。
|
|
84
87
|
* 返回声明的名单(只名、不值)。
|
|
85
88
|
*/
|
|
86
89
|
export declare function declareServerWithheldEnv(modelApiKeyEnv?: ModelApiKeyEnvTable): readonly string[];
|
|
@@ -113,10 +116,20 @@ export declare function ownCredentialDeclarationRefusal(): string | undefined;
|
|
|
113
116
|
*/
|
|
114
117
|
export declare function ownCredentialsWithheld(): boolean;
|
|
115
118
|
/**
|
|
116
|
-
* 能力词 `inherit_env.own_credential_values_withheld`(core 7.52.0)在本进程的事实:当前生效配置的每一个凭据值来源({@link serverCredentialValueSources})
|
|
117
|
-
*
|
|
119
|
+
* 能力词 `inherit_env.own_credential_values_withheld`(core 7.52.0)在本进程的事实:当前生效配置的每一个凭据值来源({@link serverCredentialValueSources})此刻装着的
|
|
120
|
+
* **每一枚**凭据({@link credentialsHeldBy})都已交进值臂、被引擎收下。位真 ⇔ 值臂真扣:有一枚被引擎判「扣不了」⇒ false;env 里没有值的来源没有东西可扣,不影响事实。
|
|
121
|
+
* 没绑活配置 / 没声明过 ⇒ false。
|
|
118
122
|
*/
|
|
119
123
|
export declare function ownCredentialValuesWithheld(): boolean;
|
|
124
|
+
/** {@link ownCredentialVariable} 的答案:这个变量是本服务的凭据,因为它的**名**是声明过的自有凭据名,还是它此刻的**值**是本服务的某一枚凭据(另一个名下的副本)。 */
|
|
125
|
+
export type OwnCredentialHold = "name" | "value";
|
|
126
|
+
/**
|
|
127
|
+
* 「本进程环境里这个变量是不是本服务的自有凭据」的**唯一读法**(判官 = 引擎此刻的声明;本仓不比值、不持值):名是声明过的自有凭据名 ⇒ `"name"`;
|
|
128
|
+
* 名不是,而它此刻的值等于引擎扣着的某一枚凭据(值臂按颗粒逐枚声明的,或引擎自己收下的)⇒ `"value"`;都不是 / 变量不在本进程环境里 ⇒ `undefined`。
|
|
129
|
+
* 值的那一半读引擎严格名单臂对这一名的扣留(`inheritedHostEnv([name])` 的 `own-credential` 发现 —— 严格名单不做形状剥,那里扣下的只有自有凭据一类)。
|
|
130
|
+
* 调用方先判过名形(引擎对坏形名同样答「有问题」,会被读成 `"name"`)。读者:名单型旋钮的解析器 ② 与声明增长时的重判(`config.ts` `ownCredentialHitsInForce`)。
|
|
131
|
+
*/
|
|
132
|
+
export declare function ownCredentialVariable(name: string): OwnCredentialHold | undefined;
|
|
120
133
|
/** 测试缝:撤回声明(引擎侧重声明为空表)、忘掉两份声明镜像、点名记账与活配置;生产代码从不调它。 */
|
|
121
134
|
export declare function __resetServerWithheldEnvForTest(): void;
|
|
122
135
|
/** 一条宿主腿要什么:现场(留痕)、继承策略(缺省 `"scrub"`;按头注「腿的继承词」一条由调用点定)、部署方点名放回的键、调用方显式交的授予(按序叠在继承之上,后者赢)。 */
|
|
@@ -133,8 +146,9 @@ export interface HostSubprocessEnvRequest {
|
|
|
133
146
|
* 不是剥除,不计数、不披露。源 env 恒为本进程 env(引擎只读 `process.env`,本仓没有第二个源)。
|
|
134
147
|
*
|
|
135
148
|
* 九条宿主腿都只经它(直接或经 {@link gitSubprocessEnv}):host shell(三个继承词原样交给 `policy`)、project-memory / center-plugins / leader 三条 git 腿、
|
|
136
|
-
* 本地语言服务器、bake-runner 的每一只子进程组、local-docker 与 adb 两条 CLI 腿(`"all"`)、settings hooks 的命令腿(十二名严格名单 + 有效环境层作授予)。`allow` 只在**有部署级出路**的现场非空(今天 = `LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW`,
|
|
137
|
-
* 它们的现场在 `SECRET_ENV_SCRUB_SITES` 里登记了 `remedy`,披露行因此是 warn + 放行名单 +
|
|
149
|
+
* 本地语言服务器、bake-runner 的每一只子进程组、local-docker 与 adb 两条 CLI 腿(`"all"`)、settings hooks 的命令腿(十二名严格名单 ∪ `HOOK_ENV_ALLOW` 放回 + 有效环境层作授予)。`allow` 只在**有部署级出路**的现场非空(今天 = `LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW`,
|
|
150
|
+
* 它们的现场在 `SECRET_ENV_SCRUB_SITES` 里登记了 `remedy`,披露行因此是 warn + 放行名单 + 出路句;settings hooks 的 `HOOK_ENV_ALLOW` 也走这只槽,但那条腿是严格名单,
|
|
151
|
+
* finding 只有自有凭据的扣留、任何名单都放不回,现场登记 `remedy: null`,理由在登记处);其余现场传 `[]`。
|
|
138
152
|
* 「扣了哪些名字」一腿一个读者:host shell 腿由引擎读 `withheldEnv` 说(`inherit_env.names_withheld`,现场登记 `told:"engine-notice"` ⇒ 本函数对它只计数、不写披露行);
|
|
139
153
|
* 其余各条腿(含 host 适配器类里不交给引擎的 git worktree 基环境,`HostEnvConfig.gitBase`)引擎没有读者,披露行是唯一的读法(`told:"log"`)。
|
|
140
154
|
* 返回 `NodeJS.ProcessEnv` 形(值可能是 `undefined` 的键本来就不会进子进程);调用方显式要交的键(`LC_ALL`、`cfg.env` …)叠在它之上。
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { declareWithheldEnv, hostChildEnv, inheritEnvPolicyProblem, inheritedHostEnv } from "@sema-agent/core";
|
|
2
2
|
import { CONFIG_CATALOG } from "./config-catalog.js";
|
|
3
|
+
import { parseApprovalHmacKeys, parseServiceAuthTokens } from "./auth-keys.js";
|
|
3
4
|
import { ENV_NAME_ALLOWLIST_KNOB_NAMES } from "./env-name-allowlist-knobs.js";
|
|
4
5
|
import { recordOwnCredentialValueNotWithheld, recordSecretEnvScrub } from "./observability/secret-env-scrub.js";
|
|
5
6
|
export const CATALOG_SECRET_ENV_NAMES = new Set(CONFIG_CATALOG.filter((spec) => spec.type === "secret").map((spec) => spec.name));
|
|
@@ -10,37 +11,72 @@ export function serverWithheldEnvNames(modelApiKeyEnv = NO_MODEL_ENTRIES) {
|
|
|
10
11
|
export function serverCredentialValueSources(modelApiKeyEnv = NO_MODEL_ENTRIES) {
|
|
11
12
|
return [...new Set([...CATALOG_SECRET_ENV_NAMES, ...Object.values(modelApiKeyEnv)])].sort();
|
|
12
13
|
}
|
|
14
|
+
const CREDENTIAL_FORM_OF = new Map(CONFIG_CATALOG.flatMap((spec) => (spec.credentialForm === undefined ? [] : [[spec.name, spec.credentialForm]])));
|
|
15
|
+
const DROPPED_KEYS_TOLD_BY_LOAD_CONFIG = () => { };
|
|
16
|
+
function credentialsIn(form, raw) {
|
|
17
|
+
switch (form) {
|
|
18
|
+
case "whole":
|
|
19
|
+
return [raw];
|
|
20
|
+
case "token-table":
|
|
21
|
+
return [raw, ...Object.keys(parseServiceAuthTokens(raw))];
|
|
22
|
+
case "key-set":
|
|
23
|
+
return [raw, ...parseApprovalHmacKeys(raw, DROPPED_KEYS_TOLD_BY_LOAD_CONFIG).map((k) => k.key)];
|
|
24
|
+
default: {
|
|
25
|
+
const unreachable = form;
|
|
26
|
+
throw new Error(`unhandled secret credential form: ${String(unreachable)}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
13
30
|
let declared;
|
|
14
31
|
let beforeLast;
|
|
15
32
|
const valueRefusalAnnounced = new Set();
|
|
16
33
|
let liveConfig;
|
|
17
34
|
const envValueOf = (name) => process.env[name];
|
|
18
|
-
|
|
35
|
+
function credentialsHeldBy(source) {
|
|
36
|
+
const raw = envValueOf(source);
|
|
37
|
+
if (raw === undefined || raw === "")
|
|
38
|
+
return [];
|
|
39
|
+
return [...new Set(credentialsIn(CREDENTIAL_FORM_OF.get(source) ?? "whole", raw))].filter((v) => v !== "");
|
|
40
|
+
}
|
|
41
|
+
function heldValues(held) {
|
|
42
|
+
return [...held].flatMap(([source, at]) => {
|
|
43
|
+
const all = credentialsHeldBy(source);
|
|
44
|
+
return [...at].flatMap((i) => {
|
|
45
|
+
const value = all[i];
|
|
46
|
+
return value === undefined ? [] : [value];
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
}
|
|
19
50
|
export function declareServerWithheldEnv(modelApiKeyEnv = NO_MODEL_ENTRIES) {
|
|
20
51
|
const names = [...new Set([...(declared?.names ?? []), ...serverWithheldEnvNames(modelApiKeyEnv)])].sort();
|
|
21
|
-
const
|
|
22
|
-
declareWithheldEnv({ names, values:
|
|
23
|
-
const valueSources = new Set(kept);
|
|
52
|
+
const held = new Map(declared?.held ?? []);
|
|
53
|
+
declareWithheldEnv({ names, values: heldValues(held) });
|
|
24
54
|
for (const source of serverCredentialValueSources(modelApiKeyEnv)) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
55
|
+
const at = new Set(held.get(source) ?? []);
|
|
56
|
+
let refusal;
|
|
57
|
+
credentialsHeldBy(source).forEach((_, i) => {
|
|
58
|
+
if (at.has(i))
|
|
59
|
+
return;
|
|
60
|
+
try {
|
|
61
|
+
declareWithheldEnv({ names, values: heldValues(new Map(held).set(source, new Set([...at, i]))) });
|
|
62
|
+
at.add(i);
|
|
63
|
+
held.set(source, new Set(at));
|
|
64
|
+
}
|
|
65
|
+
catch (e) {
|
|
66
|
+
refusal ??= e instanceof Error ? e.message : String(e);
|
|
35
67
|
}
|
|
68
|
+
});
|
|
69
|
+
if (refusal !== undefined && !valueRefusalAnnounced.has(source)) {
|
|
70
|
+
valueRefusalAnnounced.add(source);
|
|
71
|
+
recordOwnCredentialValueNotWithheld({ name: source, reason: refusal });
|
|
36
72
|
}
|
|
37
73
|
}
|
|
38
74
|
beforeLast = declared;
|
|
39
|
-
declared = { names: new Set(names),
|
|
75
|
+
declared = { names: new Set(names), held };
|
|
40
76
|
return names;
|
|
41
77
|
}
|
|
42
78
|
export function withdrawServerWithheldEnv() {
|
|
43
|
-
declareWithheldEnv({ names: [...(beforeLast?.names ?? [])], values:
|
|
79
|
+
declareWithheldEnv({ names: [...(beforeLast?.names ?? [])], values: heldValues(beforeLast?.held ?? new Map()) });
|
|
44
80
|
declared = beforeLast;
|
|
45
81
|
}
|
|
46
82
|
export function bindOwnCredentialsConfig(config) {
|
|
@@ -65,8 +101,15 @@ export function ownCredentialsWithheld() {
|
|
|
65
101
|
export function ownCredentialValuesWithheld() {
|
|
66
102
|
if (declared === undefined || liveConfig === undefined)
|
|
67
103
|
return false;
|
|
68
|
-
const
|
|
69
|
-
return serverCredentialValueSources(liveConfig.modelApiKeyEnv).every((source) =>
|
|
104
|
+
const held = declared.held;
|
|
105
|
+
return serverCredentialValueSources(liveConfig.modelApiKeyEnv).every((source) => (held.get(source)?.size ?? 0) === credentialsHeldBy(source).length);
|
|
106
|
+
}
|
|
107
|
+
export function ownCredentialVariable(name) {
|
|
108
|
+
if (inheritEnvPolicyProblem([name]) !== undefined)
|
|
109
|
+
return "name";
|
|
110
|
+
const findings = [];
|
|
111
|
+
inheritedHostEnv([name], findings);
|
|
112
|
+
return findings.some((f) => f.kind === "own-credential") ? "value" : undefined;
|
|
70
113
|
}
|
|
71
114
|
export function __resetServerWithheldEnvForTest() {
|
|
72
115
|
declareWithheldEnv({ names: [], values: [] });
|
package/dist/session-agent.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* 店原样记下,首写不可变。三只持久会话店都存:SQL 两孪生在 `session_meta` 的 `placement_*` 列(`plugins/session-placement.ts` 的 `placementFromRow`),
|
|
6
6
|
* local 后端(7.141.0 起)在会话存档行头(core 文件 repo)。读口是店的 `placementOf`(一行 PK 探针;店不交它 = 不存元组)。
|
|
7
7
|
*
|
|
8
|
-
* 读者(同一只读口
|
|
8
|
+
* 读者(同一只读口 —— 属主存取器的 `readSessionAgent`(`http/owner-access.ts`:店的行读只在存取器里)—— 同一只投影 {@link sessionAgentOf},逐值同答):
|
|
9
9
|
* · 会话视图 `GET /v1/sessions/:id` 体顶层的 `agent`(视图读才付探针;探针抛 ⇒ 视图读响亮失败,`boot/session-faces.ts`);
|
|
10
10
|
* · 运维审批队列 `GET /v1/approvals` 的停驻行(`pending[]` 的 `checkpoint` 族)与 `/v1/approvals/stream` 的 `pending` 帧:行的身份就是它那只会话
|
|
11
11
|
* (决断口 `/v1/approvals/:sessionId/decide`),行上的 `agent` = 同一只会话在视图上的 `agent`;一行读不出 ⇒ 只这一行缺键、F 类兜底计数
|
|
@@ -14,8 +14,9 @@
|
|
|
14
14
|
*
|
|
15
15
|
* 投影律(不另铸):`id` = 元组的 `handle`(耐久 agent 行的 `a…` 键,与 fleet 行的 `id`、`GET /v1/agents/roster` 的 `handle` 同一个串);`parentSessionId` /
|
|
16
16
|
* `rootSessionId` = 元组的同名格(派它的会话 / 委派树的根会话);元组缺哪一格这里就缺哪一格;元组的 `scope`(租户键)/ `placedAt` / `kind` 不上 wire。
|
|
17
|
-
* 键在场 =
|
|
18
|
-
*
|
|
17
|
+
* 键在场 = 这是一只后台委派子代的会话(普通后台子代与 `subagent_type: "fork"` 的后台子代同样落元组 —— 引擎 7.63.0 起后台 fork 建会话那一笔也交委派元组;
|
|
18
|
+
* 元组只存了 `kind` 时是 `{}`);键缺席 = 不是 placed 行(宿主会话、同步委派的子代 —— 含同步 fork,它的会话是父会话的一只普通分叉;`DB_BACKEND=local` 到 7.140.x
|
|
19
|
+
* 停驻时拷进宿主店的子代转录,那一份是普通会话行)、这台部署的店不存元组(无 repo 的内存店
|
|
19
20
|
* 〔`DB_BACKEND=memory`〕、嵌入方自建且不声明委派分区的店),或那一读没读成 —— 消费方一律读作「这台 server 没有说」,不读作「不是子代」。
|
|
20
21
|
*/
|
|
21
22
|
import type { SessionPlacementRecord } from "@sema-agent/core";
|
|
@@ -25,15 +26,6 @@ export interface SessionAgent {
|
|
|
25
26
|
parentSessionId?: string;
|
|
26
27
|
rootSessionId?: string;
|
|
27
28
|
}
|
|
28
|
-
/** 交委派元组读口的会话店面(本仓三只持久会话店与缓存包装层都交;内存店不交)。 */
|
|
29
|
-
export interface PlacementReadingSessionStore {
|
|
30
|
-
readonly placementOf?: (sessionId: string) => Promise<SessionPlacementRecord | undefined>;
|
|
31
|
-
}
|
|
32
29
|
/** {@link SessionAgent} 的唯一投影:店答出的委派元组 → `agent` 的值;`undefined`(普通会话 / 店没有这一面)⇒ 调用方不铸键。 */
|
|
33
30
|
export declare function sessionAgentOf(placement: SessionPlacementRecord | undefined): SessionAgent | undefined;
|
|
34
|
-
/**
|
|
35
|
-
* `agent` 的**唯一读口**:店的 `placementOf` 一读 → {@link sessionAgentOf}。店不交 `placementOf` ⇒ `undefined`(这台部署不存元组,不探)。
|
|
36
|
-
* 探针抛 ⇒ 原样上抛 —— 读不出怎么说归读者(视图读响亮失败;队列只缺那一行的键并计数),本函数不替任何一面把故障说成「不是子代」。
|
|
37
|
-
*/
|
|
38
|
-
export declare function readSessionAgent(store: PlacementReadingSessionStore | undefined, sessionId: string): Promise<SessionAgent | undefined>;
|
|
39
31
|
//# sourceMappingURL=session-agent.d.ts.map
|
package/dist/session-agent.js
CHANGED
|
@@ -7,10 +7,4 @@ export function sessionAgentOf(placement) {
|
|
|
7
7
|
...(placement.rootSessionId !== undefined ? { rootSessionId: placement.rootSessionId } : {}),
|
|
8
8
|
};
|
|
9
9
|
}
|
|
10
|
-
export async function readSessionAgent(store, sessionId) {
|
|
11
|
-
const placementOf = store?.placementOf;
|
|
12
|
-
if (placementOf === undefined)
|
|
13
|
-
return undefined;
|
|
14
|
-
return sessionAgentOf(await placementOf.call(store, sessionId));
|
|
15
|
-
}
|
|
16
10
|
//# sourceMappingURL=session-agent.js.map
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* ## 动词(core 板 [ref] ⑧⑨ 给定;本车第零步探针 `test/core-probe-parked-stop-root-access.test.ts` 真跑证实)
|
|
16
16
|
* · 枚举 = `agentStore.listBySession(scope, 根会话)`(耐久行,锚 = 行的 session / parent / root 任一),并上本进程登记表里的
|
|
17
17
|
* `activeDelegationHandles(scope, 根会话)`(没接耐久店的部署只有这一半)。scope 轴由调用方给(唯一派生 {@link sessionTenantScope})。
|
|
18
|
-
* · **parked** 行 ⇒ `
|
|
18
|
+
* · **parked** 行 ⇒ 按店里该卡的属主格 `expire`(`expireCardUnderStoredScope`:属主格读店的列、不读行体;与 core 自己的本进程仲裁器同一只 CAS)+
|
|
19
19
|
* `registry.reconcileParkedAgents(stores, scope)` ⇒ 行 `failed`(stoppedBy `system`)、卡离开待决面。任何副本上都做得到 ⇒ 预检把它
|
|
20
20
|
* 算作可达(前提是本部署有 checkpoint 店)。
|
|
21
21
|
* ⚠️ **不对 parked 行调 `stopTask`**:只在耐久店里的行(park 它的进程不在 / 别的副本 / 重启后 —— parked 行活过重启是常态)
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
* 两轮之间留一小段退避让它落盘。
|
|
41
41
|
*/
|
|
42
42
|
import type { BackgroundAgentStore, CheckpointStore, TaskRegistry } from "@sema-agent/core";
|
|
43
|
+
import { type CardSettlePort } from "./card-settle-stored-scope.js";
|
|
43
44
|
/** 重新枚举的轮数上界。 */
|
|
44
45
|
export declare const RETIRE_MAX_ROUNDS = 6;
|
|
45
46
|
/** 两步共用的枚举口。 */
|
|
@@ -47,14 +48,16 @@ interface ChildrenSource {
|
|
|
47
48
|
agentStore: BackgroundAgentStore | undefined;
|
|
48
49
|
registry: Pick<TaskRegistry, "activeDelegationHandles">;
|
|
49
50
|
}
|
|
51
|
+
/** 预检与结清共用的 checkpoint 店口:作废卡({@link CardSettlePort})+ 引擎对账读卡(`get`)—— 预检说「可达」的前提就是结清做得了这两件,所以两步同一个型。 */
|
|
52
|
+
export type ChildrenCheckpointStore = Pick<CheckpointStore, "get"> & CardSettlePort;
|
|
50
53
|
export interface ProbeSessionChildrenDeps extends ChildrenSource {
|
|
51
54
|
/** 缺席 = 本部署没有耐久审批;撞见 parked 行却没有它 ⇒ 那只作废不了 ⇒ 不可达(拒在删之前)。 */
|
|
52
|
-
checkpointStore:
|
|
55
|
+
checkpointStore: ChildrenCheckpointStore | undefined;
|
|
53
56
|
registry: Pick<TaskRegistry, "activeDelegationHandles" | "getAccessibleTask" | "writerId">;
|
|
54
57
|
}
|
|
55
58
|
export interface RetireSessionChildrenDeps extends ChildrenSource {
|
|
56
59
|
/** 缺席 = 本部署没有耐久审批(不可能有 parked 行);撞见 parked 行却没有它 ⇒ 那只作废不了,留到剩余(预检本该已经拒掉它)。 */
|
|
57
|
-
checkpointStore:
|
|
60
|
+
checkpointStore: ChildrenCheckpointStore | undefined;
|
|
58
61
|
registry: Pick<TaskRegistry, "stopTask" | "reconcileParkedAgents" | "activeDelegationHandles">;
|
|
59
62
|
sleep?: (ms: number) => Promise<void>;
|
|
60
63
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { partsOf } from "./owner-brands.js";
|
|
2
2
|
import { recoveredSubject } from "./owner-faces.js";
|
|
3
3
|
import { taskSpecIdentityOf } from "./engine-identity.js";
|
|
4
|
+
import { expireCardUnderStoredScope } from "./card-settle-stored-scope.js";
|
|
4
5
|
export const RETIRE_MAX_ROUNDS = 6;
|
|
5
6
|
const RETIRE_BACKOFF_MS = [0, 25, 50, 100, 200, 400];
|
|
6
7
|
export function sessionTenantScope(owner) {
|
|
@@ -74,11 +75,7 @@ async function expireParkedCard(deps, handle, scope) {
|
|
|
74
75
|
const row = await deps.agentStore.get(handle, scope);
|
|
75
76
|
if (row === null || row.status !== "parked" || row.parkedCheckpointToken === undefined)
|
|
76
77
|
return;
|
|
77
|
-
|
|
78
|
-
const cp = await deps.checkpointStore.get(token);
|
|
79
|
-
if (cp === null || cp.status !== "pending")
|
|
80
|
-
return;
|
|
81
|
-
await deps.checkpointStore.expire(token, cp.scope);
|
|
78
|
+
await expireCardUnderStoredScope(deps.checkpointStore, row.parkedCheckpointToken);
|
|
82
79
|
}
|
|
83
80
|
export class SessionChildrenStopIncompleteError extends Error {
|
|
84
81
|
sessionId;
|
|
@@ -129,14 +129,28 @@ export declare function sessionFaceOfHead(sessionId: string, head: SessionHead):
|
|
|
129
129
|
export declare function sessionFaceOfHead(sessionId: string, head: SessionHead | undefined): SessionFace | undefined;
|
|
130
130
|
/**
|
|
131
131
|
* 「这只会话店有没有按 id 读(`headOf`)」—— 全仓**只判这一处**:授权口、各路由的属主门、能力位、起服门、k8s 工作区空间的每 run 读与
|
|
132
|
-
* 一次性迁移 M23(窗内)
|
|
132
|
+
* 一次性迁移 M23(窗内)的回填读的都是这一只判({@link headReadOf} 的在不在 = 它)。缺席 = 店没有实装 `headOf`(读不出属主 / 住客 / 叶子,调用方各按自己的 fail-closed 臂处置)。
|
|
133
|
+
* 收的是任意店形(路由层的封住视图也行:在不在照判,读口在型上调不了 —— 带属主的行只经属主存取器读,`http/owner-access.ts`)。
|
|
133
134
|
*/
|
|
134
|
-
export declare function
|
|
135
|
+
export declare function headReadPresent(store: {
|
|
136
|
+
readonly headOf?: unknown;
|
|
137
|
+
} | undefined): boolean;
|
|
138
|
+
/** 会话店的按 id 读(绑好宿主的那一只;店没有 ⇒ `undefined`)。路由层的封住视图在这里读不出读口(封住的那一格型是 `undefined`,类型墙,`route-ctx.ts` 的 `RouteDeps`)。 */
|
|
139
|
+
export declare function headReadOf(store: {
|
|
140
|
+
readonly headOf?: HeadRead;
|
|
141
|
+
} | undefined): HeadRead | undefined;
|
|
135
142
|
/**
|
|
136
143
|
* 删除门(`DELETE /v1/sessions/:id` 的属主门 + 协调器的化身围栏)的头读 —— **只判这一处**:店交了 `deletionHeadOf`(删除已开始、还没收完的那一行也答得出属主与住客;local)⇒ 它;
|
|
137
|
-
* 否则 ⇒ {@link headReadOf}(删除是一个事务的店,没有删到一半的行)。缺席 = 店没有按 id
|
|
144
|
+
* 否则 ⇒ {@link headReadOf}(删除是一个事务的店,没有删到一半的行)。缺席 = 店没有按 id 读。同 {@link headReadOf}:只收值上交读口的店形。
|
|
138
145
|
*/
|
|
139
|
-
export declare function deletionHeadReadOf(store:
|
|
146
|
+
export declare function deletionHeadReadOf(store: {
|
|
147
|
+
readonly deletionHeadOf?: HeadRead;
|
|
148
|
+
readonly headOf: HeadRead;
|
|
149
|
+
}): HeadRead;
|
|
150
|
+
export declare function deletionHeadReadOf(store: {
|
|
151
|
+
readonly deletionHeadOf?: HeadRead;
|
|
152
|
+
readonly headOf?: HeadRead;
|
|
153
|
+
} | undefined): HeadRead | undefined;
|
|
140
154
|
/** 换装沿用的行格(化身 + fork 键):{@link carriedRowCells} 的答案。`forkKey: null` = 没有可沿用的键(新建行 / 行上本来没有)。 */
|
|
141
155
|
export interface CarriedRowCells {
|
|
142
156
|
readonly incarnation: string;
|
|
@@ -66,9 +66,12 @@ export function leafOfAdmittedHead(head) {
|
|
|
66
66
|
export function sessionFaceOfHead(sessionId, head) {
|
|
67
67
|
return head === undefined ? undefined : sessionFaceFromRow(sessionId, head.owner, head.incarnation);
|
|
68
68
|
}
|
|
69
|
+
export function headReadPresent(store) {
|
|
70
|
+
return typeof store?.headOf === "function";
|
|
71
|
+
}
|
|
69
72
|
export function headReadOf(store) {
|
|
70
73
|
const read = store?.headOf;
|
|
71
|
-
return
|
|
74
|
+
return headReadPresent(store) ? read.bind(store) : undefined;
|
|
72
75
|
}
|
|
73
76
|
export function deletionHeadReadOf(store) {
|
|
74
77
|
const read = store?.deletionHeadOf;
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* 别的抛 = 活流刚收尾的竞态 ⇒ 落空闲臂。
|
|
10
10
|
* · **空闲**:这只会话的完成收件箱(`externalNotificationInboxEntry`,external 键域;脱敏 / 限长在入箱口内)⇒ 下次开流排空成 `task_notification` 帧。
|
|
11
11
|
* 部署没有收件箱 ⇒ 如实交回 `no_inbox`(调用方各自答:HTTP 门 501,引擎完成告警)。
|
|
12
|
-
* · **复核住客**:两臂的投递动作(注入 / 入箱)之前**紧邻**一次头读,与目标的住客比 —— 判官是仓里那一只 `sameOccupant`(`execution-placement.ts`:属主 ∧ 化身,
|
|
12
|
+
* · **复核住客**:两臂的投递动作(注入 / 入箱)之前**紧邻**一次头读,与目标的住客比(读与判在属主存取器的 `occupantRecheckOf`)—— 判官是仓里那一只 `sameOccupant`(`execution-placement.ts`:属主 ∧ 化身,
|
|
13
13
|
* 写口与执行上下文解析器判的也是它):不在 ⇒ `withdrawn: gone`;两样里任一样变了 ⇒ `withdrawn: replaced`。会话店不铸化身(嵌入方自建的店)时两侧都没有令牌,
|
|
14
14
|
* 判官只剩属主那一半:属主没变 ⇒ 投,变了 ⇒ 撤回。这一读是动作之前的最后一次让出:判活读(及活臂刚收尾时的那次注入)都在它前面 ——
|
|
15
15
|
* 令牌永不复用,此刻还是那一位 ⇒ 判活读那一刻也是那一位 ⇒ 判出的 run 是它的 run。
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
import type { ExternalNotificationInput, SessionHead, SubagentToolOptions, TaskStream } from "@sema-agent/core";
|
|
34
34
|
import { type WorkflowCompletionInbox } from "./orchestration/workflow-completion-inbox.js";
|
|
35
35
|
import type { DelegationRootWitness } from "./delegated-session-owner.js";
|
|
36
|
-
import type {
|
|
36
|
+
import type { OwnerAwareSessionStore } from "./security.js";
|
|
37
37
|
import type { Logger } from "./observability/logger.js";
|
|
38
38
|
/** 门的一次投递结局(闭集;调用方 `switch` 穷尽)。 */
|
|
39
39
|
export type SessionNotifyOutcome = {
|
|
@@ -74,8 +74,8 @@ export interface SessionNotifyDoorIo {
|
|
|
74
74
|
/** 本副本持活 core 流的腿(taskId → 流)。 */
|
|
75
75
|
readonly liveRuns: ReadonlyMap<string, Pick<TaskStream, "notify">>;
|
|
76
76
|
readonly inbox: WorkflowCompletionInbox | undefined;
|
|
77
|
-
/**
|
|
78
|
-
readonly
|
|
77
|
+
/** 会话店(复核住客的那一读只经属主存取器)。店没有按 id 头读 ⇒ 调用方先判(路由 501;后台完成 relay 照交,门响亮抛 ⇒ relay 告警)。 */
|
|
78
|
+
readonly sessionStorage: Pick<OwnerAwareSessionStore, "headOf"> | undefined;
|
|
79
79
|
}
|
|
80
80
|
export declare function createSessionNotifyDoor(io: SessionNotifyDoorIo): SessionNotifyDoor;
|
|
81
81
|
/** 委派工具挂载期的完成 sink 的型(取自 core 的选项型,不手抄)。 */
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { externalNotificationInboxEntry } from "./orchestration/workflow-completion-inbox.js";
|
|
2
|
-
import {
|
|
2
|
+
import { occupantRecheckOf } from "./http/owner-access.js";
|
|
3
3
|
import { recordFailOpen } from "./observability/fail-open.js";
|
|
4
4
|
export function createSessionNotifyDoor(io) {
|
|
5
|
+
const occupantNow = occupantRecheckOf(io.sessionStorage);
|
|
5
6
|
async function recheck(target) {
|
|
6
|
-
if (
|
|
7
|
+
if (occupantNow === undefined)
|
|
7
8
|
throw new Error("session notify door: the session store has no head read (headOf); the occupant cannot be re-checked before delivery");
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
return { delivery: "withdrawn", reason: "gone" };
|
|
11
|
-
return sameOccupant(target.occupant, head) ? undefined : { delivery: "withdrawn", reason: "replaced" };
|
|
9
|
+
const verdict = await occupantNow(target.sessionId, target.occupant);
|
|
10
|
+
return verdict === "same" ? undefined : { delivery: "withdrawn", reason: verdict };
|
|
12
11
|
}
|
|
13
12
|
return {
|
|
14
13
|
async deliver(target, event) {
|
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
* (引擎交界的身份格,`engine-identity.ts`):带场景的会话,人 ≠ 属主,原样查 = 落到另一只桶(运维在 health 会话上 PUT 的 deny 对引擎不存在 = 安全轴放宽)。
|
|
5
5
|
*
|
|
6
6
|
* 律(一句):引擎交来的人恰是这只会话属主背后的人 ⇒ 键换成会话属主;否则原样(与今天逐字节同 —— 没带场景的会话属主与人字节相同,两种读法同一个键)。
|
|
7
|
-
* 属主只经会话店的头读一处(`
|
|
7
|
+
* 属主只经会话店的头读一处(会话行是属主的唯一事实;读与判在属主存取器 `http/owner-access.ts` 的 `engineRulesKeyOf`);读不到头(会话不在 / 店不交头读)⇒ 原样。
|
|
8
|
+
* 读写两腿与祖先会话的规则读同经这一只席。
|
|
8
9
|
*/
|
|
9
10
|
import type { SessionPolicyStore } from "@sema-agent/core";
|
|
10
|
-
import type {
|
|
11
|
-
export declare function createEngineSessionRulesSeat(store: SessionPolicyStore,
|
|
11
|
+
import type { OwnerAwareSessionStore } from "./security.js";
|
|
12
|
+
export declare function createEngineSessionRulesSeat(store: SessionPolicyStore, sessions: Pick<OwnerAwareSessionStore, "headOf"> | undefined): SessionPolicyStore;
|
|
12
13
|
//# sourceMappingURL=session-rules-seat.d.ts.map
|
|
@@ -1,14 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const keyFor = async (sessionId, principal) => {
|
|
5
|
-
if (principal === undefined || headOf === undefined)
|
|
6
|
-
return principal;
|
|
7
|
-
const owner = (await headOf(sessionId))?.owner;
|
|
8
|
-
if (owner === undefined || owner === null)
|
|
9
|
-
return principal;
|
|
10
|
-
return personOf(recoveredSubject(owner)) === principal ? owner : principal;
|
|
11
|
-
};
|
|
1
|
+
import { engineRulesKeyOf } from "./http/owner-access.js";
|
|
2
|
+
export function createEngineSessionRulesSeat(store, sessions) {
|
|
3
|
+
const keyFor = (sessionId, principal) => engineRulesKeyOf(sessions, sessionId, principal);
|
|
12
4
|
return {
|
|
13
5
|
getRules: async (sessionId, principal) => store.getRules(sessionId, await keyFor(sessionId, principal)),
|
|
14
6
|
putRules: async (sessionId, principal, rules, opts) => store.putRules(sessionId, await keyFor(sessionId, principal), rules, opts),
|
package/dist/session-sync.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { anchorEntryDeclared, classifySyncRelationship } from "./session-sync-kernel.js";
|
|
2
2
|
import { recordFailOpen } from "./observability/fail-open.js";
|
|
3
3
|
import { loosenReasons, normalizeRules } from "@sema-agent/core";
|
|
4
|
-
import {
|
|
4
|
+
import { sessionFactsReadOf } from "./http/owner-access.js";
|
|
5
5
|
import { fileHistoryBlobExchangeOf, judgeSessionRulesWrite } from "@sema-agent/core";
|
|
6
6
|
export class FileHistoryUnreadableError extends Error {
|
|
7
7
|
sessionId;
|
|
@@ -75,8 +75,8 @@ export async function exportSessionManifest(sessionId, srcBackend, historyStore)
|
|
|
75
75
|
const entryIds = await listEntryIds(sessionId);
|
|
76
76
|
if (entryIds === null)
|
|
77
77
|
return null;
|
|
78
|
-
const
|
|
79
|
-
const leafId =
|
|
78
|
+
const sessionFactsOf = sessionFactsReadOf(session);
|
|
79
|
+
const leafId = sessionFactsOf ? ((await sessionFactsOf(sessionId))?.leafId ?? null) : null;
|
|
80
80
|
const fs = historyStore;
|
|
81
81
|
const fileHistory = await exportedFileHistory(fs, sessionId);
|
|
82
82
|
const policyStore = srcBackend.sessionPolicy();
|
|
@@ -23,9 +23,9 @@
|
|
|
23
23
|
* 会话在没有记号的检出里续跑时,项目随每一次不带 `projectId` 的提交丢掉。现在:一个值进引擎、一格存引擎的答案、下一条腿读那一格。
|
|
24
24
|
*
|
|
25
25
|
* ## 不在这里的(如实)
|
|
26
|
-
* · **记忆项目层**(单用户 `userrepo:` 键,`project-identity.ts`
|
|
27
|
-
*
|
|
28
|
-
*
|
|
26
|
+
* · **记忆项目层**(单用户 `userrepo:` 键,`project-identity.ts`)不读这一格:它在装配期(任何帧之前)交给 `TaskSpec.memory.projectKey`,
|
|
27
|
+
* 身份是**检出**的函数 —— 检出带 `.sema/project` 记号 ⇒ 按记号键控,读法就是引擎判官读的同一只(core `resolveProjectId`,同一任务根);
|
|
28
|
+
* 没有记号 ⇒ git 根回落。行上 `source: "declared"` 的项目不改项目层的键(引擎 7.58.0 记忆自己的读法同:「只声明的身份不改记忆的键」)。
|
|
29
29
|
* · 多租户记忆分区(`proj:<租户>/<projectId>`)与登记簿种子读的是**这次提交**的 `projectId`(按提交挑盘,修前即如此),不读行。
|
|
30
30
|
* · 行上的项目还有一位读者:会话视图 `GET /v1/sessions/:id` 体顶层的 `projectId`(唯一读口 {@link readSessionProjectId},与视图的 `agent` 同一次视图读)。
|
|
31
31
|
* `root` / `cwd` / `entered` / `remote` 今天只存不读;fork 出的新会话从「没有记录」起(项目声明按引擎契约每腿都带,fork 的首腿由调用方带上)。
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **店的持久性准入**(起服第 2 段;只读,拒 = 抛 {@link StoreNotDurableError},机器码 `config.store_not_durable`)—— 店的拒启判都在这一只函数里,判两根:
|
|
3
|
+
*
|
|
4
|
+
* ## 规则(一句)
|
|
5
|
+
* 引擎被告知「活过一次替换」的东西,它落的那只根就必须活过替换:
|
|
6
|
+
* · **数据根**(local 后端:会话店 + 检查点店落在它上面)—— local 店的声明 `durable`(`localStoreDeclarationOf`)∧ 数据根介质判 `ephemeral` ⇒ 拒;
|
|
7
|
+
* · **显式的封签钥匙目录**(`SEALED_KEY_DIR`,数据根之外单设的那一只)—— 停驻审批在场(`DURABLE_APPROVAL`)∧ 检查点店声明 `durable`(SQL 后端恒是;local 后端随它的声明)
|
|
8
|
+
* ∧ 钥匙目录介质判 `ephemeral` ⇒ 拒:停驻任务的请求凭据封在那只目录里的钥匙上,钥匙随下一次替换而去,之前停驻的每一条续跑都答 `key_not_held` —— 行活着、钥匙没了。
|
|
9
|
+
* 缺省的钥匙目录(`<数据根>/sealed-key`)跟数据根同判:local 后端由数据根那一臂判;SQL 后端的缺省目录只进读数与起服 warn(无卷的无状态副本是 SQL 部署的常形,
|
|
10
|
+
* 没写 `SEALED_KEY_DIR` 的部署没有对钥匙目录作过承诺 —— 与「声明了的目录建不了店 ⇒ 拒,缺省 ⇒ warn」同一条律)。
|
|
11
|
+
* file 记忆根不进判据(只进读数):记忆丢了是召回变差,不让任何一笔已承诺的停驻悬空;单卷部署(只给数据根挂卷)也不因它被拒。
|
|
12
|
+
* 判不出的介质(`unknown`:非 Linux、读不懂的挂载)不拒 —— 缺证据不放宽声明,也不凭空拒。
|
|
13
|
+
*
|
|
14
|
+
* ⚠️ 位置即契约:起服第 2 段**车道合成之后**(数据根那一臂读车道:`callerOwnsWorkerDisk`)、**本进程的第一笔写之前** —— 配置根的一次性迁移(配置根缺省就是数据根)、
|
|
15
|
+
* 配置根供给(`SEMA_CONFIG_DIR`)、保管店铸钥匙与第 3 段开店都在它之后:拒的时候数据根 / 配置根 / 钥匙目录上没有本服务写下的任何东西。介质 = 第 2 段段首的取证
|
|
16
|
+
* (`captureBootStoreEvidence`)那一份,不重读挂载表。放行时交回 local 店的那一份声明(非 local 后端 ⇒ `undefined`);第 3 段开店按同一组输入经 `localStoreDeclarationOf` 交给两半店。
|
|
17
|
+
* 第 9 段布线审计的同码一臂是「引擎读到的会话店声明 × 数据根介质」的通用对表(也管没有后端的 env-only 形);local 部署走到那里时,同一组输入已在这里判过、判得一样。
|
|
18
|
+
* run-local 没有起服取证(介质 unknown ⇒ 不拒),不调。
|
|
19
|
+
*/
|
|
20
|
+
import type { StoreBackend as StoreBackendWord } from "@sema-agent/settings-schema/execution-lanes";
|
|
21
|
+
import { type LocalStoreDeclaration, type LocalStoreDeclarationDeployment } from "./local-store-declaration.js";
|
|
22
|
+
import { type StoreEvidence } from "./store-posture.js";
|
|
23
|
+
/** 准入读的部署事实(`ServiceConfig` 结构上满足):local 店声明的那几键 + 后端词 + 停驻审批在场 + 显式钥匙目录。 */
|
|
24
|
+
export type StoreAdmissionDeployment = LocalStoreDeclarationDeployment & {
|
|
25
|
+
readonly dbBackend: StoreBackendWord;
|
|
26
|
+
readonly durableApproval: boolean;
|
|
27
|
+
readonly sealedKeyDir?: string;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* 🔴 店的持久性准入(规则与位置见文件头)。两臂各判、一次说完:任一臂不过 ⇒ 抛一只 {@link StoreNotDurableError},拒句逐条列出不过的那几臂与各自的出路。
|
|
31
|
+
*/
|
|
32
|
+
export declare function admitStoreDurability(deployment: StoreAdmissionDeployment, evidence: Pick<StoreEvidence, "dataRoot" | "sealedKey">): LocalStoreDeclaration | undefined;
|
|
33
|
+
//# sourceMappingURL=store-admission.d.ts.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describeLocalStoreDeclaration, localStoreDeclarationOf } from "./local-store-declaration.js";
|
|
2
|
+
import { STORE_NOT_DURABLE_CODE, StoreNotDurableError, describeStoreRoot } from "./store-posture.js";
|
|
3
|
+
function checkpointDeclarationOf(backend, local) {
|
|
4
|
+
switch (backend) {
|
|
5
|
+
case "mysql":
|
|
6
|
+
case "pg":
|
|
7
|
+
return "durable";
|
|
8
|
+
case "local":
|
|
9
|
+
if (local === undefined)
|
|
10
|
+
throw new Error("store admission: a local backend without its declaration — wiring defect");
|
|
11
|
+
return local.durability === "durable" ? "durable" : "process-local";
|
|
12
|
+
case "memory":
|
|
13
|
+
return "none";
|
|
14
|
+
default: {
|
|
15
|
+
const unhandled = backend;
|
|
16
|
+
throw new Error(`store admission: unknown DB_BACKEND word ${String(unhandled)}`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function keyRootWaysOut(backend, path) {
|
|
21
|
+
const volume = `mount a persistent volume at ${path} (every replica must see the same directory)`;
|
|
22
|
+
const noParks = "or turn parked approvals off (DURABLE_APPROVAL=false — a task that needs an approval then waits on a live connection only)";
|
|
23
|
+
return backend === "local"
|
|
24
|
+
? `Three ways out: ${volume}; or declare the local store ephemeral (LOCAL_STORE_DURABILITY=ephemeral — sessions and parked approvals then do not survive a replacement); ${noParks}.`
|
|
25
|
+
: `Two ways out: ${volume}; ${noParks}.`;
|
|
26
|
+
}
|
|
27
|
+
export function admitStoreDurability(deployment, evidence) {
|
|
28
|
+
const local = deployment.dbBackend === "local" ? localStoreDeclarationOf(deployment, evidence.dataRoot.verdict) : undefined;
|
|
29
|
+
const findings = [];
|
|
30
|
+
if (local !== undefined && local.durability === "durable" && evidence.dataRoot.verdict === "ephemeral") {
|
|
31
|
+
const dataRoot = evidence.dataRoot;
|
|
32
|
+
findings.push(`${STORE_NOT_DURABLE_CODE}: the local store declares itself durable (${describeLocalStoreDeclaration(local)}) while its data root does not survive a container / Pod replacement ` +
|
|
33
|
+
`(${describeStoreRoot({ root: "data", ...dataRoot })}). A durable declaration on an ephemeral medium is a claim nothing can honor — sessions and parked approvals would vanish with the next replacement while the engine was told they survive. ` +
|
|
34
|
+
`Three ways out: mount a persistent volume at ${dataRoot.path ?? "the data root"} (or point LOCAL_DATA_ROOT at one); or declare the local store ephemeral ` +
|
|
35
|
+
`(LOCAL_STORE_DURABILITY=ephemeral — sessions, parked approvals and memory then do not survive a container / Pod replacement); or keep nothing on disk (DB_BACKEND=memory).`);
|
|
36
|
+
}
|
|
37
|
+
const parks = checkpointDeclarationOf(deployment.dbBackend, local);
|
|
38
|
+
if (deployment.sealedKeyDir !== undefined && deployment.durableApproval && parks === "durable" && evidence.sealedKey.verdict === "ephemeral") {
|
|
39
|
+
const keyRoot = evidence.sealedKey;
|
|
40
|
+
findings.push(`${STORE_NOT_DURABLE_CODE}: parked approvals are declared durable (${deployment.dbBackend === "local" ? "the local checkpoint store's declaration" : "the checkpoint store is the SQL database"}, DURABLE_APPROVAL on) ` +
|
|
41
|
+
`while the declared sealing-key directory SEALED_KEY_DIR does not survive a container / Pod replacement (${describeStoreRoot({ root: "sealed_key", ...keyRoot })}). ` +
|
|
42
|
+
"A parked task's request credentials are sealed to a key kept in that directory — after the next replacement the parked rows are still there and the key is not: every task parked before it answers key_not_held on resume. " +
|
|
43
|
+
keyRootWaysOut(deployment.dbBackend, keyRoot.path ?? deployment.sealedKeyDir));
|
|
44
|
+
}
|
|
45
|
+
if (findings.length === 0)
|
|
46
|
+
return local;
|
|
47
|
+
throw new StoreNotDurableError(`store durability admission failed — refusing to start before any store or sealed key is written:\n${findings.map((f) => ` · ${f}`).join("\n")}`);
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=store-admission.js.map
|