@sema-agent/server 7.125.1 → 7.127.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 +229 -2
- package/README.md +2 -2
- package/README.zh-CN.md +2 -2
- package/USAGE.md +346 -145
- package/deploy/sema-up/README.md +1 -1
- package/deploy/sema-up/chart/templates/networkpolicy-sandbox.yaml +57 -0
- package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +32 -1
- package/deploy/sema-up/chart/templates/server-v4pro.yaml +3 -2
- package/deploy/sema-up/chart/templates/server.yaml +2 -1
- package/deploy/sema-up/chart/values.yaml +22 -0
- package/deploy/sema-up/cluster-up.sh +10 -10
- package/deploy/sema-up/kube-up.sh +20 -13
- package/deploy/sema-up/lib/dataroot.sh +1 -1
- package/deploy/sema-up/lib/envf.sh +5 -5
- package/deploy/sema-up/lib/flagshape.sh +5 -5
- package/deploy/sema-up/lib/lanes.py +1 -1
- package/deploy/sema-up/lib/node-addrs.sh +42 -0
- package/deploy/sema-up/lib/parse-machines.py +2 -2
- package/deploy/sema-up/lib/shell-access.sh +1 -1
- package/deploy/sema-up/preflight.sh +4 -4
- package/deploy/sema-up/sema-up.sh +33 -33
- package/deploy/sema-up/smoke.sh +3 -3
- package/deploy/sema-up/stack/compose.yaml +1 -1
- package/deploy/sema-up/stack/sema-stack-cluster.yaml +26 -3
- package/dist/approval-card.d.ts +20 -4
- package/dist/approval-content-kind.d.ts +38 -22
- package/dist/approval-content-kind.js +20 -4
- package/dist/audit.js +3 -0
- package/dist/bench/s1/arms.d.ts +9 -37
- package/dist/bench/s1/arms.js +4 -61
- package/dist/bench/s1/live-deps.d.ts +4 -10
- package/dist/bench/s1/live-deps.js +1 -222
- package/dist/bench/s1/oracle.d.ts +1 -1
- package/dist/bench/s1/reviewer.d.ts +1 -1
- package/dist/bench/s1/row.d.ts +5 -7
- package/dist/bench/s1/row.js +0 -2
- package/dist/bench/s1/run-firm.d.ts +2 -2
- package/dist/bench/s1/run-firm.js +1 -4
- package/dist/bench/s1/runner-ctx.d.ts +17 -29
- package/dist/bench/s1/runner-ctx.js +3 -7
- package/dist/bench/s1/tasks.d.ts +9 -46
- package/dist/bench/s1/tasks.js +4 -63
- package/dist/boot/execution-env.d.ts +38 -26
- package/dist/boot/execution-env.js +87 -41
- package/dist/boot/leader.d.ts +2 -1
- package/dist/boot/memory-boundary.d.ts +1 -1
- package/dist/boot/memory-consolidation.d.ts +37 -3
- package/dist/boot/memory-consolidation.js +18 -2
- package/dist/boot/parked-revive-gate.d.ts +0 -3
- package/dist/boot/parked-revive-gate.js +2 -2
- package/dist/boot/resolve-spec.d.ts +0 -2
- package/dist/boot/resolve-spec.js +12 -17
- package/dist/boot/retention-lane.d.ts +12 -1
- package/dist/boot/retention-lane.js +2 -0
- package/dist/boot/runner-deps.d.ts +7 -6
- package/dist/boot/runner-deps.js +5 -16
- package/dist/boot/session-faces.d.ts +7 -2
- package/dist/boot/session-faces.js +37 -1
- package/dist/boot/stage-03-store-backend.d.ts +1 -0
- package/dist/boot/stage-03-store-backend.js +2 -2
- package/dist/boot/stage-04-stores.d.ts +1 -0
- package/dist/boot/stage-05-execution-env.d.ts +2 -2
- package/dist/boot/stage-05-execution-env.js +7 -4
- package/dist/boot/stage-06-runners.d.ts +2 -3
- package/dist/boot/stage-06-runners.js +13 -9
- package/dist/boot/stage-07-capability-layer.d.ts +2 -3
- package/dist/boot/stage-07-capability-layer.js +3 -4
- package/dist/boot/stage-08-reapers.d.ts +2 -3
- package/dist/boot/stage-08-reapers.js +2 -2
- package/dist/boot/stage-09-leader.d.ts +2 -3
- package/dist/boot/stage-09-leader.js +2 -1
- package/dist/boot/stage-10-http-server.d.ts +2 -3
- package/dist/boot/stage-10-http-server.js +2 -2
- package/dist/boot/stores.d.ts +1 -0
- package/dist/boot/stores.js +4 -3
- package/dist/boot/web-admin.d.ts +5 -0
- package/dist/boot/web-admin.js +28 -14
- package/dist/brain.d.ts +2 -2
- package/dist/brain.js +1 -1
- package/dist/capabilities/builtin-tools.d.ts +2 -1
- package/dist/capabilities/composite-members.d.ts +1 -1
- package/dist/capabilities/composite-members.js +1 -1
- package/dist/capabilities/hands-lane.d.ts +9 -6
- package/dist/capabilities/hands-lane.js +2 -2
- package/dist/capabilities/memory-notice.d.ts +27 -22
- package/dist/capabilities/memory-notice.js +15 -17
- package/dist/capabilities/scenarios.d.ts +9 -14
- package/dist/capabilities/scenarios.js +5 -50
- package/dist/capability-facts.d.ts +22 -11
- package/dist/capability-facts.js +13 -6
- package/dist/config-catalog.js +14 -5
- package/dist/config-provider.d.ts +5 -1
- package/dist/config-provider.js +23 -4
- package/dist/config-types.d.ts +19 -2
- package/dist/config.js +41 -6
- package/dist/deployment-governance.d.ts +19 -21
- package/dist/deployment-governance.js +7 -8
- package/dist/env-name-allowlist-knobs.d.ts +5 -5
- package/dist/env-name-allowlist-knobs.js +1 -0
- package/dist/execution-lane-caps.d.ts +12 -0
- package/dist/execution-lane-caps.js +18 -4
- package/dist/fleet/subagent-tail-bus.js +15 -5
- package/dist/hooks/hook-runner.d.ts +84 -30
- package/dist/hooks/hook-runner.js +74 -41
- package/dist/http/admission.js +17 -10
- package/dist/http/durable-run-leg.d.ts +5 -4
- package/dist/http/durable-run-leg.js +2 -4
- package/dist/http/idempotency.d.ts +28 -6
- package/dist/http/idempotency.js +10 -2
- package/dist/http/principal-gate.d.ts +14 -0
- package/dist/http/principal-gate.js +7 -0
- package/dist/http/request-guards.d.ts +2 -1
- package/dist/http/request-guards.js +14 -14
- package/dist/http/resume-legs.d.ts +2 -2
- package/dist/http/resume-legs.js +20 -10
- package/dist/http/route-ctx.d.ts +15 -7
- package/dist/http/routes/a2a-serve.js +11 -7
- package/dist/http/routes/approvals-assistant.js +18 -3
- package/dist/http/routes/background.d.ts +1 -1
- package/dist/http/routes/background.js +1 -1
- package/dist/http/routes/capabilities.js +10 -2
- package/dist/http/routes/mcp-reconnect.js +1 -1
- package/dist/http/routes/runs.d.ts +4 -3
- package/dist/http/routes/runs.js +35 -14
- package/dist/http/routes/session-sync.js +14 -11
- package/dist/http/routes/sessions.js +9 -9
- package/dist/http/routes/side-query.js +6 -19
- package/dist/http/routes/tasks.js +29 -19
- package/dist/http/routes/workflows.js +8 -0
- package/dist/http/server.d.ts +4 -0
- package/dist/http/server.js +3 -5
- package/dist/http/submit-replay.d.ts +15 -8
- package/dist/http/submit-replay.js +6 -0
- package/dist/http/wire-types.d.ts +7 -9
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/internal-run-env.d.ts +30 -0
- package/dist/internal-run-env.js +11 -0
- package/dist/leader/wire.js +3 -2
- package/dist/lsp/manager.js +1 -1
- package/dist/memory-scope.d.ts +19 -7
- package/dist/memory-scope.js +25 -8
- package/dist/observability/fail-open.d.ts +31 -3
- package/dist/observability/fail-open.js +31 -3
- package/dist/observability/metrics.d.ts +0 -1
- package/dist/observability/metrics.js +1 -4
- package/dist/observability/run-terminal.js +72 -4
- package/dist/permission-rule-vocab.d.ts +3 -2
- package/dist/permission-rule-vocab.js +1 -1
- package/dist/plan-cache-probe.d.ts +3 -2
- package/dist/plugins/approval-ask-store-sql.d.ts +1 -1
- package/dist/plugins/caching-session-store.d.ts +42 -22
- package/dist/plugins/caching-session-store.js +19 -9
- package/dist/plugins/checkpoint-store-sql.d.ts +42 -28
- package/dist/plugins/checkpoint-store-sql.js +57 -11
- package/dist/plugins/file-run-store.d.ts +25 -21
- package/dist/plugins/file-run-store.js +70 -34
- package/dist/plugins/k8s-exec-protocol.js +3 -0
- package/dist/plugins/local-checkpoint-store.d.ts +8 -5
- package/dist/plugins/local-checkpoint-store.js +5 -15
- package/dist/plugins/local-session-store.d.ts +66 -7
- package/dist/plugins/local-session-store.js +168 -101
- package/dist/plugins/mailbox-recipient-lifecycle.d.ts +1 -1
- package/dist/plugins/memory-run-store.d.ts +19 -3
- package/dist/plugins/memory-run-store.js +38 -3
- package/dist/plugins/one-time-migrations.d.ts +165 -39
- package/dist/plugins/one-time-migrations.js +51 -144
- package/dist/plugins/pg-pool.js +11 -1
- package/dist/plugins/pg-session-storage.d.ts +2 -0
- package/dist/plugins/pg-session-storage.js +9 -5
- 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 +1 -0
- package/dist/plugins/remote-env-e2b.d.ts +4 -0
- package/dist/plugins/remote-env-e2b.js +2 -1
- package/dist/plugins/remote-env-host.d.ts +23 -1
- package/dist/plugins/remote-env-host.js +20 -2
- package/dist/plugins/remote-env-k8s.d.ts +264 -41
- package/dist/plugins/remote-env-k8s.js +551 -82
- package/dist/plugins/remote-env-local-docker.d.ts +4 -0
- package/dist/plugins/remote-env-local-docker.js +2 -1
- package/dist/plugins/remote-env-ssh.d.ts +3 -0
- package/dist/plugins/remote-env-ssh.js +1 -0
- package/dist/plugins/retention-store-sql.d.ts +4 -1
- package/dist/plugins/retention-store-sql.js +11 -9
- package/dist/plugins/run-store-sql.d.ts +44 -6
- package/dist/plugins/run-store-sql.js +92 -27
- package/dist/plugins/session-capture-record-store-sql.d.ts +2 -3
- package/dist/plugins/session-store.d.ts +4 -3
- package/dist/plugins/session-store.js +1 -1
- package/dist/plugins/sql-driver.d.ts +2 -1
- package/dist/plugins/store-backend.d.ts +12 -5
- package/dist/plugins/store-backend.js +13 -4
- package/dist/plugins/store-contracts.d.ts +20 -3
- package/dist/plugins/store-open-wait.js +3 -0
- package/dist/plugins/tidb-pool.js +15 -3
- package/dist/plugins/tidb-session-storage.js +3 -2
- package/dist/plugins/tidb-session-store.d.ts +2 -0
- package/dist/plugins/tidb-session-store.js +6 -2
- package/dist/request-key-closure.d.ts +32 -7
- package/dist/request-key-closure.js +25 -9
- package/dist/router/route-orchestration.d.ts +3 -11
- package/dist/router/route-orchestration.js +0 -4
- package/dist/rule-entry-list.js +0 -4
- package/dist/rules-consent.js +3 -0
- package/dist/run-acceptance.d.ts +51 -0
- package/dist/run-acceptance.js +27 -0
- package/dist/run-local.d.ts +5 -4
- package/dist/run-local.js +23 -17
- package/dist/runs.d.ts +7 -11
- package/dist/runs.js +6 -16
- package/dist/sandbox-egress-policy.d.ts +61 -0
- package/dist/sandbox-egress-policy.js +235 -0
- package/dist/security.d.ts +7 -0
- package/dist/security.js +5 -0
- package/dist/server-secret-env.d.ts +10 -4
- package/dist/server-secret-env.js +4 -1
- package/dist/session-error-wire.d.ts +2 -2
- package/dist/session-incarnation.d.ts +46 -2
- package/dist/session-incarnation.js +14 -3
- package/dist/session-sync-kernel.d.ts +10 -3
- package/dist/session-titler.d.ts +5 -3
- package/dist/spec-fields.d.ts +24 -8
- package/dist/spec-fields.js +21 -2
- package/dist/task-workflow.d.ts +6 -4
- package/dist/task-workflow.js +3 -2
- package/dist/tool-approval.d.ts +78 -6
- package/dist/tool-approval.js +7 -3
- package/dist/trace/core-keyset-guard.d.ts +6 -5
- package/dist/trace/engine-notice-wire.d.ts +26 -1
- package/dist/trace/engine-notice-wire.js +7 -2
- package/dist/trace/ledger-events.d.ts +9 -3
- package/dist/trace/ledger-sink.d.ts +7 -0
- package/dist/trace/ledger-sink.js +14 -4
- package/dist/trace/project.d.ts +57 -11
- package/dist/trace/project.js +54 -8
- package/dist/trace/self-minted-wire.d.ts +94 -2
- package/dist/trace/self-minted-wire.js +28 -0
- package/dist/trace/tool-output-media.d.ts +26 -0
- package/dist/trace/tool-output-media.js +57 -0
- package/dist/trace/wire-projection-faces.d.ts +7 -1
- package/dist/trace/wire-projection-faces.js +2 -1
- package/dist/web-admin-client/apply-effective.d.ts +13 -7
- package/dist/web-admin-client/apply-effective.js +11 -26
- package/dist/web-admin-client/apply-ledger.d.ts +2 -2
- package/dist/web-admin-client/candidate-gate.d.ts +39 -4
- package/dist/web-admin-client/candidate-gate.js +9 -2
- package/dist/web-admin-client/facade.d.ts +2 -2
- package/dist/web-admin-client/http-client.d.ts +3 -1
- package/dist/web-admin-client/http-client.js +4 -3
- package/dist/web-admin-client/read-warnings.d.ts +33 -5
- package/dist/web-admin-client/read-warnings.js +41 -6
- package/dist/web-admin-client/types.d.ts +11 -5
- package/dist/workspace-cache.d.ts +45 -0
- package/dist/workspace-cache.js +30 -0
- package/dist/workspace-space-commit.d.ts +116 -0
- package/dist/workspace-space-commit.js +117 -0
- package/dist/workspace-space.d.ts +75 -9
- package/dist/workspace-space.js +52 -1
- package/package.json +4 -4
- package/dist/boot/session-incarnation-seat.d.ts +0 -41
- package/dist/boot/session-incarnation-seat.js +0 -69
- package/dist/capabilities/code-review-council.d.ts +0 -49
- package/dist/capabilities/code-review-council.js +0 -111
- package/dist/capabilities/collab-wire.d.ts +0 -36
- package/dist/capabilities/collab-wire.js +0 -2
- package/dist/capabilities/collab-workflows.d.ts +0 -47
- package/dist/capabilities/collab-workflows.js +0 -90
- package/dist/capabilities/prompts/code-review.d.ts +0 -25
- package/dist/capabilities/prompts/code-review.js +0 -39
- package/dist/capabilities/prompts/team.d.ts +0 -10
- package/dist/capabilities/prompts/team.js +0 -6
- package/dist/capabilities/team.d.ts +0 -113
- package/dist/capabilities/team.js +0 -70
package/USAGE.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# 调用方手册(OA / 智能体 / 你自己)
|
|
2
2
|
|
|
3
3
|
> 一句话心智模型:**你只对一个地址发普通 HTTP 请求——LB `http://<host>:8090`;请求体里只写"要做什么"。**
|
|
4
|
-
> 工具面与安全策略由服务端 `resolveSpec` 注入,请求体**替换不了**它们(`tools` 不是受理键,传了 400;按请求只能**减**:`excludeTools` / `settings.permissions` 的 deny · ask;7.117.0 起 `settings.permissions.allow` 整份交给引擎的 allow 层:表里的调用免问(普通问与没人表态两类,内置工具的写门 / shell 普通问也清),deny / ask 规则与运维的强制类照赢;能力位 `settings.permissions.allowLayer`)。
|
|
4
|
+
> 工具面与安全策略由服务端 `resolveSpec` 注入,请求体**替换不了**它们(`tools` 不是受理键,传了 400;按请求只能**减**:`allowTools`(7.126.0 起,只留列出的)/ `excludeTools` / `settings.permissions` 的 deny · ask;7.117.0 起 `settings.permissions.allow` 整份交给引擎的 allow 层:表里的调用免问(普通问与没人表态两类,内置工具的写门 / shell 普通问也清),deny / ask 规则与运维的强制类照赢;能力位 `settings.permissions.allowLayer`)。
|
|
5
5
|
> `model` 与 `systemPrompt` / `appendSystemPrompt` 是受理键,但 `model` 只能点服务端已配置目录里的名字 / 档位词 / 模型 id(未知 ⇒ 400)。凭据永远不进模型 / 请求体。
|
|
6
6
|
> 你不"调度 worker",你只发任务;LB + TiDB 自己协调。每个字段都来自当前真实代码。
|
|
7
7
|
|
|
@@ -37,8 +37,8 @@ docker compose up --build
|
|
|
37
37
|
> + TiDB 悲观事务模式(设不上就拒启),PG 腿 `READ COMMITTED`(同样是设了再验,不问服务器缺省 ——
|
|
38
38
|
> 把 PG 钉成 RR 或让部署把默认配成 RR/SERIALIZABLE,都会让每条 `FOR UPDATE` 变成 `40001`)。本仓所有
|
|
39
39
|
> `SELECT … FOR UPDATE` 的单赢家判据都以此为前提。运维读面:
|
|
40
|
-
> `GET /v1/diagnostics/wiring` 的 `sqlEngine` 段与 `GET /v1/capabilities` 的 `store.sql` 位(7.106.0 起;此前顶层 `sql
|
|
41
|
-
> `docs/DEPLOY-PREREQS.md
|
|
40
|
+
> `GET /v1/diagnostics/wiring` 的 `sqlEngine` 段与 `GET /v1/capabilities` 的 `store.sql` 位(7.106.0 起;此前顶层 `sql`)。判据与升级句见
|
|
41
|
+
> `docs/DEPLOY-PREREQS.md`(源码仓内文档,不随 npm 包;下文同名指路同)「事务读语义的结构保证」。
|
|
42
42
|
```bash
|
|
43
43
|
IMAGE_REGISTRY=<registry-namespace> \
|
|
44
44
|
DB_BACKEND=mysql MYSQL_HOST=… MYSQL_USER=… MYSQL_PASSWORD=… MYSQL_DATABASE=sema_server \
|
|
@@ -46,7 +46,7 @@ MODEL_GATEWAY_BASEURL=… MODEL_ID=<your-model-name> REMOTE_EXEC=e2b E2B_API_KEY
|
|
|
46
46
|
docker compose up --build --scale service=4 # 4 个无状态 worker,nginx LB 扇发,TiDB 协调
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
> 🔴 **7.106.0
|
|
49
|
+
> 🔴 **7.106.0 起给了 SQL 坐标而当前配置没有任何腿会用它 ⇒ 配置期拒启**:`MYSQL_HOST` 或 `PG_HOST` 在场,而会话后端不是
|
|
50
50
|
> durable(没有 `DB_BACKEND=mysql|pg`、没有 `SESSION_BACKEND=mysql|auto`),或 `SESSION_BACKEND=auto` 探的那只引擎的主机键没设(只设 `PG_HOST`
|
|
51
51
|
> 而 `DB_BACKEND` 缺省 ⇒ 探的是 mysql)。修前这三形**静默跑内存**(什么都不设是 local 文件持久,加一族坐标反而落到内存)。坐标被用到的存量形
|
|
52
52
|
> (`SESSION_BACKEND=mysql|auto` + MYSQL 坐标、不写 `DB_BACKEND`)照旧起服;「不用坐标」是显式表态的(`DB_BACKEND=memory|local`、
|
|
@@ -102,20 +102,20 @@ MODEL_GATEWAY_BASEURL=http://gw-a:8000/v1 MODEL_GATEWAY_FALLBACK_URLS=http://gw
|
|
|
102
102
|
ANTHROPIC_API_KEY=sk-ant-… # 可选:ANTHROPIC_BASE_URL / ANTHROPIC_VERSION / ANTHROPIC_CACHE_BREAKPOINTS=true
|
|
103
103
|
```
|
|
104
104
|
- 两个变量都不设 = 和以前**逐字节一致**(单网关)。
|
|
105
|
-
- 🔴 **`MODEL_GATEWAY_BASEURL` 没有出厂缺省**(
|
|
105
|
+
- 🔴 **`MODEL_GATEWAY_BASEURL` 没有出厂缺省**(BREAKING):每一条落 openai 兼容腿的路由(主模型 / cheap 槽 /
|
|
106
106
|
目录条目 / 降级目标 / titler 所选模型)都必须解析到显式端点 —— 条目自己的 `baseUrl`,或本键 —— 否则拒启,拒因
|
|
107
107
|
(码 `config.model_route_endpoint_missing`)点名解析不到的模型并给出本机写法 `MODEL_GATEWAY_BASEURL=http://127.0.0.1:8000/v1`。
|
|
108
108
|
只设 `MODEL_GATEWAY_FALLBACK_URLS` 不设主网关同拒(`config.gateway_fallback_without_primary`)。只走 Anthropic 路由的部署
|
|
109
109
|
不设网关合法;此时 `MODEL_API_KEY`(网关的凭据)不装载、不发往任何路由(启动通知 `model_gateway_key_unpaired`)。
|
|
110
110
|
中心 / config.d 下发的候选目录里有解析不到的条目 ⇒ 候选的模型面这一组被拒、上一代模型面继续服务(同候选其余组照落)。
|
|
111
|
-
- 🔴 **目录里的 anthropic 条目要本机真有 anthropic 路由**(
|
|
111
|
+
- 🔴 **目录里的 anthropic 条目要本机真有 anthropic 路由**(7.101.0 起):`provider:"anthropic"` 的目录条目只在本服务配了
|
|
112
112
|
`ANTHROPIC_API_KEY` / `ANTHROPIC_AUTH_TOKEN` 时可用;没配 ⇒ 该条目**不进可选目录**(`GET /v1/models` 不列、不当缺省,缺省顺延到下一条
|
|
113
113
|
可用条目),启动 / 刷新日志一行 `web_admin_model_no_brain`(带码 `config.model_no_brain` 与出路),显式点它(`body.model` /
|
|
114
114
|
`@名` / `settings.model` / `compactionModel` / side-query)⇒ 400 `request.model_unavailable`。**7.100.0 及以前**这种条目会落到网关
|
|
115
115
|
brain、带着**它自己的** `apiKeyEnv` 钥匙打到网关。不合 settings-schema 形的条目同律(码 `config.model_entry_invalid`,如非 http(s)
|
|
116
116
|
的 `baseUrl` —— 此前被「退回部署网关」;读腿逐条丢掉的条目同样留名字,点它照样 400);目录里**每条**都不可用 ⇒ 那一代模型面这一组被拒。
|
|
117
117
|
🔴 **日志与拒因句里的条目名先过名筛**(7.115.0 起):被拒条目的名字写进启动 / 刷新日志(`web_admin_model_no_brain` / `_entry_invalid` 的 `model`、`web_admin_models` 行的 `unavailable`、缺省 / 档位 / 角色绑定三条 warn 里指向被拒条目的名字)与「缺省模型缺席」「被拒名被增广铸出」两句拒因之前,先问 settings-schema 的 `isReportableRefusalName`(`[A-Za-z0-9._:/@+-]` 闭形、无密钥形、无带 userinfo 的 URL 形)。**不可披露的名只写码**:warn 不带 `model`、改带 `modelNameWithheld: true`(`unavailable` 只列可披露的名并带 `unavailableNamesWithheld` 个数);拒因句里该位写 `(name withheld)`。引擎自己的判决不受影响(显式点它照样拿拒因码);**要在日志里认出是哪一条**,把目录条目名改成名筛的闭形字符集(或按 `code` / `provider` / `fields` 在目录里找)。显式选中不可用条目的 400 回显不在此列(它回显的是调用方自己点的那个名)。
|
|
118
|
-
🔴 **被拒条目的名字不许被档位 /
|
|
118
|
+
🔴 **被拒条目的名字不许被档位 / 别名铸到别的条目上**:不可用条目恰好叫 `sonnet` / `opus` / `pro` / `best` 这类档位词或 CC 别名、
|
|
119
119
|
而档位表会铸出这个词时,这份候选的模型面整组被拒(码 `config.model_refused_name_minted`,warn `web_admin_model_plane_refused` 点名
|
|
120
120
|
「被拒名 → 目标条目」)—— 否则显式点它、或绑它的角色,会静默跑在另一条条目的端点与账号上;出路:改名 / 修好条目,或改绑那条档位绑定。
|
|
121
121
|
- **failover ≠ Anthropic↔vLLM**:failover 给所有 brain 发**同一个 model**(同协议同 id 的冗余);云↔本地是**按 `model.provider` 路由**的选择,不是故障转移(两者 model id/参数不同,不能透明互切)。设 `MODEL_PROVIDER=anthropic` + `MODEL_ID=claude-…` 让整个服务走 Anthropic。
|
|
@@ -154,7 +154,7 @@ AUTO_MODE_CLASSIFIER_TIMEOUT_MS=180000
|
|
|
154
154
|
WEB_SEARCH_PROVIDER=brave|tavily|searxng # 唯一的"装配开关":合法词才挂 WebSearch 工具;缺席/非法词 = 不装配(不是挂了报错)
|
|
155
155
|
WEB_SEARCH_API_KEY=… # brave/tavily 必需(searxng 不读这一键);只进 backend 闭包,永不进模型 prompt 或工具参数
|
|
156
156
|
WEB_SEARCH_ENDPOINT=https://searx.example # searxng 必需(实例地址);brave/tavily 下是可选的 base-URL 覆盖(代理/测试/**Brave 兼容网关**)
|
|
157
|
-
# Brave 兼容网关(
|
|
157
|
+
# Brave 兼容网关(2026-09-24 实跑):自托管的 Nólë 网关暴露与 Brave 同形的 `GET …/res/v1/web/search`,
|
|
158
158
|
# 认 `X-Subscription-Token`,返回 `web.results[{title,url,description,…}]` —— 用 brave 词 + endpoint 覆写即可,零新词:
|
|
159
159
|
# WEB_SEARCH_PROVIDER=brave
|
|
160
160
|
# WEB_SEARCH_ENDPOINT=https://<gateway-host>:<port>/res/v1/web/search # 完整搜索 URL(不是 base)
|
|
@@ -172,12 +172,12 @@ WEB_SEARCH_PROBE_ON_BOOT=true # boot 期一次性真出网探活,
|
|
|
172
172
|
|
|
173
173
|
| env 键 | 类型 | 默认 | 缺席行为 | 坏值行为 |
|
|
174
174
|
|---|---|---|---|---|
|
|
175
|
-
| `WEB_SEARCH_PROVIDER` | 闭集(`brave`\|`tavily`\|`searxng`) | 无 | WebSearch 工具整体不装配(功能缺席,无 warn) | 三词之外的任何值 = 同缺席处理,不装配、不 warn(`webSearchConfigFromEnv` 的词表守卫 `isWebSearchProvider`)。**读面(
|
|
175
|
+
| `WEB_SEARCH_PROVIDER` | 闭集(`brave`\|`tavily`\|`searxng`) | 无 | WebSearch 工具整体不装配(功能缺席,无 warn) | 三词之外的任何值 = 同缺席处理,不装配、不 warn(`webSearchConfigFromEnv` 的词表守卫 `isWebSearchProvider`)。**读面(7.82.1 起)**:`GET /v1/capabilities` 的 `webSearch.backend` 把这一格的生效值广告成闭集词(缺席/坏值都报 `"none"` —— 两者行为本就相同);端点/密钥/配额**不上 wire**,那些仍只在 operator 面 `GET /v1/config/catalog` |
|
|
176
176
|
| `WEB_SEARCH_API_KEY` | secret string | 无 | brave/tavily:后端仍会装配(装配只看 `PROVIDER`),**首次真实工具调用**时抛 `WEB_SEARCH_API_KEY is required for the <provider> provider`(`braveSearch` / `tavilySearch` 的首行守卫);searxng:本键无消费点 | 空串 / 纯空白同缺席。内容只有一条形律:**能当 HTTP 请求头值**(brave 放 `X-Subscription-Token`,tavily 放 `Authorization: Bearer <key>`;判的是实际发出去的那一串)。含换行 / NUL / 其它控制字符 / U+007F / U+00FF 以上字符等头不能携带的字符 ⇒ **拒启**(`config.web_search_invalid`,句 `env WEB_SEARCH_API_KEY must be usable as an HTTP header value (it contains a character an HTTP header cannot carry, such as a line break)`,**不回显值** —— 7.118.0 起;改前采纳,首次搜索时运行时抛的错误句带着整串明文)。判据就是运行时构造并发出请求头的那条流水线(Node 自带的两步:`Headers` 构造的归一化 + 发送层的字符表;不是另一张字符表):首尾空白(含换行)归一化后不剩,所以从密钥文件带出的尾随换行照收;searxng 不发鉴权头,但这一键照判(按裸 key)。出路:修好值,或不用这一键时 unset 它 |
|
|
177
|
-
| `WEB_SEARCH_ENDPOINT` | url string(brave 下 = 完整搜索 URL,可指向 Brave 兼容网关如 Nólë) | brave/tavily → 各自官方 API;searxng → 无默认 | brave/tavily:落官方 endpoint;searxng:**首次真实工具调用**时抛 `WEB_SEARCH_ENDPOINT (the SearXNG instance URL) is required for the searxng provider`(`searxngSearch` 的首行守卫) | 空串同缺席(compose 模板 `${WEB_SEARCH_ENDPOINT:-}` 铸的就是空串)。**不是 `scheme://host[:port][/path]` 形的绝对 URL ⇒ 拒启**(`searx.internal`、`localhost:8888` 这类少了 `scheme://` 的形都算;判据与模型路由 base URL 拒启同一只)。**带 userinfo ⇒ 拒启**(7.101.0
|
|
177
|
+
| `WEB_SEARCH_ENDPOINT` | url string(brave 下 = 完整搜索 URL,可指向 Brave 兼容网关如 Nólë) | brave/tavily → 各自官方 API;searxng → 无默认 | brave/tavily:落官方 endpoint;searxng:**首次真实工具调用**时抛 `WEB_SEARCH_ENDPOINT (the SearXNG instance URL) is required for the searxng provider`(`searxngSearch` 的首行守卫) | 空串同缺席(compose 模板 `${WEB_SEARCH_ENDPOINT:-}` 铸的就是空串)。**不是 `scheme://host[:port][/path]` 形的绝对 URL ⇒ 拒启**(`searx.internal`、`localhost:8888` 这类少了 `scheme://` 的形都算;判据与模型路由 base URL 拒启同一只)。**带 userinfo ⇒ 拒启**(7.101.0 起:`https://user:pw@host/…`、只带用户名的 `https://user@host/…` 都算;判据 = WHATWG 解析后用户名或口令任一非空,与运行时 fetch 拒收含凭据 URL 的约束同一条,所以修前这一形起得来、每次搜索必抛;brave / tavily 的凭据走 `WEB_SEARCH_API_KEY`,searxng 腿没有凭据通道,带鉴权的实例需在前面放一层注入鉴权的反代)。拒启句带键名与机读码 `config.web_search_invalid`,**不回显值**(这一格可能带凭据) |
|
|
178
178
|
| `WEB_SEARCH_MAX_RESULTS` | number | `10` | 用默认 10 | 空串同缺席;**非数字或 < 1 ⇒ 拒启**(`config.web_search_invalid`;改前静默落默认 10);是数则向下取整,再夹到 `[1,20]`(设 999 被夹到 20 —— 夹紧不是形错) |
|
|
179
179
|
| `WEB_SEARCH_TIMEOUT_MS` | number(ms) | `10000` | 用默认 10000 | 空串同缺席;**非数字(`"30s"` 这类带单位的也算)或 < 1 ⇒ 拒启**(`config.web_search_invalid`;改前静默落默认 10000 —— 写了 30s 的运维实际拿到 10s);是数则向下取整,再抬到 ≥ 1000ms |
|
|
180
|
-
| `WEB_SEARCH_SEARXNG_PARAMS` | string(`k=v;k=v`) | 无(不铸键 = 不传 `extraParams`,交给 core adapter 自己的缺省) | 键整个不铸 | 空串 / 纯空白 / 只有分号 = 缺席。文法与每请求 `settings.webSearch.searxngParams` 同一套:`name=value` 用 `;` 分隔(尾分号可有可无),名是参数名(字母 / 数字 / `_` / `-` / `.`),值非空,同名只许一次。**任一段不成形 ⇒ 拒启**(`config.web_search_invalid`,句里只报第几项 `entry #N`,**不回显名或值** —— 7.101.0
|
|
180
|
+
| `WEB_SEARCH_SEARXNG_PARAMS` | string(`k=v;k=v`) | 无(不铸键 = 不传 `extraParams`,交给 core adapter 自己的缺省) | 键整个不铸 | 空串 / 纯空白 / 只有分号 = 缺席。文法与每请求 `settings.webSearch.searxngParams` 同一套:`name=value` 用 `;` 分隔(尾分号可有可无),名是参数名(字母 / 数字 / `_` / `-` / `.`),值非空,同名只许一次。**任一段不成形 ⇒ 拒启**(`config.web_search_invalid`,句里只报第几项 `entry #N`,**不回显名或值** —— 7.101.0 起;改前句里回显参数名):把数组或对象的 JSON 文本(`["engines=bing"]`、`{"engines":"bing"}`)填进来、一段没有 `=`、名为空、同名两次。改前这些被静默跳过或吸成垃圾参数发给实例 |
|
|
181
181
|
| `WEB_SEARCH_PROBE_ON_BOOT` | boolean(仅认 `"true"`/`"1"`) | `false`(OFF) | 不探活 | trim+小写后不等于 `"true"`/`"1"` 的任何拼法(含 `"yes"`/`"on"`)一律当 `false`;探活失败(网络不通/后端拒绝)只 `warn`(`web_search_probe_failed`),**不拒启**——功能型能力缺席走降级,不是保护型旋钮(`main.ts:1028-1034`) |
|
|
182
182
|
|
|
183
183
|
**结论:缺席与形错分两件事。** 缺席(未设 / 空串,以及 `WEB_SEARCH_PROVIDER` 不是三词之一)一律走「不装配 / 用默认」,
|
|
@@ -241,15 +241,15 @@ MODEL_DEGRADE_ON=rate_limit,breaker_open # 可选,反应式触发器子集;词
|
|
|
241
241
|
`config.d/models.json` 或配置控制面的目录条目上,逐键盖过上面两根 env 缺省(BL-8:没写的键仍继承 env)。
|
|
242
242
|
**坏形/坏词/未知键 ⇒ 丢掉这条 `compat` 声明、模型本体保留**,并打一条 `model_compat_dropped`
|
|
243
243
|
点名 warn(引擎回落到自己的推断 = 今天的行为);一条写坏的声明**不会**让这只模型或整套目录消失。
|
|
244
|
-
- 反应式降级 brain 包在**最外层**;fallback brain 用自己的凭据(decorator 清掉主模型的 per-call key 防外泄给别的 provider)。**7.106.0
|
|
244
|
+
- 反应式降级 brain 包在**最外层**;fallback brain 用自己的凭据(decorator 清掉主模型的 per-call key 防外泄给别的 provider)。**7.106.0+**:
|
|
245
245
|
降级目标的自有凭据按**启动那一代的身份**取值(env 变量名 / 托管密钥),与目标端点同代 —— 同名变量换值热生效;目录换 key 名字 / 重贴托管密钥 / 换端点
|
|
246
246
|
要按 `/health.restart.reasons ∋ degrade-route` 重启。**坑**:`MODEL_DEGRADE_TO` 最好别和被限流的是同一网关/账号,否则反应式切过去照样撞同一个 rate_limit。
|
|
247
247
|
- **定价怎么设**:`model.cost` 来自 `MODEL_COST_INPUT/OUTPUT/CACHE_READ/CACHE_WRITE`(**USD per 1M tokens**)。**四键一个都不设 = 未定价**:成本读面**缺席**(`result.stats.costMicroUsd` 整键不出、`costBreakdown` 不产、`/v1/capabilities.pricingConfigured=false`),不是 `$0` —— 消费端应渲成「未知」。设其中任一键 ⇒ 四键在场(未设的按 0)。本地自托管(qwen)真的没有 per-token 外部花费时,请**显式**把四键设成 `0`:那是一次「声明免费」,读面照常发 `costMicroUsd:0` 且 `pricingConfigured=true`。云模型(如 review-gw 的 deepseek-v4-flash)必须设真价,否则 spend 无从计算。字段名是 `costUsd`,**非美元计价的网关要先折算**(如 DeepSeek 官方 CNY ÷ 汇率)。配置控制面管的模型走 `CenterModel.cost`(中心存价、不存 secret;名册行不带 `cost` = 该模型未定价,同上)。
|
|
248
248
|
- **成本天花板要有价才咬得住**:`MAX_TASK_COST_USD` / `MAX_PRINCIPAL_COST_USD` 量的是 `costUsd`,未定价的模型算不出成本 ⇒ 这两道闸**永不触发**。目录里一只带价模型都没有却配了天花板时,boot 会打一条 `cost_ceiling_without_pricing` 告警(不拒启);token 轴的 `MAX_TASK_TOKENS` 不受影响。
|
|
249
|
-
- 成本计量**自动开**:从 config 的 `model.cost`(per-1M 绝对 USD)注入 `pricing`,core 算出权威的整数 `costMicroUsd`(避免浮点累计误差)。`/metrics` 新增:`model_cost_micro_usd_total{model}`(覆盖所有 brain
|
|
249
|
+
- 成本计量**自动开**:从 config 的 `model.cost`(per-1M 绝对 USD)注入 `pricing`,core 算出权威的整数 `costMicroUsd`(避免浮点累计误差)。`/metrics` 新增:`model_cost_micro_usd_total{model}`(覆盖所有 brain 调用=主任务+异步+委派子任务的总花费)、`brain_first_token_ms`(网关 hang 早警)、`brain_call_latency_ms`、`tool_calls_total{name,ok}`、`budget_exceeded_total{code}`、`cost_quota_rejected_total`、`degraded_total{reason}`(1.40 降级)。
|
|
250
250
|
- **近预算降级 vs 硬闸**:降级(`MODEL_DEGRADE_TO`,到 `atCostFraction` 切便宜模型)是**撑长**预算、任务仍完成(出口质量下降、发 `task.degraded` 事件可告警);硬闸(`maxCostUsd` 全额)仍在,切了便宜模型还超全额 → `limits.max_cost_exceeded` 停。
|
|
251
251
|
- **`METRICS_TOKEN`**(可选,只读):设了它,`GET /metrics`+`/metrics/summary` 接受**它或** `SERVICE_AUTH_TOKEN`。**全 fleet 设同一个值** → 管理端(sema-web,规划名 sema-admin)用**一个** token 拉所有 worker 的指标,**无需持有各 worker 的全权 `SERVICE_AUTH_TOKEN`**(不破坏 secret 边界)。即使外泄也只暴露指标(只读)。
|
|
252
|
-
- **`GET /metrics/summary`**(token-gated,同 `/metrics`):`/metrics` 的**精炼 JSON**——`{model, runsActive, tasks{status}, tokensTotal, costUsd, costUsdByModel, taskDurationAvgSec, brainFirstTokenAvgMs, brainCallAvgMs, cacheHitRateAvg, rateLimited, costQuotaRejected, budgetExceeded, limitCeilingAfterAnswer, degraded, cascade, verifications,
|
|
252
|
+
- **`GET /metrics/summary`**(token-gated,同 `/metrics`):`/metrics` 的**精炼 JSON**——`{model, runsActive, tasks{status}, tokensTotal, costUsd, costUsdByModel, taskDurationAvgSec, brainFirstTokenAvgMs, brainCallAvgMs, cacheHitRateAvg, rateLimited, costQuotaRejected, budgetExceeded, limitCeilingAfterAnswer, degraded, cascade, verifications, toolErrors, httpErrorsByClass, httpErrorsByRoute}`(**7.126.0 起没有 `councilRuns`** —— 它数的 code-review 多镜头评审团随引擎 7.49.0 删掉团队运行时一并删除,Prometheus 的 `council_runs_total` / `council_tokens` 同删;按 v1 形解析这张表的看板升级前先把这一键改成可缺)(`httpErrorsByClass` / `httpErrorsByRoute` 自 **7.87.0**,additive:窗内 HTTP **失败**请求按状态码类聚合(键只有 `4xx` / `5xx`,2xx/3xx 不进;⚠️ `4xx` 含本仓给「客户端提前断连」铸的 `499`,SSE 长连上是常态)+ 失败最多的前 10 条 route 标签(`routeLabel` 低基数形,id 折成 `:id`,未命中路由表的落 `other`)—— 这两格是**不装 Prometheus 的部署**看见一场 404 风暴的唯一地方;两键**恒在场**,零失败时是 `{}`,不用缺席表示零)(`limitCeilingAfterAnswer` 自 7.71.0,按 `origin` 分桶的「答后撞顶」计数;与 `budgetExceeded` **总体不同** —— 前者每条引擎 run 都过,后者只由 HTTP 提交腿递增 —— 不要无条件相加)。给**轻量 fleet 看板**用(管理端 sema-web 的 fleet 页按 worker 拉、渲染卡片、按轮询算速率;**不用 Prometheus/Grafana**)。counter 是累计值、histogram 报均值。
|
|
253
253
|
- 预算闸是 core 强制的:`maxCostUsd` pre-call 估算(没花钱就拒)+ 流中途取消 + turn 边界(`limits.max_cost_exceeded`);`maxTokens` 超 → `limits.max_tokens_exceeded`(core 5.8.0 起 `budget.*` 前缀退役)。**review 网关**建议设 `MAX_TASK_COST_USD` 防单任务烧掉共享云 key。
|
|
254
254
|
- 🔴 **三道 $ 闸的分工(别拿进场门当中途停机用)**:三根旋钮名字都带 cost,但**判的时刻**和**能不能打断正在烧的那张单**完全不同。按「谁触发 / 什么时刻判 / 拒或停长什么样」对表:
|
|
255
255
|
|
|
@@ -269,7 +269,7 @@ MODEL_DEGRADE_ON=rate_limit,breaker_open # 可选,反应式触发器子集;词
|
|
|
269
269
|
受理的任务会一直烧到自己的 per-task 闸或跑完为止,期间没有任何一刻会因为该 principal 越顶而停;
|
|
270
270
|
运维看到的是「天花板说不许花的钱花完了,下一次提交才 429」。要当场停就必须配治理窗的 $ 天花板
|
|
271
271
|
(前置:模型**可计价**,否则任务门口就拒,见 §9.2)。
|
|
272
|
-
- 记账口径:`MAX_PRINCIPAL_COST_USD` 的累计来自权威的整数 `costMicroUsd`(覆盖主任务 +
|
|
272
|
+
- 记账口径:`MAX_PRINCIPAL_COST_USD` 的累计来自权威的整数 `costMicroUsd`(覆盖主任务 + 异步/委派
|
|
273
273
|
子任务,按 principal 归因);单副本部署是进程内计数,配了 SQL 后端则跨副本共享。
|
|
274
274
|
- ⏱️ **窗形是「时钟对齐的固定桶」不是滑动窗**(别按滑动窗估保护强度):桶号 = `floor(now / 窗长)`,
|
|
275
275
|
桶界按 Unix 纪元对齐(日窗 `86400` 在 UTC 零点滚),与该 principal 何时第一次花钱无关 —— 单副本内存计数与跨副本 SQL 共享计数是同一个窗形。
|
|
@@ -317,13 +317,13 @@ MODEL_DEGRADE_ON=rate_limit,breaker_open # 可选,反应式触发器子集;词
|
|
|
317
317
|
best-effort,与 `msSinceLastActivity` 同族)/ 账本读不可用,四种都是缺席;后者计数在
|
|
318
318
|
`fail_open_total{tag="server.runs.cross-slice-usage-unavailable"}`;③ 它是**给人看的预警面**,真正执法的
|
|
319
319
|
是引擎账本 —— 别拿它做自动重试/取消的机器判据。
|
|
320
|
-
- **per-principal 累计配额**:用 `AsyncLocalStorage` 把 principal 透传到 cost tracer
|
|
320
|
+
- **per-principal 累计配额**:用 `AsyncLocalStorage` 把 principal 透传到 cost tracer,所以**委派子任务的花费也算到发起人头上**。**配了 `SESSION_BACKEND=mysql` 时自动跨副本共享**(`cost_quota` 表,写后聚合的**原子自增** `micro=micro+delta`,对齐固定窗,最终一致——多副本花费 SUM 到一起、不丢增量);否则 in-memory per-replica 计数(单副本兜底;窗形与 SQL 腿同一个时钟对齐桶)。启动日志 `costQuota` 字段回显 `shared(mysql)`/`in-process`/`off`。跨副本是最终一致(flush 间隔内峰值可能略超,由**硬 per-task `maxCostUsd` 兜底**)。
|
|
321
321
|
- **`RATE_LIMIT_RPM` 请求限流同样自动跨副本**(`SESSION_BACKEND=mysql` 时,`rate_limit` 表,与配额共用 `WriteBehindCounter`;启动日志 `rateLimit` 回显)。注意:写后聚合 = **软限流**(边界上短暂略超 OK,适合公平/热调用方防护);要**硬合规上限**得另走 CAS/原子计数,不靠写后聚合——和断路器跨副本同款权衡。
|
|
322
322
|
|
|
323
323
|
**可选 — 生成调参透传 + 退化打捞(core 1.59/1.60)**
|
|
324
324
|
```bash
|
|
325
325
|
# 生成参数直透网关(core 1.60 Model.extraBody)。frequency/presence penalty 从源头压退化循环——
|
|
326
|
-
# 是 core 退化检测(安全网)
|
|
326
|
+
# 是 core 退化检测(安全网)的预防。长链推理易跑飞的部署建议设。
|
|
327
327
|
MODEL_FREQUENCY_PENALTY=0.5 MODEL_PRESENCE_PENALTY=0.3
|
|
328
328
|
MODEL_EXTRA_BODY='{"top_k":40}' # JSON 逃生口(top_k / logit_bias 等;penalty 同名键会覆盖它)
|
|
329
329
|
# ⚠️ core ≥5.15 起 max_completion_tokens 是保留键(config.extra_body_output_cap
|
|
@@ -335,7 +335,7 @@ MODEL_EXTRA_BODY='{"top_k":40}' # JSON 逃生口(top_k / logit_bias 等;penalt
|
|
|
335
335
|
🔴 **调用方读法(core 官方配方,别拿 `terminal.kind` 三元代替)**:`const out = r.result || r.salvagedOutput || "";`
|
|
336
336
|
—— 先读 `result`,空了才回落。理由:`salvagedOutput` 只在**一张七员闭集**的失败终局上在场(逐字取自 core 的 `SALVAGE_ELIGIBLE_TERMINALS`:`output.degenerate`、`limits.max_tokens_exceeded`、`limits.max_cost_exceeded`、`limits.max_turns_exceeded`、`limits.max_walltime_exceeded`、`env.lifetime_expired`、`usage.window_exhausted`),**闭集之外**的失败终局(模型/供应商错 `provider.error`、`conflict`、未铸码的失败……)它**整键缺席**,而最终助手文本仍在 `result` 里;闭集**之内**它又与 `result` 取自**同一条** final message 文本。两头都指向同一条读法:先 `result`。只读 `salvagedOutput` 会在闭集外的每一条失败路径上把真产出当成"没有输出"丢掉。penalty 是预防、这是兜底。
|
|
337
337
|
|
|
338
|
-
**可选 — 目录条目的供应商附加请求头(`headers`,
|
|
338
|
+
**可选 — 目录条目的供应商附加请求头(`headers`,7.108.0;两条读腿打通 7.109.0)**
|
|
339
339
|
```jsonc
|
|
340
340
|
// config.d/models.json(或 web-admin models 域)的一条条目:
|
|
341
341
|
{ "name": "claude-1m", "id": "<上游模型 id>", "provider": "anthropic", "api": "anthropic-messages",
|
|
@@ -352,7 +352,7 @@ MODEL_EXTRA_BODY='{"top_k":40}' # JSON 逃生口(top_k / logit_bias 等;penalt
|
|
|
352
352
|
`degrade-route` 重启片走)。
|
|
353
353
|
- **请求面没有 `betas`**:头是部署 ↔ 供应商的契约(计费 / 配额 / 兼容性由运维承担),不是租户可表态的位 —— `POST /v1/tasks` 带 `betas` ⇒
|
|
354
354
|
400 `request.body_shape`(键闭集)。
|
|
355
|
-
- **读腿**(7.109.0
|
|
355
|
+
- **读腿**(7.109.0 起):`config.d/models.json` 与 web-admin 两条读腿逐字交出 `headers`。`@sema-agent/settings-schema` 6.4.0 的 `ModelEntry.headers`
|
|
356
356
|
是解析透明键 —— **契约包只做自由文本安全判**(与 `compat` / `extraBody` 同一只闸:**秘密形的值**(形似令牌 / 密钥)在那一步就让条目不可用,拒因字段是
|
|
357
357
|
整键 `headers`、不回显值),**形(头名白名单 × 线路 × 值形)由本服务判**(`src/model-entry-headers.ts`,上面那几条)。
|
|
358
358
|
|
|
@@ -366,11 +366,15 @@ OTEL_EXPORTER_OTLP_HEADERS=authorization=Bearer xxx # 逗号分隔的 k=v
|
|
|
366
366
|
|
|
367
367
|
**可选 — 成本分层 + `@-model`(core 1.24 role map)**
|
|
368
368
|
```bash
|
|
369
|
-
# 便宜模型分层(同网关、换 id)
|
|
369
|
+
# 便宜模型分层(同网关、换 id):委派子代 / 上下文压缩走便宜档,主任务走主模型。
|
|
370
370
|
MODEL_ID=<your-model-name> MODEL_CHEAP_ID=<your-cheap-model-name>
|
|
371
371
|
```
|
|
372
|
-
-
|
|
373
|
-
|
|
372
|
+
- **角色词就是引擎认的六个**(引擎 7.49.0 / settings-schema 12 起):`default` · `summarize` · `subagent` · `verifier` · `classifier` · `consolidate`。
|
|
373
|
+
中心配置的 `models.roles`、请求的 `TaskSpec` 角色键、side query 的 `modelRole` 只认这六个;旧的 `team` / `synthesize` / `advisor` 三词已删 ——
|
|
374
|
+
存量中心配置里的 `roles.team` / `roles.synthesize` 由 settings-schema 的一次性迁移搬走,side query 带 `modelRole` 表外词(含 `advisor`)⇒ **400**
|
|
375
|
+
`request.field_invalid`(不再回落主模型;带了 `model` 也一样拒)。要给委派子代配模型就绑 `subagent`,主任务绑 `default`。
|
|
376
|
+
- 调用方可在 `objective` 里 `@<模型名>` 选模型,**只认已配置的名单**(`MODEL_ID` / `MODEL_CHEAP_ID`,或目录条目)——注入不了 `baseUrl`/`apiKey`;无 `@` → 默认模型;`@` 点中一条已发布但本部署不可用的目录条目 ⇒ 400 `request.model_unavailable`。
|
|
377
|
+
- **`GET /v1/models`**(带 Bearer)→ `{models:[{name,id,provider,reasoning,vision,…}],default,defaultModel,droppedEntries}`,只含名字/能力、**不含密钥** —— 消费方(如 OA)拿它做 `@` 自动补全下拉的数据源;`droppedEntries`(7.123.0 起恒在场,零条 = `[]`)是本代目录里被读腿逐条丢掉的条目(下标 + 闭集原因词 + 可披露身份),契约 §A.1d。 7.126.0 起另有**可选**顶层键 `candidateRejected: { domains, at, source }`:最近一次配置候选被候选判官整拒时在场(`domains` = 判形失败的配置域名,`source` = `local` 本机 `config.d` / `remote` 中心,`at` = 判下它的时刻)—— 在场就说明这份目录是回落的那一份(整份候选没收,候选里的模型一个都不在),真因域在 `domains` 里;后来的候选被判 clean 即缺席;dry-run 姿势(web-admin 腿 compare-only)恒缺席(拒判只走日志)。**缺席不等于配置一定干净**(也可能是更早的 server)。只含域名 / 时刻 / 源形态,拒因句与文件路径只在引擎日志 `config_candidate_rejected` 里,契约 §A.1d。
|
|
374
378
|
- 不设 `MODEL_CHEAP_ID` → 所有角色 = 主模型(行为不变)。
|
|
375
379
|
- `MODEL_CHEAP_ID` 写成与 `MODEL_ID` **同一个 id**(7.104.0 起)→ 那就是主模型本身:不铸 cheap 槽,所有角色 = 主模型,起服一行
|
|
376
380
|
`config_env_entry_cheap_same_as_main`(同时设的其余 `MODEL_CHEAP_*` 键一并不生效)。要给同一只上游 id 第二种形(另一个价 / 思考 /
|
|
@@ -392,6 +396,20 @@ MODEL_CODE_ROLES=default,subagent # 不设=全中立;仅这些角色在「
|
|
|
392
396
|
⚠️ **口径别混**:「受限」是**会话级的这一个声明**;请求键 `memoryWrite:false` 铸出的只读**平面**(以及 org 层默认只读、双根非写面)**保持原样的 adopt-on-read**——它只是不 harvest,照旧看得见盘上的新内容。
|
|
393
397
|
⚠️ **core 5.26.0 起,remote 执行车道上的 `# Memory` 写指令被撤**(沙箱手带够不着 host 记忆根;读与注入不变)。**判据是「`REMOTE_EXEC` 有没有设」,不是那个旗**——凡设了 `REMOTE_EXEC`(含 `host`)且这次任务真有记忆面,写指令就被撤。因此被打到的**不止**显式 `MEMORY_ENGINE_REMOTE_LANE=allow` 的部署,还包括:① `MEMORY_ENGINE_BACKEND=pg|tidb` 的 DB 记忆面(它不受那个旗管辖——旗只管 file 引擎,库是持久真身,任何车道上都照常点亮)= 最常见的云形;② `REMOTE_EXEC=host` + file 引擎(文件平面就在本机,但 core 的执行环境判决仍是 remote)。若该车道确实够得着记忆根,`MEMORY_PERSISTENCE_CAPABLE=true` 是官方恢复路径(启动日志 `memory_remote_lane_write_instruction_dropped` 会点名这一条);够不着就表 `false`,让只读状态对模型说明白。
|
|
394
398
|
另:记忆**没有写工具**,模型是用普通文件工具往记忆根写的——所以记忆根必须落在任务的 fs 授权边界内。结构性够不着时启动会 warn 并给出三条改法(把根挪进 workspace / 经 `additionalDirectories` 授权 / 声明 `MEMORY_PERSISTENCE_CAPABLE=false` 让披露诚实)。⚠️ **「挪根」用哪个旋钮按记忆后端分腿**:file 引擎是 `MEMORY_ENGINE_DIR`;`MEMORY_ENGINE_BACKEND=pg|tidb` 的根是 `<数据根>/memory-work`(数据根 = `LOCAL_DATA_ROOT`/`AGENT_DATA_DIR` 族),那两条腿**不读** `MEMORY_ENGINE_DIR`——启动 warn 的文案自己会按腿指对旋钮。
|
|
399
|
+
- **记忆店根搬了家 ⇒ 拒启(7.126.0 / 引擎 7.49.0 起,file 记忆后端)**:店根的物理路径记在店的控制面里(`root-anchor.json`),每次打开都比对。目录被搬走、
|
|
400
|
+
或换成指向别处的链接之后,起服**拒**(码 `memory.target_outside_store`;不降级成「记忆暗」—— 店还在,只是搬了家,静默降级会让这台部署在没人知道的情况下丢掉全部记忆)。
|
|
401
|
+
拒句点名控制面记下的路径与现在解析到的路径,并给出两条出路:**① 把目录放回原处**;**② 在本服务的目录里跑一次重锚**:
|
|
402
|
+
`node --input-type=module -e 'import("@sema-agent/core").then(m => m.reanchorMemoryStoreRoot("<path>", { controlDir: "<controlDir>" }))'`
|
|
403
|
+
(两个坐标拒句里逐字给出),然后再起服。`MEMORY_ENGINE_BACKEND=pg|tidb` 与 `MEMORY_ENGINE=off` 的部署不经这一判。
|
|
404
|
+
- **数据根的写豁免只有一个来源(7.126.0 / 引擎 7.49.0 起)**:路径守卫对「本服务自己的数据根」的豁免,只读引擎的 `memoryEngineDir` 一处 —— 本服务把记忆引擎根
|
|
405
|
+
**恒**交给它,**`MEMORY_ENGINE=off` 的部署也交**(否则豁免根缺席,数据根下的写会被当成普通受保护路径拒)。运维零动作。
|
|
406
|
+
- **写目标的真落点读不出 ⇒ 有守卫行的面拒这次写(7.126.0 / 引擎 7.49.0 起,所有权限模式含 `bypassPermissions`)**:`Write` / `Edit` / `NotebookEdit` 的目标
|
|
407
|
+
解析不出真实落点(链接成环、跟不下去,执行环境报错或超时)而这一面有任一守卫行(始终在岗的写保护表、部署的 `SENSITIVE_WRITE_PATTERNS`)⇒ 门上拒、
|
|
408
|
+
不出卡、不写;拒句带执行环境自己的那句话,运维日志每条腿一行 `config.path_view_unresolved`。所以**执行环境答路径问题的能力就是写面的可用性**,各车道如下:
|
|
409
|
+
`host` —— 真盘,真实落点与链接链都读;`e2b` / `k8s` / `ssh` / `adb` / `local-docker` —— 适配器答 `canonicalPath` 与链接链(一次往返),部署守卫表经写门代理
|
|
410
|
+
读落点(代理不转发链接链 ⇒ 部署守卫表在这些车道上只判落点,不逐跳;始终在岗的写保护表读适配器本身);`device` —— 设备协议必答 `canonicalPath`,
|
|
411
|
+
不答链接链(只判落点)。这些车道上一次瞬时失败(pod exec 超时、设备掉线、写门代理还没绑定本任务的沙箱)就是一次「读不出」⇒ 受守的写被拒,
|
|
412
|
+
重试那一次写即可(本服务不提供关掉始终在岗写保护表的旋钮)。
|
|
395
413
|
- **记忆检索的向量面(embedder,7.14.0 起)**:记忆检索有三档——`lexical`(词面 jaccard)/ `portable`(库内存向量,进程内算余弦)/ `native`(库侧向量算子)。⚠️ **没有档位旋钮**:档位是「记忆后端上限 × embedder 在不在场」**推断**出来的结果(显式选档只会造出「选了 native 却没 embedder」这类矛盾态)。**配上 embedder = 唯一的开法**,而且只在 `MEMORY_ENGINE_BACKEND=pg` 上有意义(tidb 记忆后端 v1 是词面档,file 引擎没有 embedder 接缝——两者配了 `MEMORY_EMBEDDER_*` 一律**拒启**点名)。不配 = 保持 `lexical`(默认姿态,零 warn)。
|
|
396
414
|
|
|
397
415
|
| env | 缺省 | 说明 |
|
|
@@ -458,7 +476,7 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
|
|
|
458
476
|
只调 `loadConfig()` 的装配对这几形照起。`ctx.configSource === "local"` 时它先读 `config.d/remote-exec.json` 覆写车道(`drivesFromFile`
|
|
459
477
|
= 你的装配驱动得了的文件车道),再判;不走文件覆写的装配直接调同一份判据 `assertExecutionLaneInvariants(config)`。
|
|
460
478
|
`ctx` 的型 `ExecutionLaneComposeCtx` 也从包根导出(自己的装配里要写它的签名 / 抽 helper 时直接 import,不必手抄一份形)。
|
|
461
|
-
- **库形嵌入的 caps `store` 位**(7.106.0
|
|
479
|
+
- **库形嵌入的 caps `store` 位**(7.106.0 起;7.106.0 合并复审 X9 补一句):`createHttpServer` 的 `storePosture` dep **不传** ⇒ `GET /v1/capabilities` 的
|
|
462
480
|
`store` 取诚实下限 `{declared: <config.dbBackend>, backend: "memory", durable: false}`,且**没有 `store.sql`**(即使递了 `sqlEngineFacts`、真有 SQL 后端 ——
|
|
463
481
|
7.105.0 上这类嵌入方的顶层 `sql` 是有值的)。要 caps 说真话就把你装出来的后端事实经 `storePosture` 递进来(形 = `src/store-posture.ts` 的 `StorePosture`)。
|
|
464
482
|
- `/health` 身份字段承诺:`pid`(= 引擎进程)、`startedAt`(**7.67.0+**,= 引擎进程起点的 epoch ms,
|
|
@@ -477,16 +495,16 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
|
|
|
477
495
|
连接类异常惯于回声整条 DSN(`mysql://user:pass@host:3306/db`)。要读全文去**日志**:探针在
|
|
478
496
|
live→dead 翻转拍打 `store_probe_dead`,`error` 字段是原文。
|
|
479
497
|
钉:`test/health-store-live.test.ts` / `test/store-live-probe.test.ts`。
|
|
480
|
-
- 就绪位(**7.102.0
|
|
498
|
+
- 就绪位(**7.102.0+**):`/health` 与 `GET /readyz` 读**同一只**就绪判。就绪 ⇒ `/health` 不带 `ready`
|
|
481
499
|
键(正常形状不变)、`/readyz` 200 `{"ready":true}`;未就绪 ⇒ `/health` 带 `ready:false` + `readyReason`(首条原因
|
|
482
500
|
的人读句,名册未落那句逐字沿用旧句)+ `reasons`(闭集 `draining` / `model_roster_pending`,按优先序,与 `/readyz`
|
|
483
501
|
逐字同一份),`/readyz` 503 `{"ready":false,"reasons":[…]}` + `Retry-After: 5`(体只这两键,不带身份字段)。
|
|
484
502
|
⚠️ 7.102.0 起 **draining 也是未就绪**:此前 draining 形的 `/health` 只有 `draining:true`、不带 `ready` 键(读成
|
|
485
503
|
「就绪」),与同一刻计费提交的 503 自相矛盾。`storeLive:false` **不进**就绪判(见上一条:披露不代裁)。fleet 心跳的
|
|
486
|
-
`ready` 同一只判(名册未落时心跳 `ready:false`,落地当刻即再报一次)。fleet 心跳另带 **`restart`**(**7.102.0
|
|
504
|
+
`ready` 同一只判(名册未落时心跳 `ready:false`,落地当刻即再报一次)。fleet 心跳另带 **`restart`**(**7.102.0+**):
|
|
487
505
|
与 `/health.restart` 同一只取值口、逐字同一个对象(形 = settings-schema `RestartSignal`:`reasons` / `version` / `since`;
|
|
488
506
|
在场即「须重启才能应用配置」,缺席即不需要 —— 中心整行替换);重启信号出现 / 消失 / 变化的当刻即再报一次,不等下一拍心跳。
|
|
489
|
-
**7.106.0
|
|
507
|
+
**7.106.0+**:候选**部分应用**(某组被整组拒、其余组已提交)的拍也照同一律算这一信号 —— 修前那一拍不签;被拒的是审批组 / 模型面组时
|
|
490
508
|
(新进程 boot 重判可能拒启)信号扣下、只出 warn `restart_signal_withheld_boot_may_refuse`(点名组与差异)。
|
|
491
509
|
钉:`test/health-readyz.test.ts` / `test/health-readiness-single-source.test.ts`。
|
|
492
510
|
- **引擎信号 `engineSignal`(7.123.0+,`/health` 与 fleet 心跳都带,恒在场)**:引擎态整只,型 = settings-schema `EngineSignal`
|
|
@@ -510,11 +528,19 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
|
|
|
510
528
|
`false` / `"unknown"`:中心车道配齐 ∧ `FLEET_ADVERTISE_ADDRESS` 在 ∧ 鉴权桥在 ∧ 会话枚举面在,缺一 ⇒ `false`;齐了而多租户 ⇒ `true`,
|
|
511
529
|
单用户 ⇒ `"unknown"`(一条会话归谁看提交它的请求带没带身份)。两只 URL 过展示面的同一条脱敏律(凭据 / query 值不上,尾斜杠折掉)。
|
|
512
530
|
⚠️ 无鉴权面:令牌、运维名单、worker 名、中心凭证都**不上**。钉:`test/health-self-report.test.ts`。
|
|
513
|
-
- 模型拒因(**7.103.0
|
|
531
|
+
- 模型拒因(**7.103.0+**):`/health.modelRefusals` 与 fleet 心跳 `modelRefusals` 同一只取值口、同一个对象(形 = settings-schema
|
|
514
532
|
6.2.0 `ModelRefusalsSignal`:`entries` = 目录条目名 → 拒因码,`plane` = 模型面组拒码;值只码)。描述最近一次被判的中心候选目录;
|
|
515
533
|
没有可报的拒 ⇒ 键缺席。名字是秘密形 / 空串 / `__proto__` 的不可用条目不上这两处(出口过名筛;引擎内部照旧拒它)。消费方读 `version`
|
|
516
534
|
判「缺席 = 没有拒」还是「老引擎、不可知」;读值用 settings-schema `readModelRefusals`。契约段:`docs/ASSISTANT-WIRE-CONTRACT.md`「模型拒因」。
|
|
517
535
|
钉:`test/model-refusals-s768.test.ts`。
|
|
536
|
+
- **`SESSION_CACHE_TTL_SEC`(缺省 `300`,单位 s;`0` = 关)—— 会话暖缓存,多副本安全**:每只副本把用过的会话留在内存里,
|
|
537
|
+
下一轮不必从店重新唤醒整只会话。每次命中先读店里这只会话的当前叶(SQL 后端一条主键 SELECT):与暖份的叶相同 ⇒ 照用;
|
|
538
|
+
不同(这只会话在别的副本上写过)或会话已不在 ⇒ 丢掉暖份、从店重读再用。所以 chart 缺省 Service、轮询负载均衡这类**任意路由**
|
|
539
|
+
下,同一会话轮流落到不同副本不会撞 `conflict`;会话亲和只提高命中率(少重读),不是正确性前提。代价:每次命中多一次读头
|
|
540
|
+
—— SQL 后端一条主键 SELECT;文件后端(`DB_BACKEND=local`)是把这只会话的存档文件再读一遍(只读头,不铸 `session.records_skipped`
|
|
541
|
+
帧)。命中却已陈旧的次数计在 `session_cache_stale_hit_total`(高 = 同一会话在 TTL 内常换副本,加亲和可省掉这些重读)。
|
|
542
|
+
`0` = 每一轮都从店唤醒,不复用。⚠️ 核的是叶:另一只副本用会话同步**整段替换**(含 `overwrite-dst`)把已有条目的内容改了、
|
|
543
|
+
末叶却没变的那一形,别的副本上的暖份在 TTL 内仍是旧内容(与改前同;写不会因此失败)。依赖这一形即时可见的部署设 `0`。
|
|
518
544
|
- 数据驻留提示:`DB_BACKEND=local` 下显式 `SESSION_BACKEND=memory` 会被收编为 **durable(local)**
|
|
519
545
|
(1.292+ 裸 boot 默认 durable;/health 的 `sessionBackend` 报 `durable(local)`)——session 行落盘在
|
|
520
546
|
数据根下,清数据/隐私预期要按「sessions 在 engine-data 里」来做,不要按「只在内存」。
|
|
@@ -561,12 +587,16 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
|
|
|
561
587
|
同一只等待腿 —— **不为前台形留第二套语义**。前台形要「立刻告诉我谁占着」就设 `STORE_LOCK_WAIT_MS=0`,
|
|
562
588
|
那是文档化的通道,而不是两条路各有一套默认。
|
|
563
589
|
🔴 **同一只窗 7.119.0 起也管「已配置的 SQL 库此刻连不上」**:`DB_BACKEND=mysql|pg`(含 `SESSION_BACKEND=auto`)boot 期开不了店 ⇒ 窗内每 250ms 重试、到窗拒启(点名后端与 host:port;
|
|
564
|
-
永久错如凭据 / DDL 权限也拖到窗尾才拒,要当场拒设 `0
|
|
590
|
+
永久错如凭据 / DDL 权限也拖到窗尾才拒,要当场拒设 `0`;唯一例外是**确定的拒启表态**,见下一条「跳版升级的旧库」,它不等窗)。k8s 清单里库与 server 同时起,所以清单把窗给到 `300000`。
|
|
565
591
|
⚠️ **裸 boot(没有显式 `DB_BACKEND`)那一形的时序也跟着变**:它的锁冲突走的不是拒启而是既有的
|
|
566
592
|
「退回内存并 warn」降级臂(`store_db_unreachable_fallback_memory`),所以第二只实例此前**当场**降级、
|
|
567
593
|
现在要**等满窗**才降级 —— 方向仍是好的(窗内拿到就用上真数据根,而不是静默丢掉持久面),但它是一次
|
|
568
594
|
真实的启动时长变化。显式 `DB_BACKEND=local` 不走降级臂(拒启),`0` 关掉两形的等待。
|
|
569
595
|
三问成文见 `docs/DEPLOY-PREREQS.md`。
|
|
596
|
+
- **跳版升级的旧库(退役表 `snapshot_manifest` / 退役列 `checkpoint.pending_steer` 还在)起服即拒 `config.schema_mismatch`,而且拒启之前库里一张表都没建**:
|
|
597
|
+
连上库后**先探这两件、再建表** —— 探针先于第一条 `CREATE`(两个 SQL 方言同一只判据),所以按拒句指路去 7.125.x 起服一次时,库还是你交给本版之前的样子,
|
|
598
|
+
不会带着本版新建的表与新列去撞 7.125.x 的对账。这是确定的拒启表态:不等 `STORE_LOCK_WAIT_MS` 的窗、当场拒,拒句点名在场的坐标与出路(先在 7.125.x 对这只库起服一次让迁移跑完,再升)。
|
|
599
|
+
探针只读(两条目录查询),新库与已经过带迁移的版本的库零命中;`DB_BACKEND=local` 没有 SQL,不探。
|
|
570
600
|
- **`RUN_STORE_STRICT_HYDRATE=on|off`(缺省 `off`)—— file 后端 run 账本的 fail-closed 启动门**:
|
|
571
601
|
`DB_BACKEND=local` 的 run 账本在 boot 时把 `<数据根>/runs/` 列一遍重建内存索引。那次 `readdir` 失败
|
|
572
602
|
(权限、挂载丢失、路径被换走)时**缺省行为是照常起动**,只留一行 `file_run_store_hydrate_unreadable`
|
|
@@ -583,7 +613,7 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
|
|
|
583
613
|
`run.json` / `events.jsonl` / claim 文件的读取。目录里本来就没有的东西(`.DS_Store` 一类杂物、
|
|
584
614
|
尚未落盘的目录)照旧跳过 —— 拒的是「读不出来」,不是「没有」。
|
|
585
615
|
⚠️ 射程**不含**「文件读得出、里面有一条坏记录」:那一形在两档下都是跳过 + 披露 + 计数(下一条)。
|
|
586
|
-
- **local 车道 JSONL 店的坏记录(7.104.0
|
|
616
|
+
- **local 车道 JSONL 店的坏记录(7.104.0 起)**:`DB_BACKEND=local` 的八本 JSONL 账(run 店三面、续跑锚、
|
|
587
617
|
发文件账本、流内审批 ask 账本、直连门 nonce 账、「本会话不再询问」豁免账、规则审批记录 / 导入票两本)重放时,
|
|
588
618
|
**中间**一条记录读不出(一行 `42`、缺本店必填键的对象、半截 JSON)不再静默:每条打一行 warn(事件名
|
|
589
619
|
`<店>_corrupt_read`,带 `path` / `line` / `form` 与本次重放的 `kept` / `dropped` / `total`),方向按店分两类 ——
|
|
@@ -607,8 +637,8 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
|
|
|
607
637
|
车道会尊重它(那条车道的定义就是"运维自己这台机、无容器边界")。所以「服务跑在 A 机、代码在 B 机」不成立
|
|
608
638
|
—— 没有任何东西把 B 的路径映到 A。要么把服务(或 `run-local`)跑在放着文件的那台机器上,要么走容器车道
|
|
609
639
|
(`e2b`/`k8s`/`local-docker`),那里的工作区是沙箱自己的,`cwd` 被忽略。
|
|
610
|
-
- **读面(
|
|
611
|
-
`{provider:<执行车道八词>, toolsOnThisHost:boolean}`。
|
|
640
|
+
- **读面(7.86.0 起)**:`GET /v1/capabilities` 的 `executionLane` 把本部署的车道广告成
|
|
641
|
+
`{provider:<执行车道八词>, toolsOnThisHost:boolean}`。7.97.0 起 `REMOTE_EXEC` 未设 ⇒
|
|
612
642
|
`provider:"in-process"`、`toolsOnThisHost:false`(本部署没有执行车道 = 无手;改前折报 `"host"`/`true`)。
|
|
613
643
|
要本机有手,显式设 `REMOTE_EXEC=host`(sema-up 产物已显式写车道)。
|
|
614
644
|
壳/客户端判「工具跑不跑在本机」**读这一位**,不要再从自己这一侧的 `REMOTE_EXEC` 猜 —— 连远端引擎时
|
|
@@ -625,7 +655,7 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
|
|
|
625
655
|
引擎把**本会话跟踪集**收敛到该消息时刻的边界;`acceptPartialRestore: true` 容忍部分恢复
|
|
626
656
|
(缺省=响亮失败带逐文件账本)。旧键 `rewindFiles` 的 capture 义=容忍 no-op(首触跟踪恒开),
|
|
627
657
|
restore 义(与 resumeAt 同发)=typed 拒并指路 `restoreFiles`(迁移显式化是 core 裁定)。
|
|
628
|
-
- **按 run 关掉捕获**(
|
|
658
|
+
- **按 run 关掉捕获**(7.104.0):提交体带 `fileHistory: "disabled"`(唯一词;坏值 400
|
|
629
659
|
`request.field_invalid`)⇒ 这条 run 的编辑照常落盘,但不做首次触碰备份、不铸回合边界,委派出去的子代继承;
|
|
630
660
|
`wiring_manifest.fileHistory` 报 `"disabled"`。同一提交再带还原键(`restoreFiles` / `rewindFilesTo` / `rewindFiles`)⇒ 受理时
|
|
631
661
|
400 `request.field_conflict`(会话与文件都不动)。
|
|
@@ -647,7 +677,7 @@ SEND_USER_FILE_SANDBOX_PUT_ENDPOINT=… # 可选:沙箱直传 PUT 的端
|
|
|
647
677
|
- **词表优先级**:内网端点 `MINIO_ENDPOINT` 优先,`S3_ENDPOINT` 是外接 S3 兼容位(内网/公网同址场景可只给它,同时充当公网面);公网端点 `S3_PUBLIC_ENDPOINT` 优先,缺席回落 `S3_ENDPOINT`。空串一律按未设处理。
|
|
648
678
|
- **🔴 云形(`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 帽,tidb 默认 6MiB,超限 PUT 413 `blob_too_large_for_sql`;帽可用 `SNAPSHOT_BLOB_SQL_MAX_BYTES` 按部署真实限值覆写,两方言生效;pg 默认无帽)。
|
|
649
679
|
- **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 便利态,会话与其他持久面不动)。
|
|
650
|
-
- **文件历史捕获总开关**(
|
|
680
|
+
- **文件历史捕获总开关**(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)。
|
|
651
681
|
- **🔴 公桶策略必须 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)。
|
|
652
682
|
- **两条轨**:ttl=0(默认)→ 匿名 GET 公桶下 `uuidv7/<name>` 不可猜 key,链接永久、可回收(删对象);0<ttl≤7 天 → 私桶 + SigV4 限时签名链接(7 天是 SigV4 物理上限,更久用 ttl=0)。
|
|
653
683
|
- **执行 lane 与租户门**:e2b/k8s 沙箱 lane 任意租户可用(沙箱文件系统=租户边界,沙箱内 `curl -T` 直传、字节不中转、凭据不进沙箱);host/ssh lane 仅单用户部署(`REQUIRE_PRINCIPAL` 未开)时挂载。
|
|
@@ -662,7 +692,7 @@ SEMA_WEB_ADMIN_URL=http://<web-admin-host>:3100 # 启动拉 GET /api/config/ef
|
|
|
662
692
|
SEMA_WEB_ADMIN_TOKEN=<SERVICE_PULL_TOKEN 的值> # 取自配置控制面主机 .env;只读拉取令牌
|
|
663
693
|
# SEMA_WEB_ADMIN_TOKEN_FILE=/path/to/token # 或:凭证文件(与上一行二选一,都设=拒启)。每条中心请求都重读 ⇒ 轮换 / 换账号改写文件即可,
|
|
664
694
|
# 中心请求面不重启;文件不可读 / 为空 = error 级 center_credential_unreadable,不当作无凭证。
|
|
665
|
-
# 内容两种类(
|
|
695
|
+
# 内容两种类(7.102.0;判别 = settings-schema parseWebAdminCredentialFile):
|
|
666
696
|
# · 裸行 = 静态 bearer(worker 拉取令牌 / 用户 JWT),不续签,行为同 7.101.0;
|
|
667
697
|
# · JSON 信封 {"kind":"user-session",accessToken,refreshToken,expiresAt,issuer}(壳登录写)
|
|
668
698
|
# = 人的会话:引擎在到期前 CENTER_CREDENTIAL_RENEW_LEAD_MS 内自己续签并原子改写同一文件
|
|
@@ -676,7 +706,7 @@ SEMA_WEB_ADMIN_TOKEN=<SERVICE_PULL_TOKEN 的值> # 取自配置控制面主机
|
|
|
676
706
|
# 走各自既有的失败臂、不带过期令牌出站;issuer 不同源 warn center_credential_issuer_mismatch 一次、不发。
|
|
677
707
|
# ⚠️ 换账号(凭证身份变化)后,只在启动期物化的面 —— 技能 / MCP / A2A / 场景 —— 仍是上一个
|
|
678
708
|
# 身份的,直到重启(`/health` 报 restartRequired,reasons 含 skills);热面(models / roles /
|
|
679
|
-
# limits / 审批名单 …)在新身份第一份候选到达那一拍换代。「换账号即降档 +
|
|
709
|
+
# limits / 审批名单 …)在新身份第一份候选到达那一拍换代。「换账号即降档 + 当场拉取」候后续单独处理(不在 7.100.0)。
|
|
680
710
|
# 用户 JWT 形(CLI 登录)⇒ skill 正文 / prompt 产物走 /api/v1/me/…;盘上 LKG 与 prompt 状态按「worker + 凭证身份」分箱。
|
|
681
711
|
SEMA_WEB_ADMIN_DRY_RUN=true # 安全灰度:只 LOG 中心配置 vs env 推导的差异,不 apply
|
|
682
712
|
# SEMA_WEB_ADMIN_LIVE_URL=http://<中心活性入口>:3100 # 7.121.0:fleet **活性组**(心跳 / 注销、服务租约 —— settings-schema FLEET_LIVE_PATHS)打的中心基址。
|
|
@@ -696,10 +726,10 @@ FLEET_ADVERTISE_ADDRESS=http://<本机可达IP>:8090 # 可选:设了才启公
|
|
|
696
726
|
# 必须是可解析的 http(s) base URL、不带 userinfo / query / fragment(与中心基址三席同一条判据)——
|
|
697
727
|
# host:port 等坏形 boot 响亮拒(拒启码 config.fleet_advertise_address_invalid;
|
|
698
728
|
# 7.121.0 起无条件解析,没接中心也拒;空串 = 未设)。公告环、成员围栏判官、/health.center 都读这一份。
|
|
699
|
-
# 🔴 7.106.0
|
|
729
|
+
# 🔴 7.106.0 起用量批报**不再**看本键:中心三件(URL + 凭证 + SEMA_WEB_ADMIN_WORKER)
|
|
700
730
|
# 配齐即每 60s 向中心 POST /api/fleet/usage(不公告的引擎也报用量;中心据此对租约单调对账)。
|
|
701
731
|
# 不想报就不配中心三件。caps `centerWiring.usageReport` 可读。
|
|
702
|
-
# FLEET_LEASE_FAIL_CLOSED=true #
|
|
732
|
+
# FLEET_LEASE_FAIL_CLOSED=true # 7.106.0 起:租约**连接级**失败(中心拒了本引擎的凭证 / 凭证文件读不出;中心
|
|
703
733
|
# 不可达 / 5xx / 超时 / 坏体;中心答 404 / 429 / 3xx 等 200·409·400·401·403 之外的
|
|
704
734
|
# 状态码同归此臂)时,计费入口答 503 `fleet.lease_unavailable`(Retry-After 5,
|
|
705
735
|
# 体 reason = auth-rejected | unreachable)而不是放行。缺省随部署姿态派生:REQUIRE_PRINCIPAL=true
|
|
@@ -732,10 +762,10 @@ FLEET_ADVERTISE_ADDRESS=http://<本机可达IP>:8090 # 可选:设了才启公
|
|
|
732
762
|
# 重启后不记得,在第一次续租答复之前会接单(中心那时再拒)。
|
|
733
763
|
```
|
|
734
764
|
- 中心**空/未发布** → `applyEffective` 回落 env + 内建 teams 并 warn `config_center_unpublished`,**不影响在跑的服务**(接了也安全)。
|
|
735
|
-
- **逐条容错**(
|
|
765
|
+
- **逐条容错**(7.101.0 起):目录里一条条目不合形(如 `contextWindow:"abc"`)⇒ **只丢那一条**(warn `config_entry_dropped`,
|
|
736
766
|
点名坐标 `models.<下标>`、闭集原因词 `reasonCode` 与字段诊断;7.123.0 起同一条也在 `GET /v1/models` 的 `droppedEntries` 里自报),其余条目照用;refresh 拍 / 迟到的 boot 拉取**不再因此整拒候选**(此前一条坏条目让整份目录停在
|
|
737
767
|
上一代 —— 上一代若还是 env,env 模型就一直服务)。颗粒更粗的失效(整个域读不懂、拼错键、坏文件)仍整拒候选、保 LKG。
|
|
738
|
-
- **没有 `MODEL_ID` 也能起**(
|
|
768
|
+
- **没有 `MODEL_ID` 也能起**(7.101.0 起):目录(`config.d/models.json` 或中心)有可用条目时 env 可以不写 `MODEL_ID` —— 缺省模型取
|
|
739
769
|
目录缺省。判据在目录应用**之后**:env `MODEL_ID` 或目录缺省任一在场即过;两者皆无 ⇒ 拒启(码 `config.default_model_missing`,指路二选一)。
|
|
740
770
|
中心拉取超出 `CONFIG_BOOT_FETCH_BUDGET_MS` 又没有 LKG 时,boot 那一刻没有目录 ⇒ 没有 `MODEL_ID` 兜底就拒启(调大预算让 boot 等目录,
|
|
741
771
|
或写一个 `MODEL_ID` 兜底)。
|
|
@@ -749,14 +779,14 @@ FLEET_ADVERTISE_ADDRESS=http://<本机可达IP>:8090 # 可选:设了才启公
|
|
|
749
779
|
| pricing / keys(env-名引用)/ prompts / teams | **热** | 值换代即生效;cost 族限额座(rate limit / cost quota)同热(限额=纯比较参数,窗内累计不动;窗长换代=记账周期重开) |
|
|
750
780
|
| tier 变更(档位组 / 档位绑定) | **热**(下一 refresh 拍) | 与 models 同一条换代腿:tier 表随模型面在 commit 前原子换进全部 boot Runner,下一任务按新档位路由,`/health` 不报 `restartRequired`(7.100.0 起 `models-tiers` 重启理由退役——它在生产上从未发出过) |
|
|
751
781
|
| plugins(插件引用) | 重启生效 | 插件只在启动期物化;改了之后 `/health` 报 `restartRequired`,`restart.reasons` 含 `skills`(7.100.0 前这一改动**不报**,要等一次无关的重启) |
|
|
752
|
-
| skills / mcp / a2a / scenarios(技能 / MCP / A2A / 场景) | 重启生效 | 只在启动期物化;中心改了 ⇒ `/health` 报 `restartRequired`,`restart.reasons` 各含其名。**换账号**(凭证身份变化)同理:这些面仍是上一身份的直到重启(
|
|
782
|
+
| skills / mcp / a2a / scenarios(技能 / MCP / A2A / 场景) | 重启生效 | 只在启动期物化;中心改了 ⇒ `/health` 报 `restartRequired`,`restart.reasons` 各含其名。**换账号**(凭证身份变化)同理:这些面仍是上一身份的直到重启(候后续单独处理) |
|
|
753
783
|
| 审批名单(`governance.approvalRequire`) | **热**(下一 refresh 拍) | 7.100.0 起:下一个任务调名单里的工具即停车等审批;撤键回 env 底(`APPROVAL_REQUIRE`)。**没有 checkpoint 店**的部署(`DURABLE_APPROVAL` 关 / 后端无 checkpoint 面)发非空名单 ⇒ 审批组**整组拒**(旧名单继续服务、`configAppliedVersion` 停旧代、拒因见日志 `web_admin_approval_require_invalid` 与诊断端点 `configApply.groups.approvalGate`)—— 与启动期「名单已配却无 durable 门 ⇒ 拒启」同一条判据。此前 `/health` 报 `restartRequired`(`runtime-gates`,该理由退役) |
|
|
754
784
|
| 记忆整理阀门判决 + 驱动席(`MEMORY_CONSOLIDATION_DRIVER=on`,或未设而出厂缺省按目录派生 —— 7.104.0 起) | 重启生效 | 阀门开关与整理跑用的模型都在启动期定一次;中心改了 roles / 档位 / 目录条目后,**判决会翻**(派生开 ↔ 关,含只把主模型挪到整理座那一条上)或开着而座漂了 ⇒ `/health` 报 `restartRequired`,`restart.reasons` 含 `memory-consolidation`(7.100.0 新理由;7.104.0 起派生关的那台同样报) |
|
|
755
785
|
|
|
756
786
|
生效与否的观测口=`/health` 世代账键(`configTargetVersion`/`configAppliedVersion`+两 ordinal+`configApplyStaleMs`,见 API 表 `/health` 行)——target≠applied 持续=「改了没生效」的机读信号。
|
|
757
|
-
- **`run-local`(一次性本机 CLI)上三张审批名单同样执法**(7.101.0 起,TTY 问 / 无 TTY 拒):`APPROVAL_DENY` 当场拒;`APPROVAL_REQUIRE` / `APPROVAL_NEVER_AUTO` 命中 ⇒ stdin 是 TTY 就 `y/N` 问人,否则当场拒并在 stderr 说出路(7.116.0 起按**这一问的类**说,不再对每一问打同一句全量旋钮清单:没人表态的服务器工具调用 ⇒ 指 `APPROVAL_GRANT`;名单产的问 ⇒ 点名三张名单键;部署治理盖了出身的问 ⇒ 点治理旋钮;引擎自己的问 ⇒ 不点旋钮、只说去终端答;引擎没有说明这一问的类 ⇒ 说明这一点、只给终端出路);`APPROVAL_AUTO_BUDGET` 照常生效(never-auto 不吃预算);TTY 问句带上这次调用的参数,人答 N 而停下的 run 退 1(停因 `haltedOnUserRejection`)。**无 TTY 的拒记在部署政策名下**(7.110.0
|
|
787
|
+
- **`run-local`(一次性本机 CLI)上三张审批名单同样执法**(7.101.0 起,TTY 问 / 无 TTY 拒):`APPROVAL_DENY` 当场拒;`APPROVAL_REQUIRE` / `APPROVAL_NEVER_AUTO` 命中 ⇒ stdin 是 TTY 就 `y/N` 问人,否则当场拒并在 stderr 说出路(7.116.0 起按**这一问的类**说,不再对每一问打同一句全量旋钮清单:没人表态的服务器工具调用 ⇒ 指 `APPROVAL_GRANT`;名单产的问 ⇒ 点名三张名单键;部署治理盖了出身的问 ⇒ 点治理旋钮;引擎自己的问 ⇒ 不点旋钮、只说去终端答;引擎没有说明这一问的类 ⇒ 说明这一点、只给终端出路);`APPROVAL_AUTO_BUDGET` 照常生效(never-auto 不吃预算);TTY 问句带上这次调用的参数,人答 N 而停下的 run 退 1(停因 `haltedOnUserRejection`)。**无 TTY 的拒记在部署政策名下**(7.110.0 起;此前记成人拒):该次调用 `tool_end.gate.settlement` = `{kind:"policy_refused", who:{party:"policy"}}`(7.113.0 起;7.110.x–7.112.x 为 `party:"none"`),模型读到的是引擎的策略句(「部署政策拒了、此处没有交互审批,别重试,先做不需要审批的部分」)加一句拒因,而不是「用户不想继续,停下来等他」—— 不停批,模型接着做别的部分;那只要人批的动作没执行 ⇒ run 照样退 1,非 `--json` 形 stderr 停因行是 `task incomplete (approval unanswered): …`。参数展示不出而不问就拒的那一形(TTY 在场也一样)同此。这条腿没有 checkpoint 店也**不**拒启 —— 它有一只当场作答的席,名单的 ask 不需要 park(上表「无 checkpoint 店 ⇒ 整组拒」只说服务腿)。此前三张名单在这条腿上无声不生效。
|
|
758
788
|
- **仅 `/v1/tasks`(同步)+ `/v1/runs`(异步)**——`/v1/tasks/stream` 不支持(多次尝试非单流,400)。与 `verify` **互斥**(同时给 → 400)。
|
|
759
|
-
- 成本上界 = 任务的 `maxCostUsd`(防冷重跑税)。**三条 core 警示**:① 每档**冷重跑**重付输入成本(便宜档常过才划算);② 门收到**未脱敏**输出(自定义门转发外部 verifier 要脱敏);③ **写工具会跑 N 次**——**只用于只读/幂等任务**(每次升级整任务重跑)。开放式任务(找全 bug/文笔)没有可判定 oracle
|
|
789
|
+
- 成本上界 = 任务的 `maxCostUsd`(防冷重跑税)。**三条 core 警示**:① 每档**冷重跑**重付输入成本(便宜档常过才划算);② 门收到**未脱敏**输出(自定义门转发外部 verifier 要脱敏);③ **写工具会跑 N 次**——**只用于只读/幂等任务**(每次升级整任务重跑)。开放式任务(找全 bug/文笔)没有可判定 oracle、级联会空转,那种让模型用 Agent 工具委派几路子代再综合(广度交叉),而非级联(深度阶梯)。
|
|
760
790
|
|
|
761
791
|
**可选 — AskUserQuestion 的裁量窗(问活人的三个帽子)**
|
|
762
792
|
```bash
|
|
@@ -803,8 +833,8 @@ QUESTION_TTL_MS=300000
|
|
|
803
833
|
STREAM_APPROVAL_ENABLED=true
|
|
804
834
|
# 活卡窗(毫秒)。**辖域=所有腿**:协议上不上场都按它计窗——无 checkpoint 店 / 关了协议的部署同样
|
|
805
835
|
# 生效(此前那几形上本键一个字节都不生效、窗恒 5min,且没有任何一行说出来)。
|
|
806
|
-
# 🔴 **缺省值是 posture
|
|
807
|
-
# **86400000(24 小时)**,多租户形 = 300000(5min)。**
|
|
836
|
+
# 🔴 **缺省值是 posture 派生的,不是一个数**:单机 turnkey(`REQUIRE_PRINCIPAL` 未设)=
|
|
837
|
+
# **86400000(24 小时)**,多租户形 = 300000(5min)。**7.81.0 起这根旋钮在单机上是要读的那一根**:
|
|
808
838
|
# local 后端自此有持久 ask 账、流内协议上场,受门 ask 先发一张活卡、**窗满才转 park**(升级前是铸造
|
|
809
839
|
# 时刻立刻 park)⇒ 不配它的**无人值守**单机最多挂一天才落 park。无人值守机器请显式给几秒;
|
|
810
840
|
# 有人值守的单机正是那一天窗口的受益者。0 = 运维显式关窗 ⇒ **一张卡都不发**、恒走无人值守终局(不是"还原",还原用上面那个键;
|
|
@@ -867,7 +897,7 @@ UNREACHED_ASK_TTL_MS=3600000
|
|
|
867
897
|
默认 5min),所以把窗改大等于把断连场景的最坏无人区拉长,改小前先看上面的跨旋钮不变量。half-open 的
|
|
868
898
|
主动探测(写水位判死)在攻关排期。
|
|
869
899
|
- **无 durable 前置的部署(四合取不满足:无 backend / 无 checkpoint 能力 / 显式关协议 / 无活卡腿;
|
|
870
|
-
⚠️ **
|
|
900
|
+
⚠️ **7.81.0 起 `local` backend 不再是其中一项** —— 它有了文件形 ask 账,配齐 `DURABLE_APPROVAL=true`
|
|
871
901
|
+ checkpoint 店的单机部署上协议**照常上场**,`GET /v1/capabilities` 的 `streamApproval` 报 true)**:流内协议整个不上场
|
|
872
902
|
(启动一行 `stream_approval_disabled` 点名缺哪项),ask 走旧活卡腿——断连场景的最坏结局是活卡 TTL
|
|
873
903
|
窗满**自动 deny(fail-closed)后 run 继续**,不会死锁在等一张没人能回的卡上;代价是断连期间用户的
|
|
@@ -878,13 +908,14 @@ UNREACHED_ASK_TTL_MS=3600000
|
|
|
878
908
|
(卡挂过窗、人才按 Yes)不能走 `POST /v1/tool-approvals/:id/respond`(没有持久 ask 行 ⇒ 恒 404
|
|
879
909
|
`tool_approval.not_pending`、不带 `cause`),要走 `POST /v1/approvals/:sessionId/decide`
|
|
880
910
|
——前提是配了 `DURABLE_APPROVAL=true`(local 车道有文件形 checkpoint 店),否则窗到期即 fail-closed 拒。
|
|
881
|
-
⚠️ **上面这段「没有持久 ask 行 ⇒ 恒 404」自
|
|
911
|
+
⚠️ **上面这段「没有持久 ask 行 ⇒ 恒 404」自 7.81.0 起不再适用于 `DB_BACKEND=local` 本身**:local 的
|
|
882
912
|
ask 账已是文件形,配齐 park 设施的单机部署上迟到决议走 `respond` 的赎回席是通的;本段现在只说
|
|
883
913
|
那几条**真的**不上场的部署形(无 backend / 无 checkpoint 能力 / 显式关协议)。
|
|
884
914
|
默认值(300s + 60s vs 7d)自然满足,只有显式改坏才会撞上。
|
|
885
915
|
- **调参方向**:想让人有更长时间点审批卡 → 调大 `STREAM_ASK_WINDOW_MS`;卡太多刷屏 → 调小两个 `ADMIT_MAX_*`
|
|
886
916
|
(代价是超限的 ask 走 park,要有人去审批队列捞);库压大 → 调小 `STREAM_APPROVAL_RECONCILE_BATCH`
|
|
887
917
|
(代价是收敛变慢,靠队列轮转保证下轮接着扫)。
|
|
918
|
+
- **停驻行最长活多久(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`(两店同辖)。
|
|
888
919
|
|
|
889
920
|
**无人值守政策(`UNATTENDED_APPROVAL_POLICY`,7.34.0 起)**
|
|
890
921
|
|
|
@@ -934,7 +965,7 @@ UNATTENDED_APPROVAL_POLICY=park
|
|
|
934
965
|
实例上(local 车道换机 / 换数据根)⇒ 名单的问再问一次 —— 方向是多问一次,不是静默放行。名单靠别名命中的调用(嵌入方给工具挂了 `Bash` 别名)
|
|
935
966
|
按调用的 wire 名记、按 wire 名读;一张卡上同时有名单的问与别人的问(人写的 ask 规则、强制位、读会话目录外 ……)⇒ 那张卡的「本会话放行」不记。
|
|
936
967
|
|
|
937
|
-
**无人值守腿:`default` 模式 + 按类作答的审批席(7.109.0
|
|
968
|
+
**无人值守腿:`default` 模式 + 按类作答的审批席(7.109.0 起)—— 没有旋钮**
|
|
938
969
|
|
|
939
970
|
- 管的是本服务**自己铸** spec、**没有调用方**能在请求上说模式的腿:leader 的 worker、leader 的修复腿(集成沙箱修复 / 模块门修复 /
|
|
940
971
|
单代理修复环)、`sema` 一次性 CLI(run-local),以及 leader worker 的 park 经 `/decide` 续跑时重挂的同一条腿。`POST /v1/tasks|runs`
|
|
@@ -943,7 +974,7 @@ UNATTENDED_APPROVAL_POLICY=park
|
|
|
943
974
|
`ordinary` 的问 —— 模式写门对普通写的问、形状层对非只读 shell 的问(写与命令照常落地,与 7.105.0 同)。其余每一类都不答:
|
|
944
975
|
写保护表目标(`write_protection`)、shell 读会话目录外(`probe`)、MCP / A2A 工具标记、transcript 写、规则店读不出、共享制品读、
|
|
945
976
|
plan 的写问(`mode`),以及运维 / hook / 规则 / 组织为人提的问(审批名单、治理层、`APPROVAL_NEVER_AUTO` …)。不答的问:
|
|
946
|
-
- **有席的腿交给席**:run-local 在终端里跑 ⇒ 问你 y/N;没有 TTY ⇒ 当场拒(stderr 一句;门记录记在部署政策名下 `policy_refused` / `who.party:"policy"`(7.113.0 起;7.110.x–7.112.x 为 `"none"`),7.110.0
|
|
977
|
+
- **有席的腿交给席**:run-local 在终端里跑 ⇒ 问你 y/N;没有 TTY ⇒ 当场拒(stderr 一句;门记录记在部署政策名下 `policy_refused` / `who.party:"policy"`(7.113.0 起;7.110.x–7.112.x 为 `"none"`),7.110.0 起)。
|
|
947
978
|
- **无席的腿**(leader 各腿):配了 durable 审批块的部署 ⇒ park,等人经 `/decide` 答(与 7.105.0 同形);没配 ⇒ 当场拒(fail-closed)。
|
|
948
979
|
- 答集是常量(只 `ordinary`,core 推荐形),**不是旋钮**。7.106.0–7.108.0 的 `UNATTENDED_PERMISSION_MODE` 已退役:**设了即拒启**
|
|
949
980
|
(`config.env_key_retired`,任何值都拒);从 `.env` 里删掉它。要让机器多答某一类问,先立案,不开旋钮。
|
|
@@ -977,7 +1008,7 @@ APPROVAL_GRANT='mcp__figma__*,a2a__planner__*,Bash(git:*)' # 逗号分隔的 a
|
|
|
977
1008
|
= 规则原样(空表 = 未设);`GET /v1/config/catalog` 一行。客户端能力面(`/v1/capabilities`)不报它。
|
|
978
1009
|
- v1 只有 env 一个写入通道(web-admin 与 settings 契约没有这个键)。
|
|
979
1010
|
|
|
980
|
-
**可选 — 写保护名表(
|
|
1011
|
+
**可选 — 写保护名表(缺席=引擎缺省表在岗)**
|
|
981
1012
|
|
|
982
1013
|
```bash
|
|
983
1014
|
WRITE_PROTECTED_EXTRA=.corp-deploy-key,team/secrets # 加法:[...引擎缺省表, 这两行]
|
|
@@ -1029,7 +1060,7 @@ READ_DENY_PATTERNS='[{"pattern":"**/secrets/**"},{"pattern":".ssh","caseSensitiv
|
|
|
1029
1060
|
退出。这些条目**叠在** core 内建 deny 集之上(内建先、追加后,重复折叠),不是替换。
|
|
1030
1061
|
- **坏形拒启,不静默折默认**:坏 JSON / 非数组 / 缺 `pattern` / 编不过 core 的 `compileReadDeny`
|
|
1031
1062
|
一律启动期点名拒——一个手滑的 deny 集比没有 deny 集更糟(以为挡了其实没挡)。
|
|
1032
|
-
- **缺席的语义按 posture 分两形(7.65.0
|
|
1063
|
+
- **缺席的语义按 posture 分两形(7.65.0 起)**:
|
|
1033
1064
|
· **单用户 turnkey**(`REQUIRE_PRINCIPAL` 未设 —— 一台机器一个可信主人,CC 的信任模型):`READ_FACE`
|
|
1034
1065
|
缺席 ⇒ 铸 **`open`**,工作区外的读默认放行。**这是 7.65.0 的行为变更**:同一台机器升级前后
|
|
1035
1066
|
「读工作区外的文件」从被拒变成放行。**要钉回收敛姿态:显式 `READ_FACE=roots`。**
|
|
@@ -1041,7 +1072,7 @@ READ_DENY_PATTERNS='[{"pattern":"**/secrets/**"},{"pattern":".ssh","caseSensitiv
|
|
|
1041
1072
|
零卡执行。**要老姿态就显式写** `READ_DENY_BUILTIN_TIERS=credentials,browser,wallet,agent-config`
|
|
1042
1073
|
(运维面全文见 `docs/DEPLOY-PREREQS.md` 的 core 7.22.0 段)。`open` 与这张表仍是**两个并列机制**:
|
|
1043
1074
|
`open` 放开的是「工作区外」那道围栏,内建表判的是名字。
|
|
1044
|
-
🔴 **7.106.0(
|
|
1075
|
+
🔴 **7.106.0(core 7.34.0 R-2)再翻一次**:未设 = **`credentials` + 家目录锚定的 `agent-config` 两档在岗**(`browser` / `wallet` /
|
|
1045
1076
|
`shell-history` 仍是选装;显式 `READ_DENY_BUILTIN_TIERS=none` 仍是空姿态)⇒ 未配置的机器升级后**读不到**根内的 `.npmrc` / `.ssh` / `.aws` 这类名字
|
|
1046
1077
|
(每种模式都拒、不出卡)。agent-config 的十五行改写成 `~/…`(`~/.claude/settings.json`、`~/.sema/settings.json` …)、只在执行环境的 home 正下方
|
|
1047
1078
|
命中 —— 工作区里的 `.claude/settings.json` 不再是内建行;🔴 `READ_DENY_BUILTIN_EXCLUDE` 里写旧行名(`.claude/settings.json`)的部署**起服拒**
|
|
@@ -1082,7 +1113,7 @@ READ_DENY_PATTERNS='[{"pattern":"**/secrets/**"},{"pattern":".ssh","caseSensitiv
|
|
|
1082
1113
|
⚠️ 本地 `config.d/` 车道今天**接不上**这一域(registry-core 的可移植域表里没有它),只有
|
|
1083
1114
|
`CONFIG_PROVIDER=remote` 的部署走得通。
|
|
1084
1115
|
|
|
1085
|
-
**可选 — 任务层「掀开写围栏」的运维天花板(`TASK_WRITE_FACE_ESCALATION`,7.95.0 起
|
|
1116
|
+
**可选 — 任务层「掀开写围栏」的运维天花板(`TASK_WRITE_FACE_ESCALATION`,7.95.0 起)**
|
|
1086
1117
|
|
|
1087
1118
|
```bash
|
|
1088
1119
|
TASK_WRITE_FACE_ESCALATION=allow # 词表 allow|refuse;坏词启动期响亮拒;空串按未设
|
|
@@ -1112,7 +1143,7 @@ TASK_WRITE_FACE_ESCALATION=allow # 词表 allow|refuse;坏词启动期响亮
|
|
|
1112
1143
|
**发起者自己的设备**上,归属由 `device_session` 绑定行 + 准入链的 owner 谓词验过 —— host 闸防的
|
|
1113
1144
|
跨租户宿主穿越在那儿**结构性不存在**(成文裁定,与 `settings.env` / `cwd` 两键在该车道上的既有臂同源),
|
|
1114
1145
|
也叠加「device 车道不额外加一道审批」这条既定纪律。⚠️ 运维的**显式** `refuse` 在 device 上照样恒赢(旋钮不挂车道派生腿)。
|
|
1115
|
-
- **「托管形」怎么判**(判据单点 `src/hosted-posture.ts`;7.99.0 起
|
|
1146
|
+
- **「托管形」怎么判**(判据单点 `src/hosted-posture.ts`;7.99.0 起):
|
|
1116
1147
|
**托管 ⇔ `REQUIRE_PRINCIPAL=true`**,只认运维自己的声明,零推断。
|
|
1117
1148
|
🔴 **签发方配置不算证据**:`PRINCIPAL_JWT_PUBKEYS` / `_ISS` / `_AUD` 与 `AUTH_BRIDGE_ISSUER` 在不在场都不改变判定
|
|
1118
1149
|
(7.95.0–7.98.x 曾把它们当证据,并对「配了签发方却没设 `REQUIRE_PRINCIPAL`」的机器拒启;那条拒启随之删除)。
|
|
@@ -1120,7 +1151,7 @@ TASK_WRITE_FACE_ESCALATION=allow # 词表 allow|refuse;坏词启动期响亮
|
|
|
1120
1151
|
⚠️ 反面是**全量召回缺口**:忘设 `REQUIRE_PRINCIPAL` 的团队机器按单用户运行、本门放行 —— 多租机器必须自己设它,
|
|
1121
1152
|
后果与被排除的候选(`OPERATOR_PRINCIPALS`、`SERVICE_AUTH_TOKENS` 条目数、`DB_BACKEND`、`BIND_HOST`)逐条写在
|
|
1122
1153
|
`docs/DEPLOY-PREREQS.md`。
|
|
1123
|
-
- ⚰️ **提交体 `approverPosture` 7.106.0 退役**(
|
|
1154
|
+
- ⚰️ **提交体 `approverPosture` 7.106.0 退役**(core 7.34.0 的单键权限模式):7.93.0–7.105.0 上它是一句宿主姿态声明、托管形上被忽略并留声;
|
|
1124
1155
|
core 7.34.0 起按名拒这个键,本仓整键删 —— fresh 提交体仍带它 ⇒ 400 `request.body_shape`(`unknownKeys:["approverPosture"]`);续跑腿重放不过受理门、
|
|
1125
1156
|
盘上体里的该键零读点(惰性),parked 任务不因它砖死。能力位 `taskApproverPosture` 同批删。
|
|
1126
1157
|
- **续跑腿**:一条在托管形机器上带着 `bypassPermissions` 的 parked 任务,赎回时被同一道门挡住,
|
|
@@ -1280,7 +1311,7 @@ ENGINE_ORPHAN_LINGER_MS=2000 # 可选,默认 2s(父没了之后的窗;见上一
|
|
|
1280
1311
|
|
|
1281
1312
|
- **默认 `false` = 不自退**,存量部署逐字零变化。一台 k8s 上的 replica 半夜没人用是**正常低谷**,
|
|
1282
1313
|
不是「该退了」;自退只对**壳自 spawn 的本机引擎**有意义,所以由壳在 spawn 时显式打开。
|
|
1283
|
-
🔴 自退腿在「本键为真 **或** `SEMA_PARENT_PID` 在场」时装配(
|
|
1314
|
+
🔴 自退腿在「本键为真 **或** `SEMA_PARENT_PID` 在场」时装配(两件并成一件)。本键关着而
|
|
1284
1315
|
`SEMA_PARENT_PID` 设了 ⇒ 父还活着期间**永不自退**(窗 = ∞,与以前逐字同),父没了才用孤儿窗。
|
|
1285
1316
|
两键都缺席 ⇒ 整件不装配,零定时器。
|
|
1286
1317
|
- **自退判据 = 三合取,且必须连续满**「当前窗」(父还活着 = `ENGINE_LINGER_MS`;父没了 =
|
|
@@ -1314,7 +1345,7 @@ ENGINE_ORPHAN_LINGER_MS=2000 # 可选,默认 2s(父没了之后的窗;见上一
|
|
|
1314
1345
|
- 🔴 **已知收窄(成文,现版不收)**:**parked-待赎回** 的 run 不在判据里,只看 running。数 parked 要发
|
|
1315
1346
|
SQL,而这是一只每 15s 无限期跑下去的定时器 —— 不值得为它引入周期性 SQL 轮询。parked 行是 **durable**
|
|
1316
1347
|
的,引擎退掉不丢:下次起来(壳的下一次 spawn)仍可赎回,伤害面是**赎回被推迟**而不是丢失。
|
|
1317
|
-
- 与 `SEMA_PARENT_PID`
|
|
1348
|
+
- 与 `SEMA_PARENT_PID` 的关系:**不是两件**,是**一件 + 一个输入** —— 那个键决定「父没了
|
|
1318
1349
|
算不算数」以及此后用哪个窗,自退判据只有本节这一条。
|
|
1319
1350
|
|
|
1320
1351
|
**布尔旋钮的取值与极性(运维必读)**
|
|
@@ -1362,9 +1393,13 @@ invalid_using_default` 警告 + 退回缺省值」这条臂随词表放宽一并
|
|
|
1362
1393
|
`ai-agent-code-lsp` 模板;⚠️ 该模板**只烤 TS/JS**——Python/Go 烤在 `dev-base`,dart/kotlin/java/swift
|
|
1363
1394
|
烤在 `dev-mobile`,四模板物理独立,`E2B_TEMPLATE` 选错则未烤语言的 LSP **静默退化到 grep/read**;
|
|
1364
1395
|
对照表见 ARCHITECTURE §7,真源=`src/lsp/e2b-bridge.ts`),host 腿缺省**开**(只要 PATH 上有 language server,没有就优雅退回 grep/read)。
|
|
1365
|
-
|
|
1396
|
+
拆分后沙箱腿是 `LSP_ENABLED`(opt-in)—— **本版在 e2b / k8s 上不挂任何东西**(见下段「LSP 的车道射程」);host 腿归 `LSP_HOST_ENABLED`(opt-out)。
|
|
1366
1397
|
🪦 `LSP_ENABLED=false`(拆分前唯一的 host 腿逃生舱)自 3.0.0 起是墓碑:拒启并指路 `LSP_HOST_ENABLED`。
|
|
1367
|
-
`LSP_ENABLED=true`
|
|
1398
|
+
`LSP_ENABLED=true` 照常起服,但本版只换来一行 `lsp_not_mounted`(沙箱两车道不挂 LSP,见下段);绑定读在这两条车道实现之日它重新生效。
|
|
1399
|
+
|
|
1400
|
+
**LSP 的车道射程(7.127.0 起,引擎 7.50.0)**:LSP 会话只读执行环境「绑定持有」的那只文件(判过的对象就是读到的对象)。host 车道实现了绑定读,LSP 照常(`LSP_HOST_ENABLED`);
|
|
1401
|
+
沙箱两车道(e2b / k8s)的执行环境本版不实现绑定读 —— 挂上就是每个文件同步都被拒 ⇒ **这两条车道不挂 LSP**:开着 `LSP_ENABLED=true` 照常起服,起服打一行
|
|
1402
|
+
`lsp_not_mounted`(带原因),模型名册里没有 LSP 工具、`wiring_manifest` 上没有 `lsp` 段,代码导航回到 grep / read。ssh / adb / device / local-docker 本来就不挂。
|
|
1368
1403
|
|
|
1369
1404
|
**host 腿语言服务器拿到的环境 + 点名放行 `LSP_ENV_ALLOW`(7.101.0 起)**:host 腿的语言服务器是本机子进程,它拿到的环境
|
|
1370
1405
|
= 服务进程环境**剥掉凭据形的键**(与模型可驱动的 host shell 同一只剥除:配置目录 `secret` 型键按名剥 ∘ 引擎按键名形状剥
|
|
@@ -1387,15 +1422,15 @@ invalid_using_default` 警告 + 退回缺省值」这条臂随词表放宽一并
|
|
|
1387
1422
|
出路**按用途分**:git 凭据走 git 配置文件 / credential helper(隔离全局配置的两条 git 腿 —— project-memory 读仓、center-plugins 克隆 ——
|
|
1388
1423
|
另见 `docs/DEPLOY-PREREQS.md` 7.103.2 那一节的「按腿分」);带口令的出网代理走不带口令的形(`~/.gitconfig` 里只带用户名的 `http.proxy` + credential helper,见
|
|
1389
1424
|
`docs/DEPLOY-PREREQS.md`「带口令的出网代理」);语言服务器 / bake-runner 走各自的名单 `LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW`;沙箱车道走
|
|
1390
|
-
`E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV`。要不要给 host shell 加部署级名单属于能力面变更(安全轴),不在文档里顺手给。
|
|
1425
|
+
`E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV` / `K8S_SANDBOX_ENV`。要不要给 host shell 加部署级名单属于能力面变更(安全轴),不在文档里顺手给。
|
|
1391
1426
|
|
|
1392
|
-
**请求级 `inheritEnv`(
|
|
1427
|
+
**请求级 `inheritEnv`(7.106.0 起)—— 调用方声明,不是部署旋钮**:提交体顶层键 `inheritEnv:"all"` 让**这一条请求**的模型驱动 host shell
|
|
1393
1428
|
继承整份用户环境(CC 交互形,凭据形变量在内);`"scrub"` / 缺席 = 上面的缺省剥除。🔴 **`"all"` 只在单用户部署兑现**:`REQUIRE_PRINCIPAL=true`
|
|
1394
1429
|
⇒ 422 `request.host_posture_refused`(射程与写容纳面那条腿同一只谓词,`TASK_WRITE_FACE_ESCALATION` 不管它);非 host 车道带键 ⇒ 400。部署方
|
|
1395
1430
|
**没有**旋钮能替调用方打开它,也没有旋钮能在单用户部署上关掉它(单用户部署的用户把他自己的环境交给他自己驱动的 shell);起服一行 info
|
|
1396
1431
|
`task_inherit_env_honored {honored}` 告诉你这台机器认不认。上面「部署级名单不给模型驱动 shell」那条不变 —— 两件事是两条轴(部署名单 vs 调用方 × 单用户射程)。
|
|
1397
1432
|
|
|
1398
|
-
**形状规则的精确名豁免(7.101.0 起,core 7.31.0
|
|
1433
|
+
**形状规则的精确名豁免(7.101.0 起,core 7.31.0)**:十个预算名(`MAX_NEW_TOKENS` `MAX_COMPLETION_TOKENS` `AIDER_MAP_TOKENS`
|
|
1399
1434
|
`AIDER_MAX_CHAT_HISTORY_TOKENS` `MAX_BATCH_PREFILL_TOKENS` `MAX_WAITING_TOKENS` `MAX_TOP_N_TOKENS` `RATE_LIMIT_TOKENS` `PROMPT_TOKENS`
|
|
1400
1435
|
`CONTEXT_TOKENS`)与两个开关(`DB_FOREIGN_KEYS` `JSON_SORT_KEYS`)不再被剥,三条子进程腿同时生效(7.100.0 上这五个 7.30.1 误剥的名
|
|
1401
1436
|
`MAX_NEW_TOKENS` / `MAX_COMPLETION_TOKENS` / `MAX_BATCH_PREFILL_TOKENS` / `DB_FOREIGN_KEYS` / `JSON_SORT_KEYS` 需要点名放行,现在不用)。
|
|
@@ -1403,36 +1438,37 @@ invalid_using_default` 警告 + 退回缺省值」这条臂随词表放宽一并
|
|
|
1403
1438
|
`config.secret_env_scrubbed` 只在引擎**自己**剥的子进程上发;本服务六条宿主腿(host shell / project-memory / center-plugins / leader 三条 git 腿 / 语言服务器 /
|
|
1404
1439
|
bake-runner)都交本服务剥好的环境,所以运维面看的是上面这行
|
|
1405
1440
|
`secret_env_scrubbed`,不是那只通告码。这只通告码**每进程一次**、引擎里几处剥除共用一闩(工具 shell / 语言服务器 / 7.103.2 起还有 `runSpec` 的
|
|
1406
|
-
oracle 门子进程,core 7.33.8
|
|
1441
|
+
oracle 门子进程,core 7.33.8):oracle 门若先于工具 shell 剥,通告只进 oracle 门那一处(`RunSpecOptions.onNotice`,没接就进 console),接在执行环境上的
|
|
1407
1442
|
结构化通告收不到。本服务不调 `runSpec`,不受影响;嵌入引擎、自接 `runSpec` 的宿主给它交同一只 `onNotice`。
|
|
1408
|
-
**OTLP 头变量一族(7.103.2 起,core 7.33.7
|
|
1443
|
+
**OTLP 头变量一族(7.103.2 起,core 7.33.7)**:`OTEL_EXPORTER_OTLP_HEADERS` 与按信号分的 `_TRACES_` / `_METRICS_` / `_LOGS_` / `_PROFILES_HEADERS`、
|
|
1409
1444
|
旧拼法 `_SPAN_` / `_METRIC_HEADERS` 按名剥(值盲);`OTEL_EXPORTER_OTLP_HEADERS` 此前已由本服务的配置目录按名剥,另外六名此前原样交出。语言服务器 /
|
|
1410
1445
|
bake-runner 要用 ⇒ `LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW` 点名放回。
|
|
1411
1446
|
|
|
1412
1447
|
**「点名放行」只有一种意思**:本服务里凡是「一张名单点到哪些环境变量键」的地方(这里的 `LSP_ENV_ALLOW`、沙箱车道的
|
|
1413
|
-
`E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV`(见下表),与**嵌入方**构造 host shell 时的 `inheritEnv:[names]` —— 后者不是部署旋钮,见上一段)用的是同一只谓词 ——
|
|
1414
|
-
点到的键从服务进程环境原样交出去(空串值照交)
|
|
1415
|
-
`E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV`)另走同一只名单解析:形错拒启点名第几段、不回显原值,点名任一只名单型旋钮拒启,
|
|
1448
|
+
`E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV` / `K8S_SANDBOX_ENV`(见下表),与**嵌入方**构造 host shell 时的 `inheritEnv:[names]` —— 后者不是部署旋钮,见上一段)用的是同一只谓词 ——
|
|
1449
|
+
点到的键从服务进程环境原样交出去(空串值照交),密钥形也照交;源里没有的名什么也不产生。四只名单型旋钮(`LSP_ENV_ALLOW` /
|
|
1450
|
+
`E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV` / `K8S_SANDBOX_ENV`)另走同一只名单解析:形错拒启点名第几段、不回显原值,点名任一只名单型旋钮拒启,
|
|
1416
1451
|
本进程环境里没有的名按序号一行 `config_env_entry_rejected`,旋钮原文按凭据类对待(配置目录只回在场位、不进任何子进程)。
|
|
1417
1452
|
几处只在**没点到的键**上不同:语言服务器在缺省剥除的结果之上放回点名键(没点到的照剥,非密钥键照常在);host shell 的
|
|
1418
|
-
`inheritEnv:[names]`
|
|
1453
|
+
`inheritEnv:[names]` 与三只沙箱名单是 hermetic 的(没点到的一律不交,与引擎 `NodeExecutionEnv` 同名选项同义)。
|
|
1419
1454
|
两处都不向对方看齐:host shell 改成「剥后放回」等于在模型可驱动的 shell 上放宽,语言服务器改成 hermetic 会让它当场找不到自己的工具链。
|
|
1420
1455
|
|
|
1421
|
-
**沙箱带外 env 名单(`E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV
|
|
1456
|
+
**沙箱带外 env 名单(`E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV` / `K8S_SANDBOX_ENV`,前两只 7.101.0 起、`K8S_SANDBOX_ENV` 7.126.0 起,同上一段的解析与谓词)**:沙箱里的工具要用本服务
|
|
1422
1457
|
进程环境里的凭据(如 git 走 `$GIT_TOKEN`),又不想让它进提示词、命令串或事件日志,就把**名字**写进名单,值只在服务进程环境里:
|
|
1423
1458
|
|
|
1424
1459
|
| 键 | 车道 | 写法 | 行为 |
|
|
1425
1460
|
|---|---|---|---|
|
|
1426
1461
|
| `E2B_SANDBOX_ENV` | `REMOTE_EXEC=e2b` | 逗号分隔的**裸变量名**,如 `E2B_SANDBOX_ENV=GIT_TOKEN,NPM_TOKEN` | 点到的键从服务进程环境**原样**注入沙箱(e2b 沙箱级 env;与 `SANDBOX_PKG_SOURCE` 推导出的包源变量同名时名单赢) |
|
|
1427
1462
|
| `DOCKER_SANDBOX_ENV` | `REMOTE_EXEC=local-docker` | 同上 | 点到的键以 `docker exec -e K=V` 注入每条命令(与单条命令自带的 env 同名时,命令自带的赢) |
|
|
1463
|
+
| `K8S_SANDBOX_ENV` | `REMOTE_EXEC=k8s` | 同上 | 点到的键从服务进程环境**原样**写进沙箱 pod 的容器 env(与 `SANDBOX_PKG_SOURCE` 推导出的包源变量同名时名单赢;详见 §9.3c) |
|
|
1428
1464
|
|
|
1429
|
-
|
|
1465
|
+
三只旋钮只在**自己的车道被选中时**才求值(别的车道上它们是无关配置,不校验、不告警)。未设 / 空串 = 不注入。**形错拒启**:空段
|
|
1430
1466
|
(`A,,B`、尾逗号)、非变量名形(`1BAD`、带空格、`NAME=value` 整段贴进来)、点名名单型旋钮(`E2B_SANDBOX_ENV=E2B_SANDBOX_ENV`,
|
|
1431
1467
|
或 `LSP_ENV_ALLOW` / 另一只沙箱名单)⇒ 起服失败,拒句点名键与第几段,**不回显原值**。形对但服务进程环境里**没有**这个名 ⇒ 不注入,
|
|
1432
1468
|
起服一行 `config_env_entry_rejected` 只报第几段。点到的键值为空串 ⇒ 照样注入一个空串(7.101.0 前跳过,见 CHANGELOG)。
|
|
1433
1469
|
`REQUIRE_PRINCIPAL=true` 下照常注入(与 `LSP_ENV_ALLOW` 不同:沙箱 env 是部署方给隔离沙箱显式配的)。
|
|
1434
|
-
**起服可见面(7.101.0
|
|
1435
|
-
(名单没配或点到的名全不在 ⇒ 不带该字段;e2b 腿不含 `SANDBOX_PKG_SOURCE` 推导出的包源变量);`REQUIRE_PRINCIPAL=true` 下另有一行 warn
|
|
1470
|
+
**起服可见面(7.101.0)**:三条车道都在 `remote_exec_enabled` 行带 `sandboxEnvKeys` —— 名单点到、且服务进程环境里真有(因而真被转发)的键名,字典序,只键名不带值
|
|
1471
|
+
(名单没配或点到的名全不在 ⇒ 不带该字段;e2b / k8s 腿不含 `SANDBOX_PKG_SOURCE` 推导出的包源变量);`REQUIRE_PRINCIPAL=true` 下另有一行 warn
|
|
1436
1472
|
`config_env_forwarded_multi_tenant`(`env` = 旋钮名、`entries` = 名单段数、`note` = 姿态句:这张 worker 级名单进每个租户的沙箱),
|
|
1437
1473
|
不回显名单原文;单用户部署不出这一行。
|
|
1438
1474
|
|
|
@@ -1452,6 +1488,10 @@ bake-runner 要用 ⇒ `LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW` 点名放回。
|
|
|
1452
1488
|
| `Authorization: Bearer <SERVICE_AUTH_TOKEN>` | 设了 `SERVICE_AUTH_TOKEN` 时,除 `/health` / `/readyz` 外的所有请求 | **谁有权调本服务**(OA 后端持有,服务到服务) |
|
|
1453
1489
|
| `x-agent-principal: user:42` | 设了 `REQUIRE_PRINCIPAL=true` 时 | **代表哪个终端用户**(决定 session 归属 + 记忆隔离;**绝不从 body 取**) |
|
|
1454
1490
|
|
|
1491
|
+
> **dev-open 部署的主体头是过滤 / 展示用,不是隔离。** 没配 `SERVICE_AUTH_TOKEN(S)`、没配 `TRACE_TOKEN`、没开 `REQUIRE_PRINCIPAL` 的部署上,`x-agent-principal` 谁都能自报:
|
|
1492
|
+
> 会话列表与单会话的读写口(视图 / head / 事件流 / 改名置顶 / 记忆状态与抹除 / 后台任务 / MCP 重连 / fork / delete)都不按它钉人,带谁的头都读得到同一批会话。
|
|
1493
|
+
> 它仍决定新会话记在谁名下、记忆按谁的 scope 走;规则口(`…/policy`)与 `…/notify` 用它选落哪一把属主键。要按用户隔离,给部署设上面三者之一(典型:service token + 写主体头的网关)。
|
|
1494
|
+
|
|
1455
1495
|
| env 键 | 缺省 | 说明 |
|
|
1456
1496
|
|---|---|---|
|
|
1457
1497
|
| `PRINCIPAL_HEADER` | `x-agent-principal` | 上面那个身份头的**名字**。🔴 **缺省值是跨仓 wire 常量,不是给部署方自定义的**:这个名字**不在任何 wire 面上广播**(`/v1/capabilities`、`/health` 都没有它),所以没有下游能在运行期问 server「你叫它什么」——SDK 有一个 `principalHeader` 逃生舱但 cli/client-core 都没接线,浏览器端 BFF 是逐字硬编码,`mcp-oa` 侧车自己读同名 env(各读各的)。本旋钮只服务于「入口网关已经把身份写进别的头名」这一种特殊部署(让 server 迁就既有网关),不是给部署方起新名字用的。**改名即断下游**,但后果分四形(逐条对过代码):`REQUIRE_PRINCIPAL=true` 下提交腿(`POST /v1/tasks|/v1/runs`)是 `401` + **粗码 `auth.unauthorized`**(authorizer 抛的无 code HttpError 走状态码兜底映射),属主寻址的读/动词面是 `401 auth.principal_required`(路由级细码);两者的文案都逐字回显**你配的头名**,那是现场唯一能指认改名的线索。`REQUIRE_PRINCIPAL` 未开时更坏:**新会话照常成功**、每一条被当成**匿名**(session 归属 / 记忆隔离 / 规则车道 / operator 判定静默走无身份分支,无任何错误码),而**接续一条已属主的会话**会 `401`(`session is principal-owned; missing principal header …`)。⚠️ **SSO 与 direct-door 两条身份来源不经过这个头**(`verifiedPrincipal`),改名对它们无影响 —— 混合形部署因此会出现「一半调用方有身份、一半静默变匿名」的混着长。改了后果自负,且必须同批把每一家下游改到同名。取值须是**单个**合法 header 名(带空格/逗号 ⇒ 启动期拒启:它会撕裂 CORS `allow-headers`)。成文契约见 [`docs/ASSISTANT-WIRE-CONTRACT.md` §0.5](docs/ASSISTANT-WIRE-CONTRACT.md) |
|
|
@@ -1465,19 +1505,19 @@ bake-runner 要用 ⇒ `LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW` 点名放回。
|
|
|
1465
1505
|
> 调用方身份,**不**叫你再去带 service token。逐字句子见 `docs/ASSISTANT-WIRE-CONTRACT.md` 附录 A.1 码表。壳(sema CLI)接 sema-up 引擎的
|
|
1466
1506
|
> 令牌交法见 `deploy/sema-up/README.md`「壳接入」。
|
|
1467
1507
|
|
|
1468
|
-
**请求体字段(常用;受理**闭集**以 `docs/ASSISTANT-WIRE-CONTRACT.md` §1 为准 ——
|
|
1508
|
+
**请求体字段(常用;受理**闭集**以 `docs/ASSISTANT-WIRE-CONTRACT.md` §1 为准 —— 本表不再重抄一份):**
|
|
1469
1509
|
| 字段 | 必填 | 说明 |
|
|
1470
1510
|
|---|---|---|
|
|
1471
1511
|
| `objective` | ✅ | 要做什么(自然语言)。**唯一必填。** |
|
|
1472
|
-
| `sessionId` | — | 续聊:带上次返回的 `sessionId`,服务端自动 wake
|
|
1512
|
+
| `sessionId` | — | 续聊:带上次返回的 `sessionId`,服务端自动 wake 历史。**只收 canonical uuid**(任意版本,小写;与会话口同一只形判):自选名 / 业务键 / 空串 ⇒ `400 request.id_invalid`,缺省请**省略这个键**(服务端现铸 uuid)。想要好记的名字,自己记 name → uuid 的映射 |
|
|
1473
1513
|
| `images` | — | 图文输入 `[{data,mimeType}|{url}]`(模型需支持 vision) |
|
|
1474
1514
|
| `attachmentIds` | — | D-1 通用文件上传(1.289+):先 `POST /v1/attachments?name=…`(raw body,content-type=mime)拿句柄,提交时引用 ≤16 个;文件物化到执行环境工作目录 `attachments/` 下,objective 尾部自动追加文件清单(内容不进会话流)。单文件缺省 ≤32 MiB(`ATTACHMENT_MAX_BYTES`);可配 mime 白名单(`ATTACHMENT_MIME_ALLOWLIST` CSV,缺省不限);上传后未引用的按 `ATTACHMENT_UNBOUND_TTL_MS`(缺省 24h)回收。**云形态(tidb/pg)字节本体存对象存储——MinIO 必配**(`MINIO_ENDPOINT/MINIO_ACCESS_KEY/MINIO_SECRET_KEY`,与快照 lane 同一组变量),未配则附件面 501;local 形走本地文件店。 |
|
|
1475
|
-
| `scenario` | — | 省略时取本部署的 `DEFAULT_SCENARIO`(工厂缺省场景名 = **`code`**——CC 编码 persona 蒸馏版,default 全量工具面,终验旋钮独立;`default` 场景**已非出厂缺省**,要用它须显式传 `scenario:"default"`,或部署方显式设 `DEFAULT_SCENARIO=default`)。可选内建名:`default`(通用/助理场景,域中性 persona,CC agent loop 全量工具面)/ `code-review`(见 §5)/ `scan`(同 §5 的 repo 只读工具但**中性无框架提示词**——objective+中心下发 skill 全权主导输出,OA 扫描类用;与 `code-review
|
|
1476
|
-
| `repo`
|
|
1515
|
+
| `scenario` | — | 省略时取本部署的 `DEFAULT_SCENARIO`(工厂缺省场景名 = **`code`**——CC 编码 persona 蒸馏版,default 全量工具面,终验旋钮独立;`default` 场景**已非出厂缺省**,要用它须显式传 `scenario:"default"`,或部署方显式设 `DEFAULT_SCENARIO=default`)。可选内建名:`default`(通用/助理场景,域中性 persona,CC agent loop 全量工具面)/ `code-review`(见 §5)/ `scan`(同 §5 的 repo 只读工具但**中性无框架提示词**——objective+中心下发 skill 全权主导输出,OA 扫描类用;与 `code-review` 同属 clone-free 无执行环境场景,见 §5 末)/ **配置控制面可声明任意新场景**(`{name, toolset: none\|repo-readonly, prompt?}`,组合即配置、能力写死在部署;restart-to-apply;center 可覆盖内建名,boot 日志 `config_center_scenarios.shadowsBuiltin` 可审计)。**缺省与指派都必须是本部署在册的场景名,悬空不回落 `default`**:`DEFAULT_SCENARIO` 不在册 ⇒ **起服拒**(拒句点名那个名字与在册词表);中心按主体指派的场景名本部署没有 ⇒ 该主体的 run 答 **503 `state.scenario_assignment_unknown`**(体 `detail: { scenario, source: "ruling" }`,不带 `Retry-After`;修法 = 中心改指派,或把那个场景加回本部署,然后原样重试);请求体显式给的未知名仍是 400 `scenario_unknown` |
|
|
1516
|
+
| `repo` | — | `code-review`/`scan` 必填,见 §5(7.126.0 起 `council` / `debate` / `lenses` / `rounds` / `team` / `businessContext` 是未知键 ⇒ 400:它们喂的评审团腿与 `discuss` 场景随引擎 7.49.0 删掉团队运行时一并删除) |
|
|
1477
1517
|
| `systemPrompt` / `appendSystemPrompt` | — | 调用方自带的系统提示 / 追加段(各 ≤16384 字符,超限 400 `request.field_invalid`;`appendSystemPrompt` 必须非空串)—— 任务自带时优先于角色 / 全局缺省(见 §模型角色) |
|
|
1478
1518
|
| `outputStyle` / `settings.outputStyle` | — | 输出风格**两个座位、两件事**:风格的**正文**(指南全文)走 `settings.outputStyle`(折进系统提示的追加段);**每轮一句**走顶层 `outputStyle: { name, turnReminder? }`(7.121.0+,能力位 `outputStyle`)—— 原样交引擎,引擎每个回合用自己的标记帧说「`<name> output style is active. <turnReminder>`」(`turnReminder` 缺席用引擎缺省句),流上回声 `steering_injected` `source:"output_style"`。只在非缺省风格时声明顶层键;它的值形由引擎判(`name` 非空、≤256 字;`turnReminder` 在场即非空串;不许别的键),坏值这一轮终态 `failed`、码 `config.output_style_invalid` |
|
|
1479
1519
|
| `model` | — | 本次用哪个模型:只能点服务端**已配置目录**里的名字 / 档位词 / 模型 id(目录见 `GET /v1/models`;请求体带不进新模型)。未知 ⇒ 400 `request.unknown_reference`(带 `available` 列表);目录里有、但本部署服务不了 ⇒ 400 `request.model_unavailable`;空串 / 非字符串 ⇒ 400 `request.field_invalid` |
|
|
1480
|
-
| ~~tools~~ | 🚫 | **不是受理键**(传了 ⇒ 400 `request.body_shape`,`unknownKeys` 点名)——工具面走场景装配;按请求只能**减**:`excludeTools` / `settings.permissions` 的 deny · ask(`allow` 7.115.0 起是 allow 规则:给出「有人表态」,回答没人表态的服务器工具 / A2A 调用;不加工具、不放掉任何一层自己的问) |
|
|
1520
|
+
| ~~tools~~ | 🚫 | **不是受理键**(传了 ⇒ 400 `request.body_shape`,`unknownKeys` 点名)——工具面走场景装配;按请求只能**减**:`allowTools`(7.126.0 起;引擎的可用性选择器,只留列出的,原样透传)/ `excludeTools` / `settings.permissions` 的 deny · ask(`allow` 7.115.0 起是 allow 规则:给出「有人表态」,回答没人表态的服务器工具 / A2A 调用;不加工具、不放掉任何一层自己的问) |
|
|
1481
1521
|
|
|
1482
1522
|
**`TaskResult`(同步 / `done` 事件里拿到的;7.64.0 起终局是**一条** `terminal` 因由,不再有 `status`/`errorCode`/`errorMessage`/`blockedReason` 四个平面键):**
|
|
1483
1523
|
```json
|
|
@@ -1518,8 +1558,11 @@ curl -s http://<host>:8090/v1/tasks -H 'content-type: application/json' \
|
|
|
1518
1558
|
-H 'x-agent-principal: user:42' \
|
|
1519
1559
|
-d '{"objective":"它和 MySQL 最大的区别是什么?","sessionId":"0190…"}' # ← 同一个 sessionId
|
|
1520
1560
|
# → 服务端自动 wake 上轮历史。「恢复对话」= 同 sessionId 再发一次,无需调别的接口。
|
|
1561
|
+
# sessionId 只能是 canonical uuid(上一轮回包里的那只就是);自选名 ⇒ 400 request.id_invalid。
|
|
1521
1562
|
```
|
|
1522
1563
|
|
|
1564
|
+
本地一次性 CLI 同一条律:`run-local "<objective>" --session <uuid>` 只收 canonical uuid(任意版本);`--session myproj` 这类自选名 ⇒ 用法错、退出码 2、stderr 是与 HTTP 提交口同一句拒句。
|
|
1565
|
+
|
|
1523
1566
|
## 4. 生产姿势——异步(长任务 / 实时 UI / 断线重连,需 TiDB)
|
|
1524
1567
|
```bash
|
|
1525
1568
|
# ① 立刻拿 id(不阻塞,后台跑)
|
|
@@ -1547,14 +1590,12 @@ curl -s http://<host>:8090/v1/runs/<taskId> -H 'x-agent-principal: user:42' #
|
|
|
1547
1590
|
> 红利:②③ 经 LB 落到**任意一个 worker** 都能拿到同一个 run 的事件(数据全在 TiDB)。OA 不用关心是哪个 worker。
|
|
1548
1591
|
|
|
1549
1592
|
## 5. 代码评审场景(`scenario:"code-review"`)
|
|
1550
|
-
请求体加 `scenario` + `repo`(`owner/name` 或 URL)
|
|
1593
|
+
请求体加 `scenario` + `repo`(`owner/name` 或 URL),单 agent 直接评审:
|
|
1551
1594
|
```bash
|
|
1552
|
-
# 直接评审(单 agent,快,默认):
|
|
1553
1595
|
-d '{"scenario":"code-review","repo":"your-org/your-repo","objective":"审 src/core/session.ts 的并发"}'
|
|
1554
|
-
# council(L1 六镜头并行取证 + L3 仲裁,分桶 BUG/DESIGN/QUESTION):加 "council":true
|
|
1555
|
-
# debate(再加 L2 平级辩论 + 工具核验,最高精度最贵):加 "council":true,"debate":true
|
|
1556
1596
|
```
|
|
1557
|
-
服务端需配 `GIT_API_BASEURL` + `GIT_API_TOKEN`(只读,服务端持有);缺则 501,缺 `repo` 则 400
|
|
1597
|
+
服务端需配 `GIT_API_BASEURL` + `GIT_API_TOKEN`(只读,服务端持有);缺则 501,缺 `repo` 则 400。
|
|
1598
|
+
(7.126.0 起没有 `council` / `debate` 档:多镜头评审团随引擎 7.49.0 删掉团队运行时一并删除,这两个键是未知键 ⇒ 400。)
|
|
1558
1599
|
|
|
1559
1600
|
> **`GIT_API_BASEURL` 写法**:必须是**含 scheme 的完整 baseURL**(如 `https://git.example.com`),
|
|
1560
1601
|
> 不是裸主机名——服务端直接拼 `${GIT_API_BASEURL}/api/v1/...` 发请求,裸 `git.example.com` 会拼出非法 URL 而 fetch 失败。
|
|
@@ -1579,12 +1620,12 @@ curl -s http://<host>:8090/v1/runs/<taskId> -H 'x-agent-principal: user:42' #
|
|
|
1579
1620
|
> `DURABLE_APPROVAL`、任务已 park),它们在新版上续跑会以 409 `checkpoint.unsupported_version` 响亮失败
|
|
1580
1621
|
> (run 行落 failed 并释放会话,不会占住会话)——重新提交该任务即可。升级前排空这类挂起任务可完全避免。
|
|
1581
1622
|
|
|
1582
|
-
**clone-free 承诺(`code-review` / `scan`)**:这两个场景(
|
|
1623
|
+
**clone-free 承诺(`code-review` / `scan`)**:这两个场景(以及
|
|
1583
1624
|
配置控制面用 `toolset: repo-readonly` / `none` 声明的场景)一律跑在**不带执行环境的引擎**上——
|
|
1584
1625
|
模型看到的工具面只有声明的只读仓库工具(`repo_tree` / `repo_read_file` / `repo_pull_diff`)加 `Now`,
|
|
1585
1626
|
**没有** `Bash` / `Edit` / `Write` / `Read` 等落盘工具,不 clone、不写盘、不起沙箱。
|
|
1586
1627
|
即使部署配了 `REMOTE_EXEC`(host/e2b/k8s/ssh/adb/local-docker)也如此:执行环境只属于 `default` / `code`
|
|
1587
|
-
|
|
1628
|
+
这类需要动手的场景。
|
|
1588
1629
|
|
|
1589
1630
|
## 6. 逐 token 直播(同步流式,连接挂着)
|
|
1590
1631
|
```bash
|
|
@@ -1593,6 +1634,15 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
|
|
|
1593
1634
|
# → SSE,每条 data: <原始 TaskEvent>,如 {"type":"text_delta","delta":"…"} … {"type":"done","result":{…}}
|
|
1594
1635
|
# 注意:这是「同实例逐 token」;跨 worker 断线重连用 §4 的 /v1/runs(turn 粒度)。
|
|
1595
1636
|
```
|
|
1637
|
+
> **7.127.0 起两处帧形(引擎 7.50.0)**:
|
|
1638
|
+
> - **`task_started`**:委派子代每个周期一帧(首派 / 复活;耐久续跑腿不发),先于子代的首个 `task_progress`,与它走同一扇门(主流、转发道、子代 tail 流)。
|
|
1639
|
+
> 键与 `task_progress` 同读法(`taskId` / `sessionId` / `model` / `startedAt` 恒在;`taskType` 缺席 = 前台子代;`seq` 缺席 = 无后台行)。**与 `task_progress` 同样落账**:
|
|
1640
|
+
> `GET /v1/runs/:id/events`(异步 run 的活流与重连面)上它同样先于子代的首个 `task_progress`。
|
|
1641
|
+
> - **`tool_end.output` 两形**:串,或块数组。块里可有图像 / 文档 `{ type, data, mimeType }` —— 本流上整块逐字(`data` 是字节编码,不过文本脱敏),
|
|
1642
|
+
> 一帧最大约为部署媒体预算的 4/3(缺省约 40 MiB),请按块 `data` 长界帧;读得慢的连接(本帧媒体 + 还没送出去的字节 > 64 MiB)上本帧媒体降成描述符。
|
|
1643
|
+
> 媒体块 `data` 的内容由引擎准入负责:引擎本版只对自己产出的块验字节(读图像 / PDF、提交的图像、用户附件),MCP 服务器与宿主自定义工具交回的块不验(引擎下一 minor 收);本服务不在 wire 上对 `data` 做任何改写。
|
|
1644
|
+
> 账本面只存描述符 —— 账本行与 `GET /v1/runs/:id/events`(断线重连面;对 `POST /v1/runs` 提交的异步 run 它**就是活流**,所以这类客户端本版拿不到整图,整图只在本流与子代 tail 流上)
|
|
1645
|
+
> `{ type, mimeType?, bytes, truncated: true }`(帧级 `truncated: true` 与 `totalChars` = 原值真实大小 —— 文本块字符数 + 媒体块 `data` 串长,引擎已截断的帧保留引擎给的值);无 `data` 且 `truncated: true` 的块渲染成「未送达」。
|
|
1596
1646
|
|
|
1597
1647
|
## 7. 其它接口(按需)
|
|
1598
1648
|
| 端点 | 用途 |
|
|
@@ -1603,10 +1653,10 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
|
|
|
1603
1653
|
| `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=`(**仅** service-token/单用户形;带 principal 的调用方锁定自己)。回体 `{agents,total,nextBefore?,truncated?}`;`truncated:true` = 租户行数超过名册窗(500),`total` 此时是窗内数。非属主的行**不出现也不计数**(无存在性 oracle) |
|
|
1604
1654
|
| `GET /v1/capabilities/scenarios/<name>` | 单个场景的只读详情:工具面、提示词概览、**本部署现在跑不跑得动**(见下) |
|
|
1605
1655
|
| `POST /v1/admin/drain` `{"actionId":"…","worker":"…","deadlineAt":…}` | **排空口**(7.121.0 起体 = settings-schema `DrainRequest`;operator-only —— principal 须在 `OPERATOR_PRINCIPALS` 内,空名单 ⇒ 501)。体 `{ actionId, worker, deadlineAt, scope?, expectedGeneration?, instanceId?, term? }`:`actionId` 幂等键(人手形自取,如 `manual:<uuid>`)、`worker` 本引擎的 worker 名(`SEMA_WEB_ADMIN_WORKER`)、`deadlineAt` 到期自停的 epoch ms、`scope` 本引擎的租户 scope(`FLEET_SCOPE`;不带 = 缺省 scope `global`,与没设 `FLEET_SCOPE` 的引擎相符 —— 多租引擎要带它自己的)、`expectedGeneration` fleet 成员的代际(`FLEET_GENERATION`;非成员不带)、`instanceId` 本进程身份(`GET /health` 读;不带则不比)。身份逐字段比(`worker` / `expectedGeneration` 缺席对缺席),不符 ⇒ `409 conflict.fleet_identity`(什么都不变);首次 ⇒ `200 {actionId, since, deadlineAt, state:"draining"}` 并**进入排空**(拒新提交、本副本可抢占的在飞腿当拍让出、活的审批卡改路耐久停驻、在飞归零或到期限即停);再来 ⇒ `200 … state:"already_draining"` 回显第一次的 `since` / `deadlineAt`,不重置。体坏 ⇒ `400 request.field_invalid`;`deadlineAt` 离本引擎此刻超过 `FLEET_LEASE_OUTAGE_MAX_MS`(缺省 24 h,一台实例活着而不服务的最长时长;7.122.0 起)⇒ 同码 400(什么都不变;拒句点名字段与上界的值;只在这一发会进入排空时判 —— 已在排空或硬停已在进行时任何重发都回 `already_draining`),已过的期限照收(进入即到期);嵌入宿主(无收尾链)⇒ `501 capability.drain_state_required`。没有 worker 身份的引擎(没配 `SEMA_WEB_ADMIN_WORKER`)恒 409 —— 用 SIGTERM。经本口进的排空之后,平台随后发的第一发 SIGTERM 被吸收、不缩短期限(第二发判据见「排空期的第二发信号」)。7.120.x 的 `{"reason":"…"}` 声明形与 `/health.drainReason` 已退役。 |
|
|
1606
|
-
| `GET /v1/config/catalog` | **配置目录自描述**(operator-only —— `explicitOperatorOk`,空名单=谁都不是)。逐 env 键一行:`{name, domain, type, semantics, writeLanes, effectiveLane, envHeld, envTiming, staticDefault?/derivedDefaultNote?, effectiveValue, valueClass, danger, center?{domain,path,precedence,timing,restartSlice?,managedNow,effectiveNow}}`(🔴 7.106.0 起 `center.domainExists` 删除:座位在加载期对契约 `DOMAIN_SCHEMAS` 核过域 + 路径,核不过即拒启 ⇒ 有 `center` 块 ⇔ 中心可写;readFace 四键 / `PERMISSIONS_DISABLE_AUTO_MODE` / `MODEL_DEGRADE_ON` / `_REACTIVE`
|
|
1607
|
-
| `POST /v1/admin/config/refresh` | **配置手动刷新**(operator-only,与上一行同门:空 `OPERATOR_PRINCIPALS` ⇒ 恒 403)。改完中心配置 / `config.d` 之后不想等 60s 轮询就调它:触发**一次既有**的 refresh 拍(在飞则汇入那一拍,绝不并发双拍),返回时该拍已落地。200 体 `{triggered:true,targetVersion,appliedVersion}` —— 两个世代号与 `/health` 的 `configTargetVersion`/`configAppliedVersion`
|
|
1656
|
+
| `GET /v1/config/catalog` | **配置目录自描述**(operator-only —— `explicitOperatorOk`,空名单=谁都不是)。逐 env 键一行:`{name, domain, type, semantics, writeLanes, effectiveLane, envHeld, envTiming, staticDefault?/derivedDefaultNote?, effectiveValue, valueClass, danger, center?{domain,path,precedence,timing,restartSlice?,managedNow,effectiveNow}}`(🔴 7.106.0 起 `center.domainExists` 删除:座位在加载期对契约 `DOMAIN_SCHEMAS` 核过域 + 路径,核不过即拒启 ⇒ 有 `center` 块 ⇔ 中心可写;readFace 四键 / `PERMISSIONS_DISABLE_AUTO_MODE` / `MODEL_DEGRADE_ON` / `_REACTIVE` 七行的座位随之删除);顶层带 `restartSlices`(重启片闭集)与 `centerDomains`(settings-schema 现行域表)。**两轴不混**:`writeLanes`=谁能写,`effectiveLane`=现在谁在生效(env-wins 族 env 占位 ⇒ center 腿 `effectiveNow:false`)。secret 类值**永不回传**(只回在场位;opaque 回在场位+长度;URL 剥 userinfo)。完备性由仓内 AST 双向对账门执法(分母=全 `src/` 生产 env 读取点)。旋钮 `CONFIG_CATALOG_ENABLED=false`(合规部署)⇒ 路径 404 + 能力位 `configCatalog:false`。响应 `Cache-Control: no-store`。**7.57.0 起**:`enum` 行的 `effectiveValue` **恒落在该行自己的 `enumValues` 闭集内**(仓内机器门执法)——此前 `SESSION_BACKEND` 回的是**内部标签** `tidb`(自 5.0.0 起是**退役公名**,写回去 boot 拒),现归一成公名 `mysql`;`MODEL_DEFAULT_THINKING` 补上一直被解析器接受、却漏在目录闭集外的 `xhigh`,并改为回**实算值**(`off`/未设 ⇒ `null`)而不是 env 原文。 |
|
|
1657
|
+
| `POST /v1/admin/config/refresh` | **配置手动刷新**(operator-only,与上一行同门:空 `OPERATOR_PRINCIPALS` ⇒ 恒 403)。改完中心配置 / `config.d` 之后不想等 60s 轮询就调它:触发**一次既有**的 refresh 拍(在飞则汇入那一拍,绝不并发双拍),返回时该拍已落地。200 体 `{triggered:true,targetVersion,appliedVersion}` —— 两个世代号与 `/health` 的 `configTargetVersion`/`configAppliedVersion` 同源。候选被判官整拒(坏域 / 坏 prompts / 带事实的退役域 —— 后者 7.126.0 起)是这一拍的结论,不是失败:照样 200,`targetVersion ≠ appliedVersion`,拒因在 `config_candidate_rejected` 日志行;拍真的失败(配置源不可达 / 读不了 / 应用抛错)⇒ `502 internal.config_refresh_failed`。本部署没有配置管道(纯 env worker:既没配 `CONFIG_CENTER`、也不是 `CONFIG_PROVIDER=local`)⇒ 409 `config.refresh_unavailable` 指路,不会回一个「触发了但什么都没发生」的 200。 |
|
|
1608
1658
|
| `GET /health` | 健康(无需鉴权)。**7.67.0+** 恒带 `startedAt`(引擎进程起点 epoch ms,一次铸;与 `pid` 并列的换代锚——值变了就是换了一条命)。7.37.0+ 配置管道在场时带世代账键:`configTargetVersion`(本副本**最后见到**的配置版本,身份标)、`configAppliedVersion`(最后一次真落地的版本)、`configTargetOrdinal`/`configAppliedOrdinal`(7.38.0-rc.2 起,**收货序数**——`CONFIG_PROVIDER=local` 下 version 是内容哈希不承诺序,「改了没生效」看序数对不对齐)、`configApplyStaleMs`(target≠applied **持续**时长;收敛时键缺席)—— 编排器据此摘掉「配置持续落后」的副本 |
|
|
1609
|
-
| `GET /v1/sessions/:id/background` · `POST /v1/sessions/:id/background/stop` `{"includeRetained":true\|false}` | **会话后台任务的读口与停止口**(**7.102.0
|
|
1659
|
+
| `GET /v1/sessions/:id/background` · `POST /v1/sessions/:id/background/stop` `{"includeRetained":true\|false}` | **会话后台任务的读口与停止口**(**7.102.0+**;能力位 `background.listFace` / `background.exitFaces`,老 server 404)。读口列**本会话**、**本副本**上的后台任务行(`{ rows: [{ id, description?, type, status, createdAt, sessionScoped, retained? }] }`,`retained:true` = 声明了保留的活服务,正常退出不会停它);停止口「软 → 等一次 `HOST_BG_KILL_GRACE_MS` → 硬」收掉本会话的后台 shell(SIGTERM 处理器先有一次宽限跑完,之后组 SIGKILL,屏蔽 SIGTERM 的循环也活不过去;有后台行时应答慢一次宽限)并逐字返回受据 `{ receipts: [{ id, outcome }] }`:`killed` / `already_gone` / `retained_skipped`(`includeRetained:false` 留下的活服务)/ `no_pgid`(远端 / 沙箱车道没有本机进程组可发,已派普通 kill,「可能还在跑」;host 车道的 `already_gone` 只由观察得出 —— 引擎已收割组长或内核答组已空,7.103.0 起;darwin 上梯子那一枪先落地、引擎还没收割的那一拍如实答 `no_pgid`,判「收掉没有」读进程)。`includeRetained` 必填:`true` = 连活服务一起停(壳的「Exit and stop tasks」),`false` = 活服务照留(`sema kill`)。鉴权 = 会话属主或显式 operator(未知与他人同 404);停止口在无凭据写门内(没配 service 凭证且未开 `ALLOW_UNAUTHED_WRITES` ⇒ 503 `auth.service_token_required`)。`DELETE /v1/sessions/:id` 走同一只收敛(`includeRetained:false`);run 取消(Esc)**不**收后台 shell。契约 §16 |
|
|
1610
1660
|
| `GET /readyz` | **就绪探针**(**7.102.0+**,无需鉴权;7.121.0 起是唯一的探针路径)。就绪 ⇒ `200 {"ready":true}`;未就绪 ⇒ `503 {"ready":false,"reasons":[…]}` + `Retry-After: 5`,`reasons` ⊆ {`draining`, `model_roster_pending`}(按优先序)。与 `/health` 的 `ready` / `readyReason` / `reasons` 同一只判;库连不上**不算**未就绪(`storeLive` 只在 `/health` 披露)。给只认状态码的**摘流**探针用(k8s `readinessProbe.httpGet`、LB 健康检查、镜像 `HEALTHCHECK`);存活探针用 TCP 探端口,不打 HTTP,见 §0「探针」 |
|
|
1611
1661
|
| `GET /metrics` | Prometheus 指标(有 token 时需带) |
|
|
1612
1662
|
|
|
@@ -1727,6 +1777,14 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
|
|
|
1727
1777
|
> 手动阀门照常可用。
|
|
1728
1778
|
> 未武装的部署,`wiring_manifest` 与它的 `configFingerprint` 对 7.20.1 **逐字节相同**;武装的部署,新段
|
|
1729
1779
|
> `autoConsolidation: { onRecommendation: true }` **只出现在 operator 帧上**(契约附录 G.13)。
|
|
1780
|
+
> - **整理每次写库都问写判官(7.127.0 起,引擎 7.50.0)**:
|
|
1781
|
+
> - **自动整理**(任务结束按建议起跑)用这条腿自己的判官 —— 该 run 主体的人的持久路径规则 + 部署的调用方策略,与模型自己的 `Write` 同一套判:
|
|
1782
|
+
> 规则点名库目录 ⇒ 在任何占位与模型调用之前拒,运维行 `engine_notice` 一条 `memory.consolidation_refused`(`detail.refusalCode: "memory.consolidation_write_refused"`);
|
|
1783
|
+
> 只点名产物文件 ⇒ 应用时拒、什么都不写,一条 `memory.consolidation_incomplete`(`stop: "refused"`);用户任务不受影响。没有主体的 run 读不到任何人的持久规则(只剩部署策略那一层)。
|
|
1784
|
+
> - **手动阀门**(`POST /v1/admin/memory/consolidation/run`)没有腿,拿不到引擎的那只判官;本服务不另造一只 ⇒ 它的写判官答「无异议」(与 7.126.0 该口行为逐字同),
|
|
1785
|
+
> 每次被问记一次 fail-open 计数 `server.memory-consolidation.operator-valve-unjudged`(计数 + 探针行 + 一次性告警)。驱动的每一条通告 —— 非收敛停的 `memory.consolidation_incomplete`、
|
|
1786
|
+
> 问判官**之前**推不出库家落点时同码拒的 `memory.consolidation_refused`(生产上可达,与判官答什么无关)—— 都经同一只 `engine_notice` 转发器进运维行;早问拒仍按路由的码臂答 `409 memory.consolidation_refused`。
|
|
1787
|
+
> - **怎么让整理写**:删掉或收窄点名记忆库路径的规则;只想禁止自动整理就设 `MEMORY_AUTO_CONSOLIDATION=off`。
|
|
1730
1788
|
> - `MEMORY_CONSOLIDATION_SCOPES=user:local[,org:acme]`:阀门的 scope 表 = 这台 worker **声明**它会折叠哪些库。
|
|
1731
1789
|
> **恰好一条**时它是两个口省略 `scope` 的唯一缺省;零条或多条时省略 `scope` 一律 400(在两个库之间替人挑
|
|
1732
1790
|
> 一个去花全库模型钱是本设计要消灭的形)。**表非空时显式 scope 必须在表里**,否则 400 —— 一个陈旧或敲错
|
|
@@ -1814,12 +1872,20 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
|
|
|
1814
1872
|
> 与历史记录轨迹;真正持久化的规则本身在 `permission_rule` 表,不受影响、不需要重建。
|
|
1815
1873
|
> 探针只在规则车道真被装配时跑(`PERMISSION_RULES_ENABLED=false` 的部署一次都不碰这张表,不受影响)。
|
|
1816
1874
|
>
|
|
1875
|
+
> **人的持久规则的读法(7.127.0 起,引擎 7.50.0)**:每个调用读一次规则店;一条持久 deny 行在门上拒它所在那一面的**每个**工具(Read / Grep / Glob / RepoMap /
|
|
1876
|
+
> 写工具 / 声明路径目标的调用方工具),`tool_end.gate.disposition.deniedBy = "persisted_rule"`。店的某个分区读不出 ⇒ 该分区上次读到的 deny / ask 行照旧生效、allow 行不在,
|
|
1877
|
+
> 射程内的调用真人问(bypass 也问,无审批面即拒),run 一条 `permission.rule_store_unavailable`。
|
|
1878
|
+
> **`~/…` 形的行以执行环境的家目录为基**:host / 未设 `REMOTE_EXEC` = 本机本用户,e2b = `/home/user`,k8s / local-docker / ssh / adb / device 不猜 —— 除非 `REMOTE_EXEC_HOME_DIR`
|
|
1879
|
+
> 声明。没声明家目录的车道上,一条 `~/…` 的 deny / ask 行判不出点的是哪里 ⇒ 该面文件工具的每个调用都成真人问(bypass 也问);要么声明 `REMOTE_EXEC_HOME_DIR`,要么把规则写成绝对路径。
|
|
1880
|
+
>
|
|
1817
1881
|
> **同批顺带:场景旧名 `team` / 工具旧名 `run_team` 退役(BREAKING)。** 此前过渡窗的别名层
|
|
1818
1882
|
> (`SCENARIO_ALIASES`)整体撤销:请求体里显式传 `scenario:"team"` 从「静默归一到 `discuss`」变成
|
|
1819
1883
|
> **未知名响亮 400**(`scenario_unknown`);指派/部署配置里悬空写着的旧名走**现有**
|
|
1820
1884
|
> unknown→default 回退(与任何其他未知名同待遇,不是新逻辑)。prompts 里写死旧工具名 `run_team` 的
|
|
1821
1885
|
> 部署 ⇒ 模型看到「工具不存在」。滚版前把请求面/配置面/prompts 里的旧名换成现役主名
|
|
1822
|
-
> `discuss` / `run_discussion`。
|
|
1886
|
+
> `discuss` / `run_discussion`。(**7.126.0 起 `discuss` 本身也退役**:引擎 7.49.0 删了团队讨论运行时,`discuss` 与任何未知名同待遇 ——
|
|
1887
|
+
> 请求面 400 `scenario_unknown`、中心指派 503、`DEFAULT_SCENARIO=discuss` 起服拒;中心 `collab` 模板照收不执行,每次应用一行
|
|
1888
|
+
> `web_admin_collab_not_executed` 点名模板 id。多成员的活改成 Agent 子代 + 主模型综合,或部署自己登记 Workflow 脚本。)
|
|
1823
1889
|
>
|
|
1824
1890
|
> **🔧 升级到 7.10.0 前:必须删库重建(BREAKING,两条同窗)。** ① pg 侧 memory 两表的
|
|
1825
1891
|
> `agent_memory_engine_entry.scope/slug` 与 `agent_memory_engine_cursor.scope` 由 `text` 收窄为
|
|
@@ -1906,7 +1972,7 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
|
|
|
1906
1972
|
|
|
1907
1973
|
**B. 智能体客户端直接调(把本服务当一个"委派工具")**
|
|
1908
1974
|
- 最简单:`POST /v1/tasks`,body `{"objective":"<要委派的子任务>"}`,拿 `result` 当工具输出。
|
|
1909
|
-
- 要让 agent 自己审仓库:`{"scenario":"code-review","repo":"…","objective":"…"
|
|
1975
|
+
- 要让 agent 自己审仓库:`{"scenario":"code-review","repo":"…","objective":"…"}`。
|
|
1910
1976
|
- 多轮:agent 维护 `sessionId` 即可保持上下文。
|
|
1911
1977
|
|
|
1912
1978
|
**C. 你自己的简单入口**
|
|
@@ -1919,21 +1985,26 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
|
|
|
1919
1985
|
|---|---|---|
|
|
1920
1986
|
| HTTP `200` + `terminal.kind:"completed"` | 成功 | 取 `result` |
|
|
1921
1987
|
| `terminal.kind:"blocked"` | agent 主动报卡住 | 看 `terminal.reason`,补信息再发 |
|
|
1922
|
-
| HTTP `200` + `terminal.kind:"paused"` + 顶层 `status:"suspended"\|"needs_review"`(同步 `POST /v1/tasks`) | run **停在门上**(工具审批 / 计划审阅 / 资源分片),没跑完。🔴 **7.104.0
|
|
1923
|
-
| `terminal:{kind:"failed", code:"conflict"}` |
|
|
1988
|
+
| HTTP `200` + `terminal.kind:"paused"` + 顶层 `status:"suspended"\|"needs_review"`(同步 `POST /v1/tasks`) | run **停在门上**(工具审批 / 计划审阅 / 资源分片),没跑完。🔴 **7.104.0 起**这张体就是那份结果本身(`terminal.gate` 说停在哪扇门、`checkpointId` 是对账位、`result`/`stats` 是停下来那一刻的读数),另叠 `taskId`/`sessionId`/`status` 三键 poll 回执 —— 修前只有这三键。恢复凭据**从不**在体上 | 按 `taskId` 轮询 `GET /v1/runs/:taskId`(park 中那一行同样带这份 `result`);审批走 `/v1/approvals`。判读用 `terminal.kind`,旧的 `status` 读法照旧成立。契约 §5.0c |
|
|
1989
|
+
| `terminal:{kind:"failed", code:"conflict"}` | 同一会话的两条写同时落在同一处(真并发:两处同时写一只会话),按叶的条件写只成一条;输的那一条以本码结束,撞上之后的内容没写进会话。多副本按任意路由轮流服务同一会话**不会**产生它(会话暖缓存命中前先核店里的当前叶,见 `SESSION_CACHE_TTL_SEC`) | 直接重试(重发接在会话当前的叶之后) |
|
|
1924
1990
|
| `terminal:{kind:"failed", code:"limits.max_walltime_exceeded"}` | 墙钟到限(6.0.0 起 `status:"timeout"` 退役;7.64.0 起平面 `status` 键退役)。🔴 **7.71.0 / 引擎 7.13.0 收窄**:上限若在最后一轮 turn **已经给出干净答案之后**才响,run 记 `completed`、答案就是 `result`,**不铸本码**——那次停机的唯一痕迹是 `engine_notice` 的 `task.interrupt_unconsumed{origin:"walltime"}`(附录 D.3)。本码此后只在**工作被真切断**时出现。⚠️ 按码计数的运维面要把 `origin ∈ walltime\|turns` 的那一帧计入才守恒(server 侧对偶计量 `limit_ceiling_after_answer_total{origin}`,与 `budget_exceeded_total{code}` **相加**才是撞顶总量) | 拆小任务 / 提高 `limits.maxWalltimeMs`(毫秒) |
|
|
1925
1991
|
| `terminal:{kind:"failed", code:"limits.max_turns_exceeded"}` | 轮数到帽。🔴 **同上收窄**(引擎 7.13.0):帽子若落在**那一轮答案自己**身上 ⇒ `completed` + `task.interrupt_unconsumed{origin:"turns"}`,不铸本码 | 提高 `limits.maxTurns` / 拆小任务 |
|
|
1926
1992
|
| HTTP `401` | 缺 `Authorization` / 缺 `x-agent-principal`(要求时) | 补头 |
|
|
1927
1993
|
| HTTP `403` / `404`(session/run) | 不是该 principal 的资源 | 用正确身份 |
|
|
1928
1994
|
| HTTP `409`(`/v1/runs`) | 同 session 已有活跃 run | 等它完成 / 用返回的 `activeTaskId` |
|
|
1995
|
+
| HTTP `409` + `errorCode:"conflict.idempotency_payload_mismatch"`(7.126.0 起;`POST /v1/tasks` · `/v1/tasks/stream` · `/v1/runs`) | `Idempotency-Key` 已绑定在一条你读得到的 run 上,而绑定它的那次请求的体与这次不同(规范化:按键排序、去掉 `taskId`;契约 §0.7「同一请求 = 同一份体」)—— 不回放、不新建、那一条不动,体不回显键与那一行;流式腿在开流之后才撞上时,是终帧 `done.result` 带同一 `errorCode` 的 `failed`。A2A 不会遇到 | 重试就重发同一份体;不同的请求换一把新键。与 `conflict.run_exists` 不同:后者是那一行你读不到、或带的 `taskId` 不是那一行 |
|
|
1929
1996
|
| HTTP `429` | 限流 | 看 `Retry-After` 退避 |
|
|
1930
1997
|
| HTTP `429` + `errorCode:"limit.cost_quota_exceeded"` | per-principal 累计成本配额越顶(`MAX_PRINCIPAL_COST_USD`;**进场门**,不打断在跑的 run) | 看 `Retry-After` / 体 `retryAfterSec` 退避(体 `windowResetsAt` = 当代窗滚过的时刻);窗滚过或调高上限后放行 |
|
|
1931
|
-
| HTTP `400` + `errorCode:"request.id_invalid"` | 调用方给的 id
|
|
1998
|
+
| 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 …`),只作展示、别锚文案 |
|
|
1999
|
+
| HTTP `400` + `errorCode:"request.field_invalid"`(`PATCH /v1/sessions/:id` / 2c 导入的 `title`) | 标题不合形:1–120 个 Unicode 字符、**首尾不得有空白**(服务端不代为 trim,先于 C0 判)、不得含 C0 控制字符;`title: null` 清回自动标题;空串是 `request.body_shape` | 客户端送出前自行 trim;按 `errorCode` 分支,句子只作展示 |
|
|
2000
|
+
| HTTP `400` + `errorCode:"config.tool_face_control"`(7.126.0 起;流式 / 后台腿是 `failed` 终局带同码) | 请求的 `allowTools` / `excludeTools` 里有一个这台部署上**没有任何工具答应**的名字(或 `allowTools` 写了 `mcp__*`、`excludeTools` 写了 `*`);引擎开跑前拒:主模型零调用、标题器零调用(7.127.0 起;7.126.x 上标题器仍会为这次提交调一次模型)。这次提交留下的会话无标题、零消息 | 只列这台部署挂得出的工具名;句子点名那一条与它在哪张表里 |
|
|
1932
2001
|
| HTTP `429` + `errorCode:"usage.window_exhausted"` | 部署级治理窗耗尽(`USAGE_WINDOWS`,token 或 $ 天花板先满者) | 看响应体 `retryAfterSec` 退避;窗滑动/桶到期后放行 |
|
|
1933
2002
|
| `terminal:{kind:"failed", code:"usage.window_exhausted"}` | 已受理的 run 在 **turn 边界**撞上治理窗且无法 durable 挂起 | 同上退避后重投;配好 checkpoint 基建则改为 `suspended` 等窗自动续跑 |
|
|
1934
|
-
| HTTP `501`(`/v1/runs`) | 内存模式不支持异步 | 配持久存储(`DB_BACKEND=mysql` / `pg` + 坐标;7.106.0 起给了坐标必须点名 `DB_BACKEND
|
|
2003
|
+
| HTTP `501`(`/v1/runs`) | 内存模式不支持异步 | 配持久存储(`DB_BACKEND=mysql` / `pg` + 坐标;7.106.0 起给了坐标必须点名 `DB_BACKEND`) |
|
|
1935
2004
|
| HTTP `500` + `errorCode:"corrupt_session"`(7.104.0 起;流式口在应答头之后则是 `done` 帧的 `result.terminal.code`,后台 run 是 run 行的 `errorCode`) | 这只会话的**存档读不出**、唤不醒(文件后端:会话日志没有会话头;SQL 后端:一行事件读不出)。这一轮没跑,一个 token 没烧。直接读这只会话的口(头探针 / 会话视图 / 导出 / 分叉 / 2c 同步)同答(契约 §17) | **别重试**(确定性失败)。运维看日志 `session_repo_corrupt_read`(带文件路径与跳过行号)修或移开那份存档;要继续工作就换一个新会话 |
|
|
1936
2005
|
| 流里一帧 `engine_notice` `code:"session.records_skipped"`,`detail:{ dropped, entriesKept, sessionId }`(7.104.0 起,只在文件后端) | 这只会话的存档打开时有 `dropped` 条记录读不出、被跳过了;这一轮**照跑**,但模型读到的历史只剩 `entriesKept` 条。存档没修好之前,每次真正打开这只会话都会再说一次(会话缓存命中的轮次不打开、不说;`SESSION_CACHE_TTL_SEC=0` 时每一轮都说) | 告诉用户「这段对话的一部分历史丢了」;运维按日志 `session_repo_corrupt_read` 查那份文件。非流式 `POST /v1/tasks` 收不到通告帧(只有日志) |
|
|
2006
|
+
| 流里一帧 `engine_notice` `code:"route.finish_reason_unknown"`,`detail:{ finishReason, sessionId, runId }`(7.126.0 起) | 提供方用一个引擎读不懂的结束原因结束了这一轮:回答按模型留下的样子保留,但可能被提供方截短了 | 告诉用户「这一段回答可能不完整」;同一个词同一 run 只说一次。运维按 `finishReason` 查是哪家网关 / 模型发的 |
|
|
2007
|
+
| 流里一帧 `engine_notice` `code:"route.stream_end_ignored"`,`detail:{ statement, word, frames, sessionId, runId }`(7.126.0 起) | 提供方已经结束了这一轮,它的流却又发来了东西(又一个结束 / 一个错误 / 内容);第一个结束算数,之后的没进这一轮 | 一般无需处置;频繁出现 ⇒ 运维查那条网关的流式实现(`statement` / `word` 说的是迟到的第一帧) |
|
|
1937
2008
|
|
|
1938
2009
|
**机器码**:每个 4xx/5xx 响应体都带一个 `errorCode`(与人类文案 `error` 并列),这是**唯一**该拿来做
|
|
1939
2010
|
程序分支的字段——**别锚 `error` 文案**。前缀族固定:`auth.` / `request.` / `not_found.` / `conflict.` /
|
|
@@ -1948,11 +2019,12 @@ tag 词表与每条的最坏后果见 `docs/FAIL-OPEN-CENSUS.md`。引擎装配
|
|
|
1948
2019
|
(core 给宿主定级的判别词;词表外原样透传并标 `classificationKnown:false`),指标 `runner_config_advisory_total`
|
|
1949
2020
|
按 `code` / `classification` / `level` 打标;其中 `shell-gate-off` 在本 session 显式声明 `permissionMode:
|
|
1950
2021
|
"bypassPermissions"` 时是调用方要的姿态,降为 **info** 并标 `expected`——只有没表态落到引擎默认 off 的无壳直连
|
|
1951
|
-
形才 warn。子代 `agents[].denyTools` / `allowTools`
|
|
1952
|
-
**开跑前拒**(`config.agent.unknown_tool
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
2022
|
+
形才 warn。子代 `agents[].denyTools` / `allowTools` 的**名字**归引擎开跑前一只判(7.126.0 起与根 run 的 `allowTools` / `excludeTools` 是同一只工具选择器):
|
|
2023
|
+
没有任何工具答应的名字(无人挂的名、退役拼法 / 大小写孪生 `bash` / `BASH`)**开跑前拒**(`config.agent.unknown_tool`;委派工具声明了逐次注入工具的名册,引擎列得出
|
|
2024
|
+
子代工具池),名字按工具名原拼写 `Bash`。这两张表管子代可挂载的**全部**工具 —— 委派池、内建 hands(`Read` / `Edit` / `Write` / `NotebookEdit` / `Bash` / `Grep` /
|
|
2025
|
+
`Glob` …)、引擎自己的工具(`Monitor` / `Artifact` / `Skill` / 记忆工具 …)与父的 MCP 挂载:`denyTools:["Write"]` ⇒ 子代没有 `Write`,`denyTools:["Monitor"]` ⇒ 子代没有
|
|
2026
|
+
`Monitor`;`allowTools` 只列调用方工具 ⇒ 子代只剩那几只与它这条腿的协议工具(要什么就点名什么,`mcp__<srv>` 点整台服务器)。内建只读子代(Explore / Plan)没有
|
|
2027
|
+
`Edit` / `Write` / `NotebookEdit`。要按路径 / 命令拦而保留工具,写调用方权限策略的 deny 规则。
|
|
1956
2028
|
|
|
1957
2029
|
🔴 **`shell-gate-off` 这条注的语义自 7.73.0(core 7.15.0)起收窄,文案由引擎改写**:`off` 档**不再**意味着
|
|
1958
2030
|
「这条会话的 shell 完全没有门」。**读边界**(内建读拒表 + 工作区容纳面)现在**每一档都判**,`shellGate` 只管
|
|
@@ -1988,9 +2060,9 @@ CC 逐字节形的提醒帧:`<total_tokens>N tokens left</total_tokens>`(**缺
|
|
|
1988
2060
|
真判据在 `taskWallClockSec`:
|
|
1989
2061
|
|
|
1990
2062
|
- **`0` / 未设** ⇒ 走 **tenancy 基值**:单用户 turnkey(`REQUIRE_PRINCIPAL` 未开)= **真无墙**;
|
|
1991
|
-
多租户 = **2400s**(
|
|
2063
|
+
多租户 = **2400s**(7.126.0 起只有这一个基值:3600s 的「大任务」档只为 council / debate / discuss 而设,随它们删除)——墙**是开着的**,不是关的。
|
|
1992
2064
|
- **设了正值** ⇒ `max(值, 基值)` = **只抬不降**。所以在多租户上写 `TASK_TIMEOUT_SEC=60` 拿到的是
|
|
1993
|
-
**2400**,不是 60(设计如此:误配的低值不许砍掉
|
|
2065
|
+
**2400**,不是 60(设计如此:误配的低值不许砍掉 tenancy 基值)。要**更紧**的配速走提交体
|
|
1994
2066
|
`limits.maxWalltimeMs`——那是 caller 的显式意图,直接生效,可以低于 env 墙。
|
|
1995
2067
|
- **负值** ⇒ **启动即拒**(7.57.0 起)。此前 `-1` 被静默折成 `0` = 「按 tenancy 走」,运维想关墙、
|
|
1996
2068
|
在多租户上反而拿到 2400s 的墙——方向相反且零提示。非数值(`3600s` 这种带单位后缀的写法)照旧拒启。
|
|
@@ -2040,41 +2112,140 @@ k8s 沙箱(pod `activeDeadlineSeconds` 真硬死线)向引擎声明 `lifetimeMs`
|
|
|
2040
2112
|
边界尝试 suspend(reason `env_lifetime`,基建合格时)而不是被平台杀在半途;单个 >60s 的工具调用仍可能
|
|
2041
2113
|
冲过该窗口,声明是尽力座不是保证。e2b 沙箱有 keepalive 滚动续期、无固定死线,不声明(行为不变)。
|
|
2042
2114
|
|
|
2043
|
-
### 9.3b k8s 车道:工作区跨轮保留(
|
|
2115
|
+
### 9.3b k8s 车道:工作区跨轮保留(按会话住客的空间快照;7.125.0 起,7.126.0 起缺省开)
|
|
2044
2116
|
|
|
2045
|
-
k8s 车道每条 run 一只新 pod(pod 寿命 = 泄漏护栏,不跨 run 复用)
|
|
2117
|
+
k8s 车道每条 run 一只新 pod(pod 寿命 = 泄漏护栏,不跨 run 复用)。配了快照存储时,`/workspace` **缺省跨轮保留**(`WORKSPACE_PERSIST` 缺省 `space`):每条 run 结束把工作区快照进本会话住客的空间,下一条 run 开始恢复最近一份。要回到每轮空目录:`WORKSPACE_PERSIST=off`。
|
|
2046
2118
|
|
|
2047
2119
|
- **空间 = 会话的这一位住客**(会话化身:会话建行时铸的不透明随机串,删会话随行消失)。同一会话的各条 run 共享一个空间;不同会话互不可见;会话删掉后同一个 id 被重新登记(不论谁)= 新住客,从空目录开始,前一位住客的对象不再被读。前缀 `WORKSPACE_SNAPSHOT_PREFIX` 缺省 `kata`,桶 `WORKSPACE_SNAPSHOT_BUCKET` 缺省 `workspaces`。
|
|
2048
2120
|
- **两条车道,两种分区**:
|
|
2049
|
-
- **跨轮保留**(本节):归档 `<前缀>/<化身>/space/<snapshotId>.tar.gz`,指针 `<前缀>/<化身>/space/latest
|
|
2050
|
-
- **停驻挂起 / 续跑**(引擎驱动的 durable suspend):归档 `<前缀>/<会话 id>/<snapshotId>.tar.gz`(
|
|
2051
|
-
-
|
|
2121
|
+
- **跨轮保留**(本节):归档 `<前缀>/<化身>/space/<snapshotId>.tar.gz`,指针 `<前缀>/<化身>/space/latest`;只有这条车道被列举、被删旧,且只在 `space/` 里。按住客分区的理由:读它的是「这只会话的下一条 run」,分区就决定了谁读得到 —— 按会话 id 分,删后重登记的另一位就会恢复前一位的文件。
|
|
2122
|
+
- **停驻挂起 / 续跑**(引擎驱动的 durable suspend):归档 `<前缀>/<会话 id>/<snapshotId>.tar.gz`(key 形与 7.125.0 之前同形,存量停驻快照照常可读,零迁移;7.126.0 起新写的 `snapshotId` 形为 `kata-<归属记号>-<时间>-<随机>`,归属记号 = 住客化身的不可逆摘要头 16 位,没有已知住客的 run 记 `none`);从不被列举、从不被删旧 —— 一条停驻中的 run 的快照不会被同会话后来的 run 挤掉。按会话 id 分区的理由:读它的只有那条停驻 run 自己的检查点(snapshotId 记在检查点里,删会话连检查点一起删),分区只做命名空间、不决定谁读。
|
|
2123
|
+
- **没有这只会话**(会话店答「没有」:删了 / 从未登记)⇒ 没有空间:这条 run 的工作区既不恢复也不保存,一行 `workspace_space_unavailable`(`reason: incarnation_unknown` / `sessionId`),**不会**改按会话 id 找空间。**读住客失败**(会话店报错,或答出的不是住客令牌)⇒ 读不到空间不等于没有空间:这条 run 连沙箱失败(见下方失败形),不在空目录里照跑。住客在建这条 run 的执行环境时从会话店读出;没有存储后端的部署(`DB_BACKEND=memory`,`/health.sessionBackend` 报 `memory`)与 `space`(含缺省)不能同开:起服拒(见下方失败形)。日志里点名空间只打化身的前 8 位(字段 `space`),完整化身不进日志。
|
|
2124
|
+
- **委派子代 / 后台代理不开空间**:它们与父 run 并发,共用一个空间就是两个写者 ⇒ 子代的工作区每轮新建、不恢复也不保存,不出任何行;父 run 的空间不受影响(停驻挂起照常)。
|
|
2052
2125
|
- **指针**:`space/latest`,体 = 最近一份 snapshotId。run 结束时先把 `/workspace` 打包上传(与挂起同一条车道:沙箱只拿到限时预签 URL,凭据留在 server),成功后**整体覆写** `latest`,再删 pod。下一条 run 第一次用到沙箱时读 `latest`:在 ⇒ 新 pod 上恢复那一份;不在(空间第一条 run)⇒ 空目录。
|
|
2053
2126
|
- **保留**:每个空间只留最近 `WORKSPACE_SNAPSHOT_KEEP` 份(缺省 3),写新快照后删最老的;`latest` 指向的那份恒留。没有另外的索引表 —— 桶里列出来的就是全部。
|
|
2054
|
-
- **只盖 `/workspace`**:进程、内存态、`/tmp` 不跨轮(与挂起 / 续跑同一口径)。大小上界 =
|
|
2055
|
-
-
|
|
2056
|
-
-
|
|
2127
|
+
- **只盖 `/workspace`**:进程、内存态、`/tmp` 不跨轮(与挂起 / 续跑同一口径)。大小上界 = 快照时限 `WORKSPACE_SNAPSHOT_TIMEOUT_MS`(缺省 300000 = 5 分钟);超时 = 快照失败,不截断。
|
|
2128
|
+
- **排除表**(`WORKSPACE_SNAPSHOT_EXCLUDE`,7.126.0 起):逗号分隔的 tar 排除模式,相对工作区根(`build,.gradle,node_modules/.cache`),每项一个 `--exclude=./<项>`,落在**那一只**打包上传上 —— run 末快照与停驻挂起是同一张表(挂起后续跑也没有被排除的目录;构建产物、依赖缓存本就可再生)。沙箱镜像的 GNU tar 按工作区根锚定(`build` 只排除 `/workspace/build`,不排除 `/workspace/app/build`;要排子模块的就写 `app/build`,或写通配 `*/build`);镜像若换成 busybox / libarchive 的 tar,同一项会在任意深度生效。不读项目的 `.gitignore`(那是另一套文法、按项目变、与 tar 的匹配不等价);中心若要下发模式表,下发的就是这一个 env 的值。项形是闭的:相对路径段,不含 `..`、前导 `/`、空白、引号(坏项拒启,码 `config.workspace_snapshot_exclude_invalid`,点名第几项)。⚠️ 已知形:打包先落 pod 里的临时文件再上传(对象存储的预签 PUT 要 `Content-Length`,流式上传不行)⇒ 打包期间工作区占双倍盘;排除表把它缩小。
|
|
2129
|
+
- **提交围栏(下一条 run 等上一条存完)**:「这个空间有提交在飞」是 run 店里的一行(表 `workspace_space_commit`;local 后端是数据根下 `space-commits/` 的一份记录),不在对象存储里。写点只有一个次序:run 的终态写入与释放会话占用**同一个事务**里落 `pending`(只在这条 run 碰过沙箱时)→ run 末保存开始时改成提交中 → 上传完、写 `latest` 之前再认领一次(改成发布中)→ 存完或失败都删行。**一行一个期限,写者写、所有人读**:每次写行都把行的期限定为「此刻 + 写它的那台副本的 `WORKSPACE_SNAPSHOT_TIMEOUT_MS`」;读的一方只看行里的期限,不拿自己副本的数判别人的提交 —— 所以滚动改这个数时,新旧副本互不误判。下一条 run 连沙箱前读这一行:有 ⇒ **等**,每 1 秒重读一次,直到行消失(再按新的 `latest` 恢复)或过了行里的期限。读到过了期限 = 上一条的 worker 死在终态与保存之间、死在上传里、或死在写 `latest` 时 —— 也可能写者正在这一刻续期,所以只读一眼不算数:下一条 run 在一个事务里锁住这一行、按锁下读到的库钟再判一次,仍是那条 run 的、仍过期限才删掉它(弃)。删成了 ⇒ 一行 `workspace_commit_abandoned`(`space` / `remainingMs`:≤ 0,过了期限多久 / `state`),照常恢复 `latest`(它仍指上一份**完整**快照);没删成(写者恰好续了期、行已换主或已删)⇒ 立刻重读、照常等。弃与续期在行锁上排队,被弃的写者再认领时找不到自己的行 ⇒ 不推进 `latest`。期限按存储自己的钟算(SQL 后端 = 数据库的钟),不拿两台副本的墙钟比。提交自己也守同一个期限:开始保存时它的行已过期限(下一条 run 可能已按「放弃」继续)⇒ 不上传、不推进 `latest`(`workspace_snapshot_failed`、`stage: fence`);上传完、写 `latest` 前那次认领发现行已过期限或已不是自己的 ⇒ 同样不推进(`stage: fence`)。所以引擎拆环境的 15 秒上界、存储慢都不会让下一条 run 恢复到旧的一份。终态帧上的 `workspaceSnapshot` 只承载本 run 在终态帧之前落定的结局:保存比终态帧晚落定时这条 run 的帧不带,那次结局只留运维行,也不会挂到同一会话下一条 run 的帧上。
|
|
2130
|
+
- **仍余的一条残窗(如实)**:`latest` 指针写是单对象预签 PUT,这次 PUT 的时限 = 围栏剩余时长。PUT 自己到点没答、写者放弃它(一行 `workspace_snapshot_failed`,`stage: "pointer"`、`code: "connect_failed"`,`error` 点名指针写)之后,对象存储侧那次 PUT 仍可能迟到落地 ⇒ 一个已失围栏的写者的 `latest` 覆盖新写者刚发布的。围栏丢失是另一臂(`stage: "fence"`、`code: "timeout"`):那一臂不发 PUT,构造不出这扇窗。只在多副本、围栏到期、且存储侧延迟大于剩余时长时构造得出;本版不读 ETag、不做条件写,只在此披露。收法(下一版):存储支持 `If-Match` 时用 ETag 条件 PUT,不支持时 `latest` 改为带提交世代的指针对象、读者取有围栏记录的最高世代;两形由能力位如实披露。
|
|
2057
2131
|
- **指针 / 列举 / 删除由 server 直接请求 `MINIO_ENDPOINT`**(快照字节仍是 pod ↔ MinIO 直传)。server 进程必须够得着这个端点(会话快照 blob 卸载本来就从 server 进程请求同一个端点)。
|
|
2058
2132
|
|
|
2059
2133
|
| 旋钮 | 缺省 | 说明 |
|
|
2060
2134
|
|---|---|---|
|
|
2061
|
-
| `WORKSPACE_PERSIST` | `
|
|
2135
|
+
| `WORKSPACE_PERSIST` | `space` | `space` = 按会话住客的空间跨轮保留(前提:有存储后端 —— `DB_BACKEND=local` 或 SQL;`DB_BACKEND=memory` × `space` ⇒ 拒启);`off` = 每轮新建(7.125.0 的缺省)。词表外 ⇒ 拒启。只在 `REMOTE_EXEC=k8s` 时读 |
|
|
2062
2136
|
| `WORKSPACE_SNAPSHOT_KEEP` | `3` | 每个空间保留的快照份数,整数 `[1, 50]`;域外 / 小数 ⇒ 拒启 |
|
|
2137
|
+
| `WORKSPACE_SNAPSHOT_TIMEOUT_MS` | `300000` | 快照时限(毫秒,整数 `[30000, 3600000]`;域外 / 小数 ⇒ 拒启):**一个数管四处** —— run 末打包上传的墙钟上界、连沙箱时恢复的墙钟上界、空间结束时回收座**一次调用**的绝对截止(一次调用一份:会话删除 = 一个空间,托管留存 sweep = 每个域每拍一次、该域这一拍结束的全部空间;**卷回收也在内**)、本副本写者给自己每次围栏期限用的数(终态 / 开始 / 发布三次续期各一界;下一条 run 等的是**上一条 run 的写者**所配的数,不按它自己副本的数 —— 各副本同配时就是同一个数);大文件读写的墙钟上界也是它(卡住不出字节的传输由命令无进展上界先切)。构建目录 GB 级的工作区撞 5 分钟 ⇒ 调大它,或配排除表 |
|
|
2138
|
+
| `WORKSPACE_SNAPSHOT_EXCLUDE` | 未设 | 快照排除表(见上「排除表」);未设 = 打包命令与旧版逐字同 |
|
|
2063
2139
|
|
|
2064
2140
|
**失败形(都是响亮的)**:
|
|
2065
2141
|
|
|
2066
|
-
- **没配快照存储**(`MINIO_ENDPOINT` / `MINIO_ACCESS_KEY` / `MINIO_SECRET_KEY` 缺任一)⇒
|
|
2067
|
-
-
|
|
2068
|
-
- **读 `latest` 失败**(非 404 的 HTTP 错、网络错)⇒ 这条 run 连沙箱失败(`connect_failed
|
|
2069
|
-
- **`latest` 指向的快照对象不在了**(被删 / 被只盖 `.tar.gz` 的生命周期规则清掉)⇒
|
|
2070
|
-
-
|
|
2142
|
+
- **没配快照存储**(`MINIO_ENDPOINT` / `MINIO_ACCESS_KEY` / `MINIO_SECRET_KEY` 缺任一)⇒ 工作区每轮为空,`GET /v1/capabilities` 不带 `workspaceSync`;起服一行 `workspace_persist_unavailable`(`missing` 点名缺的键;缺省 `space` 也出,设 `WORKSPACE_PERSIST=off` 即不出)。
|
|
2143
|
+
- **没有存储后端**(`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 却一份都不存。
|
|
2144
|
+
- **读 `latest` 失败**(非 404 的 HTTP 错、网络错)、**读或弃提交围栏那一行失败**或**读会话住客失败**(会话店报错,或答出的不是住客令牌)⇒ 这条 run 连沙箱失败(`connect_failed`,工具调用报沙箱不可用;停驻 run 续跑时的恢复同样失败),**不会**悄悄给一个空目录、也不猜「没有提交在飞」—— 「以为恢复了其实没有」比失败更坏。读住客失败时一只 pod 都不建,另有一行 `workspace_space_unreadable_occupant`(`sessionId` / `code`:会话店错误的码,不带正文),每条 run 至多一次。修好存储 / 数据库后重跑,或设 `WORKSPACE_PERSIST=off` 即恢复。
|
|
2145
|
+
- **`latest` 指向的快照对象不在了**(被删 / 被只盖 `.tar.gz` 的生命周期规则清掉)⇒ 沙箱**照常起**:一行 `workspace_latest_dangling`(`space` / `key` / `deleted`),删掉悬空的 `latest`,这条 run 从空目录开始,它的 run 末快照成为新的 `latest`(数据已被运维规则清走:响亮、可继续)。指针删不掉 ⇒ 同一行 `deleted: false` + `error`,下一条 run 再说一次。
|
|
2146
|
+
- **`latest` 指向的快照在、却拿不回来**(桶答了却不是 200 / 解压失败 / 超过快照时限 / 传输中断)⇒ 这条 run **仍然连沙箱失败**(`connect_failed`)、**不**退到空目录 —— 用户的改动还在桶里,静默空目录 = 让人以为丢了。**一条 run 只恢复一次**:瞬时类(`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 从空目录起)。与上一条「悬空」分开:那是对象**不在**(没东西可恢复 ⇒ 空目录 + 响亮),这是对象**在**而拿不回来(⇒ 拒)。
|
|
2147
|
+
- **run 末提交围栏失守、上传失败或 `latest` 写失败** ⇒ 一行 `workspace_snapshot_failed`(`space` / `stage` = `fence` \| `upload` \| `pointer` / `code` / `error`,预签 URL 已脱敏),这条 run 在 `GET /v1/fleet/stream` 上的终态 task 帧带 `workspaceSnapshot: "failed"`;pod 照删;`latest` 不动 ⇒ 下一条 run 恢复的是**上一份**。
|
|
2071
2148
|
- **删最老的失败** ⇒ 一行 `workspace_snapshot_prune_failed`,不影响本轮(空间里只是多留几份)。
|
|
2072
|
-
-
|
|
2073
|
-
|
|
2149
|
+
- **空间结束时回收快照对象失败**(列举失败、列举被截断〔分区里超过 1000 个对象〕、某个对象删失败)⇒ 会话照删,每个失败的分区 / 阶段一行 `workspace_space_reclaim_failed`(`space` 前缀 / `sessionId` / `lane` = `space` \| `park` / `stage` = `list` \| `delete` \| `budget` \| `seat_threw`(回收座违约抛了,响亮接住、不炸进程)/ `error`,预签 URL 不进行)+ 计数 `server.workspace-space.reclaim-failed`;
|
|
2150
|
+
`budget` = 回收座这一次调用的绝对截止(= 快照时限;会话删除 = 一个空间,托管留存 sweep = 每个域每拍一次;卷回收也在内)到点,这个空间余下的列举与删除没做 —— 截止在轮到这个空间之前就过了 ⇒ 它一个请求都没发,同样一行 `budget`(配了缓存卷的,它的卷再一行 `workspace_cache_reclaim_failed{reason: "budget"}`)。截止之前的失败互不牵连(另一个分区、构建缓存卷照收)。对象留在桶里,按行里的空间前缀 / 会话 id 去桶里手清,或等桶的生命周期规则;到期会话已删、不会被下一拍重新列出;一拍最长 = 有结束空间的域数 × 快照时限,超过 sweep 间隔时中间的拍被跳过(防重入守卫照旧);**快照时限 ≥ 2 × sweep 间隔(= 租约 TTL)时,一个域内的回收可把本副本的租约耗过期 ⇒ 这一拍余下的域不跑、下一拍再起(一行 `retention_sweep_lease_lost`)—— 配法:快照时限要明显小于租约 TTL,即 `WORKSPACE_SNAPSHOT_TIMEOUT_MS`(毫秒)< 2 × `RETENTION_SWEEP_INTERVAL_SEC` × 1000 再留出该域 SQL 步骤的余量;缺省时限 300 000 ms 对 sweep 间隔 ≥ 300 s 的部署成立,间隔更短的部署调大间隔或调小时限(时限下限 30 000 ms)**。
|
|
2151
|
+
- **提交围栏那一行删不掉**(数据库 / 数据根故障)⇒ 下一条 run 等到行里的期限过去,按 `workspace_commit_abandoned` 继续(有界、响亮)。
|
|
2152
|
+
|
|
2153
|
+
**能力位**:`GET /v1/capabilities.workspaceSync = { persist, keep, timeoutMs }`(`timeoutMs` 7.126.0 起 = 快照时限,壳据此告知用户「快照上界」;排除表不上 wire),只在 k8s 车道 ∧ 配了快照存储时在场;`persist` 如实报生效值(缺省 `"space"`);报 `"space"` 的部署,会话店一定读得出住客(读不出的组合起服即拒)。壳 / 中心只在 `persist:"space"` 时渲「工作文件跨轮保留」。
|
|
2154
|
+
|
|
2155
|
+
**须知**:把 `/workspace` 当一次性临时盘用的调用方,下一条 run 会看到上一条留下的文件;回到每轮为空 = `WORKSPACE_PERSIST=off`。存储增长 = 每个空间 `KEEP` 份 tar;**空间随会话结束而回收**:`DELETE /v1/sessions/:id` 的删除协调器与托管留存 sweep 删掉会话(树)之后,同一只回收座(见 9.3d;**不在删除应答路径里** —— `DELETE` 摘掉旧订阅即答,回收在应答前已起、不被等待,一次调用一份绝对截止 = 快照时限,卷回收也在内)删该空间 `<前缀>/<化身>/space/` 下的全部对象(快照与 `latest`)和该会话停驻车道 `<前缀>/<会话 id>/` 里名字带本住客归属记号的 `*.tar.gz`
|
|
2156
|
+
(用 `MINIO_*` 同一组凭据对桶做列举与删除,修剪旧快照走的也是这条车道;停驻分区按会话 id 分区,会话 id 删后可被别人重新登记 ⇒ 只按快照名字里的归属记号判、不读任何钟,后来的住客写的带它自己的记号,不碰;升级前写的不带记号的旧形与无住客的 run 写的 `none` 不删);删后前一位住客的空间不再可达(新住客是新的化身)。回收失败不挡删除:一行 `workspace_space_reclaim_failed` + 计数 `server.workspace-space.reclaim-failed`,对象留着。
|
|
2157
|
+
**桶的生命周期规则只作兜底**(回收失败的对象、回收截止到点没删的对象、回收没跑完进程就退留下的对象、会话删除那一刻上一条 run 的提交恰好才落下的对象、本版之前已结束的空间留下的对象、不带归属记号的旧形停驻快照与 `none` 停驻快照,只有它能清):规则要对**整个 `<前缀>/`** 按龄过期(跨轮保留在 `<前缀>/<化身>/space/`、停驻快照在 `<前缀>/<会话 id>/`,都在它下面;指针与快照同一条规则);只对 `.tar.gz` 配会留下指向空处的 `latest` —— 那个空间的下一条 run 按上一条「悬空」处置从空目录起(一行 `workspace_latest_dangling`),旧文件已被规则清走。
|
|
2158
|
+
|
|
2159
|
+
### 9.3c k8s 车道:点名 env 转发(`K8S_SANDBOX_ENV`)
|
|
2160
|
+
|
|
2161
|
+
沙箱 pod 里的工具要用服务进程环境里的凭据(典型:团队形态下 `git clone` / `git push` 私有仓)时,把**变量名**写进 `K8S_SANDBOX_ENV`,值留在服务进程环境里。
|
|
2162
|
+
它和 `E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV` 是**同一个法**(解析、拒启、告警、目录行、起服行 `sandboxEnvKeys`、多租户姿态全部见上文「沙箱带外 env 名单」一节),这里只写 k8s 车道自己的三件事:
|
|
2163
|
+
|
|
2164
|
+
名单里点名 `SEMA_PKG_SOURCE`(或任何包源变量)= 把服务进程自己的那份值原样下发,显式胜过 `SANDBOX_PKG_SOURCE` 推导出的那份 —— pod 的启动命令据此走包源 hook;这是「显式名单胜派生」在 k8s 车道上的自然结果,不是第二条通道。
|
|
2165
|
+
|
|
2166
|
+
- **值去哪**:只进沙箱 pod 的容器 env(pod spec 的 `containers[0].env[]`,pod 里每个进程、每条 exec 命令都看得到)。它与 `SANDBOX_PKG_SOURCE` 推导出的包源变量在装配处合并一次,**同名时名单赢**(部署方的刻意覆盖);
|
|
2167
|
+
适配器不把它写进 pod spec 的其它位置、不拼进执行命令串、不写进 `/workspace`,因而不随跨轮保留 / 挂起的快照进对象存储;不进事件日志(起服行只报键名)。
|
|
2168
|
+
pod 里的进程读得到这些值 —— 那正是转发的目的;进程自己若把值写进 `/workspace`,它就会随快照走。
|
|
2169
|
+
- **没设 = 零转发**:`K8S_SANDBOX_ENV` 未设 / 空串时,pod 与未引入本旋钮时逐字同形(env 只有包源那一组)。只在 `REMOTE_EXEC=k8s` 时求值。
|
|
2170
|
+
- **凭据的推荐用法**:`K8S_SANDBOX_ENV=GIT_TOKEN`,镜像里的 git 配成用环境变量取凭据(例如 `GIT_ASKPASS` 指向一个读 `$GIT_TOKEN` 的小脚本,或 credential helper),
|
|
2171
|
+
令牌就不进提示词、命令串和事件日志。
|
|
2172
|
+
|
|
2173
|
+
**风险模型(与 e2b / docker 同一档:运维信任形)**:值以明文写在 pod spec 里 —— 在该 namespace 有 `get pods` 权限的人、记录请求体的集群 API 审计日志、节点上能检视容器的人都读得到。
|
|
2174
|
+
所以给的是**最小权限**令牌(能只读就只读、限单仓、可吊销),并按 namespace 收紧读 pod 的 RBAC。本旋钮只是把值放进 pod;「凭证只留在适配器侧、永不进 pod」是另一种形,不是本键的语义。
|
|
2175
|
+
|
|
2176
|
+
### 9.3d k8s 车道:按工作区空间的构建缓存卷(7.126.0 起,缺省关)
|
|
2177
|
+
|
|
2178
|
+
工作区跨轮保留只盖 `/workspace`;`$HOME` 每轮新 ⇒ Gradle 发行版与依赖、npm / pip / Go / Cargo 的下载每轮重来。把这些缓存放进 `/workspace` 又会塞进快照(撞快照时限)。
|
|
2179
|
+
`WORKSPACE_CACHE=volume` 给每个**空间**(与跨轮保留同一个空间 = 会话的这一位住客)一只持久卷,挂在工作区之外:
|
|
2180
|
+
|
|
2181
|
+
- **一个空间一只卷**:连沙箱时在沙箱 ns 里按名找 PVC `ws-cache-<空间摘要 20 位十六进制>`(标签 `sema.space=<同一摘要>`;摘要是住客化身的单向哈希,化身本身不进集群),没有就建
|
|
2182
|
+
(大小 `WORKSPACE_CACHE_SIZE`,StorageClass 用集群缺省,`ReadWriteOnce`),挂到 `WORKSPACE_CACHE_MOUNT`(缺省 `/cache`)。同空间的下一条 run 复用它;pod 删了卷留着。
|
|
2183
|
+
新建的卷 = 空目录(缓存本就可再生,这是唯一「空目录是对的」的地方)⇒ 一行 `workspace_cache_fresh`(`space` / `claim` / `size`)。
|
|
2184
|
+
- **不进快照**:挂在工作区之外 ⇒ 打包上传根本不盖它(零排除规则)。
|
|
2185
|
+
- **工具缓存指过去 = pod env,不烤镜像**:只在这只 pod 真挂上卷时注入一张闭表 —— `GRADLE_USER_HOME=<挂载点>/gradle`、`MAVEN_OPTS=-Dmaven.repo.local=<挂载点>/m2`、
|
|
2186
|
+
`npm_config_cache=<挂载点>/npm`、`PIP_CACHE_DIR=<挂载点>/pip`、`UV_CACHE_DIR=<挂载点>/uv`、`GOMODCACHE=<挂载点>/go`。
|
|
2187
|
+
它层在包源 env 与运维显式点名的 env **之下**:同名的显式值胜。⚠️ 这不能用来「保留镜像自己的 `MAVEN_OPTS`」:显式转发的是**服务进程环境里**的那个名的值(不是镜像里烤的值),pod env 又胜过镜像 ENV —— 开着缓存卷,pod 里的 `MAVEN_OPTS` 就是这一项的值,镜像里烤的那份被盖。
|
|
2188
|
+
要保留镜像值里的参数:把想要的完整值(含需要的 `-Dmaven.repo.local=<挂载点>/m2`)写进服务进程的 `MAVEN_OPTS` 再把这个名字点进 `K8S_SANDBOX_ENV`(显式值胜过缓存项);或 `WORKSPACE_CACHE=off`。这张闭表没有「只关一项」的旋钮。
|
|
2189
|
+
**Rust 依赖缓存本版不盖**:Cargo 没有「只放下载缓存」的 env,`CARGO_HOME` 是它的整个家目录(镜像烤进去的工具链代理与注册表镜像配置都在那里),指到卷上会把这两样一起丢掉。
|
|
2190
|
+
- **卷拿不到 ⇒ 不拒这一轮**:卷建不成(Role 少动词 / API 拒 / 网络,`reason: claim_failed`)、卷在快照时限(`WORKSPACE_SNAPSHOT_TIMEOUT_MS`)内没绑定(集群没有缺省 StorageClass /
|
|
2191
|
+
供给器不在,`claim_unbound`)、卷绑上了而挂着它的 pod 起不来(挂载失败 / 卷所在节点没了,`claim_unmountable`)—— 后两种把等它的那只 pod 删掉、换一只不挂卷的 —— 、没有这只会话(会话店答「没有」,`incarnation_unknown`)⇒ run **照常起**:不挂卷、不注入缓存 env,
|
|
2192
|
+
计数 `server.workspace-cache.unavailable`(fail-open 登记表第 129 行)+ 一行 `workspace_cache_unavailable`(`space` / `claim` / `reason` / `error`),每条 run 至多一次。
|
|
2193
|
+
- **读住客失败 ⇒ 拒这一轮**:会话店报错(或答出的不是住客令牌)不是「没有空间」—— 与 9.3b 同一只 `connect_failed`(一只 pod 都不建,一行 `workspace_space_unreadable_occupant`,不计数);拒句点名开着的旋钮
|
|
2194
|
+
(只开卷时是 `WORKSPACE_CACHE=off`)。重跑即重新读。
|
|
2195
|
+
- **委派子代 / 后台代理不挂卷**(与不开空间同一条法:它们与父并发,不共享父的空间)—— 不计数、不出行。
|
|
2196
|
+
- **卷的寿命 = 空间的寿命**:pod 删卷留;**空间结束时随之回收** —— `DELETE /v1/sessions/:id` 的删除协调器与托管留存 sweep 删掉会话树之后,都按标签
|
|
2197
|
+
`sema.space=<那位住客空间的摘要>` 删卷(同一只回收座;找不到 = 零动作;删失败 = 一行 `workspace_cache_reclaim_failed{reason: "delete_failed"}`,回收座这次调用的截止先到〔9.3b `budget`〕= 同一行 `reason: "budget"`;各计一次 fail-open
|
|
2198
|
+
`server.workspace-cache.reclaim-failed`,**不挡删除**)。同一只回收座也删该空间的快照对象与该会话的停驻快照(9.3b;失败另一行 `workspace_space_reclaim_failed`,两部分互不牵连)。
|
|
2199
|
+
**遗留卷**(本版之前建的、或开过又关掉 `WORKSPACE_CACHE` 期间结束的空间)运维按标签 `sema.space` 清一次。`local-path` 的卷在节点本地盘上,多节点集群里 pod 会被调度到卷所在的节点。
|
|
2074
2200
|
|
|
2075
|
-
|
|
2201
|
+
| 旋钮 | 缺省 | 说明 |
|
|
2202
|
+
|---|---|---|
|
|
2203
|
+
| `WORKSPACE_CACHE` | `off` | `volume` = 每个空间一只构建缓存卷;`off` = 不建卷(pod 与旧版逐字同)。词表外 ⇒ 拒启。前提:有存储后端(`DB_BACKEND=memory` × `volume` ⇒ 拒启,出路 `WORKSPACE_CACHE=off`)。只在 `REMOTE_EXEC=k8s` 时读 |
|
|
2204
|
+
| `WORKSPACE_CACHE_SIZE` | `20Gi` | 卷的申请量(k8s quantity 形,如 `20Gi` / `500Mi`;形不合 ⇒ 拒启) |
|
|
2205
|
+
| `WORKSPACE_CACHE_MOUNT` | `/cache` | 卷在 pod 里的挂载点(绝对路径;与工作区挂载 `K8S_MOUNT_PATH` 相同 / 互相嵌套 ⇒ 拒启) |
|
|
2206
|
+
|
|
2207
|
+
**RBAC**:沙箱 Role 要有 `persistentvolumeclaims` 的 `get` / `create` / `delete` / `deletecollection`(回收按标签删 = `deletecollection`;chart 与 cluster 静态清单 7.126.0 起已带)。
|
|
2208
|
+
**能力位**:`GET /v1/capabilities.workspaceSync.cache = { mount, size }`,present-iff k8s 车道 ∧ `WORKSPACE_CACHE=volume`(与快照存储无关:没配存储而开了卷的部署,
|
|
2209
|
+
`workspaceSync` 只有 `cache` 一个成员 —— 读跨轮保留一律读 `persist === "space"`)。
|
|
2210
|
+
|
|
2211
|
+
### 9.3e k8s 车道:沙箱出网策略的射程与 pod 的 IPv6(7.127.0 起)
|
|
2212
|
+
|
|
2213
|
+
`deploy/sema-up` chart 的 `networkpolicy-sandbox.yaml` 给沙箱 ns 一条 egress 白名单。**律**:集群内只放行点名的脸(kube-dns、chart 自带 MinIO、
|
|
2214
|
+
`sandbox.networkPolicy.objectStore` 点名的 pod);集群外只放行 **IPv4 公网面** —— 私网 / 共享 / 链路本地 / 环回六段(`10/8`、`172.16/12`、`192.168/16`、`100.64/10`、`169.254/16`、`127/8`)
|
|
2215
|
+
整段不放行,公网面上的集群地址按 `clusterCidrs`(pod / service 段)与 `nodeCidrs`(全部节点的全部 IPv4 Internal / External 地址,`kube-up.sh` 遍历节点自动填)再除外;**IPv6 不写放行规则**。
|
|
2216
|
+
|
|
2217
|
+
- **为什么是整段而不是一张地址表**:节点的地址不止 kube 报的那一只(容器网桥、第二块网卡、隧道口、其余节点),点名表漏一只就漏一面;六段整段拒之后,落在私网里的节点地址
|
|
2218
|
+
不依赖枚举完整。只有**公网**节点地址仍要点名。`kube-up.sh` 自动填 `nodeCidrs` 与起服自检(下面)读的是同一处:节点对象上报的地址(`status.addresses` 里的
|
|
2219
|
+
InternalIP / ExternalIP)—— 上报了而没点名的,自检逐个报出来;**没有上报进节点对象的公网地址**(1:1 NAT 的弹性 IP、没上报的第二块公网网卡、k3s 没带
|
|
2220
|
+
`--node-external-ip` 起的节点)既不会被自动填进 `nodeCidrs`,也不会被自检报出,要运维自己写进 `nodeCidrs`;起服之后才加入的节点,到下一次起服才被判。
|
|
2221
|
+
- **为什么不写 `::/0`**:NetworkPolicy 只有放行规则、按并集叠加,`::/0 except …` 从不拒任何东西,只会把执行 IPv6 的集群上本来只剩点名脸的 v6 出网整面放开(连带没点名的节点全局 v6 地址)。
|
|
2222
|
+
不写 ⇒ 执行 v6 的实现上 v6 出网只剩点名的集群内脸(kube-dns、对象存储);**只执行 IPv4 的实现(k3s 单栈缺省即是)根本不管 pod 的 IPv6**(link-local 照通)—— 这一面要在 pod 侧关,见 `K8S_SANDBOX_IPV6`。
|
|
2223
|
+
- **射程(这张策略是收窄,不是边界)**:① pod 所在节点的往来流量是否放行由 CNI 的实现决定(k8s 规范本身不保证拒);② 宿主上被 DNAT 的发布端口(`docker -p` 一类)
|
|
2224
|
+
在策略评估之前改写目的地址,经**任何**节点地址(含点名的那只)都可能到达 —— 策略层管不到,宿主上别发布不该被沙箱摸到的端口;③ CNI 不执行 NetworkPolicy 时整张不生效(对象在、不拒启)。
|
|
2225
|
+
- **私网里的包源 / 代码托管**:六段整段拒之后沙箱到不了它们。要放行,在沙箱 ns 另建一条 NetworkPolicy 放行那个地址(或那台主机的 /32)—— 策略按并集叠加,chart 升级不碰它;
|
|
2226
|
+
放得太宽(覆盖到节点对象上报的地址)起服自检会逐个报出。
|
|
2227
|
+
- **`objectStore` 的 namespace**:条目省 `namespace` ⇒ 跟本 release 的 ns(与 chart 自带 MinIO 同一处取值);在别的 ns 的脸照写 `namespace`。
|
|
2228
|
+
|
|
2229
|
+
**pod 的 IPv6(`K8S_SANDBOX_IPV6`,chart values `sandbox.ipv6`)**:`enabled`(缺省)= 不动 pod,pod spec 与本版之前逐字同形;`disabled` = pod 级 `securityContext.sysctls`
|
|
2230
|
+
带 `net.ipv6.conf.all.disable_ipv6=1` 与 `net.ipv6.conf.default.disable_ipv6=1`。词表外 / 空串拒启(不收 `on` / `off`:YAML 会把它们读成布尔)。
|
|
2231
|
+
|
|
2232
|
+
- **前提**:这两只 sysctl 不在 k8s 的 safe sysctl 表里 ⇒ **每个节点**的 kubelet 要 `--allowed-unsafe-sysctls` 点名它们(k3s:`--kubelet-arg=allowed-unsafe-sysctls=…`),
|
|
2233
|
+
没放行的节点上沙箱 pod 起不来(车道连沙箱失败,拒句带 pod 的失败原因);沙箱 ns 不能 enforce PodSecurity `baseline` / `restricted`(那两档只许 safe 表)。
|
|
2234
|
+
Kata 运行时把 namespaced sysctl 设在客体内核里,作用在客体的网卡上。
|
|
2235
|
+
- **pod 的 sysctl 只有这一个属主**:嵌入方的 pod-spec 补丁带 `podSecurityContext.sysctls` ⇒ 拒(补丁是浅合并,会把这里的关闭项整只换掉)。
|
|
2236
|
+
|
|
2237
|
+
**起服自检**(`REMOTE_EXEC=k8s`,起服一次、不挡起服):服务用自己的 k8s 账号读节点表(`nodes` get / list,集群级)与沙箱 ns 的 NetworkPolicy(`networkpolicies` get / list),
|
|
2238
|
+
按「选中沙箱 pod 的 Egress 策略的放行规则并集」判每个节点的每个 InternalIP / ExternalIP。读出的缺口每个一行 warn `sandbox_egress_policy_gap`(带 `effect` 后果句)并计数
|
|
2239
|
+
`server.sandbox-egress.policy-gap`(fail-open 登记表第 135 行,记债);零缺口一行 info `sandbox_egress_policy_checked`。`kind` 闭集:
|
|
2240
|
+
|
|
2241
|
+
| `kind` | 什么时候 | 去哪改 |
|
|
2242
|
+
|---|---|---|
|
|
2243
|
+
| `node_address_open` | 某节点的某个地址落在一条放行 ipBlock 内(行带 `node` / `addressType` / `address` / `policy` / `ports`) | 把地址加进 `nodeCidrs`(kube-up 重跑会重新遍历节点),或收窄那条放行 |
|
|
2244
|
+
| `policy_absent` | 没有一条 NetworkPolicy 以 Egress 选中沙箱 pod | 开 `sandbox.networkPolicy.enabled`,或核 CNI 自己的策略 |
|
|
2245
|
+
| `ipv6_enabled` | `K8S_SANDBOX_IPV6=enabled` | 见上一段;集群执行 v6 时这一行只是提醒 |
|
|
2246
|
+
| `unreadable` | 节点表或策略表读不到(`what` = `nodes` / `networkpolicies`;`detail` = HTTP 码或错误句) | 给服务账号补两只只读(chart 与 cluster 静态清单已带;手写 RBAC 的部署要补) |
|
|
2076
2247
|
|
|
2077
|
-
|
|
2248
|
+
自检是**读数,不是边界**:起服之后加入的节点在下一次起服读到;射程里的三面(所在节点、宿主 DNAT、CNI 不执行)读不出,不在闭集里。
|
|
2078
2249
|
|
|
2079
2250
|
### 9.4 auto 模式(`permissionMode:"auto"`):武装三态、本地 org deny、自查(与 core 的意图武装式合修)
|
|
2080
2251
|
|
|
@@ -2092,8 +2263,8 @@ armed ⟺ 本次请求 permissionMode 是 "auto" ∧ 本部署装配了分类
|
|
|
2092
2263
|
|
|
2093
2264
|
| 旋钮 | 缺省 | 语义 |
|
|
2094
2265
|
|---|---|---|
|
|
2095
|
-
| `PERMISSIONS_DISABLE_AUTO_MODE` | `false`(opt-in) | CC/center/settings 键名 `permissions.disableAutoMode` 的 **env 腿**(settings-schema 现无 `permissions` 域 ⇒ 7.106.0 起目录行**不带**中心座位,`writeLanes` 只有 `env
|
|
2096
|
-
| `AUTO_MODE_CLASSIFIER_TIMEOUT_MS` | 未设(⇒ core 缺省) | 7.102.0
|
|
2266
|
+
| `PERMISSIONS_DISABLE_AUTO_MODE` | `false`(opt-in) | CC/center/settings 键名 `permissions.disableAutoMode` 的 **env 腿**(settings-schema 现无 `permissions` 域 ⇒ 7.106.0 起目录行**不带**中心座位,`writeLanes` 只有 `env`):**tighten-only** 棘轮 —— `true` ⇒ 本部署每个 principal 的 `caps.autoMode` 折成 `false`(center 授予 `true` **翻不回**);未设 ⇒ 不动 caps。布尔词表(`true/false/1/0/yes/no/on/off`),词表外的值**拒启**(安全轴旋钮不许静默回默认)。目录行见 `GET /v1/config/catalog`(approval 域,security 轴) |
|
|
2267
|
+
| `AUTO_MODE_CLASSIFIER_TIMEOUT_MS` | 未设(⇒ core 缺省) | 7.102.0 起:auto 分类器**一级梯子**的判决上限(整数 ms)→ core `RunnerDeps.autoMode.timeoutMs`。一级到点没裁 ⇒ 走下一级(`AUTO_MODE_CLASSIFIER_FALLBACK` 决定有没有下一级),走完仍没裁 ⇒ 这次门控调用**整条拒**、不出卡;最坏等待 ≈ 级数 × 本值 + 一次装配。**自托管慢网关**(排队 + prefill 以分钟计)在缺省上限内答不出 ⇒ auto 下工具一律被拒 —— 按后端实测留足(如 `180000`)。未设 / 空串 ⇒ 本仓**不铸键**,由引擎落它自己的缺省(不复制上游数字);合法域 `[1000, 2147483647]` 内的十进制整数,`0`(本座没有「关」)/ 亚秒 / 小数 / `1e5` 这类拼法 / 越 Node 定时器上界一律**拒启**。旋钮无条件(与 auto 是否武装无关);不影响 `permissionModeAuto` 读面。目录行见 `GET /v1/config/catalog`(approval 域) |
|
|
2097
2268
|
| `CROSS_SESSION_INBOUND` | 未设 | 跨终端会话设计线:CC settings 键名 `crossSessionInbound` 的**部署/组织层**(引擎层形的 `managed` 层,CC `policySettings` 对位)—— 本部署对**入站跨会话消息**的治理四态(词表取自引擎闭集,core 7.30.0 起):`wake` 投递且**宿主**可在收件箱增长时起一个 turn / `next-turn` 在下一个 turn 边界投递 / `hold` 停在收件会话的待审队列(模型看不见、不能据此行动)/ `refuse` 本部署整体退出这条车道。⚠️ 本服务宿主**不起 turn**,所以在本服务上 `wake` 按引擎契约的降级臂读作 `next-turn`。🔴 **旧词 `accept` 自 7.100.0(core 7.30.0 提货)起拒启** —— 它的原义就是 `next-turn`,改成那个词即可(零别名,拒启即通告)。🔴 **未设 ≠ `next-turn`**:未设 = 这一层不表态,由其余层与引擎的 mode-parity 判定决定。词表外的值**拒启**。⚠️ **生效前提**:引擎的 peer 目录席(`RunnerDeps.peerDirectory`)在场 —— 7.91.0 起已接线(会话枚举面 ∧ mailbox 店 ∧ 收件人生命周期面三合取),缺任一合取项的部署上本旋钮仍「就位待命」;目录行见 `GET /v1/config/catalog`(approval 域,security 轴) |
|
|
2098
2269
|
| `CROSS_SESSION_DIALOG_EXPIRY` | 未设(⇒ 引擎缺省 `5m`) | 同族:CC settings 键名 `dialogExpiry` 的部署层 —— 被 hold 的跨会话消息等待人审多久后按**安全缺省**结算(过期丢弃,**带回执**告知发送方,不静默吞)。词表 `60s`/`5m`/`10m`/`never`(`never` = 不设期限);未设 ⇒ 本仓**不铸键**,由引擎落它自己的缺省(不复制上游缺省值)。词表外拒启。⚠️ 生效前提同上。另注:CC 的同名键还有第二个消费面(转发到远端客户端的审批对话框停靠时长),本旋钮**今天不驱动**那一面 |
|
|
2099
2270
|
|
|
@@ -2118,7 +2289,7 @@ GET /v1/capabilities?permissionMode=auto (AUTONOMY=plan 的部署)
|
|
|
2118
2289
|
center 硬失败 fail-closed 拒项)/ `local_denied`(本地旋钮 `PERMISSIONS_DISABLE_AUTO_MODE=true`,改本机 env)/ `settings_denied`
|
|
2119
2290
|
(settings 层 `permissions.disableAutoMode:"disable"`——本面不产,归 run 级面)/ `resolver_fault`(per-principal resolver 抛错,
|
|
2120
2291
|
引擎同形全拒)。
|
|
2121
|
-
- `intentArming`:**7.107.0
|
|
2292
|
+
- `intentArming`:**7.107.0 删除**。7.57.0–7.105.0 由安装包探针答(`false` = 旧式「组织授予」极性),7.106.0 恒 `true`(引擎只剩意图武装式
|
|
2122
2293
|
一种极性:无 center 也能 auto)—— 不携带信息;「会不会武装」读 `armed` / `reason`。
|
|
2123
2294
|
- `model`:分类器**配置**路由(role 回落序 `classifier → summarize → default` 的 catalog 名),不是本次真用的模型。
|
|
2124
2295
|
- `entitlementSource` 仍只说「center 源在不在」;新武装式下它为 `false` **不再**意味着 auto 不武装。
|
|
@@ -2151,7 +2322,7 @@ plan 起步的任务把计划交给人之后停在 `plan_review` 门上。`POST
|
|
|
2151
2322
|
的任务(模型自己调 `EnterPlanMode` 那一形 —— 批准把它送回进 plan 之前的档)送它,同样是 `400`;`AUTONOMY=plan` 下提交的任务存储词就是 plan,照收。
|
|
2152
2323
|
解除会写回这条会话的续跑输入 —— 此后每一次挂起→续跑都沿用解除后的档,不回落 plan(`AUTONOMY=plan` 也不把它拉回:地板只在提交入口盖一次)。
|
|
2153
2324
|
7.122.0 之前 `edit` 之后仍是只读手(模型被告知开干却没有写手),`edit` 上送这一位是 `400`。
|
|
2154
|
-
🔴 **这个体是闭集(7.86.0
|
|
2325
|
+
🔴 **这个体是闭集(7.86.0)**:受理集 = `decision` / `editedPlan` / `reason` / `permissionModeAfter` /
|
|
2155
2326
|
`boundCallId` 五键,集外键(含定谳前的旧拼法 `nextPermissionMode`)**响亮拒** `400 request.body_shape` +
|
|
2156
2327
|
机读两桶逐字点名,**不再静默丢** —— 与 `/v1/approvals/:id/decide` 同一只机器、同码同串。
|
|
2157
2328
|
逐字契约见 `docs/ASSISTANT-WIRE-CONTRACT.md` §4c。
|
|
@@ -2180,7 +2351,7 @@ judgeRequestWebSearch(req.settings?.webSearch) // 单用户车道才问;多租
|
|
|
2180
2351
|
TOC 本地形)时被**采纳**;`REQUIRE_PRINCIPAL=true`(多租户)上一段**形对**的配置**结构性够不着**——不是被拒、也不留任何
|
|
2181
2352
|
`warn`/`capabilities` 位说"你发的 webSearch 被忽略了"(对比 `mcpServers` 有 `capabilities.mcpInjection` +
|
|
2182
2353
|
`mcp_injection_dropped` 日志可读;`settings.webSearch` 没有对应的能力探测位,`src/http/wire-types.ts:320-323`)。
|
|
2183
|
-
🔴 **别把
|
|
2354
|
+
🔴 **别把 7.82.1 起的新位读成这个探测位**:`GET /v1/capabilities` 的 `webSearch.backend`说的是
|
|
2184
2355
|
**部署默认后端是哪一只**,**不是**「我发的 per-request `settings.webSearch` 会不会被采纳」——后者在多租户上
|
|
2185
2356
|
依旧是结构性够不着、无 warn、无位,这一段的结论一字未变。
|
|
2186
2357
|
原因是安全边界(与多租户能力配置隔离规则同源):per-request `endpoint`/`apiKey` 是能力配置,多租户下一个租户把 `searxng`
|
|
@@ -2194,10 +2365,10 @@ TOC 本地形)时被**采纳**;`REQUIRE_PRINCIPAL=true`(多租户)上一段**形
|
|
|
2194
2365
|
|
|
2195
2366
|
| 键 | 类型 | 必填 | 默认 | 形错(⇒ 400) |
|
|
2196
2367
|
|---|---|---|---|---|
|
|
2197
|
-
| `provider` | `"brave"|"tavily"|"searxng"` | **是** | 无 | 缺席、不是字符串、或不是三词之一(大小写 / 首尾空白照旧归一)——一段 `settings.webSearch` 就是在点名目的地,认不出就 400 点名 `settings.webSearch.provider`(括号里只说哪一形:`it is missing` / `it is not a string` / `it is not one of them`,**不回显**收到的串 —— 7.101.0
|
|
2368
|
+
| `provider` | `"brave"|"tavily"|"searxng"` | **是** | 无 | 缺席、不是字符串、或不是三词之一(大小写 / 首尾空白照旧归一)——一段 `settings.webSearch` 就是在点名目的地,认不出就 400 点名 `settings.webSearch.provider`(括号里只说哪一形:`it is missing` / `it is not a string` / `it is not one of them`,**不回显**收到的串 —— 7.101.0 起),**不**静默换成部署 env 的 backend(改前:整段丢弃、回落 env backend、不报错)。词表守卫与 env 腿**同一只** `isWebSearchProvider`;env 腿词表外是「不装配」(部署自己的缺席) |
|
|
2198
2369
|
| `apiKey` | string(明文) | brave/tavily 建议带(缺了首次调用才报错,见下表);searxng 不读 | 无(不继承 env 的 `WEB_SEARCH_API_KEY`);空串同缺席 | 不是字符串,或不能当 HTTP 请求头值(含换行 / NUL / 其它控制字符 / U+007F / U+00FF 以上字符等;判据 = 运行时构造并发出请求头的那条流水线,与 env `WEB_SEARCH_API_KEY` 同一只;7.118.0 起,句 `settings.webSearch.apiKey must be usable as an HTTP header value (it contains a character an HTTP header cannot carry, such as a line break)`,**不回显值**;改前采纳、首次搜索时运行时抛的错误句带着整串明文)。首尾空白(含换行)运行时会剥掉 ⇒ 照收 |
|
|
2199
|
-
| `endpoint` | string | searxng 必需(缺了首次调用才报错);brave/tavily 可选覆盖 | 无(不继承 env 的 `WEB_SEARCH_ENDPOINT`);空串同缺席 | 不是字符串,或不是 `scheme://host[:port][/path]` 形的绝对 URL(`localhost:8888` 这类少了 `scheme://` 的也算),或带 userinfo(`user:pw@` / 只带 `user@`;7.101.0
|
|
2200
|
-
| `searxngParams` | `Record<string,string>`(对象)或 `"name=value;name=value"`(字符串) | 否 | 无;空串 / 空对象同缺席 | 与 env `WEB_SEARCH_SEARXNG_PARAMS` 同一套文法:数组、数字、一段不是 `name=value`、名不是参数名(字母 / 数字 / `_` / `-` / `.`)、值为空或不是字符串、同名两次(句里只报第几项 `entry #N`,不回显名或值 —— 7.101.0
|
|
2370
|
+
| `endpoint` | string | searxng 必需(缺了首次调用才报错);brave/tavily 可选覆盖 | 无(不继承 env 的 `WEB_SEARCH_ENDPOINT`);空串同缺席 | 不是字符串,或不是 `scheme://host[:port][/path]` 形的绝对 URL(`localhost:8888` 这类少了 `scheme://` 的也算),或带 userinfo(`user:pw@` / 只带 `user@`;7.101.0 起,句 `settings.webSearch.endpoint must not carry userinfo (user or user:password before the host)`,不回显值) |
|
|
2371
|
+
| `searxngParams` | `Record<string,string>`(对象)或 `"name=value;name=value"`(字符串) | 否 | 无;空串 / 空对象同缺席 | 与 env `WEB_SEARCH_SEARXNG_PARAMS` 同一套文法:数组、数字、一段不是 `name=value`、名不是参数名(字母 / 数字 / `_` / `-` / `.`)、值为空或不是字符串、同名两次(句里只报第几项 `entry #N`,不回显名或值 —— 7.101.0 起)。⚠️ **未列入已发布的 `@sema-agent/sdk` 8.8.0 `SettingsWebSearch` 类型**(该接口只有 `provider`/`apiKey`/`endpoint`/`maxResults` 四键、无开放下标)——server 侧代码认这个键,但当前发布的 TS 类型接不到它;手写 JSON 请求体仍可以发,server 会照常解析(源码头注自述:字段和消费点先落地,配置录入面尚未跟上,是一笔尚未还清的既有债务) |
|
|
2201
2372
|
| `maxResults` | number | 否 | 10(与 env 同一 clamp,`[1,20]`,小数向下取整;999 夹到 20 不是形错) | 不是数,或 < 1(`"10"` 这种字符串也算) |
|
|
2202
2373
|
| *(无)* `timeoutMs` | — | — | — | **per-request 车道没有这个字段**——即便部署用 `WEB_SEARCH_TIMEOUT_MS` 配了非默认超时,per-request 生效那次调用永远退回 backend 自己的默认(10000ms,下限 1000ms),因为"整段整取"意味着 env 的 `timeoutMs` 根本不在 `reqWebSearch` 那个新对象里 |
|
|
2203
2374
|
| *(无)* `fetchImpl` | — | — | — | 同上,仅测试注入用,per-request 车道不可达 |
|
|
@@ -2233,6 +2404,34 @@ WebSearch 工具的 `tool_result` 错误文本,回到模型的对话里,**不是
|
|
|
2233
2404
|
这纯粹是**客户端约定**,本仓不读取、不校验、也不感知任何这类客户端侧 env 命名(这条命名事实来自任务书,
|
|
2234
2405
|
本车未读 cli 源码核实,列入收车档「未闭环」)。
|
|
2235
2406
|
|
|
2407
|
+
### 9.5b `settings.hooks` 的工具事件 matcher:提交时判什么、拒什么(7.126.0 起结局只取决于配置)
|
|
2408
|
+
|
|
2409
|
+
`PreToolUse` / `PostToolUse` / `PostToolUseFailure` 三个工具事件的 `matcher` 按调用派发到的工具的 **wire 名**匹配(不是别名)。**新提交**(`POST /v1/runs` · `/v1/tasks` · `/v1/tasks/stream`;续跑重放不判)
|
|
2410
|
+
对每只 matcher 做判断,下面三种情形都答 **`400 request.field_invalid`**,体带 `field: "settings.hooks"` 与 `detail`,按 `detail.reason` 分形:
|
|
2411
|
+
|
|
2412
|
+
| `detail.reason` | 触发 | `detail` 其余键 | 怎么改 |
|
|
2413
|
+
|---|---|---|---|
|
|
2414
|
+
| `dead_alias` | matcher 选中某个工具的别名、却选不中它的正名(钩子永不命中) | `event` · `alias` · `canonical` | 改成正名(`ListPeers` → `ListAgents`) |
|
|
2415
|
+
| `over_budget` | 正则形 matcher 的一次求值在 `1 000 ms` 内没有结束(灾难回溯) | `event` · `groupIndex`(该事件下第几组,从 0 起)· `budgetMs` | 改成工具名列表(`A\|B`),或去掉嵌套重复的正则写法(如 `(.*)*`) |
|
|
2416
|
+
| `invalid_regex` | 正则形 matcher 编不过(如 `(`、`[Read`) | `event` · `groupIndex` | 修正则,或改成工具名列表(`A\|B`) |
|
|
2417
|
+
|
|
2418
|
+
- **预算是每只 matcher、每次求值各一份**:不共享、不随同一份设置里 matcher 的多少耗尽,判据里不读钟。所以同一份设置的结局只取决于设置本身 —— 机器忙闲不会让它一次 400、一次 202。
|
|
2419
|
+
良性 matcher 的求值是微秒到毫秒级,预算给的是数个数量级的余量;灾难回溯(指数级)用任何预算都跑不完。多只 matcher 时按配置顺序(事件 → 组)遇到的第一只被拒的定结局。
|
|
2420
|
+
- **为什么超时是拒绝、不是放行**:运行期每次工具调用都用同一个函数匹配 matcher、没有预算;一只在提交时都求不完的 matcher 放进去,会让之后每次工具调用卡住。
|
|
2421
|
+
- 拒句点名是**哪一只** matcher(`settings.hooks.<event>[<groupIndex>].matcher`):`over_budget` 一形带预算、不回显 matcher 原文;`invalid_regex` 一形回显 matcher 原文与编译错误句(各截 200 字符,`detail` 不带这两段)。
|
|
2422
|
+
- **这一判只在 hooks 会被采纳的车道上执行正则**(单用户车道;多租户车道 `settings.hooks` 整只被忽略,只做不执行正则的字面判):多租户部署不会因此多出一个在提交线程上跑调用方正则的面。
|
|
2423
|
+
- 不变的:matcher 超 `8 192` 字符不读(照旧受理,运维侧记一笔 `server.hooks.matcher-unjudged`)。编不过的正则:三个工具事件上 `400`(上表第三行,7.126.0 起;此前受理而运行期该组从不跑);写在 `StopFailure` / `PreCompact` / `PostCompact` 上的仍受理,运行期该组不跑并记 `hook_matcher_invalid`。
|
|
2424
|
+
|
|
2425
|
+
### 9.5c `settings.hooks` 的 `http` 钩子:header 里的环境变量展开(7.126.0 起与壳同一只读法)
|
|
2426
|
+
|
|
2427
|
+
`http` 条目 header **值**里的 `$NAME` / `${NAME}` 按条目的 `allowedEnvVars` 展开,读法是契约包 settings-schema 的 `interpolateHookEnvVars`(壳与本服务同调,同一份钩子定义到端点的字节相同):
|
|
2428
|
+
- 名字只认大写 `[A-Z_][A-Z0-9_]*`;小写名(`$my_token`)是普通文字,原样发出。
|
|
2429
|
+
- 列在 `allowedEnvVars` 里 ⇒ 展开成钩子环境里的值(钩子环境 = 净化后的进程环境 allowlist + 同请求 `settings.env`;服务进程的密钥不在其中);钩子环境里没有这个名字 ⇒ 空串。
|
|
2430
|
+
- 名单外的引用 ⇒ 空串;凭据名表里的名字(`ANTHROPIC_API_KEY` 一类,27 个名字 × 裸名与 `INPUT_` 前缀)一律空串,点进名单也一样。
|
|
2431
|
+
- 每个 header 值剥 CR / LF / NUL;header 名不展开。
|
|
2432
|
+
- 置空(名单外 / 凭据名)时日志一行 `hook_http_header_env_blanked`(名字与原因,不带值)。
|
|
2433
|
+
例:`"headers": { "Authorization": "Bearer ${HOOK_TOKEN}" }`、`"allowedEnvVars": ["HOOK_TOKEN"]`,并在请求 `settings.env` 里给 `HOOK_TOKEN`。7.125.x 及更早的读法不同(升级见 MIGRATION)。
|
|
2434
|
+
|
|
2236
2435
|
### 9.6 trace 脱敏的射程:与出站请求体无关
|
|
2237
2436
|
|
|
2238
2437
|
🔴 **脱敏 ≠ 出站控制**:trace 脱敏(账本 / SSE / turns / 审批预览里的 `«redacted»` 标记)只作用于
|
|
@@ -2257,6 +2456,8 @@ WebSearch 工具的 `tool_result` 错误文本,回到模型的对话里,**不是
|
|
|
2257
2456
|
模型服务,即便截获成功,也不满足「内容不出机器」这个更高的目标,截获只证明了脱敏的射程,不证明数据没有
|
|
2258
2457
|
离开你控制的边界**。
|
|
2259
2458
|
|
|
2459
|
+
**日志不是原形面**:运维结构化日志里 `engine_notice` 行的 `message` 与会话流上同一条通知的 `message` 过同一只脱敏(认出凭据形才换成 `«redacted…»`,没有凭据形的句子逐字节原样;会话流行另有 1000 字上限,日志行不截),`detail` 里的规则文本(`permission.allowed_tool_not_granted` 的 `entries[].entry`、`permission.rule_row_dropped` 的 `rows[].rule`)是条目的脱敏原值、`detail` 其余字段逐字 —— 同一条规则文本在 `message` 与 `detail` 里的脱敏是同一只脱敏器;已知边界:引擎把条目里的零宽字符写成 `\u200b` 转义,夹零宽字符的凭据在 `message` 里(日志行与会话流行同)认不出,机读位的规则文本是在原串上认的、不受此限。
|
|
2460
|
+
|
|
2260
2461
|
### 9.7 bake-runner(构建宿主守护进程)的 env(7.103.0 起成文)
|
|
2261
2462
|
|
|
2262
2463
|
`bake-runner`(`npm run start:bake-runner` = `node dist/bake-runner/main.js`)是跑在构建宿主上的**独立进程**:它向 image-api
|
|
@@ -2276,8 +2477,8 @@ WebSearch 工具的 `tool_result` 错误文本,回到模型的对话里,**不是
|
|
|
2276
2477
|
| `BAKE_ENV_ALLOW` | 空 | 子进程 env 的点名放行名单(见下) |
|
|
2277
2478
|
| `LOG_LEVEL` | `info` | 日志档位 |
|
|
2278
2479
|
|
|
2279
|
-
**子进程 env(7.103.0
|
|
2280
|
-
`docker image inspect` / `df` —— 拿到的 env = 本进程 env **剥密**:core 的一变量一判(7.106.0 起 = core 7.35.0
|
|
2480
|
+
**子进程 env(7.103.0,剥密收口的残余面)。** runner 起的每一只子进程组 —— `build.sh`,以及主机命令 `git fetch` / `docker push` /
|
|
2481
|
+
`docker image inspect` / `df` —— 拿到的 env = 本进程 env **剥密**:core 的一变量一判(7.106.0 起 = core 7.35.0,参照实现 CC 2.1.280 的形:
|
|
2281
2482
|
凭据名表 / `OTEL_*` 前缀 / `SEMA_` 命名空间 / 名字形状 / 值里带凭据,动作恒为整只删)∘ 服务配置目录里的 secret 名 ∘ 名单型旋钮原文。runner 自己的
|
|
2282
2483
|
`BAKE_RUNNER_TOKEN` 在内。剥了什么在第一次起组时记一行 `secret_env_scrubbed`(warn,site = `server.bake-runner.spawn-env`,带 `allowed` 与出路句;只键名,
|
|
2283
2484
|
不带值)。`PATH` / `HOME` / `NO_PROXY` / `DOCKER_CONFIG` / `BAKE_IMAGE_REGISTRY` / `RECIPE_GITSHA` 这类非密钥键照常传。
|
|
@@ -2355,7 +2556,7 @@ web-admin 拉取 / fleet 公告 / 凭证续签、WebSearch、对象存储、OTLP
|
|
|
2355
2556
|
- **启动日志**:恰一行 `outbound_proxy`,`{httpProxy, httpsProxy, noProxy, loopbackDirect, runtime}`(代理值 display-safe;`httpsProxy` 是 https
|
|
2356
2557
|
目标**实际**走的那只,含回落)。配置目录 `GET /v1/config/catalog` 有 `HTTP_PROXY` / `HTTPS_PROXY` / `NO_PROXY` 三行(`limitsHttp` 组,回显同一份
|
|
2357
2558
|
display-safe 投影)。`/health` 与 fleet 公告**不**带代理信息。
|
|
2358
|
-
- **建连失败时怎么读(7.108.0
|
|
2559
|
+
- **建连失败时怎么读(7.108.0;Node 运行时)**:任何一条出网请求在**建连时**就失败(码闭集:`UND_ERR_CONNECT_TIMEOUT` —— TCP + TLS 没在
|
|
2359
2560
|
传输层的建连时限内完成 / `ECONNREFUSED` / `ENOTFOUND` / `EAI_AGAIN` / `EHOSTUNREACH` / `ENETUNREACH`;可能出现在已建立连接上的
|
|
2360
2561
|
`ECONNRESET` / `ETIMEDOUT` 刻意不算)⇒ 三件,同一份事实:
|
|
2361
2562
|
① **运维日志** 一行 `outbound_connect_failed { targetHost, code, proxyInUse, proxyHop?, proxyReadAtMs, sinceBootMs, hint }` —— `proxyInUse` = 这一跳走的代理
|
|
@@ -2385,7 +2586,7 @@ web-admin 拉取 / fleet 公告 / 凭证续签、WebSearch、对象存储、OTLP
|
|
|
2385
2586
|
|
|
2386
2587
|
这几件是**仓库维护者**的工具面,不影响服务运行时。
|
|
2387
2588
|
|
|
2388
|
-
- **npm 发包 = 本地两阶段**(维护者裁定,2026-09-28
|
|
2589
|
+
- **npm 发包 = 本地两阶段**(维护者裁定,2026-09-28;**永久**取代「发包走 tag → CI」):`scripts/release.sh` 门 3 推 tag、核远端 tag 之后,
|
|
2389
2590
|
门 3.5 调 `bash scripts/publish-local.sh <version>`,绿了才起门 4/5/6。起链时 `NPM_TOKEN` 就要在、而且要有效:起链准备段在只带它的子进程里对
|
|
2390
2591
|
npmjs 跑一次 `npm whoami`(临时 npmrc 只写 `${NPM_TOKEN}` 引用,60 s、不重试)—— 缺席 / E401(令牌无效)/ E403(无权)/ 其它(网络不可达)⇒
|
|
2391
2592
|
第一道门之前停链,红句不打印令牌与用户名;通过 ⇒ 链日志一行 `NPM-WHOAMI ok`(它证「npmjs 认得这把令牌」,证不了对本包的发布权 / OTP,那一半仍在
|
|
@@ -2413,22 +2614,22 @@ web-admin 拉取 / fleet 公告 / 凭证续签、WebSearch、对象存储、OTLP
|
|
|
2413
2614
|
- **门 4 不再等 CI**(同一裁定):`scripts/post-publish-gates.sh` 的门 4 = 一个 45 min 的 npm 窗(公开 packument 上 `versions[<v>]` 在且
|
|
2414
2615
|
`dist-tags.latest` 追平)。判决行 `GATES456 v=… rc4=… rc5=… rc6=… shasum=… elapsed=… ciTag=… ciMain=… tagRun=… mainRun=… why4=… chain=…`:既有十一键形状不变,末尾追加第十二键 `chain=<本轮标识>`(见下一条起链启动器);
|
|
2415
2616
|
CI 两腿(`ciTag` / `ciMain` / `tagRun` / `mainRun`)是**信息格**,不进 rc4、不进 why4;`why4` 只剩 `propagation_timeout`(rc4 非零)与 `-`(绿)。
|
|
2416
|
-
自此门 4 常态首拍即绿,CI 两腿多半还在跑 ⇒ 常落 `unchecked:still-running`,如实。7.105.0 起
|
|
2617
|
+
自此门 4 常态首拍即绿,CI 两腿多半还在跑 ⇒ 常落 `unchecked:still-running`,如实。7.105.0 起CI 信息格见包后**读一次、不轮询**
|
|
2417
2618
|
(此前的有界轮询几乎恒白等 10 min 再落同一个 still-running):落了终态记 conclusion;还在跑 ⇒ `unchecked:still-running` + 一句 `gh run view <id>`
|
|
2418
2619
|
指路;列表里认不出 ⇒ `absent`(没触发 / 腿名不对,与「在跑」分开说)。认腿的身份判据与常量(`ci.yml` / `push` / `main`)只在
|
|
2419
|
-
`scripts/lib/ci-health.mjs`(`node scripts/lib/ci-health.mjs identity` / `legs <version> <sha>`),
|
|
2420
|
-
- **registry 读的单一属主**(
|
|
2620
|
+
`scripts/lib/ci-health.mjs`(`node scripts/lib/ci-health.mjs identity` / `legs <version> <sha>`),tag 跑复用 main 上同一提交的 test 结论读的是同一只。
|
|
2621
|
+
- **registry 读的单一属主**(7.105.0 起):发车链读 npmjs 的四处 —— 门 1.5(死 tag)/ 门 2.5(近 5 个 tag 都已发)/ 门 3.5
|
|
2421
2622
|
(precheck 与回读)/ 门 4(公开 packument + latest)—— 一律经 `scripts/lib/npm-registry.sh` 的 `registry_packument <包> [<版本>]`:
|
|
2422
2623
|
curl 直打 registry(不经 npm,`.npmrc` 的 scope 映射接管不了)、每次读 ≤ `NPM_REGISTRY_TIMEOUT_SEC`(60 s,重试 1 次)、一行
|
|
2423
2624
|
`<态> <versions> <latest> <shasum> <tarball> <gitHead>`,态 ∈ `present` / `absent` / `unreadable:<因>` —— 读不出与「不在」永远是两个词,
|
|
2424
2625
|
由各门自己判(门 1.5 读不出就说跳过;门 2.5 / 3.5 读不出即红)。此前门 1.5 / 2.5 / 4 的 curl 不限时(registry 挂住就挂死同步链),门 2.5 还手抄包名。
|
|
2425
2626
|
起链的 `npm whoami` 错误体两种流都认(npm 11 写 stdout、npm 8 写 stderr —— 只读 stdout 会把 npm 8 的 E401 误判成「网络不可达」)。
|
|
2426
|
-
- **门 2.9 = build + pack-hygiene**(
|
|
2627
|
+
- **门 2.9 = build + pack-hygiene**(7.105.0 起):门 2 的 tsc 之后、全量之前跑 `npm run prepublishOnly`(与门 3.5 在 tag 树里
|
|
2427
2628
|
跑的是同一只脚本;远端臂在 k3s8 的容器里 build)。此前 pack-hygiene 的 dist 字节段在远端臂恒 skip,第一次真执行是门 3.5 —— tag 已推;
|
|
2428
2629
|
本机臂又读盘上上一次留下的旧 dist,合并批改了 fail-open 词表注文就误红。
|
|
2429
|
-
- **门 3 推 main 前核 main = 起链提交**(
|
|
2630
|
+
- **门 3 推 main 前核 main = 起链提交**(7.105.0 起):发车树 detached 在起链提交上、本地 `main` 被别处挪走 ⇒ 停链点名两只 sha
|
|
2430
2631
|
(此前只核 HEAD,推出去的 main 是门 1–2 没测过的提交)。
|
|
2431
|
-
- **CI
|
|
2632
|
+
- **CI 健康读数**:`GH_TOKEN=… node scripts/ci-health-brief.mjs` 读 GitHub 上 `ci.yml` 在 `main` 的最近一次 run,一行
|
|
2432
2633
|
`CI-HEALTH main=… test=… steps=… run=… verdict=ok|red why=…`(rc 0 = ok,1 = red,2 = 用法错)—— 每小时巡查用。`test` job success,或在跑且已起步骤
|
|
2433
2634
|
⇒ ok;completed 非 success(零步骤 = CI 没起,如 7.103.1 的账单真案 / 有步骤 = CI 红)、排队零步骤、读不出 ⇒ red(读不到 ≠ 健康)。
|
|
2434
2635
|
它**不再是发车链的一道门**:门 1.11 随同一裁定撤出(它的前提「CI 起不来 ⇒ tag 推了也发不了包」已不成立),起链不再需要 `GH_TOKEN`,
|
|
@@ -2440,7 +2641,7 @@ web-admin 拉取 / fleet 公告 / 凭证续签、WebSearch、对象存储、OTLP
|
|
|
2440
2641
|
不再只读目标那一份;链断了(某环清单读不出、本地 tag 树缺了那一版)⇒ exit 2 点名。门会先 `git fetch --tags` 那个 core 克隆(不切换 checkout)。
|
|
2441
2642
|
当前钉缺省取本仓上一发版 tag 的 `package.json`,可用 `--from <x.y.z>` 显式给;`--core-repo` 必须是 core 仓的 git 根
|
|
2442
2643
|
(安装树 `node_modules/@sema-agent/core` 不再接受)。
|
|
2443
|
-
- **core RC 探针**(维护者裁定,2026-09-28
|
|
2644
|
+
- **core RC 探针**(维护者裁定,2026-09-28):core 出 RC tarball 时直讯本仓(`npm pack` 产物路径 + shasum + `wire-changes/core@<ver>.json` 的 commit),
|
|
2444
2645
|
当拍跑:
|
|
2445
2646
|
```bash
|
|
2446
2647
|
git -C ../sema-core show <commit>:wire-changes/core@<ver>.json > /tmp/rc/core@<ver>.json # 只读邻仓,不切换 checkout
|
|
@@ -2470,14 +2671,14 @@ web-admin 拉取 / fleet 公告 / 凭证续签、WebSearch、对象存储、OTLP
|
|
|
2470
2671
|
`store-contract-kit` 的 `KIT_REVIEWED_AGAINST` …)在任何新版本的 RC 上**按构造红**:它们是提货批的记账义务,不是 core 的回归 —— 回帖前逐个认出来、
|
|
2471
2672
|
从回归数里剔掉并写明。
|
|
2472
2673
|
- `ci-health-brief.mjs` 调 `gh` 时主机钉 `github.com`、只带 `GH_TOKEN`(环境里的 `GH_HOST` / 企业凭据 / `GITHUB_TOKEN` 不会影响读数)。
|
|
2473
|
-
- **core
|
|
2674
|
+
- **core 读表坐标不含行号**:`docs/core-consumption/READ-TABLE.json` 的每条坐标 = `<文件>#<名路径>`(导入读点 = 本地绑定名;闭表读点 =
|
|
2474
2675
|
`<闭表名>.<键>` / `<闭表名>[]`;同文件同名路径读了 N 次尾缀 ` ×N`)。门 1.8(`node scripts/core-read-table.mjs --check`)因此只在**读点集合真变了**
|
|
2475
2676
|
时红(多读 / 少读一个 core 名、闭表增删键、读点换了宿主);只挪行号的编辑不再要求重生成读表。提货门命中行打印同形坐标,找行拿名路径 grep。
|
|
2476
|
-
- **按名字找兄弟仓的门:改名 ⇒
|
|
2677
|
+
- **按名字找兄弟仓的门:改名 ⇒ 红**:门 1.9(`scripts/settings-schema-duty.mjs`)与测试侧按名字找兄弟仓的门一律经
|
|
2477
2678
|
`scripts/lib/sibling-repo.mjs` 三态定位 —— 名字缺席而改名映射里的对位名在场(本机 checkout 还叫旧名 `sema-web` / `sema-registry-core`,或门的名单
|
|
2478
2679
|
还写旧名)⇒ **红**并指路(把目录改名 / 重新 clone 成新名),门 1.9 上持有方与非持有方一样;两个名字都不在 ⇒ 照旧(门 1.9:持有方包仓红、非持有方
|
|
2479
2680
|
提示;测试侧:skip,skip 句点名找过的名字与 org 根)。改仓名 / 改包名的同窗动作清单在 `docs/PIPELINE-DISCIPLINE.md`。
|
|
2480
|
-
- **门 ×
|
|
2681
|
+
- **门 × 凭据**:起链环境里的凭据(`GITHUB_SEMA_ORG_API_TOKEN` / `NPM_TOKEN` / `GH_TOKEN` / `DEEPSEEK_API_KEY` / 三条 `*_TEST_URL` …)照旧由链启动器
|
|
2481
2682
|
注入,但**每一道门的子进程只拿到 `scripts/lib/gate-credentials.mjs` 表里它那一行**;表外的凭据形变量(core `scrubSecretEnv` 的判据 + 本链
|
|
2482
2683
|
DSN 名)一律不交。`release.sh` / `post-publish-gates.sh` 开头封口,每道门先换门;链日志 `scratchpad/release-gates-<v>.log` 每门一行
|
|
2483
2684
|
`GATE-ENV gate=<门> handed=<名,…|-> withheld=<名,…|->`(只有名字)。
|
|
@@ -2489,7 +2690,7 @@ web-admin 拉取 / fleet 公告 / 凭证续签、WebSearch、对象存储、OTLP
|
|
|
2489
2690
|
- `scripts/remote-gate.sh run --dsn "<名…>" <cmd>` 只把点名的 DSN 交给容器(名的闭集 = bug 复现套的 `ENGINE_DSN_ENV`,今天是
|
|
2490
2691
|
`TIDB_TEST_URL` / `PG_TEST_URL` / `INNODB_TEST_URL`;值取 `GATE_<名>`),`--dsn ""` = 一条都不给;**不给 `--dsn`(手跑)= 闭集全部点名**。
|
|
2491
2692
|
`ship` 的容器不带 DSN。
|
|
2492
|
-
- **按车分库是硬规则**(
|
|
2693
|
+
- **按车分库是硬规则**(7.105.0 起):点了名的每一条都必须显式给出非空的 `GATE_<名>` —— **在场但为空**
|
|
2493
2694
|
(`GATE_INNODB_TEST_URL=""` 想关掉那条腿)⇒ `REMOTE_GATE_DSN_EMPTY` 红停;**缺席** ⇒ `REMOTE_GATE_DSN_ABSENT` 红停。脚本不再有共享库缺省
|
|
2494
2695
|
(旧形把空串 / 缺席回落到共享的 `sema_server_test` 等库,车QC 的锁形套在共享库上整表 DELETE 了别车的行)。要关一条腿:别在 `--dsn` 里点它;
|
|
2495
2696
|
要用:给按车库(`eval "$(node scripts/db-lane.mjs <车>)"` 建 `aiagent_test_<车>` / `sema_server_test_<车>`,再显式 export 三条
|
|
@@ -2498,7 +2699,7 @@ web-admin 拉取 / fleet 公告 / 凭证续签、WebSearch、对象存储、OTLP
|
|
|
2498
2699
|
值里带 `&` / 空格也不会切断远端命令;值里有换行 ⇒ 红停。远端先 unset 点了名的每一条、只收点了名的名,核每一条都收到且解得开才起容器
|
|
2499
2700
|
(没收到 ⇒ `REMOTE_GATE_DSN_LOST`,解不开 ⇒ `REMOTE_GATE_DSN_BAD`),核完打一行机读 `REMOTE_GATE_DSN handed=<名,…|-> skipped=<n>`
|
|
2500
2701
|
(skipped = 闭集里这一跑没交给容器的引擎数);发车链把它抄上 `GATE2 …` 读数行的 `skipped=`(本机臂按换门交出的名数;远端红 ⇒ `unknown`)。
|
|
2501
|
-
- **起链 = `scripts/chain-launch.sh <version>`**(
|
|
2702
|
+
- **起链 = `scripts/chain-launch.sh <version>`**(7.105.0 起;取代 sema-internal 的 chain-helper):
|
|
2502
2703
|
```bash
|
|
2503
2704
|
nohup bash scripts/chain-launch.sh 7.105.0 > scratchpad/chain-7.105.0.log 2>&1 & disown # 在 node_modules 是真目录的树(主树)里
|
|
2504
2705
|
```
|
|
@@ -2513,16 +2714,16 @@ web-admin 拉取 / fleet 公告 / 凭证续签、WebSearch、对象存储、OTLP
|
|
|
2513
2714
|
(第二个参数),判决行末键 `chain=` 带回,等待方只读 `chain=` 等于本轮标识的那一行。同版本重起链时,上一轮留下的 `post-publish-gates.sh` 迟到追加的判决行(落在起链偏移之后,按偏移认不出)、
|
|
2514
2715
|
十一键老形、手跑的 `chain=-` 行都不是本轮的 —— 没等到本轮的行 ⇒ 退出码 1 并说没出现,不转述别轮的读数。手跑 `release.sh` / `post-publish-gates.sh` 不给标识 = `chain=-`。之后转述门 3.5 的 `PUBLISH` 行;release.sh 红而 tag 已在 ⇒ 立即收尾并指路 `publish-local.sh` + `post-publish-gates.sh`;
|
|
2515
2716
|
绿 ⇒ 等 `GATES456`(窗 = `GATE4_WINDOW_SEC` + 20 min)后转述 `GATE2` / `PUBLISH` / 判决行。退出码见脚本头注(0 = 全绿)。
|
|
2516
|
-
- **wire 变更机器清单**(
|
|
2717
|
+
- **wire 变更机器清单**(7.105.0 起):`node scripts/wire-changes-server.mjs <version>` 生成 `wire-changes/server@<version>.json`
|
|
2517
2718
|
(形照 core 的 `wire-changes/core@<ver>.json`:`added` / `removed` / `changed` / `narrowed` + `closedSetMembers` + `breaking`)。源 = 契约附录
|
|
2518
2719
|
A.1 ∪ A.1b 错误码(属性 = 状态码)、B.1 能力位 / B.2 `taskSettings` 子键(属性 = 谓词类)、D.3 `engine_notice` 白名单(属性 = `detail`)、
|
|
2519
2720
|
`src/trace/wire-projection-faces.ts` 的 `WIRE_PROJECTION_FACES`;对比上一 tag 与本版(tag 在读 tag,不在读工作树)。源读不懂 ⇒ exit 2
|
|
2520
2721
|
(不产出「零变更」清单)。随 release commit 提交;查账门(门 1.6)第三腿 `--check` 核它在仓且与生成输出逐字节同,缺席 / 陈旧 ⇒ 红。
|
|
2521
2722
|
`wire-changes/server@7.104.0.json` 是回填的第一份。
|
|
2522
|
-
- **台账 `refs:`
|
|
2723
|
+
- **台账 `refs:` 列建议**:`node scripts/ledger-refs.mjs [--comms-dir <dir>] [--json]` 只读 sema-comms 的 `debts/DEBTS-server.md` /
|
|
2523
2724
|
`PLANS.md`(server 段)/ `bugs/B-*.md`(文件头 `owner:` 格),按「一方点了另一方的号」双向互链,输出每行的 `refs:` 建议值(号按数值认,
|
|
2524
2725
|
零填充与不填充的拼法算同一只号;悬空号 stderr 点名)。不改 sema-comms,落账由主会话做。
|
|
2525
|
-
- **tag 跑复用 main 上同一提交的 test 结论**(
|
|
2726
|
+
- **tag 跑复用 main 上同一提交的 test 结论**(`.github/workflows/ci.yml`):tag run 的 `test` job 先跑 `node scripts/ci-tag-test-reuse.mjs`,
|
|
2526
2727
|
按 headSha 读 main 上同一提交那次 push run 的 `test` job —— success ⇒ 跳过全套;在跑 ⇒ 等(上限 45 min,复用步 55 min 时限之内);
|
|
2527
2728
|
失败 / 查不到 / 读不出 ⇒ 照跑全套。结论一行 `TAG-TEST-REUSE decision=reuse|run why=… mainRun=… waited=…s sha=…` 在该 job 的日志与 step summary 里。
|
|
2528
2729
|
⚠️ 这条还没在真 CI 上跑过(组织账单恢复后的第一次发车核它:tag run 的 test job 里复用步那一行应是 `decision=reuse why=main_success`)。
|
|
@@ -2566,7 +2767,7 @@ const ciphertext = Buffer.from(sodium.crypto_box_seal(sodium.from_string(apiKey)
|
|
|
2566
2767
|
不是下面三种失败词之一。
|
|
2567
2768
|
|
|
2568
2769
|
**④ 与 `apiKeyEnv` 的关系(按代码行为)。** 同一条目两个都写 ⇒ **sealed 优先,`apiKeyEnv` 整条不看**:开箱成功就用开出来的 key;开箱失败 ⇒
|
|
2569
|
-
这只模型的每一次模型调用都失败(用它的任务响亮失败),**不退回 `apiKeyEnv`,也不退回网关 `MODEL_API_KEY`**。🔴 **7.106.0
|
|
2770
|
+
这只模型的每一次模型调用都失败(用它的任务响亮失败),**不退回 `apiKeyEnv`,也不退回网关 `MODEL_API_KEY`**。🔴 **7.106.0 起两种写法
|
|
2570
2771
|
「坏了之后」方向相同**:只写 `apiKeyEnv` 而本服务进程里那个 env 变量没设(或是空串)⇒ 这只模型的钥匙同样中毒 —— 起服照常,每一次模型调用都失败、
|
|
2571
2772
|
拒句点名那个变量(理由 `env_missing`),**不退回网关 `MODEL_API_KEY`**(7.105.0 及以前在这里静默退回网关 key)。配置应用时仍有一行 warn
|
|
2572
2773
|
`web_admin_model_key_env_missing`(hint 说中毒与出路)。出路:在跑本服务的进程环境里设上那个变量并重启;真想让这只模型用网关 key,就从条目上删掉 `apiKeyEnv`。
|