@sema-agent/server 7.123.0 → 7.124.1
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 +138 -0
- package/README.md +2 -2
- package/README.zh-CN.md +2 -2
- package/USAGE.md +53 -51
- package/dist/approval-ask-audit-store.d.ts +27 -32
- package/dist/approval-ask-audit-store.js +10 -9
- package/dist/approval-crash-convergence.d.ts +2 -2
- package/dist/approval-crash-convergence.js +5 -5
- package/dist/approval-deny-reasons.d.ts +163 -51
- package/dist/approval-deny-reasons.js +79 -8
- package/dist/approval-policy-settlement.d.ts +4 -1
- package/dist/approval-policy-settlement.js +3 -2
- package/dist/approval-reconciler.d.ts +3 -3
- package/dist/approval-reconciler.js +10 -10
- package/dist/approval.d.ts +1 -1
- package/dist/approval.js +1 -1
- package/dist/audit.js +1 -1
- package/dist/auth-bridge.d.ts +8 -8
- package/dist/auth-bridge.js +6 -6
- package/dist/auth-keys.d.ts +2 -2
- package/dist/auto-mode-face.d.ts +1 -1
- package/dist/boot/budget-tracing.js +1 -1
- package/dist/boot/execution-env.js +1 -0
- package/dist/boot/leader.d.ts +1 -1
- package/dist/boot/memory-consolidation.d.ts +4 -4
- package/dist/boot/org-memory.d.ts +3 -3
- package/dist/boot/org-memory.js +5 -5
- package/dist/boot/resolve-spec.js +7 -7
- package/dist/boot/runner-deps.d.ts +1 -1
- package/dist/boot/runtime-caps.d.ts +1 -1
- package/dist/boot/runtime-caps.js +10 -10
- package/dist/boot/shutdown.d.ts +2 -2
- package/dist/boot/shutdown.js +2 -2
- package/dist/boot/side-query-lane.d.ts +3 -3
- package/dist/boot/{stage-02-registry-pull.d.ts → stage-02-web-admin-pull.d.ts} +2 -2
- package/dist/boot/{stage-02-registry-pull.js → stage-02-web-admin-pull.js} +4 -4
- package/dist/boot/stage-03-store-backend.d.ts +2 -2
- package/dist/boot/stage-04-stores.d.ts +1 -1
- package/dist/boot/stage-05-execution-env.d.ts +1 -1
- package/dist/boot/stage-06-runners.d.ts +2 -2
- package/dist/boot/stage-06-runners.js +6 -6
- package/dist/boot/stage-07-capability-layer.d.ts +2 -2
- package/dist/boot/stage-07-capability-layer.js +6 -5
- package/dist/boot/stage-08-reapers.d.ts +3 -3
- package/dist/boot/stage-08-reapers.js +3 -3
- package/dist/boot/stage-09-leader.d.ts +3 -3
- package/dist/boot/stage-09-leader.js +7 -7
- package/dist/boot/stage-10-http-server.d.ts +3 -3
- package/dist/boot/stage-10-http-server.js +19 -19
- package/dist/boot/unpriced-ceiling-verdict.d.ts +1 -1
- package/dist/boot/{config-center.d.ts → web-admin.d.ts} +11 -11
- package/dist/boot/{config-center.js → web-admin.js} +38 -22
- package/dist/boot/webfetch-summarize-lane.d.ts +2 -2
- package/dist/brain.d.ts +1 -1
- package/dist/brain.js +1 -1
- package/dist/budget.d.ts +3 -3
- package/dist/canonical-json.d.ts +2 -2
- package/dist/capabilities/center-plugins.d.ts +1 -1
- package/dist/capabilities/center-wiring.d.ts +12 -5
- package/dist/capabilities/center-wiring.js +11 -2
- package/dist/capabilities/composite-members.d.ts +1 -1
- package/dist/capabilities/composite-members.js +1 -1
- package/dist/capabilities/scenarios.js +2 -2
- package/dist/capabilities/team.d.ts +1 -1
- package/dist/capabilities/tool-defer.d.ts +1 -1
- package/dist/center-credential-renewer.d.ts +1 -1
- package/dist/center-credential-renewer.js +7 -7
- package/dist/center-credential.d.ts +9 -9
- package/dist/center-credential.js +6 -6
- package/dist/config-catalog.d.ts +7 -10
- package/dist/config-catalog.js +15 -15
- package/dist/config-d-migration.d.ts +32 -0
- package/dist/config-d-migration.js +59 -0
- package/dist/config-invariants.d.ts +14 -4
- package/dist/config-invariants.js +31 -2
- package/dist/config-lkg.d.ts +26 -6
- package/dist/config-lkg.js +43 -14
- package/dist/config-provider.d.ts +23 -19
- package/dist/config-provider.js +24 -26
- package/dist/config-types.d.ts +52 -51
- package/dist/config.d.ts +9 -46
- package/dist/config.js +33 -92
- package/dist/execution-lane-caps.d.ts +1 -1
- package/dist/fleet/drain-entry.d.ts +1 -1
- package/dist/fleet/engine-signal.d.ts +8 -8
- package/dist/fleet/fleet-bus.d.ts +12 -3
- package/dist/fleet/fleet-bus.js +13 -9
- package/dist/fleet/serving-lease.d.ts +3 -3
- package/dist/fleet/serving-lease.js +1 -1
- package/dist/fleet-client.d.ts +7 -7
- package/dist/fleet-client.js +5 -4
- package/dist/fleet-lease.d.ts +3 -3
- package/dist/fleet-lease.js +2 -2
- package/dist/host-lsp-manager.d.ts +1 -1
- package/dist/hosted-posture.d.ts +4 -4
- package/dist/http/admission.js +4 -4
- package/dist/http/checkpoint-ctx.d.ts +20 -3
- package/dist/http/checkpoint-ctx.js +13 -5
- package/dist/http/dispatch.js +2 -2
- package/dist/http/durable-run-leg.js +2 -1
- package/dist/http/health-self-report.d.ts +4 -4
- package/dist/http/health-self-report.js +3 -3
- package/dist/http/idempotency.d.ts +1 -1
- package/dist/http/resume-legs.d.ts +4 -3
- package/dist/http/resume-legs.js +31 -18
- package/dist/http/route-ctx.d.ts +8 -0
- package/dist/http/routes/admin-config-refresh.d.ts +1 -1
- package/dist/http/routes/admin-config-refresh.js +1 -1
- package/dist/http/routes/admin-drain.d.ts +1 -1
- package/dist/http/routes/admin-drain.js +5 -4
- package/dist/http/routes/approvals-assistant.js +4 -4
- package/dist/http/routes/capabilities.js +5 -5
- package/dist/http/routes/config-catalog.js +2 -3
- package/dist/http/routes/diagnostics.js +1 -1
- package/dist/http/routes/runs.js +8 -4
- package/dist/http/routes/sessions-list.js +2 -1
- package/dist/http/routes/sessions.js +1 -1
- package/dist/http/routes/side-query.js +3 -3
- package/dist/http/routes/tasks.js +3 -2
- package/dist/http/run-meta.d.ts +1 -1
- package/dist/http/run-meta.js +2 -2
- package/dist/http/server.d.ts +7 -7
- package/dist/http/server.js +1 -1
- package/dist/http/stored-body-replay.js +3 -2
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/key-resolver.d.ts +1 -1
- package/dist/leader/wire.d.ts +2 -2
- package/dist/leader/wire.js +2 -1
- package/dist/main.js +1 -1
- package/dist/mcp-tool-faces.d.ts +2 -2
- package/dist/model-entry-refusal.d.ts +3 -3
- package/dist/model-entry-refusal.js +4 -4
- package/dist/model-provider.d.ts +2 -2
- package/dist/model-route-endpoint.d.ts +1 -1
- package/dist/model-route-url.d.ts +1 -1
- package/dist/model-route-url.js +2 -1
- package/dist/model-select.d.ts +24 -21
- package/dist/model-select.js +22 -17
- package/dist/observability/metrics.d.ts +1 -1
- package/dist/org-memory-admission.d.ts +1 -1
- package/dist/plugins/approval-ask-store-file.d.ts +4 -2
- package/dist/plugins/approval-ask-store-file.js +3 -3
- package/dist/plugins/approval-ask-store-memory.d.ts +4 -3
- package/dist/plugins/approval-ask-store-memory.js +10 -4
- package/dist/plugins/approval-ask-store-sql.d.ts +16 -2
- package/dist/plugins/approval-ask-store-sql.js +20 -13
- package/dist/plugins/caching-session-store.d.ts +0 -10
- package/dist/plugins/caching-session-store.js +0 -3
- package/dist/plugins/file-run-store.js +9 -3
- package/dist/plugins/leader-run-store-sql.d.ts +3 -2
- package/dist/plugins/leader-run-store-sql.js +3 -2
- package/dist/plugins/local-session-store.d.ts +20 -27
- package/dist/plugins/local-session-store.js +19 -17
- package/dist/plugins/memory-run-store.js +9 -3
- package/dist/plugins/one-time-migrations.js +26 -2
- package/dist/plugins/scheduler-support.d.ts +26 -7
- package/dist/plugins/scheduler-support.js +96 -53
- package/dist/plugins/store-backend.js +1 -1
- package/dist/plugins/store-contracts.d.ts +20 -4
- package/dist/plugins/web-search.d.ts +1 -1
- package/dist/plugins/web-search.js +2 -1
- package/dist/posture-source.d.ts +1 -1
- package/dist/read-face-posture.d.ts +3 -3
- package/dist/read-face-posture.js +1 -1
- package/dist/run-local.js +6 -3
- package/dist/runtime-caps-resolver.d.ts +4 -4
- package/dist/runtime-caps-resolver.js +2 -2
- package/dist/runtime-governance.d.ts +5 -5
- package/dist/sealed-key.d.ts +4 -4
- package/dist/sealed-key.js +3 -2
- package/dist/security.d.ts +3 -3
- package/dist/service-version.d.ts +1 -1
- package/dist/service-version.js +5 -0
- package/dist/session-titler.d.ts +0 -11
- package/dist/session-titler.js +3 -5
- package/dist/shared-memory-scope-authorizer.d.ts +4 -4
- package/dist/task-a2a.d.ts +1 -1
- package/dist/tool-approval.d.ts +43 -4
- package/dist/tool-approval.js +147 -98
- package/dist/trace/core-keyset-guard.d.ts +1 -1
- package/dist/trace/project.d.ts +3 -3
- package/dist/trace/redact.d.ts +2 -23
- package/dist/trace/redact.js +17 -15
- package/dist/trace/self-minted-wire.d.ts +134 -3
- package/dist/trace/self-minted-wire.js +46 -9
- package/dist/url-refusal.d.ts +35 -0
- package/dist/url-refusal.js +15 -0
- package/dist/user-authored-text.d.ts +31 -0
- package/dist/user-authored-text.js +18 -0
- package/dist/{config-center → web-admin-client}/apply-effective.d.ts +13 -70
- package/dist/{config-center → web-admin-client}/apply-effective.js +100 -119
- package/dist/{config-center → web-admin-client}/apply-ledger.d.ts +2 -2
- package/dist/{config-center → web-admin-client}/center-request.d.ts +7 -7
- package/dist/{config-center → web-admin-client}/center-request.js +5 -4
- package/dist/{config-center → web-admin-client}/effective-keys.d.ts +1 -3
- package/dist/{config-center → web-admin-client}/effective-keys.js +0 -9
- package/dist/{config-center → web-admin-client}/facade.d.ts +6 -6
- package/dist/{config-center → web-admin-client}/facade.js +2 -2
- package/dist/{config-center → web-admin-client}/http-client.d.ts +6 -10
- package/dist/{config-center → web-admin-client}/http-client.js +12 -12
- package/dist/{config-center → web-admin-client}/pull-state.d.ts +1 -1
- package/dist/{config-center → web-admin-client}/read-face.js +4 -4
- package/dist/{config-center → web-admin-client}/read-warnings.d.ts +23 -16
- package/dist/{config-center → web-admin-client}/read-warnings.js +33 -23
- package/dist/{config-center → web-admin-client}/restart-signal.d.ts +5 -3
- package/dist/{config-center → web-admin-client}/restart-signal.js +3 -4
- package/dist/{config-center → web-admin-client}/skills-mcp.d.ts +1 -1
- package/dist/{config-center → web-admin-client}/skills-mcp.js +13 -13
- package/dist/{config-center → web-admin-client}/stage-limits.d.ts +1 -7
- package/dist/{config-center → web-admin-client}/stage-limits.js +6 -7
- package/dist/{config-center → web-admin-client}/types.d.ts +12 -39
- package/package.json +2 -2
- package/skills/find-skills.md +2 -2
- package/dist/url-shape.d.ts +0 -31
- package/dist/url-shape.js +0 -41
- /package/dist/{config-center → web-admin-client}/apply-ledger.js +0 -0
- /package/dist/{config-center → web-admin-client}/hot-keys-registry.d.ts +0 -0
- /package/dist/{config-center → web-admin-client}/hot-keys-registry.js +0 -0
- /package/dist/{config-center → web-admin-client}/mcp-revocation.d.ts +0 -0
- /package/dist/{config-center → web-admin-client}/mcp-revocation.js +0 -0
- /package/dist/{config-center → web-admin-client}/pull-state.js +0 -0
- /package/dist/{config-center → web-admin-client}/read-face.d.ts +0 -0
- /package/dist/{config-center → web-admin-client}/types.js +0 -0
package/USAGE.md
CHANGED
|
@@ -110,13 +110,13 @@ ANTHROPIC_API_KEY=sk-ant-… # 可选:ANTHROPIC_BASE_URL / ANTHROPIC_VERSION /
|
|
|
110
110
|
中心 / config.d 下发的候选目录里有解析不到的条目 ⇒ 候选的模型面这一组被拒、上一代模型面继续服务(同候选其余组照落)。
|
|
111
111
|
- 🔴 **目录里的 anthropic 条目要本机真有 anthropic 路由**(S-704,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
|
-
🔴 **日志与拒因句里的条目名先过名筛**(7.115.0 起):被拒条目的名字写进启动 / 刷新日志(`
|
|
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
|
🔴 **被拒条目的名字不许被档位 / 别名铸到别的条目上**(S-712):不可用条目恰好叫 `sonnet` / `opus` / `pro` / `best` 这类档位词或 CC 别名、
|
|
119
|
-
而档位表会铸出这个词时,这份候选的模型面整组被拒(码 `config.model_refused_name_minted`,warn `
|
|
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。
|
|
122
122
|
- **缺省推断**:`MODEL_PROVIDER` **未设**、但显式配了 Anthropic 协议 base URL(`ANTHROPIC_BASE_URL`)**和**对应凭证(`ANTHROPIC_API_KEY` / `ANTHROPIC_AUTH_TOKEN`)时,缺省自动判 `anthropic` 并打一行启动 warn(`model_provider_inferred`)——纯净机直连 Anthropic 兼容上游不再需要显式第七个键。显式 `MODEL_PROVIDER` 恒赢;只给 base URL 或只给凭证不推断;两者皆无 = `gateway`,与以前逐字节一致。
|
|
@@ -336,7 +336,7 @@ MODEL_EXTRA_BODY='{"top_k":40}' # JSON 逃生口(top_k / logit_bias 等;penalt
|
|
|
336
336
|
|
|
337
337
|
**可选 — 目录条目的供应商附加请求头(`headers`,S-931,7.108.0;两条读腿打通 = S-935,7.109.0)**
|
|
338
338
|
```jsonc
|
|
339
|
-
// config.d/models.json(
|
|
339
|
+
// config.d/models.json(或 web-admin models 域)的一条条目:
|
|
340
340
|
{ "name": "claude-1m", "id": "<上游模型 id>", "provider": "anthropic", "api": "anthropic-messages",
|
|
341
341
|
"headers": { "anthropic-beta": "context-1m-2025-08-07" } }
|
|
342
342
|
```
|
|
@@ -344,14 +344,14 @@ MODEL_EXTRA_BODY='{"top_k":40}' # JSON 逃生口(top_k / logit_bias 等;penalt
|
|
|
344
344
|
谁受伤 = 无(缺席 ⇒ 请求逐字节同 7.107.0);补偿 = n/a(按请求变化 ⇒ 目录写两条不同名的条目,走既有的模型选择)。
|
|
345
345
|
- **白名单是闭集**,每个名字带「读它的线路」:今天只有 `anthropic-beta`(只在 `api: "anthropic-messages"` 的条目上)。表外的名字(含
|
|
346
346
|
`authorization` / `x-api-key` —— 凭据只走 `sealedApiKey` / `apiKeyEnv`)、大小写变体(只认小写规范拼写)、写在不读它的线路上、值不是
|
|
347
|
-
可见 ASCII(或含换行)⇒ **这条条目不可用**:不进可选目录,warn `
|
|
347
|
+
可见 ASCII(或含换行)⇒ **这条条目不可用**:不进可选目录,warn `web_admin_model_entry_invalid` 点名条目与 `headers.<名>`(不回显值),
|
|
348
348
|
显式选中它 ⇒ 400 `request.model_unavailable` `[config.model_entry_invalid]`;其余条目照常服务。
|
|
349
349
|
- **per-model**:头铸进这一只模型(core `Model.headers`),同一条 anthropic 路由上别的条目不受影响;值逐字 —— core 会把它自己要开的 beta
|
|
350
350
|
并进同一个 `anthropic-beta` 头(core anthropic 腿的 beta 头合并律),不会互相覆盖。模型面热应用:改了下一次请求即生效(降级壳焊住的目标除外,那一只按
|
|
351
351
|
`degrade-route` 重启片走)。
|
|
352
352
|
- **请求面没有 `betas`**:头是部署 ↔ 供应商的契约(计费 / 配额 / 兼容性由运维承担),不是租户可表态的位 —— `POST /v1/tasks` 带 `betas` ⇒
|
|
353
353
|
400 `request.body_shape`(键闭集)。
|
|
354
|
-
- **读腿**(7.109.0 起,S-935):`config.d/models.json`
|
|
354
|
+
- **读腿**(7.109.0 起,S-935):`config.d/models.json` 与 web-admin 两条读腿逐字交出 `headers`。`@sema-agent/settings-schema` 6.4.0 的 `ModelEntry.headers`
|
|
355
355
|
是解析透明键 —— **契约包只做自由文本安全判**(与 `compat` / `extraBody` 同一只闸:**秘密形的值**(形似令牌 / 密钥)在那一步就让条目不可用,拒因字段是
|
|
356
356
|
整键 `headers`、不回显值),**形(头名白名单 × 线路 × 值形)由本服务判**(`src/model-entry-headers.ts`,上面那几条)。
|
|
357
357
|
|
|
@@ -419,7 +419,7 @@ MODEL_CODE_ROLES=default,subagent # 不设=全中立;仅这些角色在「
|
|
|
419
419
|
(env `MEMORY_SCOPE` 的 org 形 + **单用户部署**的 `projects[].defaultScopes` org 键)直通;**多租户**
|
|
420
420
|
部署里由调用方 `projectId` 选中的登记簿 org 键算 **request 来源**,必须拿到授权目录的逐 principal
|
|
421
421
|
授予才准入,拿不到一律 fail-closed 拒(纯读外泄面:projectId 只过形状门不过授权)。目录源**三态单选,
|
|
422
|
-
授权面不双源合并**:
|
|
422
|
+
授权面不双源合并**:web-admin 远程腿(per-principal `orgMemory` 段)> `MEMORY_ORG_DIRECTORY_JSON`
|
|
423
423
|
静态表 > 缺席(request 来源的 org scope 恒拒)。同一个目录也是 `GET /v1/memory/export` /
|
|
424
424
|
`POST /v1/memory/sync/:scope` 的 `org:` 属主门真源(写面另需条目 `write:true`);拒绝形 = 终局
|
|
425
425
|
`memory.admission_denied`(403)/ 瞬时 `memory.admission_required`(503,带 `retryAfterSec`)。
|
|
@@ -427,16 +427,16 @@ MODEL_CODE_ROLES=default,subagent # 不设=全中立;仅这些角色在「
|
|
|
427
427
|
| env | 缺省 | 说明 |
|
|
428
428
|
|---|---|---|
|
|
429
429
|
| `MEMORY_ORG_ADMISSION_MODE` | `enforce` | `enforce`=判决即结果;`audit`=**运维诊断位**——准入面判决照算、拒绝降为日志+metric,零行为变化(不整拒、不窄化写面);`/v1/memory/export`、`/v1/memory/sync/:scope` 的 `org:` 属主门在 audit 下**逐字保持收编前的 operator-only**(未验证的目录不得开数据面)。两模式都要求目录 client 在场 |
|
|
430
|
-
| `MEMORY_ORG_DIRECTORY_JSON` | 缺省 | 单机形静态授权表 `{"<principal>":{"org:acme":{"write":true}}}`。**启动期整表校验,坏表拒启动**。有
|
|
430
|
+
| `MEMORY_ORG_DIRECTORY_JSON` | 缺省 | 单机形静态授权表 `{"<principal>":{"org:acme":{"write":true}}}`。**启动期整表校验,坏表拒启动**。有 web-admin 时被忽略(center 胜 + warn) |
|
|
431
431
|
| `MEMORY_ORG_GRANT_TTL_MS` | `60000`(`[1000, 3600000]`) | 授予/负结果的缓存 TTL = 「有界 LKG」:**新任务/新 resume 腿**的准入判决滞后 ≤ 此值;**在跑任务不受吊销影响**(判决点在 prepare 期) |
|
|
432
432
|
| `MEMORY_ORG_UNAVAILABLE_BACKOFF_MS` | `10000`(`[500, 600000]`) | 目录取不到之后的退避窗(只用于取数失败臂,不用于负结果) |
|
|
433
433
|
| `MEMORY_DELEGATION_EVIDENCE` | 缺省(=引擎自缺省 `static-face`) | **委派臂的证据标准**(core 5.45.0 起)。`static-face`=今日行为:委派的 attestation 缺失/未知**且**静态工具面够得着外部内容 ⇒ 标记本会话记忆为已污染(可能性即暴露)。`attested-only`=**只**豁免那一条静态面标记,且**真的豁免了一次**才响亮通告(`engine_notice` 的 `memory.delegation_static_mark_waived`,每个 prepared leg **至多**一次);送达 `external` attestation 照标、非委派的污染类工具照标、委派工具自身被分类为污染的照标。⚠️ 通告**不是 leg 计数器**:不含委派的 leg、污染面没挂载的 leg、送达了 attestation 的委派都不发 —— 看不到通告的常态含义是「没有可豁免的事」,不是「配置坏了」。坏值**启动期拒**。**部署席 only**(TaskSpec 无同名键,任务/受治脚本无法据此放松部署)。⚠️ **已接受的代价**:`attested-only` 下**后台**子代的真实外部接触不标记本会话(其内容经 TaskOutput / 任务通知注入 / AgentTranscript 摘要回流,三条都不带 attestation);异常收尾的前台子代同理。已标记的会话永不回滚清洗。运维读面 = `GET /v1/diagnostics/wiring` 的 `memoryPosture.delegationEvidence`(`null`=本部署未设,引擎自缺省) |
|
|
434
434
|
| `MEMORY_PROVENANCE` | 缺省(=引擎自缺省 `carry`) | **记忆 provenance 总开关**(core 5.46.0 起)。一个会话被判为**已暴露**之后,它的记忆写怎么处置。`carry`=引擎缺省:**普通**记忆写照常提交,但带上引擎铸的 `origin` 标记(随条目走后端/同步/导出包);指令形文件(feedback / pinned / triggers / applies-when)仍被扣下隔离;派生索引的会话散文照回滚;内容扫描门原样跑(**标记不是豁免**)。`off`=此前的行为:不铸 origin 标记,已暴露会话的 harvest **一条都不收**(整体隔离候人审)。⚠️ 已提交的标记在编辑时照样带下去 —— `off` 停的是**铸**,从不抹掉已记的事实。与 `MEMORY_DELEGATION_EVIDENCE` **正交**(四种组合全合法):那一键决定**什么时候**判会话为已暴露,本键决定判了之后**对写做什么**。坏值**启动期拒**(`OFF`/`false`/`none` 这类「看起来像关掉」的写法一律不折成缺省 —— 静默回默认 = 一台自认为「暴露会话零收录」的部署其实在照常收录)。**部署席 only**(TaskSpec 无同名键,任务/受治脚本无法据此改姿态);**不冻进 checkpoint**(resume 腿跟当前部署配置走)。运维读面 = `GET /v1/diagnostics/wiring` 的 `memoryPosture.provenance`(`null`=本部署未设,引擎自缺省) |
|
|
435
|
-
| `MEMORY_CAPTURE_POLICY` | 缺省(=引擎自缺省 `open`) | **记忆采集 opt-out 的部署姿态**(core 7.0.0 起)。任务可带 `memory.capture:"off"` 声明「本会话不进长期记忆」(一次性,resume 恒不采集,无反向拼写),本键决定怎么裁那条声明:`open`=按面值生效,**只有**该 principal 的显式 `false` verdict 拒(403 `memory.capture_optout_denied`),resolver 故障**放行**+披露(隐私轴 fail-safe:错拒=记了用户明说不要记的会话,不可逆);`governed`=verdict **必答**(混合车队:部分用户强制留存),故障/缺席**拒跑**(fail-closed);`capture-required`=全体声明拒(一行配置)。verdict 源**两条**(析取):①SQL 店后端上的授权表 `memory_optout_grant`(三层折叠:per-principal 行 → 部署缺省行 → 代码缺省 allow;管理面 `GET|PUT /v1/admin/memory-optout`(PUT=部署缺省行)· `PUT|DELETE …/:principal`,operator-only,能力位 `memoryOptOutGrant`,零缓存写后即生效);②非 dry-run 的
|
|
435
|
+
| `MEMORY_CAPTURE_POLICY` | 缺省(=引擎自缺省 `open`) | **记忆采集 opt-out 的部署姿态**(core 7.0.0 起)。任务可带 `memory.capture:"off"` 声明「本会话不进长期记忆」(一次性,resume 恒不采集,无反向拼写),本键决定怎么裁那条声明:`open`=按面值生效,**只有**该 principal 的显式 `false` verdict 拒(403 `memory.capture_optout_denied`),resolver 故障**放行**+披露(隐私轴 fail-safe:错拒=记了用户明说不要记的会话,不可逆);`governed`=verdict **必答**(混合车队:部分用户强制留存),故障/缺席**拒跑**(fail-closed);`capture-required`=全体声明拒(一行配置)。verdict 源**两条**(析取):①SQL 店后端上的授权表 `memory_optout_grant`(三层折叠:per-principal 行 → 部署缺省行 → 代码缺省 allow;管理面 `GET|PUT /v1/admin/memory-optout`(PUT=部署缺省行)· `PUT|DELETE …/:principal`,operator-only,能力位 `memoryOptOutGrant`,零缓存写后即生效);②非 dry-run 的 web-admin 在该 principal 的 entitlement caps 里发 `allowMemoryOptOut`。🔴 **`governed` 而两源皆无 ⇒ 启动期拒启**(无源 = 每个声明都被拒且没人知道为什么;接 `DB_BACKEND=mysql|pg`、接非 dry-run center,或改姿态)。⚠️ **center-only 源的语义**(只配了 center、没有 SQL 授权表):`allowMemoryOptOut` 在 entitlement schema 里是**可选键** —— center 不发它 = 该 principal 的 verdict **缺席** = `governed` 下每次 opt-out 恒拒(403 `memory.capture_optout_denied`),而 worker **照常起动**;这一形部署级证不死(per-principal 授权只有运行期才知道),所以 server 两处响亮:启动期一行 `memory_capture_governed_center_only_source`、运行期首次真缺席一行 `memory_capture_governed_verdict_absent`(每进程一次,纯披露不改判)。要确定性 verdict 就接 SQL 授权表(它三层折叠后恒答显式 boolean)。坏值**启动期拒**(`Governed`/`required`/`off` 一律不折成缺省:两种姿态的故障极性**相反**)。**部署席 only**;**不冻进 checkpoint**。运维读面 = `GET /v1/diagnostics/wiring` 的 `memoryPosture.capturePolicy`(`null`=本部署未设)。记录载体(core 7.0.2 起):SQL 后端(`DB_BACKEND=mysql\|pg`)上记录落 `session_capture_optout` 表(跨副本存活,随中央 schema 自建零旋钮)——**远端车道(e2b/k8s)的 `capture:"off"` 声明自此真持久**;`DB_BACKEND=local` 自 7.71.0 起在记忆引擎接线时供 core 的控制面文件三腿(钉在本部署记忆引擎那只控制面目录上,按记忆平面取用)⇒ 单机默认部署(`CONFIG_PROVIDER=local`、`REMOTE_EXEC` 未设)上声明与中途翻转 verb 从 409 转为真受理(此前该 409 在这一形上是结构性的:core 判「远端」看的是执行环境的接口形,host 车道同被判 remote);记忆面暗的部署本席位缺席而 core 也不走采集腿。今天仍答 409 `config.memory_capture_unsupported` 的只剩把记忆引擎接了却没给任何店的自建装配形。用户面四口:提交键 `memoryCapture:"off"`(声明形)· `POST /v1/runs/:id/memory/capture-optout`(live 中途翻转,owner 门)· `POST /v1/sessions/:id/memory/erase`(所有者自助抹除,select 硬限本会话)· `GET /v1/sessions/:id/memory-status`(状态读面,owner 门,五可选键诚实缺席)。契约全文 `docs/ASSISTANT-WIRE-CONTRACT.md` §12 |
|
|
436
436
|
|
|
437
437
|
⚠️ **拒启动**:多租户 + 记忆面点亮 + `projects[].defaultScopes` 里有 org 键 + 目录源缺席 ⇒ 启动报错
|
|
438
438
|
并点名 projectId(该部署的每个此类请求都会在 prepare 期整拒,响亮拒启动比静默全拒服务诚实)。
|
|
439
|
-
⚠️ **回滚脚枪**:回滚到「无
|
|
439
|
+
⚠️ **回滚脚枪**:回滚到「无 web-admin」模板前先清掉残留的 `MEMORY_ORG_DIRECTORY_JSON` —— 否则
|
|
440
440
|
它作为 operator 自证通道**复活旧授权**(详见 `docs/DEPLOY-PREREQS.md`)。
|
|
441
441
|
|
|
442
442
|
**可选 — 模型级联 cascade(core 1.45,opt-in)**
|
|
@@ -505,7 +505,7 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
|
|
|
505
505
|
`SERVICE_AUTH_TOKEN(S)`),`kinds` ⊆ `service_token` / `sso`(装了鉴权桥:登记中心的登录令牌)/ `direct_door_jwt`(直连门),
|
|
506
506
|
`identityAttach` = `available`(kinds 里有 `sso` 或 `direct_door_jwt`)/ `unavailable`;`operator` = `present`(`OPERATOR_PRINCIPALS`
|
|
507
507
|
非空)/ `absent`;`center = { issuer?, webBaseUrl?, sessionsVisible }` —— `issuer` = 鉴权桥的 issuer(`AUTH_BRIDGE_ISSUER`,缺席回退
|
|
508
|
-
`
|
|
508
|
+
`SEMA_WEB_ADMIN_URL`;没有桥 ⇒ 缺席),`webBaseUrl` = `SEMA_WEB_ADMIN_WEB_URL` 原样(缺席即缺席),`sessionsVisible` = `true` /
|
|
509
509
|
`false` / `"unknown"`:中心车道配齐 ∧ `FLEET_ADVERTISE_ADDRESS` 在 ∧ 鉴权桥在 ∧ 会话枚举面在,缺一 ⇒ `false`;齐了而多租户 ⇒ `true`,
|
|
510
510
|
单用户 ⇒ `"unknown"`(一条会话归谁看提交它的请求带没带身份)。两只 URL 过展示面的同一条脱敏律(凭据 / query 值不上,尾斜杠折掉)。
|
|
511
511
|
⚠️ 无鉴权面:令牌、运维名单、worker 名、中心凭证都**不上**。钉:`test/health-self-report.test.ts`。
|
|
@@ -654,18 +654,18 @@ SEND_USER_FILE_SANDBOX_PUT_ENDPOINT=… # 可选:沙箱直传 PUT 的端
|
|
|
654
654
|
|
|
655
655
|
**可选 — 接配置控制面(中心化模型/角色/团队配置)**
|
|
656
656
|
```bash
|
|
657
|
-
#
|
|
658
|
-
# 凭证两键名
|
|
659
|
-
# `
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
#
|
|
657
|
+
# 名字只有一张表(settings-schema WEB_ADMIN_ENV);旧名(SEMA_REGISTRY_* 八只、更早的 CONFIG_CENTER_* / REGISTRY_ADMIN_TOKEN)在场即 boot 拒启,只认 SEMA_WEB_ADMIN_*
|
|
658
|
+
# 凭证两键名 SEMA_WEB_ADMIN_TOKEN / SEMA_WEB_ADMIN_TOKEN_FILE 与「二选一」判官的属主 = @sema-agent/settings-schema
|
|
659
|
+
# `WEB_ADMIN_ENV` / `judgeWebAdminCredentialDeclaration`(6.1.0 起;管理面 / 壳 / 本服务 import 同一处)
|
|
660
|
+
SEMA_WEB_ADMIN_URL=http://<web-admin-host>:3100 # 启动拉 GET /api/config/effective(Bearer+ETag),覆盖 env 兜底
|
|
661
|
+
SEMA_WEB_ADMIN_TOKEN=<SERVICE_PULL_TOKEN 的值> # 取自配置控制面主机 .env;只读拉取令牌
|
|
662
|
+
# SEMA_WEB_ADMIN_TOKEN_FILE=/path/to/token # 或:凭证文件(与上一行二选一,都设=拒启)。每条中心请求都重读 ⇒ 轮换 / 换账号改写文件即可,
|
|
663
663
|
# 中心请求面不重启;文件不可读 / 为空 = error 级 center_credential_unreadable,不当作无凭证。
|
|
664
|
-
# 内容两种类(S-698,7.102.0;判别 = settings-schema
|
|
664
|
+
# 内容两种类(S-698,7.102.0;判别 = settings-schema parseWebAdminCredentialFile):
|
|
665
665
|
# · 裸行 = 静态 bearer(worker 拉取令牌 / 用户 JWT),不续签,行为同 7.101.0;
|
|
666
666
|
# · JSON 信封 {"kind":"user-session",accessToken,refreshToken,expiresAt,issuer}(壳登录写)
|
|
667
667
|
# = 人的会话:引擎在到期前 CENTER_CREDENTIAL_RENEW_LEAD_MS 内自己续签并原子改写同一文件
|
|
668
|
-
# (续签只向与
|
|
668
|
+
# (续签只向与 SEMA_WEB_ADMIN_URL 同源的 issuer 发;400 先重读文件再判,壳与引擎两写者不打架)。
|
|
669
669
|
# 🔴 拒启两形:内容读不成([config.center_credential_malformed]:`{` 开头坏 JSON / 信封多键缺键 /
|
|
670
670
|
# issuer 非 http(s) / 裸行不是令牌)、多租户(REQUIRE_PRINCIPAL=true)读到信封
|
|
671
671
|
# ([config.center_credential_user_session_multi_tenant])。运行期同形 = 该次取值 fail-closed + 一次 warn。
|
|
@@ -677,23 +677,25 @@ SEMA_REGISTRY_TOKEN=<SERVICE_PULL_TOKEN 的值> # 取自配置控制面主机
|
|
|
677
677
|
# 身份的,直到重启(`/health` 报 restartRequired,reasons 含 skills);热面(models / roles /
|
|
678
678
|
# limits / 审批名单 …)在新身份第一份候选到达那一拍换代。「换账号即降档 + 当场拉取」候 S-667(不在 7.100.0)。
|
|
679
679
|
# 用户 JWT 形(CLI 登录)⇒ skill 正文 / prompt 产物走 /api/v1/me/…;盘上 LKG 与 prompt 状态按「worker + 凭证身份」分箱。
|
|
680
|
-
|
|
681
|
-
#
|
|
680
|
+
SEMA_WEB_ADMIN_DRY_RUN=true # 安全灰度:只 LOG 中心配置 vs env 推导的差异,不 apply
|
|
681
|
+
# SEMA_WEB_ADMIN_LIVE_URL=http://<中心活性入口>:3100 # 7.121.0:fleet **活性组**(心跳 / 注销、服务租约 —— settings-schema FLEET_LIVE_PATHS)打的中心基址。
|
|
682
682
|
# 给「管理面在就绪门后、活性面另走一条不看就绪的入口」的部署(k8s:第二只 Service 带
|
|
683
|
-
# publishNotReadyAddresses,Ingress 的活性规则指它)。缺席 =
|
|
684
|
-
# 拉配置始终打
|
|
683
|
+
# publishNotReadyAddresses,Ingress 的活性规则指它)。缺席 = SEMA_WEB_ADMIN_URL。用量、配额租约、
|
|
684
|
+
# 拉配置始终打 SEMA_WEB_ADMIN_URL。中心基址三席(SEMA_WEB_ADMIN_URL / _LIVE_URL / _WEB_URL)同一条判据:
|
|
685
685
|
# 可解析的 http(s) URL,不带凭据 / query / fragment(基址后面要拼路径),坏形拒启(没接中心也拒)。
|
|
686
686
|
# 拒句只点名规则,不回显 scheme:不是合格 http(s) 形的值(`host:8080`、`<令牌>:<口令>@host`、`ftp://…`)
|
|
687
687
|
# 整只成 `«redacted:url»`;合格 http(s) 形只到 `scheme://host[:port]`,后面的凭据 / 路径 / query / fragment 一律 `/…`。
|
|
688
|
-
#
|
|
688
|
+
# SEMA_WEB_ADMIN_WEB_URL=https://<中心 web 面> # 7.121.0:这个中心 **web 面**的固定基址,引擎原样转述到 `/health` 的 `center.webBaseUrl`
|
|
689
689
|
# (壳按它拼会话深链,路径怎么拼归壳)。缺席即缺席,引擎不拼不猜。形判同上(三席一条判据);
|
|
690
|
-
# 只在接了中心(
|
|
691
|
-
|
|
690
|
+
# 只在接了中心(SEMA_WEB_ADMIN_URL)时挂。7.124.0 起与中心身份起服互证:必须与 AUTH_BRIDGE_ISSUER
|
|
691
|
+
# (缺席 = SEMA_WEB_ADMIN_URL)同主机同端口(http ↔ https 同族,路径不比),否则拒启 —— 壳按 issuer 认中心、
|
|
692
|
+
# 按它拼深链,两者必须是同一台 web-admin。基址 / 活性基址是传输地址,不参与(可以是集群内地址)。
|
|
693
|
+
SEMA_WEB_ADMIN_WORKER=<worker名> # 可选:拉取 /effective?worker=<名> 取该 worker 的 roster(reconciler 按 worker 注);不设=全局 roster(向后兼容)
|
|
692
694
|
FLEET_ADVERTISE_ADDRESS=http://<本机可达IP>:8090 # 可选:设了才启公告环(announce/heartbeat/注销:在中心注册成可路由实例)。
|
|
693
695
|
# 必须是可解析的 http(s) base URL、不带 userinfo / query / fragment(与中心基址三席同一条判据)——
|
|
694
696
|
# host:port 等坏形 boot 响亮拒(拒启码 config.fleet_advertise_address_invalid;
|
|
695
697
|
# 7.121.0 起无条件解析,没接中心也拒;空串 = 未设)。公告环、成员围栏判官、/health.center 都读这一份。
|
|
696
|
-
# 🔴 7.106.0 起(S-866 ④)用量批报**不再**看本键:中心三件(URL + 凭证 +
|
|
698
|
+
# 🔴 7.106.0 起(S-866 ④)用量批报**不再**看本键:中心三件(URL + 凭证 + SEMA_WEB_ADMIN_WORKER)
|
|
697
699
|
# 配齐即每 60s 向中心 POST /api/fleet/usage(不公告的引擎也报用量;中心据此对租约单调对账)。
|
|
698
700
|
# 不想报就不配中心三件。caps `centerWiring.usageReport` 可读。
|
|
699
701
|
# FLEET_LEASE_FAIL_CLOSED=true # S-866 ③(7.106.0):租约**连接级**失败(中心拒了本引擎的凭证 / 凭证文件读不出;中心
|
|
@@ -708,9 +710,9 @@ FLEET_ADVERTISE_ADDRESS=http://<本机可达IP>:8090 # 可选:设了才启公
|
|
|
708
710
|
# generation(连同 leaseState / leaseUntil);排空口按它比 expectedGeneration。
|
|
709
711
|
# 服务租约只在中心于心跳答复里宣告 capabilities.servingLease 之后生效;没宣告 ⇒ leaseState none,
|
|
710
712
|
# 零续租零 503;位撤掉再宣告不重置 T_max 的钟(最后一次授予的到期 / 本进程第一次见到位的时刻照算)。
|
|
711
|
-
# 🔴 设了它就必须配齐中心三件(URL + 凭证 +
|
|
713
|
+
# 🔴 设了它就必须配齐中心三件(URL + 凭证 + SEMA_WEB_ADMIN_WORKER)与 FLEET_ADVERTISE_ADDRESS
|
|
712
714
|
# (不公告就听不到能力位),缺一 ⇒ 起服拒启 config.fleet_member_unfenced —— 成员不许悄悄脱离围栏。
|
|
713
|
-
# 续租打 POST /api/fleet/live/serving-lease(契约 FLEET_PATHS.servingLease;活性基址见
|
|
715
|
+
# 续租打 POST /api/fleet/live/serving-lease(契约 FLEET_PATHS.servingLease;活性基址见 SEMA_WEB_ADMIN_LIVE_URL)。
|
|
714
716
|
# 心跳答复体读不出 / 不是 JSON 对象 = 没有能力读数(位与租约钟原样,warn fleet_announce_reply_unreadable)。
|
|
715
717
|
# FLEET_SCOPE=acme # 7.121.0:本引擎所在的**租户 scope**(配置渲染器为多租引擎写 = 它的 per-worker 令牌绑的 scope)。缺席 =
|
|
716
718
|
# 缺省 scope global;在场必须是 scope id 形(小写字母 / 数字 / 内部连字符,1–64;空串拒启
|
|
@@ -736,8 +738,8 @@ FLEET_ADVERTISE_ADDRESS=http://<本机可达IP>:8090 # 可选:设了才启公
|
|
|
736
738
|
目录缺省。判据在目录应用**之后**:env `MODEL_ID` 或目录缺省任一在场即过;两者皆无 ⇒ 拒启(码 `config.default_model_missing`,指路二选一)。
|
|
737
739
|
中心拉取超出 `CONFIG_BOOT_FETCH_BUDGET_MS` 又没有 LKG 时,boot 那一刻没有目录 ⇒ 没有 `MODEL_ID` 兜底就拒启(调大预算让 boot 等目录,
|
|
738
740
|
或写一个 `MODEL_ID` 兜底)。
|
|
739
|
-
- **灰度姿势**(配置控制面 AI 建议):先 `
|
|
740
|
-
- 拉取**只读、只取逻辑配置**(模型名册/角色/团队);密钥/网关仍在本服务 env(中心只发 env-**名** 引用,不发密钥值)。回滚=去掉 `
|
|
741
|
+
- **灰度姿势**(配置控制面 AI 建议):先 `SEMA_WEB_ADMIN_DRY_RUN=true` 起一轮,看日志 `web_admin_dry_run`(中心给的 models/roles/teams + 会否覆盖 default、per-model apiKeyEnv)对得上 env 再去掉该 flag 真正 apply。
|
|
742
|
+
- 拉取**只读、只取逻辑配置**(模型名册/角色/团队);密钥/网关仍在本服务 env(中心只发 env-**名** 引用,不发密钥值)。回滚=去掉 `SEMA_WEB_ADMIN_URL` 即纯 env。
|
|
741
743
|
- **配置热更新真表(7.38+ 现行)**——refresh 拍(60s 轮询,或 `POST /v1/admin/config/refresh` 手动触发,见 API 表)对各域的生效方式:
|
|
742
744
|
|
|
743
745
|
| 域 | 生效 | 说明 |
|
|
@@ -747,7 +749,7 @@ FLEET_ADVERTISE_ADDRESS=http://<本机可达IP>:8090 # 可选:设了才启公
|
|
|
747
749
|
| tier 变更(档位组 / 档位绑定) | **热**(下一 refresh 拍) | 与 models 同一条换代腿:tier 表随模型面在 commit 前原子换进全部 boot Runner,下一任务按新档位路由,`/health` 不报 `restartRequired`(7.100.0 起 `models-tiers` 重启理由退役——它在生产上从未发出过) |
|
|
748
750
|
| plugins(插件引用) | 重启生效 | 插件只在启动期物化;改了之后 `/health` 报 `restartRequired`,`restart.reasons` 含 `skills`(7.100.0 前这一改动**不报**,要等一次无关的重启) |
|
|
749
751
|
| skills / mcp / a2a / scenarios(技能 / MCP / A2A / 场景) | 重启生效 | 只在启动期物化;中心改了 ⇒ `/health` 报 `restartRequired`,`restart.reasons` 各含其名。**换账号**(凭证身份变化)同理:这些面仍是上一身份的直到重启(候 S-667) |
|
|
750
|
-
| 审批名单(`governance.approvalRequire`) | **热**(下一 refresh 拍) | 7.100.0 起:下一个任务调名单里的工具即停车等审批;撤键回 env 底(`APPROVAL_REQUIRE`)。**没有 checkpoint 店**的部署(`DURABLE_APPROVAL` 关 / 后端无 checkpoint 面)发非空名单 ⇒ 审批组**整组拒**(旧名单继续服务、`configAppliedVersion` 停旧代、拒因见日志 `
|
|
752
|
+
| 审批名单(`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`,该理由退役) |
|
|
751
753
|
| 记忆整理阀门判决 + 驱动席(`MEMORY_CONSOLIDATION_DRIVER=on`,或未设而出厂缺省按目录派生 —— 7.104.0 起) | 重启生效 | 阀门开关与整理跑用的模型都在启动期定一次;中心改了 roles / 档位 / 目录条目后,**判决会翻**(派生开 ↔ 关,含只把主模型挪到整理座那一条上)或开着而座漂了 ⇒ `/health` 报 `restartRequired`,`restart.reasons` 含 `memory-consolidation`(7.100.0 新理由;7.104.0 起派生关的那台同样报) |
|
|
752
754
|
|
|
753
755
|
生效与否的观测口=`/health` 世代账键(`configTargetVersion`/`configAppliedVersion`+两 ordinal+`configApplyStaleMs`,见 API 表 `/health` 行)——target≠applied 持续=「改了没生效」的机读信号。
|
|
@@ -968,11 +970,11 @@ APPROVAL_GRANT='mcp__figma__*,a2a__planner__*,Bash(git:*)' # 逗号分隔的 a
|
|
|
968
970
|
- **不是白名单**:它只放,不收窄任何东西(「只许这些工具」是会话规则 `PUT /v1/sessions/:id/policy` 的 `toolAllow`)。
|
|
969
971
|
- **射程如实**:规则对**每一个租户、每一条腿**生效;`mcp__<server>__*` 也覆盖那台服务器将来新增的工具。按需写窄;要收回某一只,写
|
|
970
972
|
`APPROVAL_DENY` / `APPROVAL_REQUIRE`(压过本键),或删掉规则重启。
|
|
971
|
-
- **A2A 如实**:运维声明的对端(
|
|
973
|
+
- **A2A 如实**:运维声明的对端(web-admin / `config.d`,刻意不收 `toolAxes`)上,每次技能调用都是引擎自己的外发标记问(`tool_marks_a2a`),allow 层清不掉它;
|
|
972
974
|
只有请求体自带的对端(单用户闸)能在 `toolAxes` 里把某个技能的 `egress` 降为 `false`,那之后才是普通问,本键才清得掉。
|
|
973
975
|
- **看得见**:起服日志一行 `approval_grant_in_force`(条数 + 规则 + 一句当前效果:清什么、清不掉什么);`GET /v1/diagnostics/wiring`(运维面)`serverGates.approvalGrant`
|
|
974
976
|
= 规则原样(空表 = 未设);`GET /v1/config/catalog` 一行。客户端能力面(`/v1/capabilities`)不报它。
|
|
975
|
-
- v1 只有 env 一个写入通道(
|
|
977
|
+
- v1 只有 env 一个写入通道(web-admin 与 settings 契约没有这个键)。
|
|
976
978
|
|
|
977
979
|
**可选 — 写保护名表(S-138;缺席=引擎缺省表在岗)**
|
|
978
980
|
|
|
@@ -1062,12 +1064,12 @@ READ_DENY_PATTERNS='[{"pattern":"**/secrets/**"},{"pattern":".ssh","caseSensitiv
|
|
|
1062
1064
|
deny 追加对 leader worker **静默不生效**(core 内建 deny 表仍在)。扩面(7.56 起)再补三席:计费/遥测
|
|
1063
1065
|
`tracer`(连同后台腿的 principal 归因)、治理窗 `USAGE_WINDOWS` 两键(连同 `POST /v1/leader` 的准入门)、
|
|
1064
1066
|
center 发布的提示词目录 —— 治理窗那一席的细节见 §9.2。
|
|
1065
|
-
- **组织下发腿(
|
|
1066
|
-
`READ_DENY_BUILTIN_TIERS` / `READ_DENY_BUILTIN_EXCLUDE`)也可由
|
|
1067
|
+
- **组织下发腿(web-admin,7.29 起)**:四键(`READ_FACE` / `READ_DENY_PATTERNS` /
|
|
1068
|
+
`READ_DENY_BUILTIN_TIERS` / `READ_DENY_BUILTIN_EXCLUDE`)也可由 web-admin 的 `readFace` 域下发
|
|
1067
1069
|
(`{face?, denyPatterns?, denyBuiltinTiers?, denyBuiltinExclude?}`)。三条规则:
|
|
1068
1070
|
① **本机 env 显式恒赢**(逐键判)——env 是这台机器的部署主权,下发值不会静默盖掉它,被忽略的键留一条
|
|
1069
|
-
`
|
|
1070
|
-
保留 env 前值 + `
|
|
1071
|
+
`web_admin_read_face_env_wins` warn;② **坏值整域拒**——任何一个成员过不了引擎自己的校验器就整域不落、
|
|
1072
|
+
保留 env 前值 + `web_admin_read_face_invalid` warn(半应用一份 READ 姿态比不应用更危险);
|
|
1071
1073
|
③ **一席热、三席重启**(server 7.69.0 起,引擎侧交付了 read-face 的活体座席):`face` 是**热换席** ——
|
|
1072
1074
|
下发或撤回一个档位,下一拍 refresh(≤60s,或手动 `POST /v1/admin/config/refresh`)当场换进全部 Runner,
|
|
1073
1075
|
**不重启、也不再签 `read-face` 重启理由**(已在飞的任务保留它准备时的档位,新任务读新档);三个 deny 键
|
|
@@ -1113,7 +1115,7 @@ TASK_WRITE_FACE_ESCALATION=allow # 词表 allow|refuse;坏词启动期响亮
|
|
|
1113
1115
|
**托管 ⇔ `REQUIRE_PRINCIPAL=true`**,只认运维自己的声明,零推断。
|
|
1114
1116
|
🔴 **签发方配置不算证据**:`PRINCIPAL_JWT_PUBKEYS` / `_ISS` / `_AUD` 与 `AUTH_BRIDGE_ISSUER` 在不在场都不改变判定
|
|
1115
1117
|
(7.95.0–7.98.x 曾把它们当证据,并对「配了签发方却没设 `REQUIRE_PRINCIPAL`」的机器拒启;那条拒启随之删除)。
|
|
1116
|
-
auth-bridge 那条 `??
|
|
1118
|
+
auth-bridge 那条 `?? webAdmin.baseUrl` 回退、`SEMA_WEB_ADMIN_*`(含 worker 名)同样不读。
|
|
1117
1119
|
⚠️ 反面是**全量召回缺口**:忘设 `REQUIRE_PRINCIPAL` 的团队机器按单用户运行、本门放行 —— 多租机器必须自己设它,
|
|
1118
1120
|
后果与被排除的候选(`OPERATOR_PRINCIPALS`、`SERVICE_AUTH_TOKENS` 条目数、`DB_BACKEND`、`BIND_HOST`)逐条写在
|
|
1119
1121
|
`docs/DEPLOY-PREREQS.md`。
|
|
@@ -1599,7 +1601,7 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
|
|
|
1599
1601
|
| `POST /v1/approvals/<sessionId>/decide` `{"decision":"approve"|"deny","reason":"…"}` | 批/否并恢复挂起任务(CAS,重复决议 409);**仅 operator**(非 operator → 403)。5.0.0 起旧轮询腿 `POST /v1/approvals/<id>` 已退役 |
|
|
1600
1602
|
| `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) |
|
|
1601
1603
|
| `GET /v1/capabilities/scenarios/<name>` | 单个场景的只读详情:工具面、提示词概览、**本部署现在跑不跑得动**(见下) |
|
|
1602
|
-
| `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 名(`
|
|
1604
|
+
| `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` 已退役。 |
|
|
1603
1605
|
| `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` 七行的座位随之删除,S-866 ⑤a);顶层带 `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 原文。 |
|
|
1604
1606
|
| `POST /v1/admin/config/refresh` | **配置手动刷新**(operator-only,与上一行同门:空 `OPERATOR_PRINCIPALS` ⇒ 恒 403)。改完中心配置 / `config.d` 之后不想等 60s 轮询就调它:触发**一次既有**的 refresh 拍(在飞则汇入那一拍,绝不并发双拍),返回时该拍已落地。200 体 `{triggered:true,targetVersion,appliedVersion}` —— 两个世代号与 `/health` 的 `configTargetVersion`/`configAppliedVersion` 同源。本部署没有配置管道(纯 env worker:既没配 `CONFIG_CENTER`、也不是 `CONFIG_PROVIDER=local`)⇒ 409 `config.refresh_unavailable` 指路,不会回一个「触发了但什么都没发生」的 200。 |
|
|
1605
1607
|
| `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 **持续**时长;收敛时键缺席)—— 编排器据此摘掉「配置持续落后」的副本 |
|
|
@@ -1666,7 +1668,7 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
|
|
|
1666
1668
|
> **折叠(consolidation)阀门**。一个从未折叠过的记忆库只涨不折,检索质量随库龄衰减;开着 ⇒ worker 装上两个
|
|
1667
1669
|
> operator 口(人在自己选的节奏上跑折叠),引擎面整理两席接上(下一根旋钮的前置)。
|
|
1668
1670
|
> 🔴 **出厂缺省 = 开,但只在整理座不落主模型时生效**:
|
|
1669
|
-
> - **未设 + 便宜模型在场**(`MODEL_CHEAP_ID` 设了,或目录 `config.d/models.json` /
|
|
1671
|
+
> - **未设 + 便宜模型在场**(`MODEL_CHEAP_ID` 设了,或目录 `config.d/models.json` / web-admin 给 `roles.consolidate` /
|
|
1670
1672
|
> `roles.summarize` / `flash` 档位绑了主模型之外的条目)⇒ **开**。判据按**装配后**的整理座(引擎自己的解析器:
|
|
1671
1673
|
> consolidate → summarize,含 flash 档位缺省)解到的**目录条目**是不是主模型那一条(按条目身份,同 id 异网关的两条算两只;
|
|
1672
1674
|
> `MODEL_CHEAP_ID` 与 `MODEL_ID` 写成同一个 id 则不铸 cheap 槽 —— 那就是主模型,算「没有便宜模型」),不按 env 拼写。
|
|
@@ -1677,7 +1679,7 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
|
|
|
1677
1679
|
> ⇒ **恒关、零提示**(配不配便宜模型它都开不了;缺省不造新拒启)。
|
|
1678
1680
|
> - **显式 `on` / `off` 恒赢**。显式 `on` 而座落主模型 ⇒ 照开,起服一行 warn `memory_consolidation_on_main_model`
|
|
1679
1681
|
> 说清按主模型计费(部署方显式承担)。
|
|
1680
|
-
> -
|
|
1682
|
+
> - 起服**判一次**:之后 web-admin 热改 roles / 档位(或 boot 拉取超窗、目录迟到),阀门不跟着翻,但翻的需要由
|
|
1681
1683
|
> `restart.reasons ∋ "memory-consolidation"` 报出来 —— 判决会翻(派生开 ↔ 关,含只把主模型挪到整理座那一条上)或开着而座漂了 ⇒ 报;
|
|
1682
1684
|
> 判决不翻的目录变化不报。
|
|
1683
1685
|
> 启动日志 `memory_consolidation_driver_armed` 带一位 `valve`(`explicit` / `derived`)与模型 id。配置目录这一行
|
|
@@ -2038,7 +2040,7 @@ k8s 沙箱(pod `activeDeadlineSeconds` 真硬死线)向引擎声明 `lifetimeMs`
|
|
|
2038
2040
|
|
|
2039
2041
|
### 9.4 auto 模式(`permissionMode:"auto"`):武装三态、本地 org deny、自查(与 core 的意图武装式合修)
|
|
2040
2042
|
|
|
2041
|
-
**背景**:此前引擎只在「组织授予」(
|
|
2043
|
+
**背景**:此前引擎只在「组织授予」(web-admin 解出的 per-principal `runtimeCaps.autoMode === true`)时武装
|
|
2042
2044
|
auto 分类器,而无 center 的本地/自托管部署没有任何授予路径 ⇒ 壳选了 auto,引擎跑的仍是 default + 静态只读
|
|
2043
2045
|
白名单(真实用户诊断属实)。core 的新武装式对齐 CC 的「用户开 + 组织拒」极性:
|
|
2044
2046
|
|
|
@@ -2199,7 +2201,7 @@ WebSearch 工具的 `tool_result` 错误文本,回到模型的对话里,**不是
|
|
|
2199
2201
|
**读面 / 展示面**(账本 / SSE / turns / 审批预览 / `GET /v1/sessions/:id`);**不碰**引擎会话存档(审计真源:store 里怎么写就怎么回、HTTP 读面才脱敏,之后每一次续轮按原文回送模型路由)与 session-sync 七口 `/v1/sessions/:id/sync/*`(逐字 durable log 出入)——这两面是**原形**——**工具结果发往「该 run 实际选中的模型路由」的请求体是原文**。⚠️ 「实际选中的路由」
|
|
2200
2202
|
不等于 `MODEL_GATEWAY_BASEURL` 一根:本部署的模型出口至少四条——默认网关(`MODEL_GATEWAY_BASEURL`)、
|
|
2201
2203
|
备用网关(`MODEL_GATEWAY_FALLBACK_URLS`,failover 拓扑参与)、`provider:"anthropic"` 模型的**独立直连
|
|
2202
|
-
路由**(缺省根 `https://api.anthropic.com`,除非显式配 `ANTHROPIC_BASEURL`)
|
|
2204
|
+
路由**(缺省根 `https://api.anthropic.com`,除非显式配 `ANTHROPIC_BASEURL`)、以及 web-admin 可对**单个
|
|
2203
2205
|
目录模型**下发的 per-model `baseUrl` 覆盖(见 `docs/ARCHITECTURE.md` §10「Trace/redaction」)。要让内容
|
|
2204
2206
|
不出机器,靠**读侧敏感路径门**(`READ_DENY_PATTERNS` 等,见 §「执行 lane 与租户门」附近)并核对**上述
|
|
2205
2207
|
四条路由每一条**都指向自己控制的基础设施——只把 `MODEL_GATEWAY_BASEURL` 指到私有网关、放着
|
|
@@ -2260,7 +2262,7 @@ docker 推送的鉴权走构建宿主上的 docker 凭据存储(`docker login`),
|
|
|
2260
2262
|
### 9.8 出网代理:`HTTP_PROXY` / `HTTPS_PROXY` / `NO_PROXY`(7.103.0 起)
|
|
2261
2263
|
|
|
2262
2264
|
server 进程(HTTP 服务与 `run-local`)**所有走 undici 的出网跳**都认这三只平台标准变量:引擎 → 模型网关(http 网关发**绝对 URL 形请求**、https 网关经 `CONNECT` 隧道)、
|
|
2263
|
-
|
|
2265
|
+
web-admin 拉取 / fleet 公告 / 凭证续签、WebSearch、对象存储、OTLP、hook 模型调用、core 的 MCP streamable-http / A2A / WebFetch 工具 …
|
|
2264
2266
|
实现是**一只**进程级 dispatcher,在启动时装一次;没有另外的开关 —— 这三只变量本身就是配置通道。
|
|
2265
2267
|
|
|
2266
2268
|
- **读法**(与 undici 读 env 逐字同序):小写 `http_proxy` / `https_proxy` / `no_proxy` 同认**且优先**;https 目标在 `HTTPS_PROXY` 缺席 / 空时
|
|
@@ -2490,7 +2492,7 @@ server 进程(HTTP 服务与 `run-local`)**所有走 undici 的出网跳**都认
|
|
|
2490
2492
|
### 9.10 模型钥匙的 sealed 形(本地车道)(7.104.0 起)
|
|
2491
2493
|
|
|
2492
2494
|
把模型 key 以**密文**写进 `config.d/models.json`:文件里只有密文,明文只在引擎进程的内存里出现。本节写本地车道(`CONFIG_PROVIDER=local`
|
|
2493
|
-
的 HTTP 服务进程)
|
|
2495
|
+
的 HTTP 服务进程);接 web-admin 的部署走中心形(web 面贴 key、中心存密文),开箱是同一条路径。⚠️ `run-local` 一次性 CLI **不开保管店**:
|
|
2494
2496
|
同一份 `config.d` 里的 sealed 条目在 `run-local` 下是 `no_key_store`(见下表),响亮失败,不会退回别的 key。
|
|
2495
2497
|
|
|
2496
2498
|
**① 取公钥。** `GET /v1/capabilities`(与其它能力位同门,在服务凭据门之后)多一段 `sealedKey`,三键:
|
|
@@ -2529,15 +2531,15 @@ const ciphertext = Buffer.from(sodium.crypto_box_seal(sodium.from_string(apiKey)
|
|
|
2529
2531
|
这只模型的每一次模型调用都失败(用它的任务响亮失败),**不退回 `apiKeyEnv`,也不退回网关 `MODEL_API_KEY`**。🔴 **7.106.0 起(S-896)两种写法
|
|
2530
2532
|
「坏了之后」方向相同**:只写 `apiKeyEnv` 而本服务进程里那个 env 变量没设(或是空串)⇒ 这只模型的钥匙同样中毒 —— 起服照常,每一次模型调用都失败、
|
|
2531
2533
|
拒句点名那个变量(理由 `env_missing`),**不退回网关 `MODEL_API_KEY`**(7.105.0 及以前在这里静默退回网关 key)。配置应用时仍有一行 warn
|
|
2532
|
-
`
|
|
2534
|
+
`web_admin_model_key_env_missing`(hint 说中毒与出路)。出路:在跑本服务的进程环境里设上那个变量并重启;真想让这只模型用网关 key,就从条目上删掉 `apiKeyEnv`。
|
|
2533
2535
|
|
|
2534
2536
|
**⑤ 三种失败词**(配置应用那一拍每只模型一行 warn;用这只模型的任务在模型调用处失败,错误句里带失败词与 `publicKeyId`):
|
|
2535
2537
|
|
|
2536
2538
|
| 失败词 | 什么时候出 | 怎么修 |
|
|
2537
2539
|
|---|---|---|
|
|
2538
|
-
| `sealed_decrypt_failed` | 本进程**持有**这个 `publicKeyId` 的私钥,但开不开:密文不是对着这把公钥封的、被截断或改坏(短于 48 字节)、缺了应有的 `=` 补齐。warn `
|
|
2539
|
-
| `stale_ciphertext` | 本进程**没有**这个 `publicKeyId` 的私钥:密文是对着别的进程 / 别的宿主 / 别的副本的公钥封的,或那把钥匙的私钥文件被删、读不了。warn `
|
|
2540
|
-
| `no_key_store` | 本进程没有保管店:起服时保管目录建不了、改不了权限(只读挂载 / 权限不够 / 数据根不是目录),caps 没有 `sealedKey` 段。warn `
|
|
2540
|
+
| `sealed_decrypt_failed` | 本进程**持有**这个 `publicKeyId` 的私钥,但开不开:密文不是对着这把公钥封的、被截断或改坏(短于 48 字节)、缺了应有的 `=` 补齐。warn `web_admin_model_sealed_key_open_failed` | 对着 caps 当前给的公钥重封,写回条目 |
|
|
2541
|
+
| `stale_ciphertext` | 本进程**没有**这个 `publicKeyId` 的私钥:密文是对着别的进程 / 别的宿主 / 别的副本的公钥封的,或那把钥匙的私钥文件被删、读不了。warn `web_admin_model_sealed_key_no_private_key`(带本机持有的 id) | 对着**这个进程** caps 给的公钥重封;或把那个 id 的私钥文件放回保管目录后重启 |
|
|
2542
|
+
| `no_key_store` | 本进程没有保管店:起服时保管目录建不了、改不了权限(只读挂载 / 权限不够 / 数据根不是目录),caps 没有 `sealedKey` 段。warn `web_admin_model_sealed_key_no_store` | 修好保管目录(进程要能建它、每次起服把它设成 `0700`)后重启 |
|
|
2541
2543
|
|
|
2542
2544
|
**⑥ 保管目录与轮换。** 保管目录 = `<数据根>/sealed-key/`(数据根 = `LOCAL_DATA_ROOT` → `CONFIG_LOCAL_DIR` → `AGENT_DATA_DIR` → `~/.ai-agent`),
|
|
2543
2545
|
每把钥匙一个文件 `<publicKeyId>.json`(文件 `0600`,目录 `0700`)。**私钥只在这里**:丢了,对着它封的密文全部变成 `stale_ciphertext`,
|
|
@@ -2557,7 +2559,7 @@ const ciphertext = Buffer.from(sodium.crypto_box_seal(sodium.from_string(apiKey)
|
|
|
2557
2559
|
或放好钥匙文件)再起其余副本 —— 空目录上并发首启会各自生成一把,各副本只持有自己那一把,直到下次重启。目录要对每个副本**可写**(每次起服
|
|
2558
2560
|
会把它设成 `0700`;只读挂载 ⇒ 建店失败 ⇒ `no_key_store`)。⚠️ 只共享 `sealed-key/` 这一个子目录:`DB_BACKEND=local` 的整个数据根禁止跨容器 /
|
|
2559
2561
|
跨主机共享(`docs/DEPLOY-PREREQS.md`)。
|
|
2560
|
-
-
|
|
2562
|
+
- **走中心形**:接 web-admin,执行侧公钥登记在中心(起服时带管理令牌自动上报,否则把公钥打印出来供手工登记),web 面对着登记的公钥封、中心存密文。
|
|
2561
2563
|
调用方不必逐台去问 caps;但同一个 worker 的多个副本各有各的保管店这件事中心形不改变 —— 要它们都开得了同一份密文,仍要共享保管目录。
|
|
2562
2564
|
|
|
2563
2565
|
### 9.11 会话规则店:抹除封键与显式恢复(7.106.0 起)
|
|
@@ -35,10 +35,12 @@
|
|
|
35
35
|
* `ask` 铸造点(呈卡帧 register 后、emit 前):wire approvalId / 出处 taskId / owner /
|
|
36
36
|
* toolName / `legTracked`(出处腿是否在协调器 `runWithContext` 视野里 —— decided
|
|
37
37
|
* 臂的收敛前提,见下)/ 窗死线 / 铸造时刻。
|
|
38
|
-
* `settle` 决议点(`settle()` 公共咽喉,五臂全过):outcome(
|
|
38
|
+
* `settle` 决议点(`settle()` 公共咽喉,五臂全过):outcome({@link SETTLE_OUTCOME} 三词)+
|
|
39
39
|
* `parkRouted`(窗到期/断连等 park 路由臂如实标注 —— 该行的续命在 durable park 面,
|
|
40
|
-
* 收敛器**不**把它当孤儿)+ 宿主自报(在场才带)+
|
|
41
|
-
*
|
|
40
|
+
* 收敛器**不**把它当孤儿)+ 宿主自报(在场才带)+ **`cause`(成因闭集 {@link SettleCause},`approval-deny-reasons.ts`)**:
|
|
41
|
+
* `outcome:"expired"`(非人结局)恒带、人答(`allowed` / `denied`)恒缺席 —— 写口的联合型逼每个非人调用点点名一个;
|
|
42
|
+
* 词表外 ⇒ 计坏行、结清照认;本版之前落盘的 `expired` 行没有 `cause`(存量审计行不回填,读面缺席即缺席)。
|
|
43
|
+
* `cause:"shutdown_before_park"`(S-929)= 这次等待是被**有序关停**结束的 —— `drainForShutdown` 按断连同路收场的活卡;park 路由的那几行**等观察到 core 已处置这次调用才落**(F1:停车行提交 / 承载它的腿退场,后者恒在 `leg_closed` 之前),两者皆无就退出 ⇒ 无此行。
|
|
42
44
|
* `leg_closed` 腿闭点(`runWithContext` finally;只为铸过审计行的 taskId 落,防账本膨胀):
|
|
43
45
|
* 「决议之后腿有没有闭」是 decided 臂唯一的机械证据面 —— 没有它,「批完跑完」与
|
|
44
46
|
* 「批了、执行窗内崩」在账本字节上同形,收敛器只能二选一地撒谎。
|
|
@@ -169,7 +171,7 @@
|
|
|
169
171
|
*/
|
|
170
172
|
import type { HostDecision } from "./host-decision.js";
|
|
171
173
|
import { AppendLog } from "@sema-agent/core";
|
|
172
|
-
import
|
|
174
|
+
import { SETTLE_OUTCOME, type HolderGoneCause, type SettleCause } from "./approval-deny-reasons.js";
|
|
173
175
|
/** 数据子目录名(挂在 local 数据根下,与 `workflow-notify/` 同层同形)。 */
|
|
174
176
|
export declare const APPROVAL_ASK_AUDIT_DIR = "approval-ask-audit";
|
|
175
177
|
/** 账本文件名(目录内唯一一只;启动收敛据它判「盘上有没有前世审计证据」,见 `approval-crash-convergence.ts` 的 `openAuditEvidenceForConvergence`)。 */
|
|
@@ -194,38 +196,30 @@ export interface AuditAskInput {
|
|
|
194
196
|
expiresAtMs: number;
|
|
195
197
|
mintedAtMs: number;
|
|
196
198
|
}
|
|
197
|
-
/**
|
|
198
|
-
|
|
199
|
-
* 共用这一个词。读盘词表 = {@link AUDIT_SHUTDOWN_CAUSE_WORDS}(编译期穷举)。词表外的值(新版本写的、本二进制不认识的成因 / 手改)⇒
|
|
200
|
-
* **计坏行**(`corruptLines`,坏值响亮律 —— 不静默读成「没写成因」),但**不丢该 settle 行的结清**(F4:outcome / parkRouted 照认 ——
|
|
201
|
-
* 因为一个成因词把整条结清行丢掉,会把一张已结清的卡收成 pending 孤儿,方向比「少认一个成因」更坏)。
|
|
202
|
-
*/
|
|
203
|
-
export type AuditShutdownCause = typeof VOID_REASONS.HOLDER_SHUTDOWN;
|
|
204
|
-
/**
|
|
205
|
-
* 成因词的读盘词表(F4:`satisfies Record<AuditShutdownCause, true>` ⇒ 闭集加一个词而本表没跟 = **编译红**;判定走 `Object.hasOwn`,
|
|
206
|
-
* 原型链上的名字不算词 —— CLAUDE.md 词表律)。唯一读者 = {@link readShutdownCause}。
|
|
207
|
-
*/
|
|
208
|
-
export declare const AUDIT_SHUTDOWN_CAUSE_WORDS: {
|
|
209
|
-
readonly shutdown_before_park: true;
|
|
210
|
-
};
|
|
211
|
-
/** 收敛行 `cause` 的闭集(`GET /v1/approvals` `crashConverged[].cause`):崩溃孤儿 / 有序关停截断的腿(S-929 +1;与 `crashed_before_park` 同形)。
|
|
212
|
-
* 🔴 **词表属主 = `approval-deny-reasons.ts` 的 `VOID_REASONS`**(`HOLDER_CRASHED` / `HOLDER_SHUTDOWN`):启动收敛把同一问的问账行收成 VOID 时
|
|
213
|
-
* `deniedReason` 写的就是这两个词 —— 同一问在 `crashConverged` 与决断口 410 体上只有一个成因词(本型不再自带一份字面量)。
|
|
214
|
-
* wire 值闭集登记在 `trace/self-minted-wire.ts` 的 `crashConvergedCause`(`satisfies Record<CrashConvergedCause, …>` 双向绑定 ⇒ 加词不登记 = tsc 红,
|
|
215
|
-
* wire 变更清单从那张表 diff —— F3)。 */
|
|
216
|
-
export type CrashConvergedCause = HolderGoneReason;
|
|
217
|
-
/** 决议点入参(`settle()` 公共咽喉挂线;五臂全过,不漏窗到期/断连/取消)。 */
|
|
218
|
-
export interface AuditSettleInput {
|
|
199
|
+
/** 决议点入参共有的位(联合型的两臂都带)。 */
|
|
200
|
+
interface AuditSettleBase {
|
|
219
201
|
approvalId: string;
|
|
220
|
-
outcome: "allowed" | "denied" | "expired";
|
|
221
202
|
/** 走了 park 路由(windowRouteUnavailable)—— 该行的续命在 durable park 面,收敛器不当孤儿。 */
|
|
222
203
|
parkRouted?: boolean;
|
|
223
|
-
/** S-929:这次等待是被有序关停结束的(`ToolApprovalCoordinator.drainForShutdown`);缺席 = 别的臂。 */
|
|
224
|
-
cause?: AuditShutdownCause;
|
|
225
204
|
/** 宿主自报来源(HostSettledBy;在场才带)。收敛器不消费,纯审计。 */
|
|
226
205
|
settledBy?: string;
|
|
227
206
|
settledAtMs: number;
|
|
228
207
|
}
|
|
208
|
+
/**
|
|
209
|
+
* 决议点入参(`settle()` 公共咽喉挂线;五臂全过,不漏窗到期/断连/取消)。**判别联合**:`cause` 在场 ⇔ 非人结局(`outcome:"expired"`)——
|
|
210
|
+
* 人答(`allowed` / `denied`)不带 `cause`,非人结局不点名成因就**编译不过**(类型面上没有「`expired` 而无成因」这个值)。
|
|
211
|
+
*/
|
|
212
|
+
export type AuditSettleInput = (AuditSettleBase & {
|
|
213
|
+
outcome: typeof SETTLE_OUTCOME.ALLOWED | typeof SETTLE_OUTCOME.DENIED;
|
|
214
|
+
cause?: undefined;
|
|
215
|
+
}) | (AuditSettleBase & {
|
|
216
|
+
outcome: typeof SETTLE_OUTCOME.EXPIRED;
|
|
217
|
+
cause: SettleCause;
|
|
218
|
+
});
|
|
219
|
+
/** 收敛行 `cause` 的闭集(`GET /v1/approvals` `crashConverged[].cause`):崩溃孤儿 / 有序关停截断的腿(S-929 +1;与 `crashed_before_park` 同形)。
|
|
220
|
+
* 🔴 **词表属主 = `approval-deny-reasons.ts` 的 {@link HolderGoneCause}**(本表两词的子集):启动收敛把同一问的问账行收成 VOID 时 `denied_reason` 写的就是这两个词 ——
|
|
221
|
+
* 同一问在 `crashConverged` 与决断口 410 体上只有一个成因词。wire 值闭集登记在 `trace/self-minted-wire.ts` 的 `crashConvergedCause`
|
|
222
|
+
* (`satisfies Record<HolderGoneCause, …>` 双向绑定 ⇒ 加词不登记 = tsc 红,wire 变更清单从那张表 diff —— F3)。 */
|
|
229
223
|
/**
|
|
230
224
|
* [ref] **迟到人工结算点**入参:调用方(两条 HTTP decide 腿)只交 **checkpoint 锚 + 这次结算的事实**,
|
|
231
225
|
* `approvalId` 的反查与「算不算迟到」的判据**属主在店内**(一处成文,见 {@link FileApprovalAskAuditStore.recordLateSettle})
|
|
@@ -302,9 +296,9 @@ export interface CrashConvergedRow {
|
|
|
302
296
|
* 不是语义(改这个值会让把它当闭集判别式读的消费端整行丢弃,须与消费端同窗改)。
|
|
303
297
|
*/
|
|
304
298
|
decision: "denied";
|
|
305
|
-
/** 成因注(闭集 {@link
|
|
299
|
+
/** 成因注(闭集 {@link HolderGoneCause}):`crashed_before_park` = 进程崩了;`shutdown_before_park`(S-929)= 腿被**有序关停**截断
|
|
306
300
|
* (判据见头注「收敛行的 `cause`」)。两者的 `orphanState` / `resumeSafe` 判据相同。 */
|
|
307
|
-
cause:
|
|
301
|
+
cause: HolderGoneCause;
|
|
308
302
|
orphanState: CrashOrphanState;
|
|
309
303
|
/** decided 臂:人当时批的是 approve(这句话不许被终态同码吞掉)。 */
|
|
310
304
|
originalDecision?: "approve";
|
|
@@ -447,7 +441,7 @@ export declare class FileApprovalAskAuditStore implements ApprovalAskAuditSink,
|
|
|
447
441
|
* {@link causeOf})。前世账本里没有这只 `approvalId` 的铸造行(崩在 ask 账落行之后、本店铸造行之前,或本店那时不在)⇒ `crashed_before_park`:
|
|
448
442
|
* 没有有序关停的证据,就不说它是有序关停。判据读的是构造时冻结的前世账本 ⇒ 同一份账本跨几次启动恒得同一个词。
|
|
449
443
|
*/
|
|
450
|
-
causeFor(approvalId: string):
|
|
444
|
+
causeFor(approvalId: string): HolderGoneCause;
|
|
451
445
|
/** S-929 收敛行的成因注(判据见头注「收敛行的 `cause`」):组的最后一个事件行之后、收敛行(若已收敛)之前,有本 taskId 的
|
|
452
446
|
* 有序关停记号 ⇒ `shutdown_before_park`;否则 `crashed_before_park`。 */
|
|
453
447
|
private causeOf;
|
|
@@ -466,4 +460,5 @@ export declare class FileApprovalAskAuditStore implements ApprovalAskAuditSink,
|
|
|
466
460
|
/** 收追加口(优雅关闭腿;SIGKILL 下不跑也不丢 —— 每行已 fsync)。core `AppendLog.close()` 自己吞 `closeSync` 的错、不抛(全函数),本店不再包 catch。 */
|
|
467
461
|
close(): void;
|
|
468
462
|
}
|
|
463
|
+
export {};
|
|
469
464
|
//# sourceMappingURL=approval-ask-audit-store.d.ts.map
|
|
@@ -3,20 +3,21 @@ import { join } from "node:path";
|
|
|
3
3
|
import { AppendLog, ensureDir, isHostDecider } from "@sema-agent/core";
|
|
4
4
|
import { recordFailOpen } from "./observability/fail-open.js";
|
|
5
5
|
import { replayJsonlSkipping } from "./observability/corrupt-read-seat.js";
|
|
6
|
+
import { isSettleCause, SETTLE_OUTCOME } from "./approval-deny-reasons.js";
|
|
6
7
|
export const APPROVAL_ASK_AUDIT_DIR = "approval-ask-audit";
|
|
7
8
|
export const APPROVAL_ASK_AUDIT_LEDGER = "ledger.jsonl";
|
|
8
9
|
export const CRASH_CONVERGED_LIST_MAX = 200;
|
|
9
|
-
|
|
10
|
+
const SETTLE_OUTCOME_WORDS = new Set(Object.values(SETTLE_OUTCOME));
|
|
11
|
+
function isSettleOutcome(v) {
|
|
12
|
+
return typeof v === "string" && SETTLE_OUTCOME_WORDS.has(v);
|
|
13
|
+
}
|
|
10
14
|
export function buildCheckpointAnchorHash(token) {
|
|
11
15
|
return createHash("sha256").update(token, "utf8").digest("hex").slice(0, 16);
|
|
12
16
|
}
|
|
13
|
-
function
|
|
14
|
-
return typeof v === "string" && Object.hasOwn(AUDIT_SHUTDOWN_CAUSE_WORDS, v);
|
|
15
|
-
}
|
|
16
|
-
function readShutdownCause(v) {
|
|
17
|
+
function readSettleCause(v) {
|
|
17
18
|
if (v === undefined)
|
|
18
19
|
return { cause: undefined, outOfSet: false };
|
|
19
|
-
if (
|
|
20
|
+
if (isSettleCause(v))
|
|
20
21
|
return { cause: v, outOfSet: false };
|
|
21
22
|
return { cause: undefined, outOfSet: true };
|
|
22
23
|
}
|
|
@@ -70,11 +71,11 @@ function readLedgerRecord(u, noteOutOfSetSeat) {
|
|
|
70
71
|
const approvalId = s("approvalId");
|
|
71
72
|
const settledAtMs = num("settledAtMs");
|
|
72
73
|
const outcomeV = o.outcome;
|
|
73
|
-
const outcome = outcomeV
|
|
74
|
+
const outcome = isSettleOutcome(outcomeV) ? outcomeV : undefined;
|
|
74
75
|
if (approvalId === undefined || settledAtMs === undefined || outcome === undefined)
|
|
75
76
|
return undefined;
|
|
76
77
|
const settledBy = s("settledBy");
|
|
77
|
-
const cause =
|
|
78
|
+
const cause = readSettleCause(o.cause);
|
|
78
79
|
if (cause.outOfSet)
|
|
79
80
|
noteOutOfSetSeat();
|
|
80
81
|
return {
|
|
@@ -279,7 +280,7 @@ export class FileApprovalAskAuditStore {
|
|
|
279
280
|
}
|
|
280
281
|
}
|
|
281
282
|
indexSettle(rec) {
|
|
282
|
-
if (rec.outcome ===
|
|
283
|
+
if (rec.outcome === SETTLE_OUTCOME.EXPIRED && rec.parkRouted === true)
|
|
283
284
|
this.parkExpired.add(rec.approvalId);
|
|
284
285
|
}
|
|
285
286
|
recordAsk(input) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ApprovalAskStore } from "./plugins/approval-ask-store-sql.js";
|
|
2
2
|
import { FileApprovalAskAuditStore } from "./approval-ask-audit-store.js";
|
|
3
|
-
import { type
|
|
3
|
+
import { type HolderGoneCause } from "./approval-deny-reasons.js";
|
|
4
4
|
import { type RunStoreReader } from "./user-input-ledger.js";
|
|
5
5
|
import type { Logger } from "./observability/logger.js";
|
|
6
6
|
/** 启动收敛的读数(进启动日志)。 */
|
|
@@ -11,7 +11,7 @@ export interface CrashConvergenceReport {
|
|
|
11
11
|
readonly inconsistent: number;
|
|
12
12
|
readonly corruptLines: number;
|
|
13
13
|
/** ask 账半场:按成因词计的、本次收成 VOID 的前世 `STREAM_PENDING` 行数。 */
|
|
14
|
-
readonly asksVoided: Readonly<Record<
|
|
14
|
+
readonly asksVoided: Readonly<Record<HolderGoneCause, number>>;
|
|
15
15
|
/** 其中卡信封读不出、成因词是回落(`crashed_before_park`)的行数(同时记 fail-open tag)。 */
|
|
16
16
|
readonly causeUnreadable: number;
|
|
17
17
|
}
|
|
@@ -2,7 +2,7 @@ import { existsSync } from "node:fs";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { APPROVAL_ASK_AUDIT_DIR, APPROVAL_ASK_AUDIT_LEDGER, FileApprovalAskAuditStore } from "./approval-ask-audit-store.js";
|
|
4
4
|
import { ApprovalCardEnvelopeSchema } from "./approval-card.js";
|
|
5
|
-
import {
|
|
5
|
+
import { APPROVAL_ENDING_CAUSES } from "./approval-deny-reasons.js";
|
|
6
6
|
import { writerGoneEvidenceFor } from "./user-input-ledger.js";
|
|
7
7
|
import { recordFailOpen } from "./observability/fail-open.js";
|
|
8
8
|
const CONVERGE_PAGE = 500;
|
|
@@ -20,7 +20,7 @@ export function openAuditEvidenceForConvergence(live, localDataRoot) {
|
|
|
20
20
|
export async function convergeCrashedAsksAtBoot(input) {
|
|
21
21
|
const now = input.now ?? Date.now;
|
|
22
22
|
const audited = input.audit?.convergeCrashedAsks() ?? { pending: 0, decided: 0, inconsistent: 0, corruptLines: 0 };
|
|
23
|
-
const asksVoided = { [
|
|
23
|
+
const asksVoided = { [APPROVAL_ENDING_CAUSES.CRASHED_BEFORE_PARK]: 0, [APPROVAL_ENDING_CAUSES.SHUTDOWN_BEFORE_PARK]: 0 };
|
|
24
24
|
let causeUnreadable = 0;
|
|
25
25
|
const store = input.askStore;
|
|
26
26
|
if (store !== undefined) {
|
|
@@ -34,9 +34,9 @@ export async function convergeCrashedAsksAtBoot(input) {
|
|
|
34
34
|
const envelope = ApprovalCardEnvelopeSchema.safeParse(row.cardJson);
|
|
35
35
|
if (!envelope.success) {
|
|
36
36
|
causeUnreadable += 1;
|
|
37
|
-
recordFailOpen("server.approval-crash-convergence.cause-unreadable", `ask ${row.askId} (task ${row.taskId}): card envelope unreadable — cause defaulted to ${
|
|
37
|
+
recordFailOpen("server.approval-crash-convergence.cause-unreadable", `ask ${row.askId} (task ${row.taskId}): card envelope unreadable — cause defaulted to ${APPROVAL_ENDING_CAUSES.CRASHED_BEFORE_PARK}`);
|
|
38
38
|
}
|
|
39
|
-
const reason = envelope.success && input.audit !== undefined ? input.audit.causeFor(envelope.data.approvalId) :
|
|
39
|
+
const reason = envelope.success && input.audit !== undefined ? input.audit.causeFor(envelope.data.approvalId) : APPROVAL_ENDING_CAUSES.CRASHED_BEFORE_PARK;
|
|
40
40
|
let won;
|
|
41
41
|
try {
|
|
42
42
|
won = await store.transitionAsk(row.askId, "STREAM_PENDING", "VOID", { deniedReason: reason, updatedAtMs: now() });
|
|
@@ -67,7 +67,7 @@ export async function runBootCrashConvergence(ctx) {
|
|
|
67
67
|
corruptLines: converged.corruptLines,
|
|
68
68
|
asksVoided: converged.asksVoided,
|
|
69
69
|
causeUnreadable: converged.causeUnreadable,
|
|
70
|
-
note: "approval asks left pending by the previous process were converged: each such ask is now VOID in the ask ledger (the decision endpoint answers 410 gone.ask with this cause
|
|
70
|
+
note: "approval asks left pending by the previous process were converged: each such ask is now VOID in the ask ledger (the decision endpoint answers 410 gone.ask with this cause) and is listed under GET /v1/approvals crashConverged when the audit ledger saw it (cause crashed_before_park = the process crashed; cause shutdown_before_park = an orderly shutdown cut the leg off before it closed or before its park committed)",
|
|
71
71
|
});
|
|
72
72
|
}
|
|
73
73
|
return converged;
|