@sema-agent/server 7.135.0 → 7.137.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 +237 -2
- package/USAGE.md +146 -41
- package/deploy/sema-up/README.md +15 -1
- package/deploy/sema-up/chart/templates/NOTES.txt +1 -1
- package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +7 -1
- package/deploy/sema-up/chart/values.yaml +1 -1
- package/deploy/sema-up/stack/sema-stack-cluster.yaml +7 -1
- package/dist/approval-card.d.ts +60 -4
- package/dist/approval-card.js +18 -1
- package/dist/approval-content-kind.d.ts +7 -0
- package/dist/approval-content-kind.js +8 -1
- package/dist/approval-gate-currency.d.ts +48 -6
- package/dist/approval-gate-currency.js +16 -9
- package/dist/approval-hmac.d.ts +13 -1
- package/dist/approval-hmac.js +1 -1
- package/dist/approval.js +3 -3
- package/dist/auth-bridge.d.ts +34 -5
- package/dist/auth-bridge.js +22 -5
- package/dist/boot/budget-tracing.d.ts +1 -2
- package/dist/boot/budget-tracing.js +2 -2
- package/dist/boot/execution-env.d.ts +24 -1
- package/dist/boot/execution-env.js +69 -6
- package/dist/boot/org-memory.js +1 -1
- package/dist/boot/reapers.d.ts +3 -0
- package/dist/boot/reapers.js +15 -1
- package/dist/boot/resolve-spec.d.ts +4 -0
- package/dist/boot/resolve-spec.js +24 -22
- package/dist/boot/runner-deps.js +4 -2
- package/dist/boot/stage-03-store-backend.d.ts +1 -1
- package/dist/boot/stage-04-stores.d.ts +2 -2
- package/dist/boot/stage-05-execution-env.d.ts +4 -2
- package/dist/boot/stage-05-execution-env.js +11 -2
- package/dist/boot/stage-06-runners.d.ts +4 -2
- package/dist/boot/stage-06-runners.js +5 -1
- package/dist/boot/stage-07-capability-layer.d.ts +5 -2
- package/dist/boot/stage-07-capability-layer.js +1 -1
- package/dist/boot/stage-08-reapers.d.ts +5 -2
- package/dist/boot/stage-08-reapers.js +2 -1
- package/dist/boot/stage-09-leader.d.ts +5 -2
- package/dist/boot/stage-10-http-server.d.ts +5 -2
- package/dist/boot/stage-10-http-server.js +3 -2
- package/dist/boot/stores.d.ts +1 -1
- package/dist/boot/stores.js +2 -3
- package/dist/boot/web-admin.js +10 -17
- package/dist/brain.js +12 -3
- package/dist/broker/broker.d.ts +89 -0
- package/dist/broker/broker.js +287 -0
- package/dist/broker/constants.d.ts +39 -0
- package/dist/broker/constants.js +15 -0
- package/dist/broker/http1.d.ts +111 -0
- package/dist/broker/http1.js +256 -0
- package/dist/broker/lease-book.d.ts +87 -0
- package/dist/broker/lease-book.js +142 -0
- package/dist/broker/lease-file.d.ts +60 -0
- package/dist/broker/lease-file.js +127 -0
- package/dist/broker/main.d.ts +2 -0
- package/dist/broker/main.js +20 -0
- package/dist/budget.d.ts +3 -3
- package/dist/budget.js +4 -0
- package/dist/capabilities/composite-members.d.ts +2 -1
- package/dist/capabilities/composite-members.js +2 -1
- package/dist/capabilities/engine-capabilities.js +1 -0
- package/dist/capabilities/mcp-probe.d.ts +5 -4
- package/dist/capabilities/scenarios.d.ts +23 -8
- package/dist/capabilities/scenarios.js +13 -8
- package/dist/capabilities/skills.d.ts +28 -5
- package/dist/capabilities/skills.js +8 -0
- package/dist/capability-facts.d.ts +3 -7
- package/dist/capability-facts.js +3 -5
- package/dist/center-credential-renewer.js +5 -1
- package/dist/center-credential.d.ts +18 -4
- package/dist/center-credential.js +18 -3
- package/dist/checkpoint-ctx-seal-m28.d.ts +3 -10
- package/dist/checkpoint-ctx-seal-m28.js +1 -1
- package/dist/checkpoint-identity.d.ts +3 -3
- package/dist/config-catalog.js +21 -16
- package/dist/config-d-migration.js +1 -1
- package/dist/config-invariants.d.ts +1 -1
- package/dist/config-invariants.js +8 -0
- package/dist/config-lkg.d.ts +0 -38
- package/dist/config-lkg.js +1 -83
- package/dist/config-types.d.ts +21 -4
- package/dist/config.d.ts +1 -1
- package/dist/config.js +60 -16
- package/dist/deployment-governance.d.ts +2 -2
- package/dist/deployment-governance.js +7 -9
- package/dist/deployment-shape.d.ts +1 -1
- package/dist/device-ws-protocol.d.ts +1 -0
- package/dist/device-ws-protocol.js +2 -1
- package/dist/elicitation.d.ts +8 -7
- package/dist/elicitation.js +9 -8
- package/dist/file-history-disclosure.d.ts +8 -4
- package/dist/file-history-disclosure.js +24 -6
- package/dist/fleet-client.d.ts +32 -1
- package/dist/fleet-client.js +24 -4
- package/dist/fleet-lease.d.ts +7 -3
- package/dist/fleet-lease.js +31 -23
- package/dist/hooks/classic-stations.d.ts +11 -54
- package/dist/hooks/hook-llm.js +14 -8
- package/dist/hooks/hook-runner.d.ts +4 -4
- package/dist/hooks/hook-runner.js +2 -2
- package/dist/http/admission.js +10 -7
- package/dist/http/approval-sweeps.js +4 -3
- package/dist/http/caller-trust.d.ts +2 -1
- package/dist/http/caller-trust.js +1 -3
- package/dist/http/checkpoint-ctx.d.ts +76 -16
- package/dist/http/checkpoint-ctx.js +45 -1
- package/dist/http/dispatch.js +24 -19
- package/dist/http/durable-run-leg.d.ts +5 -1
- package/dist/http/durable-run-leg.js +1 -1
- package/dist/http/health-self-report.d.ts +2 -2
- package/dist/http/mcp-remote-text.d.ts +5 -0
- package/dist/http/mcp-remote-text.js +7 -0
- package/dist/http/principal-gate.d.ts +15 -10
- package/dist/http/principal-gate.js +48 -9
- package/dist/http/resume-ctx-read.d.ts +2 -16
- package/dist/http/resume-ctx-read.js +2 -3
- package/dist/http/resume-legs.d.ts +2 -20
- package/dist/http/resume-legs.js +52 -38
- package/dist/http/route-ctx.d.ts +8 -2
- package/dist/http/routes/approvals-assistant.d.ts +0 -2
- package/dist/http/routes/approvals-assistant.js +65 -16
- package/dist/http/routes/capabilities.js +11 -1
- package/dist/http/routes/mcp-reconnect.d.ts +1 -1
- package/dist/http/routes/mcp-reconnect.js +5 -7
- package/dist/http/routes/runs.js +4 -3
- package/dist/http/routes/session-sync.js +70 -12
- package/dist/http/routes/sessions.js +20 -15
- package/dist/http/routes/tasks.js +8 -6
- package/dist/http/server.d.ts +4 -0
- package/dist/http/stored-body-replay.d.ts +3 -3
- package/dist/http/stored-body-replay.js +6 -1
- package/dist/http/submit-ctx.js +1 -1
- package/dist/http/wire-types.d.ts +57 -30
- package/dist/identity-source.d.ts +65 -0
- package/dist/identity-source.js +45 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/memory-store-refusal.d.ts +3 -3
- package/dist/model-entry-refusal.d.ts +2 -0
- package/dist/model-entry-refusal.js +11 -0
- package/dist/model-route-gap.d.ts +49 -4
- package/dist/model-route-gap.js +34 -1
- package/dist/model-select.d.ts +6 -17
- package/dist/model-select.js +0 -3
- package/dist/namespaced-rule-name.d.ts +1 -1
- package/dist/object-store-buckets.d.ts +13 -0
- package/dist/object-store-buckets.js +3 -0
- package/dist/observability/fail-open.d.ts +19 -7
- package/dist/observability/fail-open.js +19 -7
- package/dist/observability/metrics.js +2 -2
- package/dist/observability/principal-context.d.ts +4 -1
- package/dist/observability/principal-context.js +6 -1
- package/dist/observability/run-terminal.js +1 -0
- package/dist/operator-ask.d.ts +2 -2
- package/dist/org-delivery-gate.js +1 -1
- package/dist/org-memory-admission.d.ts +2 -2
- package/dist/org-memory-admission.js +39 -2
- package/dist/parked-decide.d.ts +4 -3
- package/dist/parked-decide.js +29 -9
- package/dist/peer-directory.d.ts +2 -1
- package/dist/peer-directory.js +7 -5
- package/dist/permission-rule-list.d.ts +2 -2
- package/dist/plugins/approval-ask-store-sql.d.ts +8 -0
- package/dist/plugins/approval-ask-store-sql.js +3 -0
- package/dist/plugins/blob-backend.js +2 -1
- package/dist/plugins/checkpoint-index-columns.d.ts +50 -1
- package/dist/plugins/checkpoint-index-columns.js +28 -0
- package/dist/plugins/checkpoint-store-sql.d.ts +46 -12
- package/dist/plugins/checkpoint-store-sql.js +64 -35
- package/dist/plugins/file-history-store-sql.d.ts +6 -0
- package/dist/plugins/file-history-store-sql.js +37 -37
- package/dist/plugins/file-run-store.js +8 -2
- package/dist/plugins/k8s-exec-protocol.d.ts +69 -13
- package/dist/plugins/k8s-exec-protocol.js +116 -28
- package/dist/plugins/k8s-pod-delete-failure.d.ts +25 -0
- package/dist/plugins/k8s-pod-delete-failure.js +17 -0
- package/dist/plugins/k8s-pod-reaper.d.ts +112 -0
- package/dist/plugins/k8s-pod-reaper.js +122 -0
- package/dist/plugins/local-checkpoint-store.d.ts +3 -6
- package/dist/plugins/local-checkpoint-store.js +7 -6
- package/dist/plugins/memory-run-store.js +2 -0
- package/dist/plugins/one-time-migrations.d.ts +49 -37
- package/dist/plugins/one-time-migrations.js +87 -128
- package/dist/plugins/permission-rule-store-file.js +2 -2
- package/dist/plugins/permission-rule-store-sql.d.ts +17 -17
- package/dist/plugins/permission-rule-store-sql.js +16 -17
- package/dist/plugins/pg-pool.js +4 -0
- package/dist/plugins/pg-session-storage.js +2 -2
- package/dist/plugins/remote-env-adb.d.ts +3 -0
- package/dist/plugins/remote-env-adb.js +1 -0
- package/dist/plugins/remote-env-device.d.ts +3 -0
- package/dist/plugins/remote-env-device.js +4 -0
- package/dist/plugins/remote-env-e2b.d.ts +3 -0
- package/dist/plugins/remote-env-e2b.js +1 -0
- package/dist/plugins/remote-env-file-error.d.ts +8 -1
- package/dist/plugins/remote-env-file-error.js +7 -1
- package/dist/plugins/remote-env-host.d.ts +7 -1
- package/dist/plugins/remote-env-host.js +7 -1
- package/dist/plugins/remote-env-k8s.d.ts +154 -14
- package/dist/plugins/remote-env-k8s.js +300 -21
- package/dist/plugins/remote-env-local-docker.d.ts +3 -0
- package/dist/plugins/remote-env-local-docker.js +1 -0
- package/dist/plugins/remote-env-ssh.d.ts +3 -0
- package/dist/plugins/remote-env-ssh.js +1 -0
- package/dist/plugins/run-store-sql.js +10 -4
- package/dist/plugins/s3-answer.d.ts +15 -0
- package/dist/plugins/s3-answer.js +5 -0
- package/dist/plugins/session-ownership.d.ts +17 -3
- package/dist/plugins/session-ownership.js +19 -5
- package/dist/plugins/snapshot-bucket-presence.d.ts +39 -4
- package/dist/plugins/snapshot-bucket-presence.js +35 -7
- package/dist/plugins/sql-errors.d.ts +60 -0
- package/dist/plugins/sql-errors.js +64 -1
- package/dist/plugins/store-contracts.d.ts +10 -1
- package/dist/plugins/store-fault.d.ts +4 -0
- package/dist/plugins/store-fault.js +10 -0
- package/dist/plugins/tidb-pool.js +8 -1
- package/dist/plugins/tidb-session-store.js +2 -2
- package/dist/plugins/web-search.d.ts +1 -1
- package/dist/plugins/web-search.js +2 -2
- package/dist/request-declaration-refusal.d.ts +94 -0
- package/dist/request-declaration-refusal.js +78 -0
- package/dist/request-image-urls.d.ts +6 -0
- package/dist/request-image-urls.js +27 -0
- package/dist/request-key-closure.d.ts +5 -0
- package/dist/request-key-closure.js +5 -2
- package/dist/request-value-closure.d.ts +31 -2
- package/dist/request-value-closure.js +7 -1
- package/dist/rule-entry-list.d.ts +3 -2
- package/dist/rule-entry-list.js +2 -2
- package/dist/rules-consent.js +7 -4
- package/dist/run-local.d.ts +2 -2
- package/dist/runs.js +2 -1
- package/dist/runtime-caps-resolver.js +1 -1
- package/dist/runtime-governance.d.ts +5 -5
- package/dist/sandbox-git-lease.d.ts +49 -0
- package/dist/sandbox-git-lease.js +60 -0
- package/dist/sandbox-git-plane.d.ts +84 -0
- package/dist/sandbox-git-plane.js +105 -0
- package/dist/sandbox-pkg-source.d.ts +28 -1
- package/dist/sandbox-pkg-source.js +1 -2
- package/dist/sealed-key.d.ts +19 -0
- package/dist/sealed-key.js +47 -4
- package/dist/security.d.ts +16 -4
- package/dist/security.js +8 -3
- package/dist/server-secret-env.d.ts +6 -0
- package/dist/server-secret-env.js +14 -0
- package/dist/session-error-wire.d.ts +14 -6
- package/dist/session-error-wire.js +2 -1
- package/dist/session-incarnation.d.ts +16 -5
- package/dist/session-incarnation.js +7 -4
- package/dist/session-policy-wire.d.ts +1 -1
- package/dist/session-policy-wire.js +2 -2
- package/dist/session-row.d.ts +3 -17
- package/dist/session-row.js +4 -0
- package/dist/session-sync.d.ts +15 -3
- package/dist/session-sync.js +5 -2
- package/dist/spec-fields.d.ts +43 -13
- package/dist/spec-fields.js +33 -12
- package/dist/stored-request-prompt-profile-m30.d.ts +49 -0
- package/dist/stored-request-prompt-profile-m30.js +25 -0
- package/dist/task-a2a.d.ts +52 -81
- package/dist/task-a2a.js +219 -89
- package/dist/task-mcp.d.ts +7 -15
- package/dist/task-mcp.js +16 -51
- package/dist/task-settings.d.ts +2 -2
- package/dist/tool-approval.d.ts +35 -12
- package/dist/tool-approval.js +59 -39
- package/dist/tool-rule-names.d.ts +5 -0
- package/dist/trace/core-keyset-guard.d.ts +5 -5
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +2 -0
- package/dist/trace/exit-seat.js +37 -13
- package/dist/trace/ledger-sink.d.ts +4 -5
- package/dist/trace/ledger-sink.js +3 -7
- package/dist/trace/project.d.ts +37 -3
- package/dist/trace/project.js +20 -6
- package/dist/trace/redact.d.ts +40 -12
- package/dist/trace/redact.js +332 -128
- package/dist/trace/self-minted-wire.d.ts +641 -0
- package/dist/trace/self-minted-wire.js +201 -0
- package/dist/trace/wire-projection-faces.d.ts +5 -3
- package/dist/trace/wire-projection-faces.js +7 -3
- package/dist/url-credential-refusal.d.ts +52 -0
- package/dist/url-credential-refusal.js +14 -0
- package/dist/url-refusal.d.ts +13 -4
- package/dist/url-refusal.js +8 -2
- package/dist/web-admin-client/apply-effective.js +0 -1
- package/dist/web-admin-client/center-request.d.ts +20 -2
- package/dist/web-admin-client/center-request.js +40 -5
- package/dist/web-admin-client/http-client.js +6 -6
- package/dist/web-admin-client/types.d.ts +5 -5
- package/dist/workspace-space.d.ts +27 -0
- package/dist/workspace-space.js +26 -0
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +11 -5
- package/package.json +4 -3
- package/scripts/drain-check-local.mjs +2 -1
- package/scripts/drain-check.mysql.sql +3 -1
- package/scripts/drain-check.pg.sql +3 -1
- package/skills/schedule.md +1 -1
- package/wire-changes/server@7.104.0.json +134 -0
- package/wire-changes/server@7.105.0.json +53 -0
- package/wire-changes/server@7.106.0.json +475 -0
- package/wire-changes/server@7.107.0.json +64 -0
- package/wire-changes/server@7.108.0.json +153 -0
- package/wire-changes/server@7.109.0.json +46 -0
- package/wire-changes/server@7.110.0.json +90 -0
- package/wire-changes/server@7.111.0.json +86 -0
- package/wire-changes/server@7.112.0.json +60 -0
- package/wire-changes/server@7.113.0.json +26 -0
- package/wire-changes/server@7.114.0.json +26 -0
- package/wire-changes/server@7.115.0.json +97 -0
- package/wire-changes/server@7.116.0.json +218 -0
- package/wire-changes/server@7.116.1.json +26 -0
- package/wire-changes/server@7.117.0.json +65 -0
- package/wire-changes/server@7.118.0.json +129 -0
- package/wire-changes/server@7.119.0.json +65 -0
- package/wire-changes/server@7.120.0.json +89 -0
- package/wire-changes/server@7.121.0.json +219 -0
- package/wire-changes/server@7.122.0.json +54 -0
- package/wire-changes/server@7.123.0.json +26 -0
- package/wire-changes/server@7.124.0.json +336 -0
- package/wire-changes/server@7.124.1.json +26 -0
- package/wire-changes/server@7.125.0.json +111 -0
- package/wire-changes/server@7.126.0.json +285 -0
- package/wire-changes/server@7.127.0.json +77 -0
- package/wire-changes/server@7.127.1.json +26 -0
- package/wire-changes/server@7.127.2.json +53 -0
- package/wire-changes/server@7.128.0.json +26 -0
- package/wire-changes/server@7.129.0.json +106 -0
- package/wire-changes/server@7.129.1.json +26 -0
- package/wire-changes/server@7.130.0.json +26 -0
- package/wire-changes/server@7.131.0.json +118 -0
- package/wire-changes/server@7.132.0.json +226 -0
- package/wire-changes/server@7.133.0.json +1155 -0
- package/wire-changes/server@7.134.0.json +107 -0
- package/wire-changes/server@7.135.0.json +174 -0
- package/wire-changes/server@7.136.0.json +448 -0
- package/wire-changes/server@7.137.0.json +825 -0
- package/dist/url-credential.d.ts +0 -52
- package/dist/url-credential.js +0 -49
package/USAGE.md
CHANGED
|
@@ -123,6 +123,8 @@ ANTHROPIC_API_KEY=sk-ant-… # 可选:ANTHROPIC_BASE_URL / ANTHROPIC_VERSION /
|
|
|
123
123
|
中心 / config.d 下发的候选目录里有解析不到的条目 ⇒ 候选的模型面这一组被拒、上一代模型面继续服务(同候选其余组照落)。
|
|
124
124
|
- **目录与 `MODEL_ID` 并存**:目录一旦被采纳就整张换掉 env 表 —— 缺省模型取目录缺省,env 的 `MODEL_ID` 不服务。此时起服**不拒**(哪怕没设网关:目录条目自带 `baseUrl` 即可),
|
|
125
125
|
而是说一行 warn `config_env_set_but_ineffective`(`env: "MODEL_ID"`,点名目录缺省是谁),让你知道改 `MODEL_ID` 没有效果 —— 目录第一次被采纳的那一拍说,恰好一次(启动时就采纳、或目录晚到 / 刷新时才第一次采纳都一样)。`MODEL_ID` 仍是目录缺席时的兜底(目录迟到且无上一份 / 候选被整拒 / 没有启用条目时由它服务,届时按 env 表判)。
|
|
126
|
+
- **anthropic 车道两把凭据一名一义**:`ANTHROPIC_API_KEY` ⇒ 只发 `x-api-key` 头;`ANTHROPIC_AUTH_TOKEN` ⇒ 只发 `Authorization: Bearer` 头;两把都非空 ⇒ 起服拒(`[config.anthropic_credential_conflict]`,
|
|
127
|
+
点名两键、不回显值)—— 只留你的端点要的那一把。空串 = 未设(compose 透传的 `${VAR:-}` 不算同设)。会话标题 / hook 的直连腿与主请求读同一只判断,发同一种头。
|
|
126
128
|
- **凭据什么时候生效**:网关 key `MODEL_API_KEY`(以及 `ANTHROPIC_API_KEY` / `ANTHROPIC_AUTH_TOKEN`)在启动时读一次,**换值 = 重启本服务**
|
|
127
129
|
—— 没有热应用通道(以前的版本同样如此:这几把 key 只来自启动时的环境变量;引擎 7.51.0 起模型路由的构造配置也只读一次)。
|
|
128
130
|
目录条目**自己的**凭据(`apiKeyEnv` 点名的变量、`sealedApiKey`)跟着中心 / config.d 的模型面热应用走,不用重启:新起的任务与调用
|
|
@@ -151,7 +153,7 @@ ANTHROPIC_API_KEY=sk-ant-… # 可选:ANTHROPIC_BASE_URL / ANTHROPIC_VERSION /
|
|
|
151
153
|
引擎(core)不读 `MODEL_*` / `ANTHROPIC_*` 这些键,键表和读法属本服务;壳首启、客户端运行时要在引擎起之前问「这个环境模型走哪条车道、端点 / 凭据取哪一对键」,读这一只,不各抄键名。
|
|
152
154
|
`MODEL_ROUTE_ENV_KEYS` 是九把路由键的闭表(网关端点 / 备用端点 / 网关凭据、provider、主 / cheap 模型 id、三把 Anthropic 键),`readModelRouteEnv(env)` 是读口(`env` 交整张 `process.env` 或等价快照;空串 = 未设),
|
|
153
155
|
`laneAndEndpointFor(env, model, routes)` 是值级端口:`routes` 是引擎路由表的原型(`anthropic` 键在场 ⇔ anthropic 路由装上),答 `{ lane, endpointEnvName, endpointDeclared, apiKeyEnvName }` ——
|
|
154
|
-
**只有键名与在场性,没有任何 env 值**(端点与凭据的值调用方自己从 env 取)
|
|
156
|
+
**只有键名与在场性,没有任何 env 值**(端点与凭据的值调用方自己从 env 取)。anthropic 凭据键与引擎装配读同一只判断(一名一义:设了哪一把就报哪一把;两把同设 ⇒ 抛起服的那一句拒句,不替你挑一把);网关凭据 `MODEL_API_KEY` 只与网关端点配对,
|
|
155
157
|
网关端点没设就不报凭据键(与起服不装载那把 key 同向)。
|
|
156
158
|
|
|
157
159
|
**可选 — 韧性栈:分级超时 + 断路器(core 1.38,叠在 failover 之下)**
|
|
@@ -417,10 +419,10 @@ MODEL_ID=<your-model-name> MODEL_CHEAP_ID=<your-cheap-model-name>
|
|
|
417
419
|
|
|
418
420
|
**可选 — 开发者模式 building blocks(core 1.43,默认中立)**
|
|
419
421
|
```bash
|
|
420
|
-
#
|
|
421
|
-
MODEL_CODE_ROLES=default,subagent #
|
|
422
|
+
# 给指定角色按词点名引擎目录的「编码角色基底」(去品牌的通用编码提示词:理解后改/最小复杂度/危险操作谨慎/验证后报告)。
|
|
423
|
+
MODEL_CODE_ROLES=default,subagent # 不设=全中立;仅这些角色在「任务自己没说角色基底」时跑编码基底
|
|
422
424
|
```
|
|
423
|
-
- 经 `RoleSpec.
|
|
425
|
+
- 经 `RoleSpec.promptRole: "code"` 挂在**角色**上(7.137.0 起 / 引擎 7.56.0;此前是把同一段目录文本粘进 `RoleSpec.systemPrompt`),非开发角色保持中立。角色基底只有一条来源链:任务 → 请求的模型角色的预设 → 引擎委派子代的人设,**第一个说出基底的来源整个说了算** —— 任务自带 `systemPrompt`(或客户端注入)、或场景自己写了角色词(cowork 场景写 `cowork`)时任务优先;这些 run 的宿主角色是 `code`(按 `when.host.role` 条件的 overlay 自此对它们生效),基底字节与此前相同。
|
|
424
426
|
- **验证门**(core 1.44,opt-in):请求体带 `verify:true`(可选 `verifyRounds`,夹到 [1,5]、默认 2)→ 任务跑完后由**独立只读 verifier**(`verifier` 角色,默认=主模型)证据强制地"试图 break 它",FAIL 则把 findings 注回同 session 续跑修复→重验,循环到 PASS 或轮数上限。结果带 `verification:{verdict,rounds,findings,evidence}`(`verdict` 看质量,`result`/`status` 仍是实现的)。**仅 `/v1/tasks`(同步)与 `/v1/runs`(异步)**——`/v1/tasks/stream` 不支持(多轮非单流,请求 verify 会 400)。verifier 工具默认 = 实现任务工具滤掉 `effect:"write"`(只读边界)。`/metrics` 加 `verifications_total{verdict}`。⚠️ 同步 `/v1/tasks` 形**没有事件通道**(过程帧无处回放,只回终局 JSON)——带 `verify`/`cascade` 的 200 体自 7.23.0 起附可选 `notice` 键指路 `POST /v1/runs`(要过程可见性用异步形)。
|
|
425
427
|
- **记忆(文件记忆引擎,2026-07-08 起唯一记忆面)**:core 注入式文件引擎——任务开始时 materialize 记忆目录(`MEMORY_ENGINE_DIR`,默认 `~/.ai-agent`),模型用**普通文件技能**读写记忆(CC `# Memory` 指令 + 派生索引;无 remember/recall 工具),任务边界 harvest 门(secret/cap 扫描)提交。单用户默认开,`MEMORY_ENGINE=off` 显式关;多租户恒关(文件基座无租户隔离,fail-closed)。旧 SQL 记忆面(`MEMORY_BACKEND`/`EMBEDDING_*`/`MEMORY_READ_LIMIT`/去重/向量检索、`GET/DELETE /v1/memory` 与 session memory 写 verb)已退役,数据不迁移——升级后对库跑一次 `scripts/drop-memory-tables.sql`。`body.memoryWrite:false` 仍是每请求只读开关(harvest 不提交)。
|
|
426
428
|
**`MEMORY_PERSISTENCE_CAPABLE`(三态,7.14.0 起)**=**部署自述**「本机能不能把用户的『记住 X』落到持久处」,与请求键 `memoryWrite` 两轴正交(后者收紧本次运行,前者是 operator 声明;任一取否即只读方向)。缺省不设=引擎按 roster 自行推断;`true`=本部署有推断看不见的持久通道(自定义 writer / 记忆 MCP / 远程执行车道与记忆根共享挂载);`false`=强制只读披露并关掉文件工具往记忆根的写通道。不认得的词拒启。
|
|
@@ -702,6 +704,9 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
|
|
|
702
704
|
引擎把**本会话跟踪集**收敛到该消息时刻的边界;`acceptPartialRestore: true` 容忍部分恢复
|
|
703
705
|
(缺省=响亮失败带逐文件账本)。旧键 `rewindFiles` 的 capture 义=容忍 no-op(首触跟踪恒开),
|
|
704
706
|
restore 义(与 resumeAt 同发)=typed 拒并指路 `restoreFiles`(迁移显式化是 core 裁定)。
|
|
707
|
+
- rewind 与设备车道(`REMOTE_EXEC=device`):带 `resumeAt` 的那一发是**根提交**(不是续跑一族),`deviceId` 照收、按根提交的规矩判 —— 已绑:缺席 = 沿用 / 同 = 幂等 /
|
|
708
|
+
异 = 403 `device.identity_mismatch`(rewind 不换设备,换设备走显式换绑);未绑:携 = 首绑、缺席 = 409 `device.not_bound`;非 device 车道携它 = 400 `request.precondition_unmet`。
|
|
709
|
+
`resumeAt` 自己的拒(值形 400、`resume_at.*`)先答,设备准入链后答。
|
|
705
710
|
- **按 run 关掉捕获**(7.104.0):提交体带 `fileHistory: "disabled"`(唯一词;坏值 400
|
|
706
711
|
`request.field_invalid`)⇒ 这条 run 的编辑照常落盘,但不做首次触碰备份、不铸回合边界,委派出去的子代继承;
|
|
707
712
|
`wiring_manifest.fileHistory` 报 `"disabled"`。同一提交再带还原键(`restoreFiles` / `rewindFilesTo` / `rewindFiles`)⇒ 受理时
|
|
@@ -724,6 +729,7 @@ SEND_USER_FILE_SANDBOX_PUT_ENDPOINT=… # 可选:沙箱直传 PUT 的端
|
|
|
724
729
|
- **词表优先级**:内网端点 `MINIO_ENDPOINT` 优先,`S3_ENDPOINT` 是外接 S3 兼容位(内网/公网同址场景可只给它,同时充当公网面);公网端点 `S3_PUBLIC_ENDPOINT` 优先,缺席回落 `S3_ENDPOINT`。空串一律按未设处理。
|
|
725
730
|
- **🔴 云形(`DB_BACKEND=mysql|pg`)快照 blob 强制对象存储(1.295+,clay 拍)**:缺 MinIO 三键 ⇒ **boot 拒启**——单行 SQL blob 写会撞 TiDB `txn-entry-size-limit`(默认 6MiB,真库实测矮墙)/ mysql 协议 `max_allowed_packet`,大字节归对象存储(与 D-1 附件面同裁定)。单机/测试台显式逃生:`SNAPSHOT_BLOB_ALLOW_SQL_BYTES=true`(SQL 店此时带 per-blob 帽,MySQL 协议(InnoDB 与 TiDB)默认 = **写入那条数据库连接**报的两堵已知墙取最小:单行上限减 1 KiB(blob 行自己的余量:哈希键 + 长度 + 时间戳;TiDB 才有)与包墙 `max_allowed_packet` 扣 1 KiB 语句余量后折半(⌊(包墙 − 1 KiB) ÷ 2⌋:文本协议把 blob 写成十六进制,语句至少是它的 2 倍);任一读数读不出时按该库族文档缺省并留一行运维日志 `fail_open`(tag `server.sql.packet-cap-unread` / `server.sql.tidb-row-cap-unread`),超限 PUT 413 `blob_too_large_for_sql`,句子按帽的出处分三形(运维旋钮 / 库报的墙 / 读不出而按文档缺省假定的墙),机读位 `wallSource` 同词 `operator` / `reported` / `assumed`。包墙是每条连接自己的会话值(7.133.0 起按连接判):DBA 调大 `max_allowed_packet` 之后,池里的旧连接在它第一次按包墙拒 blob 时自换(运维日志一行 `sql_connection_packet_wall_stale`,那一笔在新连接上重判),不必重启副本;`GET /v1/diagnostics/wiring` 的 `sqlEngine.maxRowBytes` / `sqlEngine.maxPacketBytes` 是最近一次初始化的那条连接的读数(见部署前置清单 DEPLOY-PREREQS(仓内)「数据库」条);帽可用 `SNAPSHOT_BLOB_SQL_MAX_BYTES` 按部署真实限值覆写,两方言生效;pg 默认无帽)。
|
|
726
731
|
- **per-file rewind 历史的边界上限**(与 workspace 浏览面同一个整树快照纪元退役批留下的保留条款):`FILE_HISTORY_RETENTION_KEEP` —— 每个 scope(会话)存活的**最新** boundary 数,**每次 boundary 提交后自剪**(最旧者先亡,刚提交者恒存活;失引用的版本行级联删,blob 字节走异步 grace 窗 GC;v1 基线永存)。未设=core 缺省 **100**(由 core 的 `resolveFileHistoryRetention` 兑现,server 不复制那个数);`unbounded` 是**唯一**的「不剪」拼法(整店自剪关闭,GC 归部署自排 `reap`);`0`/负数/小数/非数 ⇒ **boot 拒启**(码 `config.retention_policy_invalid`,与 core 内置后端同码同判——**省略这个键不等于不剪**,它选的是缺省 100)。三条车道(TiDB / PG / local 文件店)同旋钮同判。⚠️ 升级即生效:存量超 100 boundary 的 scope 在下一次提交时被剪到 100;要保全量先写 `unbounded`。被剪 entry 的键**已花掉**(再次 snapshot 同 entry 典型拒,不复铸)。排序=DB 分配的 per-scope 发布序号 `file_history_boundary.publish_seq`(不是墙钟):**自 7.50.x 有存量 `file_history_boundary` 的部署升级本版会在 boot 期被 `assertFileHistorySchema` 拒启并指明 `DROP TABLE file_history_boundary`**(本仓无 ALTER;丢的是 per-file rewind 便利态,会话与其他持久面不动)。
|
|
732
|
+
- **会话同步重推(同图)要读 blob**(7.136.0 起):`POST /v1/sessions/:id/sync/import` 的 identical 重推臂,对云端「已握着同一张文件历史图」的目标,也先把信封引用的每一枚 blob 经云端自己的店读一遍、核哈希(不回写),再答成功 —— 对象存储里的对象若在图发布之后丢了,这一腿答 `422 missing_blob`(`PUT` 缺的 blob 再推;图不动),不再答 `200 identical`。代价:无变化会话的频繁重推多付对象存储读流量(每枚引用的对象一次下载,正比于引用的 blob 字节);没有旋钮。
|
|
727
733
|
- **文件历史捕获总开关**(7.97.0 立,7.104.0 改语义):`FILE_HISTORY_CAPTURE`(缺省 `true`;布尔词表 true/false/1/0/yes/no/on/off,未知词 ⇒ boot 拒启;TiDB / PG / local 三条车道同义)。`false` ⇒ **只关捕获**:历史店照接,引擎拿到部署捕获位 `RunnerDeps.fileHistoryCapture: "off"`,改动文件的首次快照**一个都不捕获**、不铸回合边界(`wiring_manifest.fileHistory` 报 `"disabled"`);**已存历史照常可用** —— `resumeAt`+`restoreFiles` / `rewindFilesTo` 照常还原、2c session-sync 照常导出 / 导入。`GET /v1/capabilities` 的 `fileHistoryCapture` 报 `"off"`,而 `rewindFiles` / `restoreFiles` / `rewindFilesTo` / `sessionSync` 仍为 `true`(缺省部署报 `"on-optional"` = 捕获中且请求可按 run 关掉,见上一段)。🔴 **7.97.0–7.103.x 的旧语义**:`false` = 不把店交给引擎,连已存历史也读不了(还原走 `rewind.store_unconfigured`、sync 501)—— 把本旋钮当成「整个文件历史功能关掉」来用的部署,升级后已存历史**重新可以被还原 / 同步**。要让已存历史也不可用,今天的出路只有**逐会话** `DELETE /v1/sessions/:id`(擦除面不受本旋钮管)与 SQL 后端的留存清扫(`RETENTION_MODE=enforce` + `RETENTION_MAX_AGE_DAYS` + `RETENTION_SWEEP_INTERVAL_SEC`,按会话年龄删,local 车道没有留存车道);**没有**「整库历史一键不可用」的旋钮。⚠️ **关掉 ≠ 删掉**:翻旋钮不批量删除存量历史;local 车道上删会话走 `reap` —— 边界删光、v1 基线按 core 契约留存,既有残余。关着时 `FILE_HISTORY_RETENTION_KEEP` 没有效果(不再有边界提交,自剪不会触发),值仍照常校验。没有存储后端的部署(env-only worker)本来就没有历史店,旋钮在那里无事可关、不下发捕获位(引擎对「off 但没店」拒 prepare)。
|
|
728
734
|
- **🔴 公桶策略必须 GetObject-only**:`mc anonymous set download` 会**连带打开 ListBucket**——匿名 `GET /<bucket>/?list-type=2` 能枚举全部不可猜 key,能力链接设计即告失效。正确姿势=`mc anonymous set-json`,policy 只含 `Action:["s3:GetObject"]` on `arn:aws:s3:::<bucket>/*`(验证:对象 GET 200、桶 LIST 403)。
|
|
729
735
|
- **两条轨**:ttl=0(默认)→ 匿名 GET 公桶下 `uuidv7/<name>` 不可猜 key,链接永久、可回收(删对象);0<ttl≤7 天 → 私桶 + SigV4 限时签名链接(7 天是 SigV4 物理上限,更久用 ttl=0)。
|
|
@@ -736,6 +742,8 @@ SEND_USER_FILE_SANDBOX_PUT_ENDPOINT=… # 可选:沙箱直传 PUT 的端
|
|
|
736
742
|
# 凭证两键名 SEMA_WEB_ADMIN_TOKEN / SEMA_WEB_ADMIN_TOKEN_FILE 与「二选一」判官的属主 = @sema-agent/settings-schema
|
|
737
743
|
# `WEB_ADMIN_ENV` / `judgeWebAdminCredentialDeclaration`(6.1.0 起;管理面 / 壳 / 本服务 import 同一处)
|
|
738
744
|
SEMA_WEB_ADMIN_URL=http://<web-admin-host>:3100 # 启动拉 GET /api/config/effective(Bearer+ETag),覆盖 env 兜底
|
|
745
|
+
# 必须是中心的最终地址:中心回重定向(301/302/303/307/308)⇒ 该条请求被拒、不跟(请求体与 Authorization 不发往 Location),
|
|
746
|
+
# 拒句 `web-admin center request: endpoint answered with a redirect (HTTP <n>); …` 点名这一席;LIVE_URL 同。反代请直接回答。
|
|
739
747
|
SEMA_WEB_ADMIN_TOKEN=<SERVICE_PULL_TOKEN 的值> # 取自配置控制面主机 .env;只读拉取令牌
|
|
740
748
|
# SEMA_WEB_ADMIN_TOKEN_FILE=/path/to/token # 或:凭证文件(与上一行二选一,都设=拒启)。每条中心请求都重读 ⇒ 轮换 / 换账号改写文件即可,
|
|
741
749
|
# 中心请求面不重启;文件不可读 / 为空 = error 级 center_credential_unreadable,不当作无凭证。
|
|
@@ -795,7 +803,7 @@ FLEET_ADVERTISE_ADDRESS=http://<本机可达IP>:8090 # 可选:设了才启公
|
|
|
795
803
|
# (不公告就听不到能力位),缺一 ⇒ 起服拒启 config.fleet_member_unfenced —— 成员不许悄悄脱离围栏。
|
|
796
804
|
# 续租打 POST /api/fleet/live/serving-lease(契约 FLEET_PATHS.servingLease;活性基址见 SEMA_WEB_ADMIN_LIVE_URL)。
|
|
797
805
|
# 心跳答复体读不出 / 不是 JSON 对象 = 没有能力读数(位与租约钟原样,warn fleet_announce_reply_unreadable)。
|
|
798
|
-
# FLEET_SCOPE=acme # 7.121.0:本引擎所在的**租户 scope**(
|
|
806
|
+
# FLEET_SCOPE=acme # 7.121.0:本引擎所在的**租户 scope**(配置渲染器对每台受管引擎写,缺省 scope 也写 = 它的 per-worker 令牌绑的 scope)。缺席 =
|
|
799
807
|
# 缺省 scope global;在场必须是 scope id 形(小写字母 / 数字 / 内部连字符,1–64;空串拒启
|
|
800
808
|
# config.fleet_scope_invalid)。与 FLEET_GENERATION 不联动。只进排空口的身份比对:请求的 scope 与它比,
|
|
801
809
|
# 请求不带 scope 读成 global。续租体与心跳不带它(scope 由中心凭证绑定)。
|
|
@@ -978,6 +986,7 @@ UNREACHED_ASK_TTL_MS=3600000
|
|
|
978
986
|
(代价是超限的 ask 走 park,要有人去审批队列捞);库压大 → 调小 `STREAM_APPROVAL_RECONCILE_BATCH`
|
|
979
987
|
(代价是收敛变慢,靠队列轮转保证下轮接着扫)。
|
|
980
988
|
- **停驻行最长活多久(local 文件后端与 SQL 店同一公式)**:一张 pending 的 checkpoint 行在 `max(createdAt + APPROVAL_TERMINAL_BACKSTOP_MS, deadline + 1h)` 到点后由后台 reaper(每 `REAP_INTERVAL_SEC`,缺省 60 s 一拍)expire;`deadline` 只在配了审批 SLA / TTL 时有,没配就是 `createdAt + 后备上限`。旋钮缺省 30 天、下钳 60 s、非有限值落缺省。到点的行先 expire,挂起的 run 行随后由 reaper 翻成 `failed`(`errorCode: approval.expired`)。所以「老停驻行在 local 后端最长活多久」= 后备上限(缺省 30 天)+ 最多一拍;要缩短就调 `APPROVAL_TERMINAL_BACKSTOP_MS`(两店同辖)。
|
|
989
|
+
- **SQL 店写停驻行的预判与 SLA 拒扫的取行序(7.136.0 起)**:`put` 先看这份检查点放不放得进索引列 —— 没有 `gate` / `pendingAction` 对象,或门上卡词的 UTF-8 **字节**数超过 `approval_card` 列的容量(MySQL 协议 `TEXT` 65,535 字节;PG 无上限),抛 `CheckpointIndexRefusedError`(`err.refusal.kind` 为 `unreadable` / `card_too_long`),一句 SQL 都不发 —— 严格与非严格 sql_mode 的库都一样,不会落成半只卡词;引擎自己铸的检查点不会触发。SLA 拒扫每拍至多取 `LIMIT`(缺省 100)行,取行序是「卡列有词的先取,其后按到期时刻」:混跑窗里旧副本停驻的问答行(卡列为空)排在带卡词的真到期行之后。
|
|
981
990
|
- **审批账本保留多久(`APPROVAL_ASK_RETENTION_MS`,缺省 `2592000000` = 30 天;local 文件后端与 SQL 店同一条律)**:审批在盘上有三本账 —— 问账(SQL 的 `approval_ask` / `approval_batch`,local 的
|
|
982
991
|
`<数据根>/approval-asks/asks.jsonl`)与 local 审计账(`<数据根>/approval-ask-audit/ledger.jsonl`,`GET /v1/approvals` 的 `crashConverged` 读它)。一问**结了**且它最后一次动静早于
|
|
983
992
|
`now − APPROVAL_ASK_RETENTION_MS`,它在三本账里的全部记录由维护 tick 整问删掉。「结了」= 该问在它那本账里到了终局,**且它挂靠的东西都不再能动**:挂靠 = 发起它的 run、宿主会话、
|
|
@@ -1317,6 +1326,11 @@ TASK_WRITE_FACE_ESCALATION=allow # 词表 allow|refuse;坏词启动期响亮
|
|
|
1317
1326
|
内建 `code` 与 `default` 共用一只工厂,技能按 `default` 过滤 —— 只写了 `scenarios: code` 的技能不进内建 `code` 的 run,卡上也不列(要进,标 `default` 或不标);配置中心声明了同名场景 `code` 盖掉内建时,按它自己的名字 `code` 过滤。
|
|
1318
1327
|
能力位 `scenarioSkills`(恒 `true`)缺席 = 老 server,卡上没有这一键;别从 `400 config.user_invoked_skill_unknown` 的拒句里刮名字(那是引擎的散文,会变)。
|
|
1319
1328
|
|
|
1329
|
+
**看一条 run 装着哪些技能(7.137.0 起)**:这条 run 的 `wiring_manifest` 帧(流式 SSE、`GET /v1/runs/:id/events`、非流式 `POST /v1/tasks` 200 体的 `wiringManifest`)带本服务自铸段
|
|
1330
|
+
`skills` = 上面 1–4 叠出来、交给引擎的那一组(同一组、同一个序),每条 `{ name, description, source }`:`source` 除了 `"factory"` / `"center"` 再多一个 `"request"`(第 4 条,请求体自带的;
|
|
1331
|
+
与部署同名的那条被丢,不在册)。`disableSkills: true` 或一只都没有 ⇒ `skills: []`;委派子代的帧不带这一段;缺席 = 老 server 或装配不是本服务出口产的,别读成 `[]`。
|
|
1332
|
+
续跑腿报的是续跑那一次装配的名册。`description` 是展示文本(请求自带的那几条是调用方写的,上 wire 前过脱敏、截长)。部署技能正文超过引擎加载上限的那一只,这一段仍会列出(引擎不装它,见上一段)。
|
|
1333
|
+
|
|
1320
1334
|
**MCP 条目的工具风险轴(`toolAxes`,7.128.0 起)**
|
|
1321
1335
|
|
|
1322
1336
|
`config.d/mcp.json` 的 `servers[]`(或配置中心下发的同形条目)可带 `toolAxes`,为这台 server 的**每只工具**声明风险轴;声明逐字上引擎的 `McpServerSpec.toolAxes`,喂审批门与计划模式围栏:
|
|
@@ -1364,6 +1378,8 @@ MCP_ELICITATION_TTL_MS=300000 # 一张没人填的表单挂多久后释放(
|
|
|
1364
1378
|
- 四个节流钮都是 `numEnvBounded`:**越界启动期响亮拒,不静默夹取**(一个手滑的多小时 TTL 会让表单实际上永不过期)。
|
|
1365
1379
|
- 总开关关着时四个节流钮解析照跑但无消费者——不设=行为不变。帧格式见
|
|
1366
1380
|
[`docs/ASSISTANT-WIRE-CONTRACT.md` §4a-quater](docs/ASSISTANT-WIRE-CONTRACT.md)(`elicitation` / `elicitation_complete`)。
|
|
1381
|
+
- **回给 MCP server 的词**(一事一词):没人应答的释放 —— 答题窗到期、run 中止、腿退出、表单没送达 —— 回 `cancel`;本部署按规则当场拒(没有 run 上下文 / url 形 / 节流越界)回 `decline`;
|
|
1382
|
+
用户经 respond 答的词原样落地。7.135.x 及以前,答题窗到期与表单没送达回 `decline`。判法与 helper 该怎么读见契约 §4a-quater 的「终局词律」。
|
|
1367
1383
|
- 两帧同样**随 run 账本回放**(与上面的问题卡同一条规则:`/v1/tasks/stream` 腿有 durable run 行时先落账再直写,`/v1/runs` 腿只有账本),重载后经 `GET /v1/runs/:id/events` 取回表单卡;
|
|
1368
1384
|
`elicitation` 开帧带 `expiresInMs` / `expiresAtMs`,待答判法与问题卡同一把。**答题口不变**:`POST /v1/elicitations/:id/respond` 只在受理那一张表单的副本内、窗内有效。
|
|
1369
1385
|
|
|
@@ -1609,7 +1625,7 @@ invalid_using_default` 警告 + 退回缺省值」这条臂随词表放宽一并
|
|
|
1609
1625
|
(`SERVICE_AUTH_TOKEN(S)` / `APPROVAL_HMAC_KEYS` / `MODEL_API_KEY` …)与名单型旋钮名起服已向引擎声明为本进程自有凭据,生效配置里每条模型条目的 `apiKeyEnv` 也在名单里
|
|
1610
1626
|
(中心热应用新增 / 改名条目时在模型目录换代前重声明,名单只随落地的世代增长 —— 改名后旧名照扣到重启,没落地的候选不留痕;条目的 `apiKeyEnv` 是引擎扣不住的名 ⇒ 该候选整拒),`"all"` = 整份环境**减**它们,任何继承词都不把它们
|
|
1611
1627
|
交给模型 shell(能力位 `inherit_env.own_credentials_withheld` = 最近一次声明 ⊇ 当前生效配置应声明的集合;真要让模型 shell 持某枚令牌 ⇒ 另放一个不在配置目录、也不是任何模型条目 `apiKeyEnv` 的名,或经 `settings.env` 显式授予);`"scrub"` / 缺席 = 上面的缺省剥除。🔴 **`"all"` 只在单用户部署兑现**:`REQUIRE_PRINCIPAL=true`
|
|
1612
|
-
⇒ 422 `request.host_posture_refused`(射程与写容纳面那条腿同一只谓词,`TASK_WRITE_FACE_ESCALATION` 不管它);非 host
|
|
1628
|
+
⇒ 422 `request.host_posture_refused`(射程与写容纳面那条腿同一只谓词,`TASK_WRITE_FACE_ESCALATION` 不管它);非 host 车道带 `"all"` ⇒ 400。词表外的值(`"ALL"` · `""` · 名单 `["PATH"]` · 布尔 · `null` …)、非 host 车道带 `"all"`、部署不接 authorizer 时带 `"all"` 这三只都是 `400 request.field_invalid`,7.137.0 起体另带 `field: "inheritEnv"` 与 `reason`(`"unknown_word"` = 是字符串但不在词表;`"not_a_string"` = 根本不是字符串;`"lane_not_host"` = `"all"` 落在非 host 车道;`"no_authorizer_session"` = `"all"` 而部署不接 authorizer),壳按这两位认拒因、不解析句子(句子 / 码 / 状态不变;续跑腿同判,折叠 409 随体带同一组)。托管部署的 `422 request.host_posture_refused` 不在这组:它说的是「这台部署的姿态不给」,不是这个键写错,按 `errorCode` 认、不带这两位。部署方
|
|
1613
1629
|
**没有**旋钮能替调用方打开它,也没有旋钮能在单用户部署上关掉它(单用户部署的用户把他自己的环境交给他自己驱动的 shell);起服一行 info
|
|
1614
1630
|
`task_inherit_env_honored {honored}` 告诉你这台机器认不认。上面「部署级名单不给模型驱动 shell」那条不变 —— 两件事是两条轴(部署名单 vs 调用方 × 单用户射程)。
|
|
1615
1631
|
|
|
@@ -1644,7 +1660,7 @@ bake-runner 要用 ⇒ `LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW` 点名放回。
|
|
|
1644
1660
|
|---|---|---|---|
|
|
1645
1661
|
| `E2B_SANDBOX_ENV` | `REMOTE_EXEC=e2b` | 逗号分隔的**裸变量名**,如 `E2B_SANDBOX_ENV=GIT_TOKEN,NPM_TOKEN` | 点到的键从服务进程环境**原样**注入沙箱(e2b 沙箱级 env;与 `SANDBOX_PKG_SOURCE` 推导出的包源变量同名时名单赢) |
|
|
1646
1662
|
| `DOCKER_SANDBOX_ENV` | `REMOTE_EXEC=local-docker` | 同上 | 点到的键以 `docker exec -e K=V` 注入每条命令(与单条命令自带的 env 同名时,命令自带的赢) |
|
|
1647
|
-
| `K8S_SANDBOX_ENV` | `REMOTE_EXEC=k8s` | 同上 |
|
|
1663
|
+
| `K8S_SANDBOX_ENV` | `REMOTE_EXEC=k8s` | 同上 | 点到的键的值从服务进程环境**原样**写进该沙箱 pod 自己的 Secret,pod 的容器 env 只带 `secretKeyRef` 引用(pod spec 里没有值;与 `SANDBOX_PKG_SOURCE` 推导出的包源变量同名时名单赢;要 Role 的 secrets `create`,详见 §9.3c) |
|
|
1648
1664
|
|
|
1649
1665
|
三只旋钮只在**自己的车道被选中时**才求值(别的车道上它们是无关配置,不校验、不告警)。未设 / 空串 = 不注入。**形错拒启**:空段
|
|
1650
1666
|
(`A,,B`、尾逗号)、非变量名形(`1BAD`、带空格、`NAME=value` 整段贴进来)、点名名单型旋钮(`E2B_SANDBOX_ENV=E2B_SANDBOX_ENV`,
|
|
@@ -1868,10 +1884,10 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
|
|
|
1868
1884
|
| 端点 | 用途 |
|
|
1869
1885
|
|---|---|
|
|
1870
1886
|
| `GET /v1/sessions/<id>` | 审计回溯:当前上下文 + 摘要(owner 校验);消息里模型 / 工具产出的文本与 live 帧、账本帧同一只脱敏(密钥形换占位,其余字节不变);Read 图像 / PDF 的媒体卡(工具结果消息的 `details`)的 `file.base64` 是字节编码,逐字。**委派子代的转录**也走这一口:`<id>` = 子代帧 / fleet 子代行上的 `transcriptId`(未压缩过的子代首条用户消息是委派 prompt;子代跑过的工具是 assistant 消息里的 `toolCall` 段 + 同 `toolCallId` 的 `toolResult` 行;整段逐字条目走 `…/sync/entries`)。体顶层加性键 `agent: { id?, parentSessionId?, rootSessionId? }`(**7.133.0+**)说这是哪只子代:只投影会话行上存下来的委派元组,`id` 与 fleet 行的 `id`、`GET /v1/agents/roster` 的 `handle` 同一个串;键缺席 = 普通会话 / 老 server / 会话店不存元组(local 后端),读作「没存」、回落 fleet 行 / 名册上的 id。子代会话的属主是铸行那一刻它所在委派树的根会话的属主(**7.133.0+**,写下那一行时按当时读到的写入;此前 SQL 后端上子代会话无主、谁都读得到)⇒ 用发起子代的那位主体读;别的主体与不存在的 id 同一个 `404 not_found.session`。属主为空的委派子代行(根会话本就无主,或升级前写下、还没回填)只有 fleet 调用方读得到(出示 service 凭据、不带主体头;dev-open 部署不钉人),任何具名主体、没有 service 凭据的无身份调用方同样 404(存量无主会话互认臂 —— 单用户形下 `default` 主对无主的普通会话放行,fork / 删除 / `/sync/*`(同步族自 7.134.0 起)同走这一只 —— 对这些 placed 行同样不放行,审批豁免、notify 同样不放行;fork / 删除 / `/sync/*` 的 fleet 调用方与此处同一个判据:出示 service 凭据、不带主体头,带主体头的 service 凭据请求是那位主体,7.134.0 起);SQL 后端每次起服回填这些行(起服读数行 `placedSessionOwnersRemaining` 非零 = 还有没定下的,再起直到为 0,或等它们下一次停驻)。推不出属主的子代写成属主 `_orphan:<id>`,只有 fleet-wide / operator 读得到:SQL 后端的子代行根会话已删时按它记录的父会话,父也删了才是 `_orphan:`;停驻时拷进宿主店的那一份(local 后端)只按根判,根会话已删 ⇒ `_orphan:`;子代被派出 / 批准之后、再次停驻读根之前根会话被删并由别人以同一 id 重新登记 ⇒ 其下的子代再次停驻被否决(读过根之后才换的,写进去的仍是读根那一刻的属主) |
|
|
1871
|
-
| `GET /v1/approvals?owner=user:42` | 高危写审批(durable,需 `DURABLE_APPROVAL=true`):**operator** 看待办队列(可按 owner 过滤);非 operator 只看自己的。队列里某一行的存储体读不出来、重试不会好时,答 `500` + `errorCode:"approvals.read_corrupt"`(**7.134.0+**;与订阅流同码、同一只判官;运维处置那一行后再读,别原样重试);此刻读不出队列(连接 / 超时 / 店抖动)⇒ `503` + `errorCode:"approvals.read_unavailable"`(**7.135.0+**,此前 500 `internal.error`;稍后重发,这一答不表示队列为空)。`GET /v1/assistant/inbox` / `GET /v1/assistant/tasks` 读同一个队列,两种读失败同码同状态(**7.135.0+**) |
|
|
1872
|
-
| `GET /v1/approvals/stream` | 同一队列的 SSE 推送(范围同上):首拍快照 → `pending` / `resolved` 增量(开了工具审批时另有 `live_pending` / `live_resolved`),空闲时心跳,15 分钟上限(`STREAM_MAX_DURATION` 帧后重连)。**心跳只表示流活着,不表示这一拍读过队列**:队列读不出时每次读失败发一帧 `error`(`approvals.read_unavailable`,流留着、下一拍再读,恢复后同一条流继续);某一行的存储体读不出来、重试不会好时发 `approvals.read_corrupt` 并收流(运维处置那一行后再重连)。15
|
|
1873
|
-
| `POST /v1/approvals/<sessionId>/decide` `{"decision":"approve"|"deny","reason":"…"}` | 批/否并恢复挂起任务(CAS,重复决议 409);**仅 operator**(非 operator → 403)。5.0.0 起旧轮询腿 `POST /v1/approvals/<id>`
|
|
1874
|
-
| `GET /v1/tool-approvals/<approvalId>` | 按问读口(**7.133.0+**;与 `POST …/respond` 同门同身份,不是你的 ask 与查无同形 404):一只审批问此刻在哪一态,`state` ∈ `pending` / `decided` / `parked` / `void`(闭集)。停驻卡被答过 ⇒ `decided`、停驻 run 被取消 ⇒ `void`(**7.134.0+**;7.133.x 恒答 `parked`);停驻行的门已结算却读不出可用的决议(含坏行:状态词 / 种类 / 当事方不在闭集、结算记录过不了引擎的不变量筛、缺必填成员、时刻 / 方向读不出)⇒ 404 `tool_approval.not_pending`,运维日志一行 error(`defect` 首条缺陷句 · `defectCount`)。停驻行的 `decided` 可回到 `parked`:续跑腿在被批准的动作开跑前失败时,引擎重开同一张门 —— 读口答 `parked` + `decidePath` 并另带 `reopened`(**7.135.0+**):`reason` 是引擎的重开因由词(`env_failed` 按原决议经 `decidePath` 再交一次 —— 本副本还留着首次受理回执时(缺省 1 小时内)`…/respond` 回放那张回执、不驱续跑;`tool_unavailable` / `constraint_rejected` 重新决;`config_refused` 改好配置再交或重新决),`decision` / `decidedAtMs` / `decisionNote?` 是重开之前的那次决议(读不出 ⇒ 只带 `reason`);再决之后回到 `decided`。续跑被取消 ⇒ 重开的门被收掉,读作 `void`。全文见契约 §4a-bis-read |
|
|
1887
|
+
| `GET /v1/approvals?owner=user:42` | 高危写审批(durable,需 `DURABLE_APPROVAL=true`):**operator** 看待办队列(可按 owner 过滤);非 operator 只看自己的。队列里某一行的存储体读不出来、重试不会好时,答 `500` + `errorCode:"approvals.read_corrupt"`(**7.134.0+**;与订阅流同码、同一只判官;运维处置那一行后再读,别原样重试);此刻读不出队列(连接 / 超时 / 店抖动)⇒ `503` + `errorCode:"approvals.read_unavailable"`(**7.135.0+**,此前 500 `internal.error`;稍后重发,这一答不表示队列为空)。`GET /v1/assistant/inbox` / `GET /v1/assistant/tasks` 读同一个队列,两种读失败同码同状态(**7.135.0+**)。停驻的**技能卡**(对引擎 `Skill` 工具的调用)的行带 `boundGrantDigest` / `boundGrants`(批准它还授予的东西:技能名 + 两只工具清单;清单只在哈希等于摘要时才有,是部署文本,渲染要按上下文转义),分类器没给判词的卡带 `classifierNoRuling`(`error` / `timeout` / `parse_error`)—— **7.136.0+**,与 `GET /v1/assistant/inbox` 行逐字同值(此前只有收件箱行与活卡帧带);普通卡三位都缺席。`GET /v1/assistant/inbox` 行的 `objective` 是门所属 run 的**目标预览**(写入时已脱敏、截到约 120 字符,与 `GET /v1/assistant/tasks` 读同一列;委派子代的门读派它的那条 run;没有 run 账本 ⇒ `null`;**7.136.0+**,此前原样回显存着的任务目标,运维 `?owner=` 跨看也是原文) |
|
|
1888
|
+
| `GET /v1/approvals/stream` | 同一队列的 SSE 推送(范围同上):首拍快照 → `pending` / `resolved` 增量(开了工具审批时另有 `live_pending` / `live_resolved`),空闲时心跳,15 分钟上限(`STREAM_MAX_DURATION` 帧后重连)。**心跳只表示流活着,不表示这一拍读过队列**:队列读不出时每次读失败发一帧 `error`(`approvals.read_unavailable`,流留着、下一拍再读,恢复后同一条流继续);某一行的存储体读不出来、重试不会好时发 `approvals.read_corrupt` 并收流(运维处置那一行后再重连)。15 分钟上限对读失败的拍同样算数。`pending` 帧带的行与 `GET /v1/approvals` 同一只投影(含上面的技能卡三位,**7.136.0+**) |
|
|
1889
|
+
| `POST /v1/approvals/<sessionId>/decide` `{"decision":"approve"|"deny","reason":"…"}` | 批/否并恢复挂起任务(CAS,重复决议 409);**仅 operator**(非 operator → 403)。5.0.0 起旧轮询腿 `POST /v1/approvals/<id>` 已退役。停驻的后台子代**复活不了**(行上没有 agent 名 —— 今天未命名的后台子代就是这一形 —— 或没有根会话锚)⇒ `deny` 作废这张卡:`200 {taskId, status:"failed", decision:"deny"}`,卡离开待决队列、子代终局 `failed`;`approve` 答 `409 decide.row_unrevivable`、拒句指路 deny(**7.136.0+**;此前两个决定都 409,卡处理不掉) |
|
|
1890
|
+
| `GET /v1/tool-approvals/<approvalId>` | 按问读口(**7.133.0+**;与 `POST …/respond` 同门同身份,不是你的 ask 与查无同形 404;**7.136.0+** 两扇决议口 —— `POST …/respond` 与耐久决断口 `POST /v1/tasks/<taskId>/asks/<askId>/decision` —— 与本口同一只属主判据:有行的问读行上当下的属主,收编之后新属主三口都认、前属主三口都 404;本口在店读不出时只对本副本自己铸的活登记回落登记(登记属主认你 ⇒ `pending`),起服接管的登记不回落、答 404 —— 与同一刻的应答口同答,此前本副本上的活卡读的是铸卡那一刻记下的属主):一只审批问此刻在哪一态,`state` ∈ `pending` / `decided` / `parked` / `void`(闭集)。停驻卡被答过 ⇒ `decided`、停驻 run 被取消 ⇒ `void`(**7.134.0+**;7.133.x 恒答 `parked`);停驻行的门已结算却读不出可用的决议(含坏行:状态词 / 种类 / 当事方不在闭集、结算记录过不了引擎的不变量筛、缺必填成员、时刻 / 方向读不出)⇒ 404 `tool_approval.not_pending`,运维日志一行 error(`defect` 首条缺陷句 · `defectCount`)。停驻行的 `decided` 可回到 `parked`:续跑腿在被批准的动作开跑前失败时,引擎重开同一张门 —— 读口答 `parked` + `decidePath` 并另带 `reopened`(**7.135.0+**):`reason` 是引擎的重开因由词(`env_failed` 按原决议经 `decidePath` 再交一次 —— 本副本还留着首次受理回执时(缺省 1 小时内)`…/respond` 回放那张回执、不驱续跑;`tool_unavailable` / `constraint_rejected` 重新决;`config_refused` 改好配置再交或重新决),`decision` / `decidedAtMs` / `decisionNote?` 是重开之前的那次决议(读不出 ⇒ 只带 `reason`);再决之后回到 `decided`。续跑被取消 ⇒ 重开的门被收掉,读作 `void`。全文见契约 §4a-bis-read |
|
|
1875
1891
|
| `GET /v1/agents/roster` | 后台 agent **名册**(7.29 起,需 durable background-agent store,否则 501 `capability.background_agent_store_required`):调用方自己 scope 的 `a…` 行,新→旧。行只带 `handle`/`name`/`agentType`/`status`/`spawnedAt`/`updatedAt`/`settledAt`(content-free;子代产物正文走 `GET /v1/runs/<id>/subagents/<handle>/output`)。查询:`?limit=`(1..100,默认 20)、`?before=`(上一页的 `nextBefore` 游标)、`?status=`(running\|parked\|completed\|failed\|killed)、`?session=`(按会话树窄化)、`?scope=`(**仅** fleet 调用方:不带主体头的 service 凭据 / 只读 `TRACE_TOKEN`(只在没配 service 凭据的部署上够得着这一口)/ dev-open;带主体头的调用方 —— 含带 service 凭据的 —— 锁定自己)。回体 `{agents,total,nextBefore?,truncated?}`;`truncated:true` = 租户行数超过名册窗(500),`total` 此时是窗内数。非属主的行**不出现也不计数**(无存在性 oracle) |
|
|
1876
1892
|
| `GET /v1/capabilities/scenarios/<name>` | 单个场景的只读详情:工具面、提示词概览、**本部署现在跑不跑得动**、附件供给、**技能清单**(见下) |
|
|
1877
1893
|
| `GET /v1/capabilities/mcp[?scenario=<场景名>]` | **无需先跑 run 的逐台 MCP 状态**(现连、列工具、关掉;按调用方限速 + 30 秒复用)。答的是**某个场景**会挂哪些中心声明的 MCP 服务器:不带参数 = 这位主体 run 时的场景(**7.131.0+**:中心按主体指派的场景胜出,否则部署缺省场景;此前恒读部署缺省场景;指派悬空 ⇒ `503 state.scenario_assignment_unknown`,与提交任务时同一句;`GET /v1/sessions/:id/mcp` 与 `…/a2a` 两张会话面板同读这位主体 run 时的场景:a2a 面板悬空时同样整面 503,而 mcp 面板另有「这个会话上一次真挂上了什么」那一半(`lastLegMcp`),悬空时答 `200`、`lastLegMcp` 照答、`servers` 缺席、拒因在 `scenarioRefusal: { errorCode, error, detail }`(与状态面整面 503 的体是同一份成员,`detail` 是门拒的机读事实 `{ scenario, source }`;`servers` 缺席 = 场景这一半没答,`servers: []` = 该场景没配 MCP));`?scenario=<名>`(**7.130.0+**)= 点名场景,可见集与同场景任务 run 时是同一只解析、过同一只场景门 —— 不在册 `400 scenario_unknown`(附在册词表;空串 `?scenario=` 同此,与提交 `scenario: ""` 同答)、这位 principal 的场景白名单集外 `400 scenario_not_allowed`(附 allowlist),两者的体与提交任务时的拒逐字同形;重复给出参数 `400 request.field_invalid`(提交腿没有这一形)。场景门接缝 `scenarioGate` 是 `createHttpServer` 的必填件:自建 `ServiceDeps` 的嵌入方用包根导出的 `createScenarioGate` 铸它(`"peek"` 腿给读面),没递就是 tsc 红。老 server 忽略这个参数、回缺省场景那一份,按 `GET /v1/capabilities` 的 `version` 判。`REQUIRE_PRINCIPAL` 下无身份 `401`(先于场景门)。每一行(两条口同形)在 §G.7 的行形之外多两个可选键(**7.133.0+**):`toolNames`(这台会挂上的工具名,引擎命名空间形 `mcp__<server>__<tool>`,读数序)+ `toolNamesTruncated: true`(名单超过 200 只时只截表,`toolCount` 仍是真数;读数用 `toolCount`,别用 `toolNames.length`);该台读数里没有名单 ⇒ 两键缺席(不编 `[]`),没连上的台是在场的 `[]`;老 server 没有这两键,按缺席读。自带 `.mcp.json` 的探测口是 `POST /v1/capabilities/mcp/probe` |
|
|
@@ -1906,9 +1922,10 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
|
|
|
1906
1922
|
> 单用户 + 名单空 + 没声明 + 有审批底座 ⇒ 起服 warn `operator_undeclared_single_user`(不拒启)。
|
|
1907
1923
|
> ⚠️ **多租户形拒启**:`DURABLE_APPROVAL=true` + `REQUIRE_PRINCIPAL=true` 而 `OPERATOR_PRINCIPALS` 空 ⇒ 进程启动失败并点名修法(空名单下没人能当这台多租 worker 审批队列的 operator)。设名单,或确属单租户则不设 `REQUIRE_PRINCIPAL`。
|
|
1908
1924
|
> 🔒 **停驻任务续跑上下文里的凭据是密封的(7.134.0 起)**:任务停驻时存下的提交体里,请求自带的凭据(`mcpServers[].transport.headers` / `.env` 的值、`a2aPeers[].headers` 的值、`settings.env` 的值、
|
|
1909
|
-
> `settings.webSearch.apiKey`、http hook 条目的静态头 `settings.hooks.<事件>[].hooks[].headers` 的值、command hook 条目的插件选项 `settings.hooks.<事件>[].hooks[].plugin.options` 的值〔7.135.0
|
|
1925
|
+
> `settings.webSearch.apiKey`、http hook 条目的静态头 `settings.hooks.<事件>[].hooks[].headers` 的值、command hook 条目的插件选项 `settings.hooks.<事件>[].hooks[].plugin.options` 的值〔7.135.0 起〕、子进程的命令行 —— stdio 传输的 `mcpServers[].transport.command` / `.args` 与 command hook 条目的 `settings.hooks.<事件>[].hooks[].command` / `.args`〔7.136.0 起;`args` 整只数组封成一个值〕)只以封签落盘(对着这一域自己的保管钥匙 `<数据根>/sealed-key/checkpoint-ctx-keys/` 最新那把公钥封、
|
|
1910
1926
|
> 绑着这一行的会话;与 sealed 模型钥匙那对钥匙不共用),续跑时开封;开不开 ⇒ `409 conflict.checkpoint_ctx_unsealable`(`field` + `reason`)。
|
|
1911
|
-
>
|
|
1927
|
+
> 入封签位的判据只有一条:本服务替调用方原样交出去的配置值(子进程的命令行与 env、发往对端的静态请求头、搜索钥匙),值是不是秘密本服务不判、整格封 —— 所以命令行里的 `--token=…` 不必挪进 env。
|
|
1928
|
+
> URL 一类自由文本不入封签位:体里「值是 URL」的座位(MCP `transport.url` · A2A 对端 `url` / `cardUrl` · http 钩子 `url` · 搜索后端 `endpoint`;图片 `images[i].url` 7.136.0 起)改由受理口拒绝带凭据的 URL(见 9.5g)。
|
|
1912
1929
|
> 因此耐久审批在场的部署(`DURABLE_APPROVAL`;单用户 + 有耐久店时缺省开)要能建保管目录(建不了 ⇒ 起服拒;不要停驻就显式 `DURABLE_APPROVAL=false`),`<数据根>/sealed-key/` 要跨重启持久
|
|
1913
1930
|
> (SQL 后端也一样:钥匙不进库;不持久 ⇒ 每次重启换钥匙,重启前停驻的任务续跑答 `reason:"key_not_held"`),多副本要让每个副本的 `<数据根>/sealed-key/` 是同一份(§9.10 ⑦)。升级与回滚序见 §9.10 ⑦ 与 MIGRATION 7.134.0 第 1 条。
|
|
1914
1931
|
>
|
|
@@ -2257,7 +2274,7 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
|
|
|
2257
2274
|
| HTTP `429` + `errorCode:"limit.cost_quota_exceeded"` | per-principal 累计成本配额越顶(`MAX_PRINCIPAL_COST_USD`;**进场门**,不打断在跑的 run) | 看 `Retry-After` / 体 `retryAfterSec` 退避(体 `windowResetsAt` = 当代窗滚过的时刻);窗滚过或调高上限后放行 |
|
|
2258
2275
|
| HTTP `400` + `errorCode:"request.id_invalid"` | 调用方给的 id 形不对。**会话 id 的形门只有一只** = 任意版本 canonical uuid(壳预设的 v4 也过门):会话口 `:id`(fork / 抹除 / 会话策略 / 2c 同步)与提交口(`POST /v1/tasks` / `/v1/tasks/stream` / `/v1/runs` 体里的 `sessionId`,A2A 的 `contextId` 同)同判,提交口的句子在句首之后点名「改用 uuid、自己记映射」;`GET /v1/sendfile-links?before=` 游标与 `body.taskId` 幂等键要求 uuidv7(时间序 / 幂等键契约) | 按 `errorCode` 分支;句子各自点名要求的形(`must be a canonical uuid` / `must be a uuidv7 …`),只作展示、别锚文案 |
|
|
2259
2276
|
| HTTP `400` + `errorCode:"request.invalid_json"` + `reason:"not_json"` \| `"not_object"`(`reason` 7.133.0 起) | 请求体不是合法 JSON(`not_json`;所有读体的口同一个体 —— 含 `notify` / `wake` / 权限规则这几口,它们 7.132.x 及以前答 `request.body_shape`,其余多数口答 `request.rejected`),或合法 JSON 却不是这一口要的对象(`not_object`,`POST /v1/runs/:id/compact`)。读体超 8 MiB 是 `413 request.payload_too_large`(`field:"body"` / `reason:"too_large"` / `cap`,没有 `count`),不是本码 | 修请求体;按 `errorCode` + `reason` 分支,句子只作展示 |
|
|
2260
|
-
| HTTP `400` + `errorCode:"request.field_invalid"`(`PATCH /v1/sessions/:id` / 2c 导入的 `title`) | 标题不合形:1–120 个 Unicode 字符、**首尾不得有空白**(服务端不代为 trim,先于 C0 判)、不得含 C0
|
|
2277
|
+
| HTTP `400` + `errorCode:"request.field_invalid"`(`PATCH /v1/sessions/:id` / 2c 导入的 `title`) | 标题不合形:1–120 个 Unicode 字符、**首尾不得有空白**(服务端不代为 trim,先于 C0 判)、不得含 C0 控制字符、**至少有一个可见字符**(只由零宽 / 方向控制符等不可见码点构成的串被拒,句子 `title must contain at least one visible character`;夹在可见字符之间的照收);`title: null` 清回自动标题;空串是 `request.body_shape` | 客户端送出前自行 trim、拒空显示的标题;按 `errorCode` 分支,句子只作展示 |
|
|
2261
2278
|
| HTTP `400` + `errorCode:"request.field_invalid"`(撞 413 的帽见 `request.payload_too_large`)+ `reason:"over_cap"` / `"too_long"` / `"bad_entry"`(7.132.0 起:工具名清单、子代定义、两张宿主路径清单;7.133.0 起提交体上其余的帽同样带:`jobId` · `systemPrompt` · `appendSystemPrompt` · `settings.outputStyle` · `settings.env` · `clientContext.*` · `sandboxImageProfile` · `outputSchema` · `resumeAt` · 顶层技能 `content` / `baseDir`;子代定义里技能自己的 `content` / `baseDir` 同样带,坐标 `agents[i].skills[j].content`,句子仍是形句) | 撞了帽:`field` = 撞帽那一处的体内坐标(`skills[1].allowedTools` · `agents[0].denyTools[3]` · `agents` · `additionalDirectories` · `systemPrompt` · `settings.env.<变量名>` …),`count` = 送来的条数(`over_cap`)或字符数(`too_long`),`cap` = 帽;`bad_entry` = 清单第 `index` 条坏了(`field` = 那张清单)。7.132.0 起覆盖的面句子头一句恒为 `<field> has <count> entries\|characters — at most <cap> are accepted`;7.133.0 起补上的面句子不变、只加这一组键。同一个字段的形错(不是串、空串)只有句子 | 按 `field` 改那一处(合并 / 拆小条目、缩短文字、修那一条路径);句子只作展示 |
|
|
2262
2279
|
| HTTP `400` + `errorCode:"request.field_conflict"`(7.133.0 起带 `detail`;`requireExistingSession` 缺 `sessionId` 一处是 422) | 请求里的字段互斥或互为前置(`verify` × `cascade`、`capabilitiesNeeded` 没有 `sandboxImageProfile`、`disableSkills` 同体带技能、决定口的 `remember` / `answer` / `editedPlan` 错门 …)。体带 `detail: { kind, fields }`:`kind` 是闭集词(`cascade_verify` · `image_profile` · `resume_anchor` · `system_prompt_fold` · `file_history_restore` · `skills_disabled` · `existing_session` · `scratchpad_root` · `ask_answer` · `policy_person_ask` · `plan_edit` · `plan_mode_after` · `bound_call_door` · `remember_decision`,全表见契约附录 A),`fields` = 冲突涉及的请求体坐标(句子主语在前;互斥的类只点请求里真带了的键,「X 要求 Y」的类 Y 是缺席的前置、也在里面);`system_prompt_fold` 另带 `count` / `cap`(折叠后的字符数与帽) | 按 `detail.kind` 分支(例:`cascade_verify` ⇒ 去掉 `verify` / `cascade` 重提),按 `detail.fields` 定位到那几个键;句子只作展示、别锚文案 |
|
|
2263
2280
|
| HTTP `400` + `errorCode:"config.tool_face_control"`(7.126.0 起;流式 / 后台腿的引擎侧拒是 `failed` 终局带同码,受理口拒三条腿都是提交时 400) | 请求的 `allowTools` / `excludeTools` 里有一个**读成空**的条目:任何工具名都不可能有的拼法(空白、括号不配、空名)、`allowTools` 写了 `mcp__*`(引擎拒)、`excludeTools` 写了 `*`,或(7.131.0 起,受理口拒)**近似名**(大小写孪生 `BASH`;放行侧 `allowTools` 与限制侧 `excludeTools` 都拒 —— 想放的什么也没放 / 想禁的照用)与**限制侧的退役名**(`excludeTools: ["Task"]`)。放行侧 `allowTools` 的**内容形**(`Agent(Explore)`)与没有后继的退役名(`MultiEdit`)照 7.130.0 受理、交引擎既有通告(运维行 `runner_config_advisory`);**有后继的退役名**(`Task` · `bash`)7.132.0 起由引擎开跑前拒(`config.tool_face_control`,同步腿 400)。**真未知名**(拼写合法、这台部署没有工具叫它,懒挂载的合法形)7.129.0 起照跑:条目原样留在表里,运维行告知一次。选择器只取工具名那一段判名字(限制侧 `Bash(echo $HOME)` 不拒),`allowTools:["*"]` 与限制侧 `mcp__*` / `a2a__*`(整个协议)合法。清单超 1000 条 ⇒ 整只 `400 request.field_invalid`(`count` / `cap`),帽内拒句只点名前 5 条 + `; and K more`。拒时:主模型零调用、标题器零调用(7.127.0 起;7.126.x 上标题器仍会为这次提交调一次模型)。这次提交留下的会话无标题、零消息 | 改成句子里 `write` 后的现名(或删掉条目);只列这台部署挂得出的工具名 |
|
|
@@ -2270,7 +2287,8 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
|
|
|
2270
2287
|
| HTTP `429` + `errorCode:"usage.window_exhausted"` | 部署级治理窗耗尽(`USAGE_WINDOWS`,token 或 $ 天花板先满者) | 看响应体 `retryAfterSec` 退避;窗滑动/桶到期后放行 |
|
|
2271
2288
|
| `terminal:{kind:"failed", code:"usage.window_exhausted"}` | 已受理的 run 在 **turn 边界**撞上治理窗且无法 durable 挂起 | 同上退避后重投;配好 checkpoint 基建则改为 `suspended` 等窗自动续跑 |
|
|
2272
2289
|
| HTTP `501`(`/v1/runs`) | 内存模式不支持异步 | 配持久存储(`DB_BACKEND=mysql` / `pg` + 坐标;7.106.0 起给了坐标必须点名 `DB_BACKEND`) |
|
|
2273
|
-
| HTTP `500` + `errorCode:"corrupt_session"`(7.104.0 起;流式口在应答头之后则是 `done` 帧的 `result.terminal.code`,后台 run 是 run 行的 `errorCode`) | 这只会话的**存档读不出**、唤不醒(文件后端:会话日志没有会话头;SQL 后端:一行事件读不出)。这一轮没跑,一个 token 没烧。直接读这只会话的口(头探针 / 会话视图 / 导出 / 分叉 / 2c 同步)同答(契约 §17) | **别重试**(确定性失败)。**文件后端**:运维看 `fail_open_total{tag="server.session-repo.corrupt-record-skipped"}` 的计数、stderr 契约行 `[sema] fail-open: server.session-repo.corrupt-record-skipped` 与随带的一条 `fail_open` warn(后两者每进程每 tag 只一次,warn 带首条明细);逐条明细(文件路径、会话 id、行号、成因)每命中一行进探针文件 —— 起服前设 `SEMA_FAILOPEN_PROBE=<文件路径>`(会话头那一行本身读不出时,它就是其中一条);修或移开那份存档。**SQL 后端**不经这只座:坏的那一行由错误句子点名(`session_event.payload` + 会话 id + 序号 `seq`)。要继续工作就换一个新会话 |
|
|
2290
|
+
| HTTP `500` + `errorCode:"corrupt_session"`(7.104.0 起;流式口在应答头之后则是 `done` 帧的 `result.terminal.code`,后台 run 是 run 行的 `errorCode`) | 这只会话的**存档读不出**、唤不醒(文件后端:会话日志没有会话头;SQL 后端:一行事件读不出)。这一轮没跑,一个 token 没烧。直接读这只会话的口(头探针 / 事件订阅 / 会话视图 / 改名 / 导出 / 分叉 / 2c 同步)同答(契约 §17;文件后端上事件订阅与改名 7.136.0 起同答,此前一个 200 起流却零帧、一个 404) | **别重试**(确定性失败)。**文件后端**:运维看 `fail_open_total{tag="server.session-repo.corrupt-record-skipped"}` 的计数、stderr 契约行 `[sema] fail-open: server.session-repo.corrupt-record-skipped` 与随带的一条 `fail_open` warn(后两者每进程每 tag 只一次,warn 带首条明细);逐条明细(文件路径、会话 id、行号、成因)每命中一行进探针文件 —— 起服前设 `SEMA_FAILOPEN_PROBE=<文件路径>`(会话头那一行本身读不出时,它就是其中一条);修或移开那份存档。**SQL 后端**不经这只座:坏的那一行由错误句子点名(`session_event.payload` + 会话 id + 序号 `seq`)。要继续工作就换一个新会话 |
|
|
2291
|
+
| HTTP `500` + `errorCode:"invalid_session"`(7.137.0 起;流式口在应答头之后则是 `done` 帧的 `result.terminal.code`,后台 run 是 run 行的 `errorCode`) | 这只会话**存着的日志**读得出、结构走不通:某条目的父不在日志里(悬空)、条目是自己的祖先(自指 / 成环)、叶子不在日志里。这一轮没跑、一个 token 没烧(同步提交答 500、不计费)。读整条分支的口(会话视图 `GET /v1/sessions/:id` / `…/memory-status`)同答;事件订阅与头探针只读会话头的叶子,对这一形照旧 200 / 起流(契约 §17.7)。体的 `error` 是引擎原句(点名条目 id,不含盘上路径与会话 id),没有 `reason` 机读位。会话同步**导入**时调用方交来的日志没过校验是另一件事:`422 invalid_entries` | **别重试**(确定性失败)。换一个新会话;或从断点之上的条目分叉;或由运维修好 / 移走那份日志。同一 `Idempotency-Key` 重发会重新受理(这类零工作的拒不占键) |
|
|
2274
2292
|
| 流里一帧 `engine_notice` `code:"session.records_skipped"`,`detail:{ dropped, entriesKept, sessionId }`(7.104.0 起,只在文件后端) | 这只会话的存档打开时有 `dropped` 条记录读不出、被跳过了;这一轮**照跑**,但模型读到的历史只剩 `entriesKept` 条。存档没修好之前,每次真正打开这只会话都会再说一次(会话缓存命中的轮次不打开、不说;`SESSION_CACHE_TTL_SEC=0` 时每一轮都说) | 告诉用户「这段对话的一部分历史丢了」;运维按 `fail_open_total{tag="server.session-repo.corrupt-record-skipped"}` 的计数与探针文件(`SEMA_FAILOPEN_PROBE=<文件路径>`,每命中一行)里的逐条明细(文件路径、会话 id、行号、成因)查那份文件。非流式 `POST /v1/tasks` 收不到通告帧(只有运维面的计数与探针) |
|
|
2275
2293
|
| 流里一帧 `engine_notice` `code:"route.finish_reason_unknown"`,`detail:{ finishReason, sessionId, runId }`(7.126.0 起) | 提供方用一个引擎读不懂的结束原因结束了这一轮:回答按模型留下的样子保留,但可能被提供方截短了 | 告诉用户「这一段回答可能不完整」;同一个词同一 run 只说一次。运维按 `finishReason` 查是哪家网关 / 模型发的 |
|
|
2276
2294
|
| 流里一帧 `engine_notice` `code:"route.stream_end_ignored"`,`detail:{ statement, word, frames, sessionId, runId }`(7.126.0 起) | 提供方已经结束了这一轮,它的流却又发来了东西(又一个结束 / 一个错误 / 内容);第一个结束算数,之后的没进这一轮 | 一般无需处置;频繁出现 ⇒ 运维查那条网关的流式实现(`statement` / `word` 说的是迟到的第一帧) |
|
|
@@ -2386,7 +2404,7 @@ k8s 沙箱(pod `activeDeadlineSeconds` 真硬死线)向引擎声明 `lifetimeMs`
|
|
|
2386
2404
|
|
|
2387
2405
|
k8s 车道每条 run 一只新 pod(pod 寿命 = 泄漏护栏,不跨 run 复用)。配了快照存储时,`/workspace` **缺省跨轮保留**(`WORKSPACE_PERSIST` 缺省 `space`):每条 run 结束把工作区快照进本会话住客的空间,下一条 run 开始恢复最近一份。要回到每轮空目录:`WORKSPACE_PERSIST=off`。
|
|
2388
2406
|
|
|
2389
|
-
- **空间 = 会话的这一位住客**(会话化身:会话建行时铸的不透明随机串,删会话随行消失)。同一会话的各条 run 共享一个空间;不同会话互不可见;会话删掉后同一个 id 被重新登记(不论谁)= 新住客,从空目录开始,前一位住客的对象不再被读。前缀 `WORKSPACE_SNAPSHOT_PREFIX` 缺省 `kata
|
|
2407
|
+
- **空间 = 会话的这一位住客**(会话化身:会话建行时铸的不透明随机串,删会话随行消失)。同一会话的各条 run 共享一个空间;不同会话互不可见;会话删掉后同一个 id 被重新登记(不论谁)= 新住客,从空目录开始,前一位住客的对象不再被读。前缀 `WORKSPACE_SNAPSHOT_PREFIX` 缺省 `kata`,可以是**多段路径**(`libA/ws`:一只桶按属主分区;7.136.0 起,单段旧值照常合法),桶 `WORKSPACE_SNAPSHOT_BUCKET` 缺省 `workspaces`(**server 不建桶**:起服时 server 核这只桶在不在,缺 ⇒ 拒启,见下方失败形)。
|
|
2390
2408
|
- **两条车道,两种分区**:
|
|
2391
2409
|
- **跨轮保留**(本节):归档 `<前缀>/<化身>/space/<snapshotId>.tar.gz`,指针 `<前缀>/<化身>/space/latest`;只有这条车道被列举、被删旧,且只在 `space/` 里。按住客分区的理由:读它的是「这只会话的下一条 run」,分区就决定了谁读得到 —— 按会话 id 分,删后重登记的另一位就会恢复前一位的文件。
|
|
2392
2410
|
- **停驻挂起 / 续跑**(引擎驱动的 durable suspend):归档 `<前缀>/<会话 id>/<snapshotId>.tar.gz`(key 形与 7.125.0 之前同形,存量停驻快照照常可读,零迁移;7.126.0 起新写的 `snapshotId` 形为 `kata-<归属记号>-<时间>-<随机>`,归属记号 = 住客化身的不可逆摘要头 16 位,没有已知住客的 run 记 `none`);从不被列举、从不被删旧 —— 一条停驻中的 run 的快照不会被同会话后来的 run 挤掉。按会话 id 分区的理由:读它的只有那条停驻 run 自己的检查点(snapshotId 记在检查点里,删会话连检查点一起删),分区只做命名空间、不决定谁读。
|
|
@@ -2406,11 +2424,14 @@ k8s 车道每条 run 一只新 pod(pod 寿命 = 泄漏护栏,不跨 run 复用)
|
|
|
2406
2424
|
| `WORKSPACE_SNAPSHOT_KEEP` | `3` | 每个空间保留的快照份数,整数 `[1, 50]`;域外 / 小数 ⇒ 拒启 |
|
|
2407
2425
|
| `WORKSPACE_SNAPSHOT_TIMEOUT_MS` | `300000` | 快照时限(毫秒,整数 `[30000, 3600000]`;域外 / 小数 ⇒ 拒启):**一个数管四处** —— run 末打包上传的墙钟上界、连沙箱时恢复的墙钟上界、空间结束时回收座**一次调用**的绝对截止(一次调用一份:会话删除 = 一个空间,托管留存 sweep = 每个域每拍一次、该域这一拍结束的全部空间;**卷回收也在内**)、本副本写者给自己每次围栏期限用的数(终态 / 开始 / 发布三次续期各一界;下一条 run 等的是**上一条 run 的写者**所配的数,不按它自己副本的数 —— 各副本同配时就是同一个数);大文件读写的墙钟上界也是它(卡住不出字节的传输由命令无进展上界先切)。构建目录 GB 级的工作区撞 5 分钟 ⇒ 调大它,或配排除表 |
|
|
2408
2426
|
| `WORKSPACE_SNAPSHOT_EXCLUDE` | 未设 | 快照排除表(见上「排除表」);未设 = 打包命令与旧版逐字同 |
|
|
2427
|
+
| `WORKSPACE_SNAPSHOT_BUCKET` | `workspaces` | 快照桶。起服核它在不在(与会话快照桶 `SESSION_SNAPSHOT_BUCKET` 同一只探针、同一条规则);桶必须预先存在 |
|
|
2428
|
+
| `WORKSPACE_SNAPSHOT_PREFIX` | `kata` | 快照 key 前缀:`/` 分隔的**一段或多段**普通 key 段(`kata`、`libA/ws`);每段以字母或数字开头,其后字母、数字、`.` `_` `-`;空段(首尾或连续的 `/`)/ `.` / `..` / 其它字符 ⇒ 拒启(码 `config.workspace_snapshot_prefix_invalid`,点名第几段),k8s 车道上不论配没配快照存储都核。改前缀 = 新 key 空间,旧前缀下的存量快照不再被读 |
|
|
2409
2429
|
|
|
2410
2430
|
**失败形(都是响亮的)**:
|
|
2411
2431
|
|
|
2412
2432
|
- **没配快照存储**(`MINIO_ENDPOINT` / `MINIO_ACCESS_KEY` / `MINIO_SECRET_KEY` 缺任一)⇒ 工作区每轮为空,`GET /v1/capabilities` 不带 `workspaceSync`;起服一行 `workspace_persist_unavailable`(`missing` 点名缺的键;缺省 `space` 也出,设 `WORKSPACE_PERSIST=off` 即不出)。
|
|
2413
2433
|
- **没有存储后端**(`DB_BACKEND=memory`;配了 SQL 的 `DB_BACKEND` 却显式 `SESSION_BACKEND=memory` 时库不开,同一形)× `space`(含缺省,且配了快照存储)⇒ **拒启**,拒句一行点名 `WORKSPACE_PERSIST=space`、缺的存储后端、会话店种类(与 `/health.sessionBackend` 同一读法)与可用的后端。出路:`DB_BACKEND=local`,或 SQL(`DB_BACKEND=mysql|pg`,`SESSION_BACKEND` 不设 `memory`),或设 `WORKSPACE_PERSIST=off`。`SESSION_BACKEND=memory` 配 `DB_BACKEND=local` 不在此列:被收编成 local 店,照起。拒启而不是降级:否则能力位报 `space`、每条 run 却一份都不存。
|
|
2434
|
+
- **快照桶不存在**(`WORKSPACE_SNAPSHOT_BUCKET` 指的桶没建):**起服拒启**(k8s 车道配了快照存储时,server 在打开任何店之前核这只桶;拒句点名桶名、`MINIO_ENDPOINT` 的显示安全形与出路 —— 建桶,或把 `WORKSPACE_SNAPSHOT_BUCKET` 指向已有的桶;建桶晚于 server 起是编排里的常态,所以缺桶在 `STORE_LOCK_WAIT_MS` 窗内重试、到窗才拒);连不上 / 403 / 5xx 这些证明不了桶缺的答复不拒启,走 fail-open 兜底口 `server.snapshot-bucket.presence-unproven`。起服之后桶才没的(被删),或起服核证明不了的部署:读 `latest` 得 `404 NoSuchBucket` ⇒ 这条 run **连沙箱失败**(`connect_failed`,句子带 `snapshot_bucket_missing`、桶名与出路),一只 pod 都不建,另有一行 `workspace_snapshot_bucket_missing`(`space` / `bucket` / `code` / `effect`),每只执行环境至多一次;**不再**读成「空间第一条 run」而给一个空目录、每轮只留下一条不点名桶的存失败。无 `NoSuchBucket` 的 404(桶在、键不在;代理的 404)仍是「空间第一条 run」。
|
|
2414
2435
|
- **读 `latest` 失败**(非 404 的 HTTP 错、网络错)、**读或弃提交围栏那一行失败**或**读会话住客失败**(会话店报错、答出的不是住客令牌,或没在 `rpcTimeoutMs`〔30 秒,与读围栏行、读 `latest` 同一只墙〕内答 —— 挂住的会话店不再让 run 的起步永挂)⇒ 这条 run 连沙箱失败(`connect_failed`,工具调用报沙箱不可用;停驻 run 续跑时的恢复同样失败),**不会**悄悄给一个空目录、也不猜「没有提交在飞」—— 「以为恢复了其实没有」比失败更坏。读住客失败时一只 pod 都不建,另有一行 `workspace_space_unreadable_occupant`(`sessionId` / `code`:会话店错误的码,不带正文;读超时的码是 `incarnation_read_timeout`),每条 run 至多一次。修好存储 / 数据库后重跑,或设 `WORKSPACE_PERSIST=off` 即恢复。
|
|
2415
2436
|
- **`latest` 指向的快照对象不在了**(被删 / 被只盖 `.tar.gz` 的生命周期规则清掉)⇒ 沙箱**照常起**:一行 `workspace_latest_dangling`(`space` / `key` / `deleted`),删掉悬空的 `latest`,这条 run 从空目录开始,它的 run 末快照成为新的 `latest`(数据已被运维规则清走:响亮、可继续)。指针删不掉 ⇒ 同一行 `deleted: false` + `error`,下一条 run 再说一次。
|
|
2416
2437
|
- **`latest` 指向的快照在、却拿不回来**(桶答了却不是 200 / 解压失败 / 超过快照时限 / 传输中断)⇒ 这条 run **仍然连沙箱失败**(`connect_failed`)、**不**退到空目录 —— 用户的改动还在桶里,静默空目录 = 让人以为丢了。**一条 run 只恢复一次**(同一只执行环境上并发的连沙箱 —— 显式 `connect()` 与首个工具调用的懒连接 —— 共用同一次建连:一只 pod、一次恢复、一个终局,被拒也是同一个拒;恢复还没完成时才到的工具调用和 `reconnect` 也等这一次建连 —— pod 先于它的工作区存在,不在半恢复的 `/workspace` 上先跑):瞬时类(`timeout` 超过快照时限 / `transport` 传输中断:执行通道断了,或 pod 取包的 GET 没拿到完整应答 —— 连不上、被重置、传到一半被截)在恢复这一步里换一只新 pod 再试一次(约 1 秒后),再试成了 ⇒ 照常起、不出行;`store_busy`(桶答 `408` / `429` / `500` / `502` / `503` / `504` —— 「现在不行」,同一请求再发可能换答案)同属瞬时类;非瞬时类(`download` 桶答了、答的是其余非 200 —— 如 `401` / `403` / `501`;`untar` 包完整下来了却解不开)不重试。每次没成的尝试,新起的 pod 当场删掉(残缺目录既不给工具用,也不会在 run 末被快照回去覆盖 `latest`)。恢复终局是拒 ⇒ **恰一行** `workspace_restore_failed`(`space` / `snapshotId` / `class` = `store_busy` \| `download` \| `untar` \| `timeout` \| `transport` / `attempts` = 这次恢复的尝试次数(`1` 或 `2`)/ `boundMs` = 每次尝试的时限 / `error`,预签 URL 已脱敏),这条 run 之后的每次沙箱操作都答**同一句**拒(不再建 pod、不再读 `latest`、不再出行);建 pod 本身失败不是恢复的结局(不出这行,下一次操作重来)。拒句写明类别与尝试次数,点名两条出路:**调大 `WORKSPACE_SNAPSHOT_TIMEOUT_MS`**(大工作区撞上界),或 **`WORKSPACE_PERSIST=off` 跑一轮**(空目录起,不删任何快照;改回 `space` 后下一条 run 照常恢复那一份)。没有「删指针」的接口:丢弃这份快照 = 丢弃用户数据,要做就由运维在对象存储侧有意识地删 `<前缀>/<化身>/space/latest`(下一条 run 从空目录起)。与上一条「悬空」分开:那是对象**不在**(没东西可恢复 ⇒ 空目录 + 响亮),这是对象**在**而拿不回来(⇒ 拒)。
|
|
@@ -2432,19 +2453,30 @@ k8s 车道每条 run 一只新 pod(pod 寿命 = 泄漏护栏,不跨 run 复用)
|
|
|
2432
2453
|
### 9.3c k8s 车道:点名 env 转发(`K8S_SANDBOX_ENV`)
|
|
2433
2454
|
|
|
2434
2455
|
沙箱 pod 里的工具要用服务进程环境里的凭据(典型:团队形态下 `git clone` / `git push` 私有仓)时,把**变量名**写进 `K8S_SANDBOX_ENV`,值留在服务进程环境里。
|
|
2435
|
-
它和 `E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV` 是**同一个法**(解析、拒启、告警、目录行、起服行 `sandboxEnvKeys`、多租户姿态全部见上文「沙箱带外 env 名单」一节),这里只写 k8s
|
|
2456
|
+
它和 `E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV` 是**同一个法**(解析、拒启、告警、目录行、起服行 `sandboxEnvKeys`、多租户姿态全部见上文「沙箱带外 env 名单」一节),这里只写 k8s 车道自己的四件事:
|
|
2436
2457
|
|
|
2437
2458
|
名单里点名 `SEMA_PKG_SOURCE`(或任何包源变量)= 把服务进程自己的那份值原样下发,显式胜过 `SANDBOX_PKG_SOURCE` 推导出的那份 —— pod 的启动命令据此走包源 hook;这是「显式名单胜派生」在 k8s 车道上的自然结果,不是第二条通道。
|
|
2438
2459
|
|
|
2439
|
-
-
|
|
2440
|
-
|
|
2460
|
+
- **值去哪**(7.137.0 起):只进**该 pod 自己的一只 Secret**(名 `sbx-env-<随机>`,键 `env.<变量名>`,`immutable`,`ownerReferences` 指向这只 pod —— pod 删了,
|
|
2461
|
+
集群的垃圾回收把 Secret 一起删掉);pod spec 里的容器 env 只带 `valueFrom.secretKeyRef` 引用,**不带值**。容器起来之后 pod 里每个进程、每条 exec 命令照样看得到这些变量。
|
|
2462
|
+
次序是先建 pod、再用 pod 的 `uid` 建 Secret(容器在 Secret 到位前不启动);Secret 建不成 ⇒ 这只 pod 被删、这次连接拒(Role 不许 = `auth_failed`,其余 = `connect_failed`,拒句不带值),
|
|
2463
|
+
**不回落**成把值写进 pod spec。它与 `SANDBOX_PKG_SOURCE` 推导出的包源变量在装配处分座一次,**同名时名单赢**(派生的那条不出现;部署方的刻意覆盖);
|
|
2464
|
+
适配器不把值拼进执行命令串、不写进 `/workspace`,因而不随跨轮保留 / 挂起的快照进对象存储;不进事件日志(起服行只报键名)。
|
|
2441
2465
|
pod 里的进程读得到这些值 —— 那正是转发的目的;进程自己若把值写进 `/workspace`,它就会随快照走。
|
|
2466
|
+
- **RBAC**:沙箱命名空间里服务账号要有 `secrets` 的 `create`(Role 规则 `- apiGroups: [""] resources: [secrets] verbs: [create]`;随包 `deploy/sema-up` 的 Role 已带)。
|
|
2467
|
+
它恰是服务为 env Secret 真发的调用;不要给 `get`(服务从不读回,给了就读得到命名空间里每一只 Secret)与 `delete`(Secret 随属主 pod 由集群回收)。租约 Secret 的 `patch` 只在 §9.3h 的平面打开时要。
|
|
2468
|
+
名单非空时起服各问一次 SelfSubjectAccessReview:答了「不许」⇒ **拒启**,拒句带 `[sandbox-secret-rbac]`、点名缺的动词与这条规则;问不到(API 连不上 / 审查本身被拒)⇒ 照常起服,
|
|
2469
|
+
一行 `sandbox_secret_seat_unverified` + fail-open 计数 `server.sandbox-secret.seat-unverified` —— 那时若 Role 真缺,第一个要转发的 run 在连接时被拒、它的 pod 被删。
|
|
2470
|
+
- **放弃的 pod 删不掉(运维面,7.137.0 起)**:上面这类拒绝(env Secret 建不成 · 成员凭据代理的租约 Secret 建不成 · 代理镜像拉不下)要删那只刚建的 pod;这一删自己失败(请求抛 / API 回 403 或 5xx,比如 Role 缺 pods `delete`)时,
|
|
2471
|
+
连接的拒答不变,服务多记一行 error `sandbox_pod_delete_failed`(`pod` / `namespace` / `why` / `cause`,不带值、不带 API 文本)+ fail-open 计数 `server.sandbox-pod.delete-failed`;pod 与它名下的 Secret 留给回收器(§9.3g)。
|
|
2472
|
+
计数持续非零就按 `cause` 核 Role 的 pods `delete` 与 API 可达性。
|
|
2442
2473
|
- **没设 = 零转发**:`K8S_SANDBOX_ENV` 未设 / 空串时,pod 与未引入本旋钮时逐字同形(env 只有包源那一组)。只在 `REMOTE_EXEC=k8s` 时求值。
|
|
2443
2474
|
- **凭据的推荐用法**:`K8S_SANDBOX_ENV=GIT_TOKEN`,镜像里的 git 配成用环境变量取凭据(例如 `GIT_ASKPASS` 指向一个读 `$GIT_TOKEN` 的小脚本,或 credential helper),
|
|
2444
2475
|
令牌就不进提示词、命令串和事件日志。
|
|
2445
2476
|
|
|
2446
|
-
**风险模型(与 e2b / docker 同一档:运维信任形)
|
|
2447
|
-
|
|
2477
|
+
**风险模型(与 e2b / docker 同一档:运维信任形)**:值不再出现在 pod spec 里(`get pods` 读不到),但它在该 pod 的 Secret 里 —— 在该 namespace 有 `get secrets` 权限的人、
|
|
2478
|
+
节点上能检视容器的人、以及集群 etcd(Secret 是否静态加密取决于集群配置,服务读不到)都读得到;记录请求体的 API 审计日志按其审计策略可能记下建 Secret 的请求体。
|
|
2479
|
+
所以给的仍是**最小权限**令牌(能只读就只读、限单仓、可吊销),并按 namespace 收紧读 secrets 的 RBAC、给集群开 Secret 静态加密。本旋钮只是把值放进 pod;「凭证只留在适配器侧、永不进 pod」是另一种形,不是本键的语义。
|
|
2448
2480
|
|
|
2449
2481
|
### 9.3d k8s 车道:按工作区空间的构建缓存卷(7.126.0 起,缺省关)
|
|
2450
2482
|
|
|
@@ -2549,6 +2581,50 @@ e2b SDK 自己的建命令握手另有一界(缺省 60 s),比命令的界早时
|
|
|
2549
2581
|
ssh / adb / local-docker 三条车道的流式调用同读这一条律:没有车道活性界,只受自己说的静默界 `readTimeoutMs` 约束,到点的切断句与 host 同一句
|
|
2550
2582
|
(`no output for <N> s — cut off by the command's own idle bound (readTimeoutMs)`);`readTimeoutMs` 为 `0` / 负数 / 非数不算说了界。
|
|
2551
2583
|
|
|
2584
|
+
### 9.3g k8s 车道:收不掉的沙箱 Pod 由服务删(reaper;7.137.0 起)
|
|
2585
|
+
|
|
2586
|
+
`activeDeadlineSeconds` 到点只让 pod 的容器终止(pod 进 `Failed`),**不删 pod 对象**;run 收尾时服务自己删 pod,而服务进程崩溃、或收尾那一删失败时,pod 对象(连同它的 Secret,§9.3c)就一直留着。
|
|
2587
|
+
本版起每个副本在起服后的第一个维护 tick 跑一轮回收、此后每 5 分钟一轮,只删两种 pod(按身份标签 `app=sema-sandbox,managed-by=sema-server` 列本沙箱命名空间):
|
|
2588
|
+
|
|
2589
|
+
- **容器已全部结束**(`status.phase` 是 `Succeeded` / `Failed`)—— 删它不掐断任何东西;
|
|
2590
|
+
- **它的 run 已终**:顶层 run 的 pod 带标签 `sema.io/run=<run id>`;run 行在服务的 run 账本里已是终态(completed / failed / blocked),且自 run 行**转终态的那一刻**起超过
|
|
2591
|
+
`max(6 分钟, 工作区快照时限 + 15 秒)` —— 快照时限是**建这只 pod 的副本**的 `WORKSPACE_SNAPSHOT_TIMEOUT_MS`(缺省 300 000 ⇒ 6 分钟;抬到 600 000 ⇒ 615 秒),因为 run 收尾在写完终态之后还可能在 pod 上存快照,至多这么久。
|
|
2592
|
+
这个上界在建 pod 时算一次、写进 pod 的注解 `sema.io/run-end-hold-ms`(毫秒),回收器只读 pod 自带的这一格、不读自己副本的配置:滚动升级或改 `WORKSPACE_SNAPSHOT_TIMEOUT_MS` 时,每只 pod 按建它的副本的时限,
|
|
2593
|
+
时限短的副本不会提前删时限长的副本还在存快照的 pod。带 run 标签的 pod 上注解缺席、不是十进制整数或不在 [360 000, 3 615 000] 里(不是本服务按配置写的数)⇒ 不按这一条删(跳过原因 `hold_unreadable`),容器结束后按上一条删;本版之前建的 pod 既没有 run 标签、也没有这个注解 —— 它们落在下面「不带 run 标签」一类(跳过原因 `no_run_label`),只在容器结束后删。
|
|
2594
|
+
持久化开 / 关的部署同一条;没有单独的旋钮。
|
|
2595
|
+
|
|
2596
|
+
还在跑的 run(以及停驻待决的 run)的 pod **从不**被它删,哪怕 pod 排队很久才起来;查不到 run 行、读不了 run 账本的 pod 只按第一条删。
|
|
2597
|
+
**不带 run 标签的 pod**(委派子代 / 后台代理 / 服务自己起的 run)**只在它的容器全部结束之后才删** —— 会话级后台代理刻意活过父 run 的那一轮,「会话里已没有在跑的 run」不等于「这只 pod 没人用」。
|
|
2598
|
+
每轮一行 `sandbox_pod_reap_round`(`listed` / `scanned` / 按原因分的 `deleted` 与 `skipped` / `gone` / `deleteFailed`),每删一只一行 `sandbox_pod_reaped`(`pod` / `reason` / `run`),计数 `sandbox_pods_reaped_total{reason}`。
|
|
2599
|
+
**列举失败**(API 不可达 / 403)⇒ 这一轮一只都不删:一行 warn `sandbox_pod_reap_list_failed`(带原因)+ fail-open 计数 `server.sandbox-pod-reaper.list-failed`,下一轮照常再列 —— 计数持续非零就核 API 可达性与 Role 的 pods `list`。
|
|
2600
|
+
**某一只删失败**(Role 缺 pods `delete` / API 抖)⇒ 不挡同轮其余:一行 error `sandbox_pod_delete_failed`(`pod` / `namespace` / `why` = `reaper_containers_ended` · `reaper_run_ended_past_hold` / `cause` = `http_<状态>` · `request_failed`,不带值、不带 API 文本)+ fail-open 计数 `server.sandbox-pod.delete-failed`(与连接清理删失败是同一个计数,按 `why` 分座),这一只下一轮再判再删 —— `why` 以 `reaper_` 起头的计数持续非零就核 Role 的 pods `delete`:兜底本身删不动,残留会随轮次累积。
|
|
2601
|
+
没有存储后端(查不了 run 行)的部署上本回收照跑:「容器已全部结束」那一条照删,带 run 标签的 pod 跳过(`run_store_absent`)。
|
|
2602
|
+
用的是 Role 里既有的 pods `list` / `delete`。**运维可见的差别**:已结束的沙箱 pod 不再留着供事后 `kubectl logs` / `kubectl describe` 看;run 自己的日志面(事件流、账本)不受影响。没有旋钮。
|
|
2603
|
+
**两套部署共用一个沙箱命名空间**(缺省都是 `sandboxes`):回收器按身份标签 `app=sema-sandbox,managed-by=sema-server` 列 pod,标签里没有部署身份 ⇒ 一套部署会删另一套**容器已结束**的 pod
|
|
2604
|
+
(只有调用方把同一个 run id 交给两套部署时才会碰到对方的活 pod)。用 `K8S_NAMESPACE` 给每套部署各一个沙箱命名空间。
|
|
2605
|
+
|
|
2606
|
+
### 9.3h k8s 车道:成员 git 凭据代理(7.137.0 起落组件;本版平面恒关)
|
|
2607
|
+
|
|
2608
|
+
成员的 git 凭据只进**本人 run 的 pod**,而且不进 agent 容器:它进同一只 pod 里的第二只容器 —— 凭据代理(`containers[1]`)。agent 容器里的 git 经
|
|
2609
|
+
`url."http://127.0.0.1:47311/o/<i>/".insteadOf = "<origin>/"`(非秘密 env `GIT_CONFIG_COUNT` / `GIT_CONFIG_KEY_<i>` / `GIT_CONFIG_VALUE_<i>`)把对 `<origin>` 的 clone / fetch / push 交给代理;
|
|
2610
|
+
代理按条目把请求转给 origin,只在条目的**租约**有效时加 `Authorization`。
|
|
2611
|
+
|
|
2612
|
+
- **代理只做 git smart-HTTP**:`/o/<i>/<owner>/<repo>/info/refs`(GET,`service=git-upload-pack|git-receive-pack`)与两个 pack 端点(POST);别的路径、带 `%` 的路径、别的方法 / 查询 / framing 一律 400,
|
|
2613
|
+
上游收不到。重定向从不跟随;上游 401 改写成 git 自己的报错(`fatal: remote error: <句子>`),句子由服务写在条目上。只对 https 的 origin 附凭据;http 的 origin 照常路由(公开仓可用),永不带凭据。
|
|
2614
|
+
上游每一等都有界(常量,无旋钮):连接(TCP + TLS 握手)10 秒;应答头自请求**发完**起 30 秒(大 push 上传中不受它限)。超时 ⇒ 502,正文带 `upstream_timeout` 与一句原因,上游连接被断。
|
|
2615
|
+
- **租约**:服务把租约写进该 pod 自己的一只 Secret(`sema.io/sandbox-lease`,只一个键 `broker.json`,属于这只 pod,随 pod 由集群回收);每个可用条目一份租约(`leaseId` + `ttlMs`,至多 5 分钟)。
|
|
2616
|
+
代理按**自己的单调钟**从「第一次见到这个 `leaseId`」起算,到点对该条目所有已认证连接立即掐断(不做优雅关)、此后不再附认证;续用只有一种 —— 服务写新租约(新 `leaseId`)。
|
|
2617
|
+
**硬界是 pod 被删**(run 收尾删,或 §9.3g 的回收);「最后一份租约什么时候被投影进 pod」取决于 k8s,不是服务的承诺;上游已经收到的字节不在任何界内。
|
|
2618
|
+
- **pod 形**:只在**平面开着且该 run 的身份来源是 `sso`** 时挂代理;否则 pod 与不挂时逐字节同。代理容器的 `securityContext` 由适配器逐键写死(uid 0、只读根、丢全部能力、禁提权、`RuntimeDefault` seccomp),
|
|
2619
|
+
租约卷只读、0400、只挂代理容器;`shareProcessNamespace:false`;连接要等代理容器就绪(exec 探针)才算成。部署的 pod 补丁在代理在场时只许 `volumes` / `volumeMounts` / `containerSecurityContext` / `labels`
|
|
2620
|
+
(容器级只落 agent 容器),**`podSecurityContext` 整只不许** —— 用了它的部署,平面关(`pod_patch_reaches_broker`)而不拒启;把 pod 级加固挪进 `containerSecurityContext`。
|
|
2621
|
+
- **平面开没开,看一处**:`GET /v1/capabilities` 的 `credentialPlane.sandboxGit`(`on` + 逐 origin `delivers`,或 `off` + 第一个不成立的前提词,九词闭集与判序见契约附录 B)。
|
|
2622
|
+
起服时若是 k8s 多租户部署,日志一行 `credential_plane_sandbox_git`(`state` / `reason` / `why`)。
|
|
2623
|
+
- **旋钮**:`K8S_SANDBOX_BROKER_IMAGE`(代理容器的镜像;建议填本引擎自身镜像 —— 代理是本包里固定路径的程序;不设 ⇒ `broker_image_unset`)。Role 在 §9.3c 的 `create` 之外还要 `patch`(改写租约;租约 Secret 只用这两个;本版平面恒关,不用先给)。
|
|
2624
|
+
- ⚠️ **7.137.0 平面恒 `off`**:两处读口还没接线 —— 取值口的 worker 凭据、有效配置的 `sandboxGit` 块;读数停在 `no_caller_credential`(或更前的词),没有 pod 会挂上代理。
|
|
2625
|
+
run 行的身份来源不是接线点:读的是受理时记在 run 行上的那一份身份。本节写的是组件的行为与边界;
|
|
2626
|
+
打开平面的那一版会在本节补上取值与续期。
|
|
2627
|
+
|
|
2552
2628
|
### 9.4 auto 模式(`permissionMode:"auto"`):武装三态、本地 org deny、自查(与 core 的意图武装式合修)
|
|
2553
2629
|
|
|
2554
2630
|
**背景**:此前引擎只在「组织授予」(web-admin 解出的 per-principal `runtimeCaps.autoMode === true`)时武装
|
|
@@ -2805,7 +2881,7 @@ WebSearch 工具的 `tool_result` 错误文本,回到模型的对话里,**不是
|
|
|
2805
2881
|
|
|
2806
2882
|
条目的受理键 = `name` · `transport` · `allowTools` · `toolAxes` · `toolFaces` · `contentOrigin` · `elicitation` · `source` · `startupTimeoutMs`(7.132.0 起)(`transport` 的子键:`kind` · `command` · `args` · `env`(stdio),`kind` · `url` · `headers` · `principalHeader` · `assertionHeader`(http))。**新提交上每个键只有一只判官,判官在受理口,写坏的答案只有一种**(下面第一条:400)。`startupTimeoutMs`(这一只 server 自己的启动界,毫秒整数,范围与引擎 env 旋钮 `MCP_TIMEOUT` 同一段)7.134.0 起同样在受理口判 —— 判官是引擎包根导出的 `mcpStartupTimeoutMs`(启动界的唯一读者):数字串 / 小数 / 越界 / `null` ⇒ `400 request.field_invalid`,原因句是引擎的那一句(7.132.0–7.133.x:受理口不判、请求照常 200,引擎拨号时只拒这一只 server);耐久 resume 重放存量请求不判(原样交引擎,坏值在拨号时只拒这一只 server —— `config.mcp_startup_timeout_invalid`,兄弟照挂)。
|
|
2807
2883
|
- **新提交**(`POST /v1/tasks` · `/v1/tasks/stream` · `/v1/runs`):键在场却判不出形 ⇒ `400 request.field_invalid`,拒句形如 `mcpServers[2].allowTools (server "crm") is not a usable declaration — must be an array of strings (omit the key to leave the server's tools unrestricted) — got a string of 4 characters. …`:点名 `mcpServers[i].<键路径>`、server 名与该键判官的原因句,**整份请求不收** —— 不挂一半、不丢声明、不默认。原因句不回显声明的值,有几处例外,回显只有一只口径(剥控制与双向字符、截到 64 字、超出加 `…`):server 名(含写坏的 `name` 值,另报送来的原长度)、`contentOrigin` 词表外的值;`toolAxes` / `toolFaces` 的原因句是引擎写的(7.134.0 起 `toolFaces` 的判官也是引擎包根导出的 `readToolFaces`):点名工具名(引擎截 80 字)与成员,`toolFaces` 还会点名闭词表拒的那个词,整句详情由引擎截到 240 字、本服务再剥控制与双向字符 —— 不回显显示文本、不回显合规的列表;`transport.url` 的值从不回显(带凭据的 url 被拒时,原因句只点名命中的那一段与凭据名表里的那个词)。可选键**缺席**永远合法(真「没声明」),必填的 `name` / `transport` 缺席同样 400;在场的 `null` 不是缺席。与单用户 / 多租户无关。列表本身同一条律(7.132.0 起,先于条目判):`mcpServers` 在场却不是数组、超过 32 条(整份拒,不挂前 32 条)、某一条不是对象 ⇒ `400 request.field_invalid`,拒句点名 `mcpServers` 或 `mcpServers[i]` 与形的种类;条目层或 `transport` 层出现词表外的键 ⇒ `400 request.body_shape`,拒体带 `unknownKeys`(全路径、至多 20 条)。`mcpServers` 缺席或为 `null` = 没声明,照常受理。
|
|
2808
|
-
- **`transport.url` 不得带凭据(7.135.0 起)**:http 传输的 `url` 带 userinfo(`https://user:pass@host/mcp`,只有用户名也算),或带名字在凭据名表里的 query / fragment 参数(`?api_key=` · `?key=` · `?token=` · `#access_token=` …;表 = 本服务日志脱敏器认作凭据名的那一张,全表见契约附录 A `request.field_invalid` 行;名字大小写不敏感,按解一次百分号编码读)⇒ 新提交 `400 request.field_invalid`,体另带 `field: "mcpServers[i].transport.url"` 与 `reason`(`url_userinfo` | `url_credential_param`),原因句点名出路:**凭据改写进 `transport.headers`**(如 `"Authorization": "Bearer …"`)。为什么:任务停驻时提交体整份进续跑上下文,`transport.headers` 的值在那里只以封签存在,`url` 照原样存(日志与读面同理)。值从不回显;解析不了的 url 不在这一问里判(拨号时失败)。7.134.x 及以前照收、原样落停驻上下文;带 userinfo 的 url 本就连不上(fetch 对带凭据的 URL 构造即抛),带名表参数的升级后要改。同一条规则在 A2A 对端 / http 钩子 /
|
|
2884
|
+
- **`transport.url` 不得带凭据(7.135.0 起)**:http 传输的 `url` 带 userinfo(`https://user:pass@host/mcp`,只有用户名也算),或带名字在凭据名表里的 query / fragment 参数(`?api_key=` · `?key=` · `?token=` · `#access_token=` …;表 = 本服务日志脱敏器认作凭据名的那一张,全表见契约附录 A `request.field_invalid` 行;名字大小写不敏感,按解一次百分号编码读)⇒ 新提交 `400 request.field_invalid`,体另带 `field: "mcpServers[i].transport.url"` 与 `reason`(`url_userinfo` | `url_credential_param`),原因句点名出路:**凭据改写进 `transport.headers`**(如 `"Authorization": "Bearer …"`)。为什么:任务停驻时提交体整份进续跑上下文,`transport.headers` 的值在那里只以封签存在,`url` 照原样存(日志与读面同理)。值从不回显;解析不了的 url 不在这一问里判(拨号时失败)。7.134.x 及以前照收、原样落停驻上下文;带 userinfo 的 url 本就连不上(fetch 对带凭据的 URL 构造即抛),带名表参数的升级后要改。同一条规则在 A2A 对端 / http 钩子 / 搜索后端 / 图片 URL 上的几座见 9.5g。
|
|
2809
2885
|
- **耐久 resume 重放存量请求**:不拒(拒会把升级前 park 的任务永久卡死;`transport.url` 带凭据的存量条目照挂、url 逐字交引擎)。写坏的 `name` · `transport` 的 `kind`/`command`/`url` · `allowTools` · `toolAxes` · `toolFaces` ⇒ 这一条 server 整条不进,运维 warn `mcp_injection_dropped`(`reason: malformed_entry`,`field` 点名)并投用户面通告 `mcp.injection_dropped`(其中 `toolAxes` 在一次性迁移 M19 的窗开着时先按升级前读法重读一次,过了就挂,见上文 `toolAxes` 一节);写坏的 `contentOrigin` · `elicitation` · `source` · `transport` 的 `args`/`principalHeader` 与词表外的键 ⇒ 丢掉那一个键、server 照挂;`transport` 的 `env`/`headers`(名 → 串记录)对 JSON 存体按 7.131.x 的读法交:有值不是串的成员 ⇒ 整键不交,只有 `__proto__` 名的成员坏 ⇒ 剔掉它、其余照交;server 照挂,一行运维 warn `mcp_transport_members_dropped` 点名(见 9.5c-bis 同段);条目不是对象 ⇒ 这一条不进(`malformed_entry`);超过 32 条 ⇒ 第 33 条起只记 `over_cap`;整只不是数组 ⇒ 整只忽略 + 运维 warn `mcp_injection_ignored`。
|
|
2810
2886
|
- `POST /v1/capabilities/mcp/probe` 问的是新提交那**同一只判官**:上面每一形同码同句(含 `unknownKeys`,与 url 带凭据那一形的 `field` / `reason`),拒之前零拨号;判官受理的申报,探测口拨的就是一条任务提交会交给引擎的那一份(判官的合法归一照收)。它多出的两形:本口的体就是这份列表,`mcpServers` 缺席或为 `null` ⇒ `400 request.body_shape`(缺必需字段);声明了 `transport.assertionHeader` 的条目 ⇒ `400 request.field_invalid`(见 9.5d:本版承载、不发送,新提交照挂,探测不拨一份少了断言头的配置 —— 同一只谓词,后果按门分)。7.131.x 上本口另有一只判法(申报原文与读器产物逐字比),列表不是数组 / 条目不是对象答 `request.body_shape`。
|
|
2811
2887
|
- **`toolFaces` 的判官是引擎的 `readToolFaces`(7.134.0 起;此前本服务自带一只只判 JSON 形的读器)**:它在门口就判声明本身能说的一切 —— 成员与类型、五张闭词表、上界、跨成员律 ——
|
|
@@ -2814,6 +2890,8 @@ WebSearch 工具的 `tool_result` 错误文本,回到模型的对话里,**不是
|
|
|
2814
2890
|
`patternParam` 只在 `absent: "base"` 旁有意义,单独写是一份读不到的声明(inert)、同样整条拒;未知成员(笔误)、安全轴写成面成员(`effect` 属于 `toolAxes`)、非布尔旗、错型成员。
|
|
2815
2891
|
部署腿(中心 / `config.d`)的坏声明若绕过本服务直接到引擎,引擎在准备门**整任务**拒(`config.tool_face_invalid`)—— 一处笔误整任务起不来,写之前对一下这三条。
|
|
2816
2892
|
要看物化 schema 才判得了的那一半(`param` 是不是工具的顶层参数、审批卡要的键在不在)与 `resultCards` 落在别的命名空间,仍由引擎在列举处**按工具**判(那一只工具不挂,通告 `config.tool_face_invalid`,兄弟照挂)。
|
|
2893
|
+
- **列表上限的拒句(7.136.0 起换成全仓同一形)**:`mcpServers` 超过 32 条 ⇒ `400 request.field_invalid`,句子头一句是 `mcpServers has 33 entries — at most 32 are accepted`,体另带撞帽机读位一组键(`field:"mcpServers"` · `reason:"over_cap"` · `count` · `cap`),
|
|
2894
|
+
与 `a2aPeers` 超界、`images` 超界同一形;7.135.0 及以前的头一句是 `mcpServers carries 33 declarations, above this deployment's ceiling of 32`、体没有机读位。码与状态(400 `request.field_invalid`)不变,探测口同一句同一组机读位。**按 `errorCode` 与 `field` / `reason` 分支,不要匹配句子。**
|
|
2817
2895
|
- 7.130.x 及更早:`allowTools` / `toolFaces` / `name` / `transport` 写坏 ⇒ 200 + 这条 server 不挂;`elicitation` / `source` / `transport` 的可选子键写坏 ⇒ 200 + 悄悄丢键。7.131.x 及更早:列表整只不是数组 ⇒ 200、整只忽略;条目不是对象 ⇒ 200 + 点名丢弃;超过 32 条 ⇒ 200、挂前 32 条;词表外的键 ⇒ 悄悄不读。升级后这些请求变 400:改对那个键(或删掉它)、把列表改成数组、删掉第 33 条起的条目。
|
|
2818
2896
|
|
|
2819
2897
|
### 9.5f 中心提示层:一种 wire 形、按场景挂段、品牌行与「无持久记忆」披露(7.132.0 起)
|
|
@@ -2834,10 +2912,10 @@ WebSearch 工具的 `tool_result` 错误文本,回到模型的对话里,**不是
|
|
|
2834
2912
|
- **「本会话没有持久记忆」**:引擎判、引擎渲染(记忆面没挂 ∧ 名册不能持久 ∧ 不是委派子代)。本部署有引擎推断看不见的持久通道(MCP 记忆服务器、自定义写工具)⇒ `MEMORY_PERSISTENCE_CAPABLE=true`
|
|
2835
2913
|
撤回这段披露;host 车道自动声明。
|
|
2836
2914
|
|
|
2837
|
-
### 9.5g 请求体里的 URL 不得带凭据:MCP 传输 · A2A 对端 · http 钩子 · 搜索后端(7.135.0
|
|
2915
|
+
### 9.5g 请求体里的 URL 不得带凭据:MCP 传输 · A2A 对端 · http 钩子 · 搜索后端(7.135.0 起)· 图片 URL(7.136.0 起)(新提交 400)
|
|
2838
2916
|
|
|
2839
2917
|
任务停驻时提交体整份进续跑上下文,请求自带的凭据位(`mcpServers[i].transport.headers` · `a2aPeers[i].headers` · `settings.hooks.<事件>[g].hooks[h].headers` · `settings.webSearch.apiKey`)在那里只以封签存在;
|
|
2840
|
-
而「值是 URL」的字段不在封签位里 —— URL 里的凭据会原样落库 / 落盘(日志与读面同理)
|
|
2918
|
+
而「值是 URL」的字段不在封签位里 —— URL 里的凭据会原样落库 / 落盘(日志与读面同理)。所以同一条规则落在五类座位上,**一只判官、一个拒形**:
|
|
2841
2919
|
|
|
2842
2920
|
| 座位(体内坐标) | 凭据该写在哪(已封签的位) |
|
|
2843
2921
|
|---|---|
|
|
@@ -2845,13 +2923,29 @@ WebSearch 工具的 `tool_result` 错误文本,回到模型的对话里,**不是
|
|
|
2845
2923
|
| `a2aPeers[i].url` · `a2aPeers[i].cardUrl` | 同对端的 `headers` |
|
|
2846
2924
|
| `settings.hooks.<事件>[g].hooks[h].url`(`type:"http"` 条目) | 同条目的 `headers`(`$NAME` + `allowedEnvVars`,配置本身不带密钥值,见 9.5c) |
|
|
2847
2925
|
| `settings.webSearch.endpoint` | 同段的 `apiKey`(brave / tavily 发成请求头;searxng 腿不发鉴权头,要凭据的实例在部署面声明 `WEB_SEARCH_ENDPOINT`) |
|
|
2926
|
+
| `images[i].url`(`{ url }` 臂;7.136.0 起) | 没有封签的头位 —— 图片字节改走内联 `{ data, mimeType }`(7.136.0 起) |
|
|
2848
2927
|
|
|
2849
2928
|
- **判据**:URL 带 userinfo(`https://user:pass@host/…`,只有用户名也算),或带名字在凭据名表里的 query / fragment 参数(`?api_key=` · `?key=` · `?token=` · `#access_token=` …;表 = 本服务日志脱敏器认作凭据名的那一张,全表见契约附录 A `request.field_invalid` 行;名字大小写不敏感,解一次百分号编码读,整名相等)。
|
|
2850
2929
|
- **答案**:新提交(`POST /v1/tasks` · `/v1/tasks/stream` · `/v1/runs`)`400 request.field_invalid`,体另带 `field`(上表的体内全坐标)与 `reason`(`url_userinfo` | `url_credential_param`);句子点名坐标、命中的那一段与表里的词,**不回显 URL 的值**,并指出路。MCP 座另有探测口(`POST /v1/capabilities/mcp/probe`,同码同句同机读位)。
|
|
2851
|
-
- **A2A
|
|
2852
|
-
|
|
2930
|
+
- **A2A 座**并进 `a2aPeers` 的条目判官表(7.136.0 起,与 `mcpServers` 同一副拒绝骨架,见下):`url` / `cardUrl` 先判是 http(s) 串、再判凭据;`a2aPeers` 超过 32 条(挂载上限)整份 `400 request.field_invalid`(`field:"a2aPeers"` · `reason:"over_cap"` · `count` · `cap`),
|
|
2931
|
+
判官要么看过每一条对端的 `url` / `cardUrl`,要么这份体不收。7.134.x 及以前超过 32 条是 200、第 33 条起丢弃(运维 warn 点名条数)。**搜索后端座**:userinfo 形此前已是 400(句子逐字不变,本版起体另带 `field` / `reason`),凭据名参数是本版新拒的。
|
|
2932
|
+
- **图片座**(7.136.0 起):`images[i]` 的 `{ url }` 臂过同一只判官;内联臂 `{ data, mimeType }` 不落 URL、不归本座。个数帽(20,`413 request.payload_too_large`)与内联字节帽排在它前面。
|
|
2933
|
+
**谁受伤**:用带签名的临时地址传图的调用方 —— `?sig=` · `?signature=` · `?token=` · `?key=` · `#access_token=` 这类名字在表里的参数(Azure SAS 的 `sig`、S3 预签名的 `X-Amz-Signature` / `X-Amz-Credential`、OSS / COS 的 `Signature`、带 `?key=` 的图床),7.135.0 及以前照收、取图时把令牌发给图床,本版起 400。
|
|
2934
|
+
**补偿**:图片字节读下来改走内联 `{ data, mimeType }`(内联图有自己的字节帽,不落 URL);或换一个不靠这些参数名鉴权的地址(路径上的签名、公开地址)。
|
|
2935
|
+
- **不判**:续跑重放存量请求(拒会把升级前停驻的任务永久卡死;已落盘的字节不因拒而消失 —— 介意的部署把这些任务结掉后轮换那几枚凭据);解析不了的 url(拨号 / 取图期失败);部署面(中心 / `config.d` / 部署 env)声明的地址 —— 那是运维的配置,不进任何请求的停驻上下文。
|
|
2853
2936
|
- 7.134.x 及以前照收、原样落停驻上下文。升级后用凭据名参数鉴权的对端 / 托管 MCP / 搜索后端要改:凭据挪进上表的位;只认 query 鉴权、不收请求头的服务端,改在部署面声明。
|
|
2854
2937
|
|
|
2938
|
+
### 9.5h 请求体 `a2aPeers` 的列表与条目键:写坏,整份新提交 400(7.136.0 起)
|
|
2939
|
+
|
|
2940
|
+
与 9.5e(`mcpServers`)**同一副拒绝骨架、同一个拒形**。条目的受理键 = `name` · `url` · `cardUrl` · `headers` · `principalHeader` · `allowSkills` · `toolAxes`(与引擎的 A2A 对端声明逐键对应)。新提交(`POST /v1/tasks` · `/v1/tasks/stream` · `/v1/runs`)上:
|
|
2941
|
+
- `a2aPeers` 在场却不是数组 ⇒ `400 request.field_invalid`(`a2aPeers is a string of 4 characters, not an array of A2A peer declarations. …`);超过 32 条 ⇒ `400 request.field_invalid` + `field:"a2aPeers"` · `reason:"over_cap"` · `count` · `cap`;某一条不是对象 ⇒ `400 request.field_invalid`,点名 `a2aPeers[i]`。缺席 / `null` 是「没声明」,空数组受理。
|
|
2942
|
+
- 条目上词表外的键 ⇒ `400 request.body_shape`,体带 `unknownKeys`(全路径形,如 `a2aPeers[0].allowSkill`)。
|
|
2943
|
+
- 键在场却判不出形 ⇒ `400 request.field_invalid`,拒句形如 `a2aPeers[1].url (peer "billing") is not a usable declaration — must be a string starting with "http://" or "https://" (never file://, never a bare host) — got a string of 15 characters. …`:点名 `a2aPeers[i].<键路径>`、peer 名与该键判官的原因句,**整份请求不收**,原因句不回显值。判官就是读器用的那一只:`name`(非空串、线长界、无首尾空白)· `url`(必填,http(s) 串)· `cardUrl`(可缺席,在场必须是 http(s) 串)· `headers`(「名 → 串」的纯数据记录,无 `__proto__` 名成员)· `principalHeader`(串)· `allowSkills`(串数组)· `toolAxes`(引擎的判官,A2A 一面,请求侧不收 `boundaryFlow`)。`url` / `cardUrl` 带凭据另见 9.5g(体另带 `field` / `reason`)。
|
|
2944
|
+
- 判序:先列表、后条目;条目里先词表外的键、后键值(`name` → `url` → `cardUrl` → `headers` → `principalHeader` → `allowSkills` → `toolAxes`),第一处坏形即答。纯值域、与租户车道无关(多租户部署整只 `a2aPeers` 本会被忽略,告诉调用方「这样写不行」只多不少)。
|
|
2945
|
+
- **耐久 resume 重放存量请求**不拒(拒会把升级前 park 的任务永久卡死):写坏的条目不挂、运维 warn `a2a_injection_dropped` 点名 —— `dropped` 的每个条目是 `{ peer, reason, field }`:`reason` 是上面判官对那个键的原因句(与受理口 400 拒句里的同一句,不回显值;只有一臂例外:一次性迁移的重读入参克隆不了 —— 访问器成员 / BigInt / Proxy / 环 / 非纯数据 —— 时带前缀 `declaration not cloneable (<because>) — the one-time migration re-reads the stored declaration as plain JSON data and this one is not; `,**其后**才是判官的那一句),`field` 是 `a2aPeers[i].<键路径>`(与 MCP 面 `mcp_injection_dropped` 的条目同键名,名字键 `server` ↔ `peer` 除外);整只不是数组 ⇒ 整只忽略 + `a2a_injection_ignored`。
|
|
2946
|
+
- 7.135.0 及以前:这些形全部 200 —— 坏形的对端悄悄不挂(只在运维 warn 里点名)、词表外的键悄悄不读(`allowSkills` 拼成 `allowSkill` 的对端照样挂上远端卡声明的全部技能)。升级后这些请求变 400:改对那个键(或删掉它)、把列表改成数组、删掉第 33 条起的条目。
|
|
2947
|
+
- A2A 没有运维锁、没有合规能力位:引擎的锁键与合规能力两个闭集里都没有 A2A 的词,所以 `LOCKED_CONFIG_KEYS` 写不进 `a2a`、合规档位也禁不了 A2A(7.135.0 及以前的代码里有一条永远不触发的「锁了就整拒」分支,本版删掉)。`GET /v1/capabilities` 的 `a2aInjection` 只回答「单用户」这一问。
|
|
2948
|
+
|
|
2855
2949
|
### 9.6 trace 脱敏的射程:与出站请求体无关
|
|
2856
2950
|
|
|
2857
2951
|
🔴 **脱敏 ≠ 出站控制**:trace 脱敏(账本 / SSE / turns / 审批预览里的 `«redacted»` 标记)只作用于
|
|
@@ -3018,9 +3112,9 @@ web-admin 拉取 / fleet 公告 / 凭证续签、WebSearch、对象存储、OTLP
|
|
|
3018
3112
|
`tag`(本地 tag 在、tag 树的版本对)→ `precheck`(registry 上本版在不在;读不出 ⇒ 红,不盲发)→ `token` → `clone`(干净 tag 树 =
|
|
3019
3113
|
`git clone --depth 1 --branch v<v> file://<仓>`)→ `npm-ci`(**k3s8**:tag 树上船、容器里 `npm ci`)→ `prepublish`(**k3s8**:`npm run prepublishOnly`)→
|
|
3020
3114
|
`pack`(**k3s8** 上 `npm pack --ignore-scripts`,远端打 sha1 → 取回、sha1 核同 → 解进本机 tag 树 → 本机 `npm pack --ignore-scripts` 复包 → 两只解压后的
|
|
3021
|
-
tar 流逐字节同才往下走;读数 `remote-shasum=… tar=identical`)→ `publish`(**在 tag 树目录里** `npm publish --ignore-scripts`)→ `readback-wait`(轮询 registry 至多 20 min)→ `readback-shasum`(dist.shasum = 本地 tarball 的 sha1)→
|
|
3115
|
+
tar 流逐字节同才往下走;读数 `remote-shasum=… tar=identical`)→ `publish`(**在 tag 树目录里** `timeout -k 10 "${PUBLISH_TIMEOUT_S:-300}" npm publish --ignore-scripts`)→ `readback-wait`(轮询 registry 至多 20 min)→ `readback-shasum`(dist.shasum = 本地 tarball 的 sha1)→
|
|
3022
3116
|
`readback-bytes`(curl 回读 tarball,与本地 `cmp` 逐字节同;**传输失败**在 `readback-wait` 用的同一只窗里重试、窗满才红,**字节差**不重试、立即红,两种红句分开说)→ `githead`(registry 的 gitHead = tag commit)。脚本钉 `umask 022`(文件权限进 tar 头,
|
|
3023
|
-
换 umask 重跑会 pack 出另一份字节);每次 registry 请求有硬上限(60 s),回读等待另有 20 min
|
|
3117
|
+
换 umask 重跑会 pack 出另一份字节);每次 registry 请求有硬上限(60 s),回读等待另有 20 min 墙钟截止,`npm publish` 进程自己有进程级墙钟(`PUBLISH_TIMEOUT_S`,缺省 300 秒,到点杀、宽限 10 s 后强杀 ⇒ 退出码 5 + `PUBLISH-FAIL … step=publish`,红句说「超时」;包可能已上,重跑即可,幂等边判),registry 挂住不会挂死链。成功尾行
|
|
3024
3118
|
`PUBLISH v=<v> shasum=<sha1> gitHead=<sha> bytes=<n> readback=identical`(发车帖 / CHANGELOG 回填 shasum 与 gitHead 从这里取);任一步红 ⇒
|
|
3025
3119
|
`PUBLISH-FAIL v=<v> step=<步骤>` + 非零退出 + 一句点名步骤的红因(红因与步骤行同走 stdout,链日志里步骤行之后紧跟)。
|
|
3026
3120
|
- **幂等**:registry 上已有本版 ⇒ 不重发,只做回读比对 —— 与本地 pack 逐字节同且 gitHead 对 ⇒ 绿并说「已在」;不同 ⇒ 红(别人发的字节,版本不可覆盖)。
|
|
@@ -3040,8 +3134,8 @@ web-admin 拉取 / fleet 公告 / 凭证续签、WebSearch、对象存储、OTLP
|
|
|
3040
3134
|
链日志尾行 `PUBLISH-DRYRUN v=<v> stop=pack`(不写 `PUBLISH v=…`)。
|
|
3041
3135
|
- **凭据**:**需要环境变量 `NPM_TOKEN`**(只在要发的时候查;发车机 `~/.npmrc` 那一行本身就是 `//registry.npmjs.org/:_authToken=${NPM_TOKEN}`
|
|
3042
3136
|
引用)。脚本不读 `~/.npmrc`:每条 npm 调用带临时 `--userconfig`,临时 npmrc 只写 `${NPM_TOKEN}` 引用、不写值;开头封口,`remote-gate.sh`
|
|
3043
|
-
与它起的 ssh / 容器(`npm ci` 的安装脚本 / 构建 / pack-hygiene 测试)、本机复包拿不到任何凭据,只有发车机上 `npm publish`
|
|
3044
|
-
`NPM_TOKEN`(且带 `--ignore-scripts`)——
|
|
3137
|
+
与它起的 ssh / 容器(`npm ci` 的安装脚本 / 构建 / pack-hygiene 测试)、本机复包拿不到任何凭据,只有发车机上 `timeout` 与它起的这一个 `npm publish`(同一条进程链)拿到
|
|
3138
|
+
`NPM_TOKEN`(且带 `--ignore-scripts`)—— 令牌不出发车机。令牌从不打印。发车机需有 GNU `timeout`(coreutils;macOS 上 `brew install coreutils`),缺它在最开头红(退出码 2),不碰网络。
|
|
3045
3139
|
- **门 4 不再等 CI**(同一裁定):`scripts/post-publish-gates.sh` 的门 4 = 一个 45 min 的 npm 窗(公开 packument 上 `versions[<v>]` 在且
|
|
3046
3140
|
`dist-tags.latest` 追平)。判决行 `GATES456 v=… rc4=… rc5=… rc6=… shasum=… elapsed=… ciTag=… ciMain=… tagRun=… mainRun=… why4=… chain=…`:既有十一键形状不变,末尾追加第十二键 `chain=<本轮标识>`(见下一条起链启动器);
|
|
3047
3141
|
CI 两腿(`ciTag` / `ciMain` / `tagRun` / `mainRun`)是**信息格**,不进 rc4、不进 why4;`why4` 只剩 `propagation_timeout`(rc4 非零)与 `-`(绿)。
|
|
@@ -3161,7 +3255,7 @@ web-admin 拉取 / fleet 公告 / 凭证续签、WebSearch、对象存储、OTLP
|
|
|
3161
3255
|
做**键级**比较,与面的增删用同一张表、同一只比较器:删一个键或改了键的值形 / 容器 / 嵌套面 / 由必选变可选 ⇒ `removed` / `narrowed`(`breaking`),
|
|
3162
3256
|
新增键 ⇒ `added`,由可选变必选 ⇒ `changed`;该面此前没有键声明(首次登记)时既有的键进 `registered`,不当本版新上 wire。其余帧面的键集仍由 `coreType`
|
|
3163
3257
|
在安装态 core 里展开,生成器看不见它们的键级增删(core 升版带来的键变化由提货批的读面 × core 清单求交门与各面的 `_Guard*` 编译钉盯)。随 release commit 提交;查账门(门 1.6)第三腿 `--check` 核它在仓且与生成输出逐字节同,缺席 / 陈旧 ⇒ 红。
|
|
3164
|
-
`wire-changes/server@7.104.0.json` 是回填的第一份。
|
|
3258
|
+
`wire-changes/server@7.104.0.json` 是回填的第一份。7.136.0 起 `wire-changes/` 随 npm 包出:装包的人在包内 `wire-changes/server@<ver>.json` 就能读到每一版的清单(生成时清单里的 `text` 过发布面内部编号词表,命中的编号换成 `[ref]`;`sources` 里点名的契约文档不随包)。
|
|
3165
3259
|
- **台账 `refs:` 列建议**:`node scripts/ledger-refs.mjs [--comms-dir <dir>] [--json]` 只读 sema-comms 的 `debts/DEBTS-server.md` /
|
|
3166
3260
|
`PLANS.md`(server 段)/ `bugs/B-*.md`(文件头 `owner:` 格),按「一方点了另一方的号」双向互链,输出每行的 `refs:` 建议值(号按数值认,
|
|
3167
3261
|
零填充与不填充的拼法算同一只号;悬空号 stderr 点名)。不改 sema-comms,落账由主会话做。
|
|
@@ -3184,6 +3278,8 @@ web-admin 拉取 / fleet 公告 / 凭证续签、WebSearch、对象存储、OTLP
|
|
|
3184
3278
|
|
|
3185
3279
|
- `alg`:恒为 `libsodium-sealedbox-x25519`(当前唯一取值);`publicKeyId`:写进密文条目的那个 id;`publicKey`:X25519 公钥,标准 base64(32 字节)。
|
|
3186
3280
|
- 值是**本进程**保管店里最新那把钥匙(`createdAt` 最新)。只有公钥:私钥、私钥文件路径、本机还持有的旧钥匙 id 都不在这个面上。
|
|
3281
|
+
- 答出的公钥恒过一只判断(7.136.0 起):规范的标准 base64、32 字节、不是 X25519 小阶点。保管目录里公钥过不了的钥匙文件不装载(warn `sealed_key_file_unreadable` 带 `flaw`),也不向中心登记;
|
|
3282
|
+
你这一侧封之前同样该拒小阶点(libsodium 的 `crypto_box_seal` 对小阶公钥本身就抛;换用别的库时先确认它也拒)。
|
|
3187
3283
|
- **键缺席**有两种含义:引擎早于 7.104.0(不认识这一位),或这台引擎今天没有保管店(起服时建店失败,日志 `sealed_key_store_unavailable`)。
|
|
3188
3284
|
同一个体里的 `version` 能区分这两种;不论哪种,调用方的动作相同 —— **不要封**:改用 `apiKeyEnv`,或修好保管目录后重启再取。
|
|
3189
3285
|
|
|
@@ -3219,10 +3315,17 @@ const ciphertext = Buffer.from(sodium.crypto_box_seal(sodium.from_string(apiKey)
|
|
|
3219
3315
|
| 失败词 | 什么时候出 | 怎么修 |
|
|
3220
3316
|
|---|---|---|
|
|
3221
3317
|
| `sealed_decrypt_failed` | 本进程**持有**这个 `publicKeyId` 的私钥,但开不开:密文不是对着这把公钥封的、被截断或改坏(短于 48 字节)、缺了应有的 `=` 补齐。warn `web_admin_model_sealed_key_open_failed` | 对着 caps 当前给的公钥重封,写回条目 |
|
|
3222
|
-
| `stale_ciphertext` | 本进程**没有**这个 `publicKeyId` 的私钥:密文是对着别的进程 / 别的宿主 /
|
|
3318
|
+
| `stale_ciphertext` | 本进程**没有**这个 `publicKeyId` 的私钥:密文是对着别的进程 / 别的宿主 / 别的副本的公钥封的(含读同一份 `config.d`、保管目录不同的另一台引擎),或那把钥匙的私钥文件被删、读不了。warn `web_admin_model_sealed_key_no_private_key`(带本机持有的 id) | 对着**这个进程** caps 给的公钥重封;或把那个 id 的私钥文件放回保管目录后重启;多台引擎共用一份 `config.d` 时让它们共用保管目录(`SEALED_KEY_DIR`,见 ⑥) |
|
|
3223
3319
|
| `no_key_store` | 本进程没有保管店:起服时保管目录建不了、改不了权限(只读挂载 / 权限不够 / 数据根不是目录),caps 没有 `sealedKey` 段。warn `web_admin_model_sealed_key_no_store` | 修好保管目录(进程要能建它、每次起服把它设成 `0700`)后重启 |
|
|
3224
3320
|
|
|
3225
|
-
|
|
3321
|
+
**sealed 条目绑定的是钥匙身份,不是数据根。** 一条 sealed 条目只对持有封它的那把钥匙(`publicKeyId` → 钥匙对)的进程可开;共享同一份 `config.d` 的**每一台**引擎都必须持有那把钥匙,
|
|
3322
|
+
否则那只模型在那台引擎上响亮失败、不回落(缺这个 id ⇒ `stale_ciphertext` · 同 id 而私钥不对 ⇒ `sealed_decrypt_failed` · 没有保管店 ⇒ `no_key_store`)。持钥的办法两条:共用保管目录(`SEALED_KEY_DIR`,下一段),
|
|
3323
|
+
或把同一把钥匙文件放进每台引擎各自的保管目录。典型形:终端与桌面端读同一份 `config.d`,而桌面端自起引擎的数据根固定在应用数据目录下 —— 不共用保管目录时,一端封进去的条目在另一端是 `stale_ciphertext`。
|
|
3324
|
+
|
|
3325
|
+
**⑥ 保管目录与轮换。** 保管目录 = `SEALED_KEY_DIR`(7.136.0 起;绝对路径),缺席时 = `<数据根>/sealed-key/`(数据根 = `LOCAL_DATA_ROOT` → `CONFIG_LOCAL_DIR` → `AGENT_DATA_DIR` → `~/.ai-agent`)。
|
|
3326
|
+
设了 `SEALED_KEY_DIR`:它是唯一保管目录(停驻任务凭据封签的子目录 `checkpoint-ctx-keys/` 跟着它走);相对路径起服拒;起服在它上面建不了保管店(建不了 / 设不成 `0700` / 只读挂载)⇒ **起服拒**
|
|
3327
|
+
(不会以「没有保管店」的状态起来);多台引擎(同一个 OS 用户)把它设成同一个路径 ⇒ 持有同一把钥匙。缺席时建不了只告警(`sealed_key_store_unavailable`,与 7.135.x 相同)。
|
|
3328
|
+
已有部署切到 `SEALED_KEY_DIR` 时先把原保管目录(`<数据根>/sealed-key/`)里的钥匙文件连同子目录 `checkpoint-ctx-keys/` 整个搬进新目录(停服时搬),再设旋钮起服 —— 否则新目录是空目录,起服生成一把新钥:对着旧钥封的 sealed 模型全部 `stale_ciphertext`,停驻、带请求凭据的任务续跑答 `409 conflict.checkpoint_ctx_unsealable`(`key_not_held`)。
|
|
3226
3329
|
每把钥匙一个文件 `<publicKeyId>.json`(文件 `0600`,目录 `0700`)。**私钥只在这里**:丢了,对着它封的密文全部变成 `stale_ciphertext`,
|
|
3227
3330
|
只能重封 —— 备份时按私钥对待。7.134.0 起它下面还有一个子目录 `checkpoint-ctx-keys/`:停驻任务续跑上下文里请求凭据的封签钥匙(一个域一对钥匙,与模型钥匙不共用;
|
|
3228
3331
|
同样的文件形、同样的「空目录生成一把」;不向中心登记、不出现在 caps 上)。它丢了,封在停驻行里的凭据就开不开(续跑答 `409 conflict.checkpoint_ctx_unsealable` `reason:"key_not_held"`)——
|
|
@@ -3239,11 +3342,11 @@ const ciphertext = Buffer.from(sodium.crypto_box_seal(sodium.from_string(apiKey)
|
|
|
3239
3342
|
—— 在负载均衡后面取 caps,取到哪台就是哪台的钥匙。出路两条:
|
|
3240
3343
|
|
|
3241
3344
|
- **共享保管目录**:让每个副本的 `<数据根>/sealed-key/` 是同一份钥匙文件(例如把同一个卷挂到这个路径)。先把钥匙备好(先起一个副本让它生成,
|
|
3242
|
-
或放好钥匙文件)再起其余副本 ——
|
|
3345
|
+
或放好钥匙文件)再起其余副本 —— 钥匙创建是先读后写、没有互斥:两个副本在同一个目录上同时首启、都没读到可用的钥匙文件时,各副本可能各自生成一把、只持有自己那一把,直到下次重启。目录要对每个副本**可写**(每次起服
|
|
3243
3346
|
会把它设成 `0700`;只读挂载 ⇒ 建店失败 ⇒ `no_key_store`)。⚠️ 只共享 `sealed-key/` 这一个子目录:`DB_BACKEND=local` 的整个数据根禁止跨容器 /
|
|
3244
3347
|
跨主机共享(`docs/DEPLOY-PREREQS.md`)。
|
|
3245
3348
|
照随包 `deploy/sema-up` 图表部署时这一步图表替你做了:`sealedKey.persistence` 块(缺省开)把整个 `sealed-key/` 目录挂成一只 PVC,访问模式缺省单副本 `ReadWriteOnce`、多副本 `ReadWriteMany`
|
|
3246
|
-
(要存储类支持 RWX;空卷首装后 `rollout restart`
|
|
3349
|
+
(要存储类支持 RWX;空卷首装后 `rollout restart` 一次,让并发首启时可能各生成的钥匙互相都持有);显式关掉它(`sealedKey.persistence.enabled: false`)图表会同时给 worker 设 `DURABLE_APPROVAL=false`,并拒绝再配 `server.durableApproval: true`。
|
|
3247
3350
|
- **走中心形**:接 web-admin,执行侧公钥登记在中心(起服时带管理令牌自动上报,否则把公钥打印出来供手工登记),web 面对着登记的公钥封、中心存密文。
|
|
3248
3351
|
调用方不必逐台去问 caps;但同一个 worker 的多个副本各有各的保管店这件事中心形不改变 —— 要它们都开得了同一份密文,仍要共享保管目录。
|
|
3249
3352
|
|
|
@@ -3251,7 +3354,7 @@ const ciphertext = Buffer.from(sodium.crypto_box_seal(sodium.from_string(apiKey)
|
|
|
3251
3354
|
在 B 副本上续跑答 `409 conflict.checkpoint_ctx_unsealable` `reason:"key_not_held"`;单副本也要让这个目录跨重启持久。**7.133.x 与 7.134.0 不能混跑**:先停掉全部 7.133.x 副本,再起第一个 7.134.0 副本
|
|
3252
3355
|
(一次性迁移 M28 在它起服时把升级前停驻行里的明文封好);混跑时 7.133.x 副本续跑一条已封签的行会把凭据位当空值、任务无凭据照跑、不报错。降级到 7.133.x 之前先把带凭据的停驻行结掉(决定或取消)。
|
|
3253
3356
|
多副本升级时先起一个 7.134.0 副本、等它起好再起其余副本(耐久审批开着时它起好即打出 `checkpoint_ctx_credentials_sealed` 一行):停驻封签的钥匙在新子目录 `checkpoint-ctx-keys/` 里,
|
|
3254
|
-
升级后第一个起服的副本生成它(`sealed-key/` 早已共享好也一样)
|
|
3357
|
+
升级后第一个起服的副本生成它(`sealed-key/` 早已共享好也一样);同时起多个副本可能各建一把、互相答 `key_not_held`,直到各自重启一次(钥匙只在起服时读目录里的全部文件;各建只发生在两个副本在同一个目录上同时首启、都没读到可用的钥匙文件时)。
|
|
3255
3358
|
|
|
3256
3359
|
### 9.11 会话规则店:抹除封键与显式恢复(7.106.0 起)
|
|
3257
3360
|
|
|
@@ -3302,14 +3405,16 @@ UPDATE session_policy_rev_mark SET rev = 0, gen = gen + 1 WHERE policy_key = '<w
|
|
|
3302
3405
|
**协议命名空间的覆盖形(7.135.0 起有能力位)。** 条目写协议命名空间 —— 整对端 `mcp__srv` / `a2a__peer`、工具段通配 `mcp__srv__*`、工具段 glob `mcp__srv__get_*` —— 写口收(`200`),引擎把它们读成**一组工具**:
|
|
3303
3406
|
`toolDeny` 里拒这一组,`toolAllow` 里只放这一组。要不要放开对这几形的扣下,读 `GET /v1/capabilities` 的顶层布尔 `sessionRulesNamespaceForms`(不按版本号猜):`true` = 引擎协议表上每个命名空间的这三形写口真收、
|
|
3304
3407
|
引擎真认成一组;键缺席(≤7.134.x)或 `false` ⇒ 当不放宽。它与 `sessionPolicy`(这台有没有会话规则写面)正交 —— 没有会话规则店的部署上它也是 `true`,所以条件是两位同真。位只说命名空间覆盖形:
|
|
3305
|
-
`*`、对端段的部分通配(`mcp__git*`)、尾分隔(`mcp__srv__`)照旧 `400 rules.inert_entry`(下),`mcp__*` 只在 `toolDeny` 收。
|
|
3408
|
+
`toolAllow` 里的 `*`、对端段的部分通配(`mcp__git*`)、尾分隔(`mcp__srv__`)照旧 `400 rules.inert_entry`(下),`mcp__*` 只在 `toolDeny` 收。
|
|
3409
|
+
**`toolDeny` 里带 `*` 的工具名是整名 glob(7.137.0 起,引擎会话规则文法 5)**:`*` 拒每一只工具,`mcp__git*` 拒名字以 `mcp__git` 起头的每一只工具(`mcp__github__…`、`mcp__gitlab__…`),
|
|
3410
|
+
`Bas*` 拒 `Bash`;写口收(`200`),行盖文法 5 的戳。`toolAllow` 不变:`*` 只在协议名的工具段里读(`mcp__srv__get_*`),别处的 `*` 不指任何范围、照旧拒。尾分隔 `mcp__srv__` 两只桶都照旧拒。
|
|
3306
3411
|
|
|
3307
3412
|
**两只拒码**(判官是引擎的那一只,两条写口与店里的拒同码、同一份拒体;同步导入的写口在句首加记录坐标 `policy: [i].rules: `;店里一个字节都不落):
|
|
3308
3413
|
|
|
3309
3414
|
- `400 rules.invalid_shape` —— 规则体形不对:`rules` 缺席或不是对象、有一个不是桶的键(含退役的命令桶,**不会被静默忽略**)、一只桶不是一列字符串、`allowDirs` 里有空目录。
|
|
3310
3415
|
体顶层 `field`(哪个键;整体不是对象时是空串)与 `got`(那个值的类型,不回显值),`error` 是引擎的句子。
|
|
3311
|
-
- `400 rules.inert_entry` —— 一条条目读成「什么都不是」,写进去什么都不拒 / 不许:退役的工具名、带 `__` 却没有协议前缀的名、协议名里铸不出的段(
|
|
3312
|
-
`toolAllow`
|
|
3416
|
+
- `400 rules.inert_entry` —— 一条条目读成「什么都不是」,写进去什么都不拒 / 不许:退役的工具名、带 `__` 却没有协议前缀的名、协议名里铸不出的段(尾分隔 `mcp__srv__`;`toolAllow` 里对端段的部分通配 `mcp__git*`)、
|
|
3417
|
+
`toolAllow` 里的 `*` 与整协议名 `mcp__*`(7.135.0 起 `toolDeny` 里的 `mcp__*` / `mcp__*__*` 是一条规则:拒每一台 MCP 服务器的每一只工具;7.137.0 起 `toolDeny` 里任何带 `*` 的名都是整名 glob,不在本码)、工具没有的规则形
|
|
3313
3418
|
(`Monitor(touch:*)`)、文法不成的写法(空串、`Bash(`)。体顶层 `field` / `entry` / `reason`,外加 `verdict` / `currentName` / `guidance`(有就带)。按 `reason` 改写那一条再写。
|
|
3314
3419
|
|
|
3315
3420
|
一条什么都不拒的 deny 已经在库里时,会话属主自己删得掉(删它不算放宽)。
|
|
@@ -3319,8 +3424,8 @@ UPDATE session_policy_rev_mark SET rev = 0, gen = gen + 1 WHERE policy_key = '<w
|
|
|
3319
3424
|
下一次工具调用起生效;收不回 operator 写下的那一部分(两边都写过的条目也算):`403 loosen_forbidden`,句子 `a non-operator session-rule write may not loosen what an operator wrote; …`
|
|
3320
3425
|
(7.134.x 及以前的句子是 `… may only tighten …`,按句子匹配的脚本改读码)。operator 写一次就把整行变成 operator 的。`operatorRules` 由引擎铸,客户端改规则时**不要**回传它。
|
|
3321
3426
|
|
|
3322
|
-
**行自带文法戳。** 7.129.0 起每一行会话规则自带规则文法戳 `grammar`(写口判官盖引擎当前的文法:7.129.0–7.131.x 写下的行是 `1`,7.132.0–7.134.x 写下的行是 `2`,7.135.0 起写下的行是 `
|
|
3323
|
-
早先写下的行保留自己的戳、引擎按它的戳读;店整只存、读回原样交出;7.134.x 及更早的引擎读不了戳 3 / 4 的行 —— 整行拒、那条会话每一次调用都拒,升级序见 MIGRATION 7.135.0)—— `GET` / `PUT` 回包的 `rules` 与同步导出的
|
|
3427
|
+
**行自带文法戳。** 7.129.0 起每一行会话规则自带规则文法戳 `grammar`(写口判官盖引擎当前的文法:7.129.0–7.131.x 写下的行是 `1`,7.132.0–7.134.x 写下的行是 `2`,7.135.0–7.136.x 写下的行是 `4`,7.137.0 起写下的行是 `5`;
|
|
3428
|
+
早先写下的行保留自己的戳、引擎按它的戳读;店整只存、读回原样交出;7.134.x 及更早的引擎读不了戳 3 / 4 的行、7.136.x 及更早的读不了戳 5 的行 —— 整行拒、那条会话每一次调用都拒,升级序见 MIGRATION 7.135.0 / 7.137.0)—— `GET` / `PUT` 回包的 `rules` 与同步导出的
|
|
3324
3429
|
记录里多这一个键。客户端改规则时只写三只桶,不要把读回的 `rev` / `gen` / `grammar` / `operatorRules` 一起回传:带着 `rev` 却没有 `grammar` 的值引擎按存量行的文法(0)读,括号形 allow 条目答
|
|
3325
3430
|
`400 rules.inert_entry`(`reason: "earlier_grammar"`;出路在句子里);`grammar` 坏或比引擎新 ⇒ `400 rules.invalid_shape`(`field: "grammar"`)。
|
|
3326
3431
|
|