@sema-agent/server 7.54.0 → 7.56.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.
Files changed (76) hide show
  1. package/MIGRATION.md +3 -3
  2. package/README.md +12 -6
  3. package/README.zh-CN.md +9 -5
  4. package/USAGE.md +108 -52
  5. package/deploy/sema-up/chart/values.yaml +1 -1
  6. package/dist/approval-ask-audit-store.d.ts +129 -0
  7. package/dist/approval-ask-audit-store.js +284 -0
  8. package/dist/approval-card.d.ts +18 -6
  9. package/dist/approval-card.js +2 -2
  10. package/dist/approval-content-kind.d.ts +22 -0
  11. package/dist/approval-content-kind.js +5 -0
  12. package/dist/approval-reconciler.d.ts +2 -1
  13. package/dist/approval-reconciler.js +1 -1
  14. package/dist/boot/coordinators.d.ts +2 -0
  15. package/dist/boot/coordinators.js +18 -1
  16. package/dist/boot/leader.d.ts +21 -0
  17. package/dist/boot/leader.js +6 -0
  18. package/dist/boot/reapers.d.ts +2 -1
  19. package/dist/boot/runtime-caps.d.ts +2 -1
  20. package/dist/boot/runtime-caps.js +3 -1
  21. package/dist/boot/stores.js +8 -1
  22. package/dist/config-center/types.d.ts +2 -1
  23. package/dist/config-provider.js +1 -0
  24. package/dist/device-store.d.ts +66 -2
  25. package/dist/device-store.js +35 -0
  26. package/dist/device-ws-hub.d.ts +8 -0
  27. package/dist/device-ws-hub.js +6 -0
  28. package/dist/http/route-ctx.d.ts +15 -3
  29. package/dist/http/routes/approvals-assistant.js +3 -2
  30. package/dist/http/routes/devices.d.ts +64 -0
  31. package/dist/http/routes/devices.js +173 -0
  32. package/dist/http/routes/leader.js +2 -2
  33. package/dist/http/routes/workflows.js +1 -1
  34. package/dist/http/server.d.ts +14 -0
  35. package/dist/http/server.js +30 -5
  36. package/dist/leader/endpoint.js +5 -4
  37. package/dist/leader/wire.d.ts +50 -0
  38. package/dist/leader/wire.js +19 -7
  39. package/dist/main.js +4 -2
  40. package/dist/observability/fail-open.d.ts +7 -3
  41. package/dist/observability/fail-open.js +7 -3
  42. package/dist/plugins/approval-ask-store-memory.d.ts +11 -1
  43. package/dist/plugins/approval-ask-store-memory.js +20 -3
  44. package/dist/plugins/approval-ask-store-sql.d.ts +82 -0
  45. package/dist/plugins/approval-ask-store-sql.js +41 -10
  46. package/dist/plugins/checkpoint-store-sql.d.ts +12 -0
  47. package/dist/plugins/checkpoint-store-sql.js +4 -1
  48. package/dist/plugins/device-store-sql.d.ts +38 -1
  49. package/dist/plugins/device-store-sql.js +82 -2
  50. package/dist/plugins/file-run-store.js +2 -0
  51. package/dist/plugins/local-checkpoint-store.js +2 -0
  52. package/dist/plugins/local-session-store.d.ts +9 -9
  53. package/dist/plugins/local-session-store.js +5 -3
  54. package/dist/plugins/memory-run-store.js +2 -0
  55. package/dist/plugins/permission-rule-store-file.d.ts +22 -4
  56. package/dist/plugins/permission-rule-store-file.js +17 -8
  57. package/dist/plugins/permission-rule-store-sql.d.ts +60 -30
  58. package/dist/plugins/permission-rule-store-sql.js +23 -13
  59. package/dist/plugins/pg-session-storage.js +17 -13
  60. package/dist/plugins/remote-env-device.js +8 -1
  61. package/dist/plugins/run-store-sql.js +6 -4
  62. package/dist/plugins/sql-errors.d.ts +12 -0
  63. package/dist/plugins/sql-errors.js +10 -0
  64. package/dist/plugins/store-contracts.d.ts +4 -0
  65. package/dist/plugins/tidb-session-store.js +18 -14
  66. package/dist/rules-consent.d.ts +5 -4
  67. package/dist/rules-consent.js +5 -33
  68. package/dist/runs.js +1 -0
  69. package/dist/runtime-caps-resolver.js +3 -1
  70. package/dist/security.d.ts +7 -0
  71. package/dist/tool-approval.d.ts +66 -83
  72. package/dist/tool-approval.js +222 -186
  73. package/dist/trace/ledger-events.d.ts +9 -0
  74. package/package.json +3 -3
  75. package/skills/find-skills.md +1 -1
  76. package/skills/loop.md +1 -1
package/USAGE.md CHANGED
@@ -49,7 +49,7 @@ ANTHROPIC_API_KEY=sk-ant-… # 可选:ANTHROPIC_BASE_URL / ANTHROPIC_VERSION /
49
49
  - 两个变量都不设 = 和以前**逐字节一致**(单网关)。
50
50
  - **failover ≠ Anthropic↔vLLM**:failover 给所有 brain 发**同一个 model**(同协议同 id 的冗余);云↔本地是**按 `model.provider` 路由**的选择,不是故障转移(两者 model id/参数不同,不能透明互切)。设 `MODEL_PROVIDER=anthropic` + `MODEL_ID=claude-…` 让整个服务走 Anthropic。
51
51
  - **缺省推断**:`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`,与以前逐字节一致。
52
- - 启动日志的 `brain` 字段会回显当前组合(`failover` / `anthropicRoute` / `anthropicCacheBreakpoints`)。详见 `design/15`。
52
+ - 启动日志的 `brain` 字段会回显当前组合(`failover` / `anthropicRoute` / `anthropicCacheBreakpoints`)
53
53
 
54
54
  **可选 — 韧性栈:分级超时 + 断路器(core 1.38,叠在 failover 之下)**
55
55
  ```bash
@@ -66,7 +66,7 @@ ANTHROPIC_MAX_RETRIES=10 # 云 Anthropic 腿
66
66
  - **全部默认关**(超时=0、断路器=false)→ 不设就和以前**逐字节一致**。
67
67
  - 断路器**只在配了 `MODEL_GATEWAY_FALLBACK_URLS`(≥2 路)时才有意义**——它的价值是"开路即快速失败 → failover 立刻切备";单网关下它是 no-op(启动日志 `circuitBreakerNoop` 会提示)。只 `network/server/rate_limit` 计入连败,`auth`/`invalid_request` 不计(坏 key 熔断整网关无意义)。备用网关(最后一路)不套断路器。
68
68
  - **重试上限**(2026-07-31):两个键**不设就不传给引擎** —— 引擎默认当家。以前这里是 server 侧硬编码 `2`(openai 腿甚至零配置出口),而**显式传参压过引擎默认**,于是引擎抬默认对 server 部署毫无效果;第三方限流 provider 下"重试两次就放弃"正是由此而来。现在:不设=继承引擎默认(抬默认那天自动跟上),设了=按设的走。钉在 `test/brain.test.ts` 的「网关腿重试次数」两条上。
69
- - 断路器状态:配了 `SESSION_BACKEND=mysql` 时自动用**跨副本共享态**(SQL `circuit_breaker` 表,写穿+刷新最终一致),否则进程内 Map。启动日志 `breakerState` 字段回显 `shared(mysql)`/`in-process`/`off`。详见 `design/27`。
69
+ - 断路器状态:配了 `SESSION_BACKEND=mysql` 时自动用**跨副本共享态**(SQL `circuit_breaker` 表,写穿+刷新最终一致),否则进程内 Map。启动日志 `breakerState` 字段回显 `shared(mysql)`/`in-process`/`off`。
70
70
 
71
71
  **可选 — WebSearch 后端(core 只留注入口,不自带任何 provider)**
72
72
  ```bash
@@ -91,7 +91,7 @@ WEB_SEARCH_TIMEOUT_MS=10000 # 单次搜索墙钟
91
91
  ```bash
92
92
  # 每任务成本/token 上限(运维 CEILING)。调用方可在 body 传更小的 maxCostUsd/maxTokens,但会被夹到这个上限以下。
93
93
  MAX_TASK_COST_USD=0.50 MAX_TASK_TOKENS=200000 # 0/不设 = 不限。超限 → 任务 failed + errorCode limits.max_{tokens,cost,turns,walltime}_exceeded
94
- # 每 principal 跨任务累计成本配额(滚动窗口)。某人窗口内累计花费超顶 → 下一个任务被拒(429 + retry-after)。
94
+ # 每 principal 跨任务累计成本配额(固定桶窗,非滑动窗)。某人桶内累计花费超顶 → 下一个任务被拒(429 + retry-after)。
95
95
  MAX_PRINCIPAL_COST_USD=5.00 COST_QUOTA_WINDOW_SEC=86400 # 0/不设 = 不限;默认窗口 1 天
96
96
  # ⚠️ 上面两组都是 **per-SLICE / per-任务** 的窗:一次 park→resume(审批、plan_review、抢占、预算切片)
97
97
  # 会给引擎**重开一个满窗**。要给「整条 park/resume 链」封顶,用下面这组**跨片总额**(需 RESOURCE_SUSPEND=true)。
@@ -109,13 +109,52 @@ MODEL_DEGRADE_ON=rate_limit,breaker_open # 可选,反应式触发器子集;词
109
109
  # (主模型 5xx、以及兜底的一般 http 错误类也切便宜模型)。
110
110
  # 不想要这两类触发就显式设回 rate_limit,breaker_open。拼错词 boot 拒启(不静默忽略)。
111
111
  ```
112
- - 反应式降级 brain 包在**最外层**(core council #8);fallback brain 用自己的凭据(decorator 清掉主模型的 per-call key 防外泄给别的 provider)。**坑**:`MODEL_DEGRADE_TO` 最好别和被限流的是同一网关/账号,否则反应式切过去照样撞同一个 rate_limit。
112
+ - 反应式降级 brain 包在**最外层**;fallback brain 用自己的凭据(decorator 清掉主模型的 per-call key 防外泄给别的 provider)。**坑**:`MODEL_DEGRADE_TO` 最好别和被限流的是同一网关/账号,否则反应式切过去照样撞同一个 rate_limit。
113
113
  - **定价怎么设**:`model.cost` 来自 `MODEL_COST_INPUT/OUTPUT/CACHE_READ/CACHE_WRITE`(**USD per 1M tokens**,默认 0 = `costUsd` 读 0)。云模型(如 review-gw 的 deepseek-v4-flash)必须设,否则 spend 恒为 $0;本地自托管(qwen)留 0 即对(无 per-token 外部花费)。字段名是 `costUsd`,**非美元计价的网关要先折算**(如 DeepSeek 官方 CNY ÷ 汇率)。配置控制面管的模型走 `CenterModel.cost`(中心存价、不存 secret)。
114
114
  - 成本计量**自动开**:从 config 的 `model.cost`(per-1M 绝对 USD)注入 `pricing`,core 算出权威的整数 `costMicroUsd`(避免浮点累计误差)。`/metrics` 新增:`model_cost_micro_usd_total{model}`(覆盖所有 brain 调用=主任务+异步+council 子任务的总花费)、`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 降级)。
115
115
  - **近预算降级 vs 硬闸**:降级(`MODEL_DEGRADE_TO`,到 `atCostFraction` 切便宜模型)是**撑长**预算、任务仍完成(出口质量下降、发 `task.degraded` 事件可告警);硬闸(`maxCostUsd` 全额)仍在,切了便宜模型还超全额 → `limits.max_cost_exceeded` 停。
116
116
  - **`METRICS_TOKEN`**(可选,只读):设了它,`GET /metrics`+`/metrics/summary` 接受**它或** `SERVICE_AUTH_TOKEN`。**全 fleet 设同一个值** → 管理端(sema-web,规划名 sema-admin)用**一个** token 拉所有 worker 的指标,**无需持有各 worker 的全权 `SERVICE_AUTH_TOKEN`**(不破坏 secret 边界)。即使外泄也只暴露指标(只读)。
117
117
  - **`GET /metrics/summary`**(token-gated,同 `/metrics`):`/metrics` 的**精炼 JSON**——`{model, runsActive, tasks{status}, tokensTotal, costUsd, costUsdByModel, taskDurationAvgSec, brainFirstTokenAvgMs, brainCallAvgMs, cacheHitRateAvg, rateLimited, costQuotaRejected, budgetExceeded, degraded, cascade, verifications, councilRuns, toolErrors}`。给**轻量 fleet 看板**用(管理端 sema-web 的 fleet 页按 worker 拉、渲染卡片、按轮询算速率;**不用 Prometheus/Grafana**)。counter 是累计值、histogram 报均值。
118
118
  - 预算闸是 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。
119
+ - 🔴 **三道 $ 闸的分工(别拿进场门当中途停机用)**:三根旋钮名字都带 cost,但**判的时刻**和**能不能打断正在烧的那张单**完全不同。按「谁触发 / 什么时刻判 / 拒或停长什么样」对表:
120
+
121
+ | 旋钮 | 谁触发、什么时刻判 | 拒/停的形 | 能打断在跑的 run 吗 |
122
+ |------|------------------|-----------|--------------------|
123
+ | `MAX_TASK_COST_USD` | 引擎(core)按**这一片任务**的累计花费判:首个模型调用前的 pre-call 估算 + turn 结束回查;**未开** `RESOURCE_SUSPEND` 时另有流中途投影取消(开了 resource-suspend 这一臂自动关,只在边界判) | 未开/不合格 ⇒ 终态 `failed` + `errorCode: limits.max_cost_exceeded`(HTTP 仍 200 / SSE `done` 帧带码);**开了 resource-suspend 且合格 ⇒ `suspended` + `resource_limit` 卡**(可续跑,不是失败)——消费方两种终态都要认 | ✅ 能,但只封**单片**:一次 park→resume 会重开满窗(见下条「per-slice 窗 vs 跨片总额」) |
124
+ | `MAX_PRINCIPAL_COST_USD`(+ `COST_QUOTA_WINDOW_SEC`) | **HTTP 提交面**在受理请求**之前**读该 principal 桶内累计。挂门的是**任务/run 族**:`/v1/tasks`、`POST /v1/runs`、steer/resume/wake、workflows、leader、a2a,以及 park 后赎回时开的续跑腿(⚠️ 例外见下:operator-only 的记忆整合口不在此列) | `429` + `errorCode: limit.cost_quota_exceeded` + `Retry-After` 头 + 体 `{usedMicroUsd, limitMicroUsd, retryAfterSec}` | ❌ **不能**。跑起来之后不再回查:越顶那张单会**跑完**,天花板从**下一次提交**起生效 |
125
+ | `USAGE_WINDOWS[].maxCostUsd`(§9.2) | 引擎在**每个 turn 边界**把累计花费记进跨任务账本再回查窗;提交面另有同店同键的快速门 | 基建合格 ⇒ `suspended` + `checkpointGate.reason=usage_window` + `resumeAfterMs`(等窗放行原样续跑);不合格 ⇒ `failed` + `errorCode: usage.window_exhausted` + `retryAfterMs`;提交面 ⇒ `429 usage.window_exhausted` | ✅ 能,且是唯一按**跨任务累计**花费中途停的一根(第一根按的是这一片自己的额度,管不到租户总账) |
126
+
127
+ - **怎么选**:想给**单张任务**封顶 → `MAX_TASK_COST_USD`;想给**某租户按天/周限额、超了就不许再提新单** →
128
+ `MAX_PRINCIPAL_COST_USD`;想让**正在烧的那张单**在租户总账超额时当场停下 → `USAGE_WINDOWS` 的
129
+ `maxCostUsd`。后两根**可以同时配**(一个管进场、一个管中途),账本互相独立:提交面配额是 per-principal
130
+ **固定桶**窗(见下),治理窗是 core 的跨任务账本(带 principal 计到各自的窗、匿名计到 GLOBAL 锅)。
131
+ 还有第四根 `RESOURCE_SUSPEND_TOTAL_BUDGET_USD`(下一条)——它约束的是**同一条 park/resume 链**的
132
+ 跨片总额,轴是「这条链」,不是「这个租户」,与上面三根不互相替代。
133
+ - ⚠️ **最常见的误配**:只配 `MAX_PRINCIPAL_COST_USD` 就以为封住了「疯跑单」。它是**进场门**——一张已经
134
+ 受理的任务会一直烧到自己的 per-task 闸或跑完为止,期间没有任何一刻会因为该 principal 越顶而停;
135
+ 运维看到的是「天花板说不许花的钱花完了,下一次提交才 429」。要当场停就必须配治理窗的 $ 天花板
136
+ (前置:模型**可计价**,否则任务门口就拒,见 §9.2)。
137
+ - 记账口径:`MAX_PRINCIPAL_COST_USD` 的累计来自权威的整数 `costMicroUsd`(覆盖主任务 + 异步/council
138
+ 子任务,按 principal 归因);单副本部署是进程内计数,配了 SQL 后端则跨副本共享。
139
+ - ⏱️ **窗形是「固定桶」不是滑动窗**(别按滑动窗估保护强度):内存腿从该 principal 的**首次记账**开锚,
140
+ 满 `COST_QUOTA_WINDOW_SEC` 后整桶清零重开;SQL 腿(跨副本)用**对齐桶**(桶号 = `floor(now / 窗长)`),
141
+ 桶界随时钟对齐。两种形都在**桶边界**清零 ⇒ 紧贴边界前后的两个桶可以在很短时间内合计花掉近两倍额度。
142
+ ⚠️ **别用「把窗调短」当护栏**:额度不变而缩窗,边界突发仍是近两倍额度,长期允许速率反而抬高
143
+ (每天 $5 改成每小时 $5,一天就成了约 $120)。要压边界突发,窗与额度**按目标速率同比调小**,
144
+ 或改用治理窗——`USAGE_WINDOWS` 的 `rolling` anchor 才是真滑动窗。
145
+ ⚠️ 换窗长 = 换记账周期,而且**改后重启**才生效:内存腿换窗长即清当代累计;SQL 腿的桶号由
146
+ `floor(now / 窗长)` 算出,新窗长**通常**落到不同桶号(= 从零记起),但两个相近的窗长在当下时刻
147
+ 可能算出**同一个**桶号,那一次换窗就会继续沿用旧桶的累计。要确保从零记起,换窗长时顺手清一次
148
+ `cost_quota` 表里该 principal 的旧桶行。
149
+ - 🚧 **不受这道门保护的花费**(如实登记,别把它算进保护范围):operator-only 的
150
+ `POST /v1/admin/memory/consolidation/run`(一次整库蒸馏,量级 ~1e5 prompt token)既不过这道门,
151
+ 花费也**不入**该 principal 的桶。它不是一条 Runner 任务(自带的操作面记忆引擎直调驱动模型),
152
+ 所以上表三根 $ 闸**对它都不生效**,治理窗账本也收不到这笔。现阶段可用的手段:`OPERATOR_PRINCIPALS`
153
+ 名单收紧、给 consolidate 驱动席位挑便宜模型、以及网关/账号侧的限额。
154
+ - ⚠️ **门键 = 发起请求的 principal**,而一条被跨租户 operator 恢复(`/decide`、`/plan_review`、`/preempt`
155
+ 续跑)的挂起腿,花费记到**卡带持久化的属主**。⇒ 属主已越顶、operator 没越顶时,这次恢复照样放行,
156
+ 钱记在属主账上。要让「属主越顶就不许再被人续跑」生效,配治理窗:它按**账本键**(挂起时那位属主)
157
+ 在恢复腿**进场**就回查,不依赖谁按的那次按钮。
119
158
  - 🔴 **per-slice 窗 vs 跨片总额(别把前者当后者用)**:`MAX_TASK_TOKENS` / `MAX_TASK_COST_USD` / 任务墙钟都是 core 的
120
159
  **per-SLICE 窗**——引擎每次被调用都重铸它们,所以一次 `POST /v1/approvals/:id/decide`、`…/plan_review`、
121
160
  `…/resume`、`/wake` 续跑拿到的是**全新的满窗**;停顿前那条腿烧掉的量只在**跨片账本**上被扣。那本账 core
@@ -128,17 +167,21 @@ MODEL_DEGRADE_ON=rate_limit,breaker_open # 可选,反应式触发器子集;词
128
167
  - **`_MAX_SLICES` 不冻结**:引擎不把它存进账本,每片现读当期部署值去比账本上累计的片数。⇒ 改它会**立刻**
129
168
  作用于已 park 的链(调大=放宽、设 0=解除兜底)。这是部署级旋钮跟随当期部署的常态,但别把它误读成
130
169
  「和两条总额一样冻在链上」——两者的运维含义不同。
131
- - 🔭 **跑的过程中怎么看还剩多少**([3833] S-4,7.48 起):配了总额的 run 在 **running** 期间,
170
+ - 🔭 **跑的过程中怎么看还剩多少**(7.48 起):配了总额的 run 在 **running** 期间,
132
171
  `GET /v1/runs/:id` 顶层多一只 additive 的 `crossSliceUsage`
133
172
  (`{totalTokens, spentTokens, totalBudgetMicroUsd, spentMicroUsd, maxSlices, sliceCount}`,三轴各自成对、
134
173
  上限没配那根就整对缺席;`$` 轴是 **micro-USD** 整数,显示端 ÷1e6)。**用途 = 提前介入**:此前只有爆窗
135
174
  那一刻(行翻 `suspended` + `resource_limit` gate)才知道逼近过。
136
- ⚠️ 三条读法纪律:① 读数是**下界**(用量在 turn 边界入账,滞后至多一个 turn;不含委派子代花费);
175
+ ⚠️ 三条读法纪律:① 读数是**下界**。**多数 running 态 poll 场景(异步 bg 循环、resume 续跑腿)在每个
176
+ turn 边界都会把这一 turn 的用量落进 durable 账本**,故照旧「至多滞后一个 turn」;**唯一的例外是不带
177
+ detach opt-in 的单次同步流**(`POST /v1/tasks/stream` 未带 `x-detach-on-disconnect`)——该跑形从 turn
178
+ 边界到最终 park/结算**全程不给这本账写一个字**(不是「终局才补账」),该读数在这条腿存续期间维持它
179
+ 开始前的值,直到同一任务上别的 durable 腿(异步/detach/resume)写入新账才会变。不含委派子代花费);
137
180
  ② **整键缺席不等于「没花钱」**——没配总额 / 非 running / poll 落到没跑过这条 run 的副本(记录是同副本
138
181
  best-effort,与 `msSinceLastActivity` 同族)/ 账本读不可用,四种都是缺席;后者计数在
139
182
  `fail_open_total{tag="server.runs.cross-slice-usage-unavailable"}`;③ 它是**给人看的预警面**,真正执法的
140
183
  是引擎账本 —— 别拿它做自动重试/取消的机器判据。
141
- - **per-principal 累计配额**:用 `AsyncLocalStorage` 把 principal 透传到 cost tracer,所以**council/team 子任务的花费也算到发起人头上**。**配了 `SESSION_BACKEND=mysql` 时自动跨副本共享**(`cost_quota` 表,写后聚合的**原子自增** `micro=micro+delta`,对齐固定窗,最终一致——多副本花费 SUM 到一起、不丢增量);否则 in-memory per-replica 滚动窗(单副本兜底)。启动日志 `costQuota` 字段回显 `shared(mysql)`/`in-process`/`off`。跨副本是最终一致(flush 间隔内峰值可能略超,由**硬 per-task `maxCostUsd` 兜底**)。
184
+ - **per-principal 累计配额**:用 `AsyncLocalStorage` 把 principal 透传到 cost tracer,所以**council/team 子任务的花费也算到发起人头上**。**配了 `SESSION_BACKEND=mysql` 时自动跨副本共享**(`cost_quota` 表,写后聚合的**原子自增** `micro=micro+delta`,对齐固定窗,最终一致——多副本花费 SUM 到一起、不丢增量);否则 in-memory per-replica 固定桶窗(首次记账开锚,单副本兜底)。启动日志 `costQuota` 字段回显 `shared(mysql)`/`in-process`/`off`。跨副本是最终一致(flush 间隔内峰值可能略超,由**硬 per-task `maxCostUsd` 兜底**)。
142
185
  - **`RATE_LIMIT_RPM` 请求限流同样自动跨副本**(`SESSION_BACKEND=mysql` 时,`rate_limit` 表,与配额共用 `WriteBehindCounter`;启动日志 `rateLimit` 回显)。注意:写后聚合 = **软限流**(边界上短暂略超 OK,适合公平/热调用方防护);要**硬合规上限**得另走 CAS/原子计数,不靠写后聚合——和断路器跨副本同款权衡。
143
186
 
144
187
  **可选 — 生成调参透传 + 退化打捞(core 1.59/1.60)**
@@ -151,7 +194,7 @@ MODEL_EXTRA_BODY='{"top_k":40}' # JSON 逃生口(top_k / logit_bias 等;penalt
151
194
  # 响亮拒)——输出上限请用任务面的 limits.maxOutputTokens,别从这里塞
152
195
  ```
153
196
  - **brain 拥有的键永远赢**:`temperature`/`max_tokens` 等放进 `extraBody` 会被 strip + core 警告(`phase:"config"`),不会静默改;auth/content-type/version 头硬锁不可顶替(1.60 安全修复)。
154
- - **必须静态**:`extraBody` 在 boot 时按固定 env 建一次(稳定键序),**不可逐任务变**,否则破前缀缓存(design/9/31)。未设 → 请求字节级不变。
197
+ - **必须静态**:`extraBody` 在 boot 时按固定 env 建一次(稳定键序),**不可逐任务变**,否则破前缀缓存。未设 → 请求字节级不变。
155
198
  - **退化打捞**:模型尾部循环退化时 core 切断,任务 `status:"failed"` + `errorCode:"output.degenerate"`,`salvagedOutput` 带回那一退化 turn 的文本,**尾部已由 core 在 brain 流层裁掉**(逐字节重复只留一份重复单元 —— 声明为**有损**归一:不丢唯一字节,但重复**次数**不保真,「输出 N 份」这类语义拿回来只有一份)。
156
199
  🔴 **调用方读法(core 官方配方,别拿 `status` 三元代替)**:`const out = r.result || r.salvagedOutput || "";`
157
200
  —— 先读 `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 是预防、这是兜底。
@@ -171,7 +214,7 @@ MODEL_ID=<your-model-name> MODEL_CHEAP_ID=<your-cheap-model-name>
171
214
  ```
172
215
  - 调用方可在 `objective` 里 `@<模型名>` 选模型,**只认已配置的名单**(`MODEL_ID` / `MODEL_CHEAP_ID`)——注入不了 `baseUrl`/`apiKey`;无 `@` → 默认模型。
173
216
  - **`GET /v1/models`**(带 Bearer)→ `{models:[{name,id,provider,reasoning,vision}],default}`,只含名字/能力、**不含密钥** —— 消费方(如 OA)拿它做 `@` 自动补全下拉的数据源。
174
- - 不设 `MODEL_CHEAP_ID` → 所有角色 = 主模型(行为不变)。详见 `design/19`。
217
+ - 不设 `MODEL_CHEAP_ID` → 所有角色 = 主模型(行为不变)
175
218
  - **1M 双窗**(`autoCompactTokens`):`967000` 只发给 **claude-sonnet-5 系 id ∧ 窗 ≥1M**(CC 的 per-model 值,不是窗宽几何)。其他 ≥1M 模型要双窗触发点请显式配 —— 主槽 `MODEL_AUTO_COMPACT_TOKENS=<tokens>`、cheap 槽 `MODEL_CHEAP_AUTO_COMPACT_TOKENS=<tokens>`(**两槽各配各的**:主槽的值按主模型 id/窗声明,不会外溢到 cheap)。不配 → core 按整窗的 `W-33000` 平几何触发,boot 期发 `auto_compact_window_not_derived_1m` warn 点名槽位(`slot=main|cheap`)。⚠️ cheap 槽不设 `MODEL_CHEAP_CONTEXT_WINDOW` 时**继承主模型的窗**,主模型是 1M 则 cheap 也按 1M 计。取值范围:**必须低于该槽的物理窗**(CC 的形是 `窗-33000`)—— 高于物理窗 = 触发点永远到不了 = autocompact 事实上关掉、改由破坏性 guard 砍历史;配高了照配的落但 boot 期发 `auto_compact_tokens_above_context_window` warn(不拒启、不夹紧)。
176
219
 
177
220
  **可选 — 开发者模式 building blocks(core 1.43,默认中立)**
@@ -180,12 +223,12 @@ MODEL_ID=<your-model-name> MODEL_CHEAP_ID=<your-cheap-model-name>
180
223
  MODEL_CODE_ROLES=default,subagent # 不设=全中立;仅这些角色在「任务没自带 systemPrompt」时跑编码提示
181
224
  ```
182
225
  - 经 `RoleSpec.systemPrompt` 挂在**角色**上,非开发角色保持中立、全局默认 `DEFAULT_SYSTEM_PROMPT` 不变;任务自带 `systemPrompt`(或客户端注入)时仍优先。
183
- - **验证门**(core 1.44,opt-in):请求体带 `verify:true`(可选 `verifyRounds`,夹到 [1,5]、默认 2)→ 任务跑完后由**独立只读 verifier**(`verifier` 角色,默认=主模型)证据强制地"试图 break 它",FAIL 则把 findings 注回同 session 续跑修复→重验,循环到 PASS 或轮数上限。结果带 `verification:{verdict,rounds,findings,evidence}`(`verdict` 看质量,`result`/`status` 仍是实现的)。**仅 `/v1/tasks`(同步)与 `/v1/runs`(异步)**——`/v1/tasks/stream` 不支持(多轮非单流,请求 verify 会 400)。verifier 工具默认 = 实现任务工具滤掉 `effect:"write"`(只读边界)。`/metrics` 加 `verifications_total{verdict}`。⚠️ 同步 `/v1/tasks` 形**没有事件通道**(过程帧无处回放,只回终局 JSON)——带 `verify`/`cascade` 的 200 体自 7.23.0 起附可选 `notice` 键指路 `POST /v1/runs`(要过程可见性用异步形;[3979])。
184
- - **记忆(design/138 文件记忆引擎,2026-07-08 起唯一记忆面)**:core 注入式文件引擎——任务开始时 materialize 记忆目录(`MEMORY_ENGINE_DIR`,默认 `~/.ai-agent`),模型用**普通文件技能**读写记忆(CC `# Memory` 指令 + 派生索引;无 remember/recall 工具),任务边界 harvest 门(secret/cap 扫描)提交。单用户默认开,`MEMORY_ENGINE=off` 显式关;多租户恒关(文件基座无租户隔离,fail-closed)。旧 SQL 记忆面(`MEMORY_BACKEND`/`EMBEDDING_*`/`MEMORY_READ_LIMIT`/去重/向量检索、`GET/DELETE /v1/memory` 与 session memory 写 verb)已退役,数据不迁移——升级后对库跑一次 `scripts/drop-memory-tables.sql`。`body.memoryWrite:false` 仍是每请求只读开关(harvest 不提交)。
226
+ - **验证门**(core 1.44,opt-in):请求体带 `verify:true`(可选 `verifyRounds`,夹到 [1,5]、默认 2)→ 任务跑完后由**独立只读 verifier**(`verifier` 角色,默认=主模型)证据强制地"试图 break 它",FAIL 则把 findings 注回同 session 续跑修复→重验,循环到 PASS 或轮数上限。结果带 `verification:{verdict,rounds,findings,evidence}`(`verdict` 看质量,`result`/`status` 仍是实现的)。**仅 `/v1/tasks`(同步)与 `/v1/runs`(异步)**——`/v1/tasks/stream` 不支持(多轮非单流,请求 verify 会 400)。verifier 工具默认 = 实现任务工具滤掉 `effect:"write"`(只读边界)。`/metrics` 加 `verifications_total{verdict}`。⚠️ 同步 `/v1/tasks` 形**没有事件通道**(过程帧无处回放,只回终局 JSON)——带 `verify`/`cascade` 的 200 体自 7.23.0 起附可选 `notice` 键指路 `POST /v1/runs`(要过程可见性用异步形)。
227
+ - **记忆(文件记忆引擎,2026-07-08 起唯一记忆面)**:core 注入式文件引擎——任务开始时 materialize 记忆目录(`MEMORY_ENGINE_DIR`,默认 `~/.ai-agent`),模型用**普通文件技能**读写记忆(CC `# Memory` 指令 + 派生索引;无 remember/recall 工具),任务边界 harvest 门(secret/cap 扫描)提交。单用户默认开,`MEMORY_ENGINE=off` 显式关;多租户恒关(文件基座无租户隔离,fail-closed)。旧 SQL 记忆面(`MEMORY_BACKEND`/`EMBEDDING_*`/`MEMORY_READ_LIMIT`/去重/向量检索、`GET/DELETE /v1/memory` 与 session memory 写 verb)已退役,数据不迁移——升级后对库跑一次 `scripts/drop-memory-tables.sql`。`body.memoryWrite:false` 仍是每请求只读开关(harvest 不提交)。
185
228
  **`MEMORY_PERSISTENCE_CAPABLE`(三态,7.14.0 起)**=**部署自述**「本机能不能把用户的『记住 X』落到持久处」,与请求键 `memoryWrite` 两轴正交(后者收紧本次运行,前者是 operator 声明;任一取否即只读方向)。缺省不设=引擎按 roster 自行推断;`true`=本部署有推断看不见的持久通道(自定义 writer / 记忆 MCP / 远程执行车道与记忆根共享挂载);`false`=强制只读披露并关掉文件工具往记忆根的写通道。不认得的词拒启。
186
229
  🔴 **`false` 自 core 5.27.0(7.14.0 提货)起还多一层——但只在 file 记忆引擎上**(`MEMORY_ENGINE_BACKEND` 未设 = 默认单机形):该会话是**受限会话**——materialize/搜索/harvest 一律按**已提交账**供给,记忆根下无事务背书的磁盘分歧既不收编也不供给,而是留盘 + 响亮点名(`restricted_divergence`,本服务把它计进 `memory_harvest_rejections_total{code}` 并 warn 一条),等下一次**非受限**会话走正常门收编。合法流程不受影响:用户手改、`git pull` 落下的删除照旧(延后,不销毁),并发的可写会话提交的变更算有事务背书。
187
230
  ⚠️ **`MEMORY_ENGINE_BACKEND=pg|tidb` 上没有这一层**(引擎的受限视图是 file 后端的实装):那两条腿的读侧本来就走库、盘上目录只是每任务的投影工作区,不存在「读磁盘即收编」的通道,所以也无洞可堵——`false` 在它们上仍然照常买到披露、写门与零收编 harvest 三件(与后端无关)。
188
- 🔴 **降级前必须先停写排空(core 5.33.0 / server 7.21.0 起,运维 BREAKING)**:file 记忆后端的账本升到 **schema v2**(slug/scope rebind 与跨 scope transfer 改成 journaled 事务),而 **v2 journal 被 ≤5.32 的旧读者响亮拒**——这是**有意的 fail-closed**(拒启比静默读半张账好)。⇒ 要把服务降回 <5.33 的镜像:①**先停写、把在跑的记忆写排空**;②按 core 设计稿 §3.6 用 `transfers.jsonl` + journal 回放清账;③**再**降版。**排空这一步没做就降级 = 数据面响亮拒,不是静默丢**(三步全文见黑板 [3935])。`MEMORY_ENGINE_BACKEND=pg|tidb` 的部署不受此条约束(账本在库里,不走 file journal)。
231
+ 🔴 **降级前必须先停写排空(core 5.33.0 / server 7.21.0 起,运维 BREAKING)**:file 记忆后端的账本升到 **schema v2**(slug/scope rebind 与跨 scope transfer 改成 journaled 事务),而 **v2 journal 被 ≤5.32 的旧读者响亮拒**——这是**有意的 fail-closed**(拒启比静默读半张账好)。⇒ 要把服务降回 <5.33 的镜像:①**先停写、把在跑的记忆写排空**;②按 core 设计稿 §3.6 用 `transfers.jsonl` + journal 回放清账;③**再**降版。**排空这一步没做就降级 = 数据面响亮拒,不是静默丢**。`MEMORY_ENGINE_BACKEND=pg|tidb` 的部署不受此条约束(账本在库里,不走 file journal)。
189
232
  ⚠️ **口径别混**:「受限」是**会话级的这一个声明**;请求键 `memoryWrite:false` 铸出的只读**平面**(以及 org 层默认只读、双根非写面)**保持原样的 adopt-on-read**——它只是不 harvest,照旧看得见盘上的新内容。
190
233
  ⚠️ **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`,让只读状态对模型说明白。
191
234
  另:记忆**没有写工具**,模型是用普通文件工具往记忆根写的——所以记忆根必须落在任务的 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 的文案自己会按腿指对旋钮。
@@ -213,7 +256,7 @@ MODEL_CODE_ROLES=default,subagent # 不设=全中立;仅这些角色在「
213
256
  - **没配 `MEMORY_EMBEDDER_*` 时门整个不装**(元表不动,旧指纹保留),启动日志 `memory_embedder_identity_absent` 说明:缺席期间**被改写过**的行,其 embedding 本来就被写成 NULL,所以「配置回归同一 identity 后向量续用」只对缺席期间**未被触碰**的行成立。
214
257
  - 残余:同名模型在不同供应商若其实是不同实现(自托管 finetune 撞名),指纹辨不出——明知异实现时改 `MEMORY_EMBEDDER_MODEL` 名,或手动跑上面那条清列 SQL。`MEMORY_ENGINE_BACKEND=tidb` 无向量面(v1 词面档),没有这张元表也没有这道门。
215
258
  📋 **档位对运维可见**:启动日志 `memory_engine_enabled` 行带 `vectorMode` 字段(取自后端实例的真值,不是配置推断)——`lexical` 在跑就必须看得见;配了 embedder 时同行还带 `embedderModel`/`embedderDim`,换模型这件事在日志里留痕。
216
- - **org 记忆准入(design/170 件A,7.0.0 起 BREAKING)**:`org:*` 记忆 scope 分**两个来源**——部署自证
259
+ - **org 记忆准入(7.0.0 起 BREAKING)**:`org:*` 记忆 scope 分**两个来源**——部署自证
217
260
  (env `MEMORY_SCOPE` 的 org 形 + **单用户部署**的 `projects[].defaultScopes` org 键)直通;**多租户**
218
261
  部署里由调用方 `projectId` 选中的登记簿 org 键算 **request 来源**,必须拿到授权目录的逐 principal
219
262
  授予才准入,拿不到一律 fail-closed 拒(纯读外泄面:projectId 只过形状门不过授权)。目录源**三态单选,
@@ -228,9 +271,9 @@ MODEL_CODE_ROLES=default,subagent # 不设=全中立;仅这些角色在「
228
271
  | `MEMORY_ORG_DIRECTORY_JSON` | 缺省 | 单机形静态授权表 `{"<principal>":{"org:acme":{"write":true}}}`。**启动期整表校验,坏表拒启动**。有 config-center 时被忽略(center 胜 + warn) |
229
272
  | `MEMORY_ORG_GRANT_TTL_MS` | `60000`(`[1000, 3600000]`) | 授予/负结果的缓存 TTL = 「有界 LKG」:**新任务/新 resume 腿**的准入判决滞后 ≤ 此值;**在跑任务不受吊销影响**(判决点在 prepare 期) |
230
273
  | `MEMORY_ORG_UNAVAILABLE_BACKOFF_MS` | `10000`(`[500, 600000]`) | 目录取不到之后的退避窗(只用于取数失败臂,不用于负结果) |
231
- | `MEMORY_DELEGATION_EVIDENCE` | 缺省(=引擎自缺省 `static-face`) | **委派臂的证据标准**(core 5.45.0 design/324)。`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`=本部署未设,引擎自缺省) |
232
- | `MEMORY_PROVENANCE` | 缺省(=引擎自缺省 `carry`) | **记忆 provenance 总开关**(core 5.46.0 design/336 §13-3)。一个会话被判为**已暴露**之后,它的记忆写怎么处置。`carry`=引擎缺省:**普通**记忆写照常提交,但带上引擎铸的 `origin` 标记(随条目走后端/同步/导出包);指令形文件(feedback / pinned / triggers / applies-when)仍被扣下隔离;派生索引的会话散文照回滚;内容扫描门原样跑(**标记不是豁免**)。`off`=design/336 之前的行为:不铸 origin 标记,已暴露会话的 harvest **一条都不收**(整体隔离候人审)。⚠️ 已提交的标记在编辑时照样带下去 —— `off` 停的是**铸**,从不抹掉已记的事实。与 `MEMORY_DELEGATION_EVIDENCE` **正交**(四种组合全合法):那一键决定**什么时候**判会话为已暴露,本键决定判了之后**对写做什么**。坏值**启动期拒**(`OFF`/`false`/`none` 这类「看起来像关掉」的写法一律不折成缺省 —— 静默回默认 = 一台自认为「暴露会话零收录」的部署其实在照常收录)。**部署席 only**(TaskSpec 无同名键,任务/受治脚本无法据此改姿态);**不冻进 checkpoint**(resume 腿跟当前部署配置走)。运维读面 = `GET /v1/diagnostics/wiring` 的 `memoryPosture.provenance`(`null`=本部署未设,引擎自缺省) |
233
- | `MEMORY_CAPTURE_POLICY` | 缺省(=引擎自缺省 `open`) | **记忆采集 opt-out 的部署姿态**(core 7.0.0 design/383 §3.1;server S-49/S-50)。任务可带 `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`,零缓存写后即生效)。🔴 **`governed` 而无 SQL 后端 ⇒ 启动期拒启**(无源 = 每个声明都被拒且没人知道为什么;要么 `DB_BACKEND=mysql|pg` 要么改姿态)。坏值**启动期拒**(`Governed`/`required`/`off` 一律不折成缺省:两种姿态的故障极性**相反**)。**部署席 only**;**不冻进 checkpoint**。运维读面 = `GET /v1/diagnostics/wiring` 的 `memoryPosture.capturePolicy`(`null`=本部署未设)。记录载体(S-53②,core 7.0.2 起):SQL 后端(`DB_BACKEND=mysql\|pg`)上记录落 `session_capture_optout` 表(跨副本存活,随中央 schema 自建零旋钮)——**远端车道(e2b/k8s)的 `capture:"off"` 声明自此真持久**;无 SQL 店的远端形仍 409 `config.memory_capture_unsupported`(诚实拒:落不进 resume 副本可见的载体就不许诺);单机本地车道走 core 文件三件套照常。用户面四口:提交键 `memoryCapture:"off"`(声明形)· `POST /v1/runs/:id/memory/capture-optout`(live 中途翻转,owner 门)· `POST /v1/sessions/:id/memory/erase`(所有者自助抹除,select 硬限本会话)· `GET /v1/sessions/:id/memory-status`(S-7 状态读面,owner 门,五可选键诚实缺席)。契约全文 `docs/ASSISTANT-WIRE-CONTRACT.md` §12 |
274
+ | `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`=本部署未设,引擎自缺省) |
275
+ | `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`=本部署未设,引擎自缺省) |
276
+ | `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`,零缓存写后即生效)。🔴 **`governed` 而无 SQL 后端 ⇒ 启动期拒启**(无源 = 每个声明都被拒且没人知道为什么;要么 `DB_BACKEND=mysql|pg` 要么改姿态)。坏值**启动期拒**(`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"` 声明自此真持久**;无 SQL 店的远端形仍 409 `config.memory_capture_unsupported`(诚实拒:落不进 resume 副本可见的载体就不许诺);单机本地车道走 core 文件三件套照常。用户面四口:提交键 `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 |
234
277
 
235
278
  ⚠️ **拒启动**:多租户 + 记忆面点亮 + `projects[].defaultScopes` 里有 org 键 + 目录源缺席 ⇒ 启动报错
236
279
  并点名 projectId(该部署的每个此类请求都会在 prepare 期整拒,响亮拒启动比静默全拒服务诚实)。
@@ -256,7 +299,7 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
256
299
  默认 15000;0=关;负/非数启动响亮拒)缓存一份 DB 真往返结果,`storeProbe:{live,ageMs,error?}` 在
257
300
  探针接线时恒在,顶层告警键 `storeLive:false` 只在死时出现。**status 保持 "ok"**(liveness≠readiness
258
301
  ——DB 死不是进程死,摘流语义留给读键的编排器/LB)。local/memory 部署形状不变。
259
- `error` 是**闭集词**(7.36.0 起,#305):`probe_timeout` / `connection_refused` / `connection_reset` /
302
+ `error` 是**闭集词**(7.36.0 ):`probe_timeout` / `connection_refused` / `connection_reset` /
260
303
  `host_unreachable` / `dns_failure` / `network_timeout` / `auth_failed` / `connection_limit` /
261
304
  `probe_failed`(未识别)。**驱动原始异常 message 不上这一面**——`/health` 免鉴权且缺省全网卡监听,而
262
305
  连接类异常惯于回声整条 DSN(`mysql://user:pass@host:3306/db`)。要读全文去**日志**:探针在
@@ -265,7 +308,7 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
265
308
  - 数据驻留提示:`DB_BACKEND=local` 下显式 `SESSION_BACKEND=memory` 会被收编为 **durable(local)**
266
309
  (1.292+ 裸 boot 默认 durable;/health 的 `sessionBackend` 报 `durable(local)`)——session 行落盘在
267
310
  数据根下,清数据/隐私预期要按「sessions 在 engine-data 里」来做,不要按「只在内存」。
268
- - **任务清单(TaskCreate/TaskList 家族)的持久边界**(#318):清单按**会话**分区,同一会话
311
+ - **任务清单(TaskCreate/TaskList 家族)的持久边界**:清单按**会话**分区,同一会话
269
312
  跨 turn/跨 resume 连续,跨会话互不可见。配了 SQL 后端(`SESSION_BACKEND=mysql` / `DB_BACKEND=pg`)
270
313
  时落 `task_list_meta`/`task_list_item` 两表 ⇒ **跨副本、跨重启都续得上**;**没有 SQL 后端的形态
271
314
  (`local`/纯内存)清单只活在进程内 —— 重启即丢待办**(同时活跃会话超过 4096 时最老的会话清单被
@@ -298,7 +341,7 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
298
341
  `run.json` / `events.jsonl` / claim 文件的读取。目录里本来就没有的东西(`.DS_Store` 一类杂物、
299
342
  尚未落盘的目录)照旧跳过 —— 拒的是「读不出来」,不是「没有」。
300
343
 
301
- **监听绑址(1.306+,[1934])—— 桌面/单机形请注意**
344
+ **监听绑址(1.306+)—— 桌面/单机形请注意**
302
345
  - `BIND_HOST` = 监听地址;显式设置**恒生效**(要在无鉴权下对外暴露,显式写 `BIND_HOST=0.0.0.0` 即可)。
303
346
  - `HOST` 不再被读取(曾经的兼容别名已撤——zsh 默认把 `HOST` 设成机器名的暗通道风险已消除):
304
347
  绑址唯一口 = `BIND_HOST`,设或不设都与 `HOST` 环境变量的值无关。
@@ -307,7 +350,7 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
307
350
  `REMOTE_EXEC=host`(用户真机、非沙箱),绑全接口=同网段任何人可无鉴权提交任务并执行。
308
351
  - 配了凭证的部署(云形/k8s/compose)缺省**不变**(全接口),既有部署零影响。
309
352
 
310
- **workspace 浏览面 —— 已随整树快照纪元退役(S-15/design/381)**
353
+ **workspace 浏览面 —— 已随整树快照纪元退役**
311
354
  - 旧四端点(list/tree/file/archive)恒 `501 capability.workspace_retired`,`capabilities.workspace`
312
355
  恒 `false`。理由:rewind 换代为 per-file 历史(只备份代理**编辑过**的文件,无树遍历、无体积门),
313
356
  结构上不再存在「整个工作区」的 manifest 可投影;把 tracked-set 呈成 workspace 是语义谎。
@@ -332,7 +375,7 @@ SEND_USER_FILE_SANDBOX_PUT_ENDPOINT=… # 可选:沙箱直传 PUT 的端
332
375
  ```
333
376
  - **词表优先级**:内网端点 `MINIO_ENDPOINT` 优先,`S3_ENDPOINT` 是外接 S3 兼容位(内网/公网同址场景可只给它,同时充当公网面);公网端点 `S3_PUBLIC_ENDPOINT` 优先,缺席回落 `S3_ENDPOINT`。空串一律按未设处理。
334
377
  - **🔴 云形(`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 默认无帽)。
335
- - **per-file rewind 历史的边界上限(S-15 twin 保留条款,core design/381 §片2)**:`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 便利态,会话与其他持久面不动)。
378
+ - **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 便利态,会话与其他持久面不动)。
336
379
  - **🔴 公桶策略必须 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)。
337
380
  - **两条轨**:ttl=0(默认)→ 匿名 GET 公桶下 `uuidv7/<name>` 不可猜 key,链接永久、可回收(删对象);0<ttl≤7 天 → 私桶 + SigV4 限时签名链接(7 天是 SigV4 物理上限,更久用 ttl=0)。
338
381
  - **执行 lane 与租户门**:e2b/k8s 沙箱 lane 任意租户可用(沙箱文件系统=租户边界,沙箱内 `curl -T` 直传、字节不中转、凭据不进沙箱);host/ssh lane 仅单用户部署(`REQUIRE_PRINCIPAL` 未开)时挂载。
@@ -352,12 +395,12 @@ FLEET_ADVERTISE_ADDRESS=http://<本机可达IP>:8090 # 可选:设了才启 flee
352
395
  - 中心**空/未发布** → `applyEffective` 回落 env + 内建 teams 并 warn `config_center_unpublished`,**不影响在跑的服务**(接了也安全)。
353
396
  - **灰度姿势**(配置控制面 AI 建议):先 `SEMA_REGISTRY_DRY_RUN=true` 起一轮,看日志 `sema_registry_dry_run`(中心给的 models/roles/teams + 会否覆盖 default、per-model apiKeyEnv)对得上 env 再去掉该 flag 真正 apply。
354
397
  - 拉取**只读、只取逻辑配置**(模型名册/角色/团队);密钥/网关仍在本服务 env(中心只发 env-**名** 引用,不发密钥值)。回滚=去掉 `SEMA_REGISTRY_URL` 即纯 env。
355
- - **配置热更新真表(7.38+ 现行,#322 战役/[4728]③)**——refresh 拍(60s 轮询,或 `POST /v1/admin/config/refresh` 手动触发,见 API 表)对各域的生效方式:
398
+ - **配置热更新真表(7.38+ 现行)**——refresh 拍(60s 轮询,或 `POST /v1/admin/config/refresh` 手动触发,见 API 表)对各域的生效方式:
356
399
 
357
400
  | 域 | 生效 | 说明 |
358
401
  |---|---|---|
359
402
  | models / roles / default 模型 | **热**(下一 refresh 拍) | 全 boot Runner **原子换代**(swap 失败=候选整拒,活配置零触碰);此前「改 models 需重启」的时代已随 Runner swap 腿落地终结 |
360
- | pricing / keys(env-名引用)/ prompts / teams | **热** | 值换代即生效;cost 族限额座(rate limit / cost quota)#322 批1 起同热(限额=纯比较参数,窗内累计不动;窗长换代=记账周期重开) |
403
+ | pricing / keys(env-名引用)/ prompts / teams | **热** | 值换代即生效;cost 族限额座(rate limit / cost quota)同热(限额=纯比较参数,窗内累计不动;窗长换代=记账周期重开) |
361
404
  | **tier 变更**(models-tiers plane 与 tier-frozen 基线不一致) | **defer 到重启**(唯一 defer 臂) | 候选 durable 落地(LKG,`CONFIG_LKG_DURABLE`)后 `/health` 报 `restartRequired`;无 durable handoff ⇒ `/health` 报 `modelPlaneDeferred{version,since,blockedReasons}` 候运维(不强制重启,plane 保持未应用) |
362
405
 
363
406
  生效与否的观测口=`/health` 世代账键(`configTargetVersion`/`configAppliedVersion`+两 ordinal+`configApplyStaleMs`,见 API 表 `/health` 行)——target≠applied 持续=「改了没生效」的机读信号。
@@ -392,7 +435,7 @@ QUESTION_TTL_MS=300000
392
435
  要在活流腿上保留旧的自答续跑行为,目前的办法是把该请求改走 `/v1/tasks`(非流)或 `/v1/runs`,
393
436
  或带上 `x-detach-on-disconnect: true`。
394
437
 
395
- **流内审批协议(`approval_request` 帧族,design/172)—— 总开关 + 九个调优钮**
438
+ **流内审批协议(`approval_request` 帧族)—— 总开关 + 九个调优钮**
396
439
 
397
440
  > 🔴 **默认已是 ON**(clay 裁 2026-08-08;7.3.0/7.4.0 的**已发布**线上仍是 OFF,翻转落在其后的下一个发布)。
398
441
  > 开关只是发帧五合取里的一项:还要**活卡腿开着 ∧ 非 local 的持久 store backend ∧ park 设施在场**
@@ -425,11 +468,11 @@ STREAM_APPROVAL_PENDING_GRACE_MS=30000
425
468
  STREAM_APPROVAL_ADHOC_GRACE_MS=60000
426
469
  # 遗孤最终可判上界(毫秒,默认 604800000=7d,有界 [60000, 7776000000=90d]),量的是 createdAtMs
427
470
  # 单机交互形推荐调低到 172800000(48h):一张卡挂两天没人批在单机上基本等于死卡,7d 默认是按
428
- # 多副本云形定的;调低让 resume 对账少扫陈年行([3899]④/[3900] 线,clay 裁=文档推荐不改全局默认)
471
+ # 多副本云形定的;调低让 resume 对账少扫陈年行(clay 裁=文档推荐不改全局默认)
429
472
  STREAM_APPROVAL_ORPHAN_TTL_MS=604800000
430
473
  # 案A(7.52.0):**零活流铸造**的 ask 的悬挂窗(毫秒,默认 3600000=1h,有界 [60000, 86400000=24h])。
431
474
  # 只作用于「到达时该 (owner, session) 下一条活 SSE 都没有」的那一类 —— 典型是 workflow 子代 ask
432
- # (workflow 恒后台跑,人不在场);S-52 起 bg 提交腿(POST /v1/runs)与 resume 腿的委派子代 ask 同席
475
+ # (workflow 恒后台跑,人不在场);bg 提交腿(POST /v1/runs)与 resume 腿的委派子代 ask 现同席
433
476
  # (此前那两条腿结构性无席位,恒即拒,连本窗都够不着)。此前那一刻即答「无人可答」⇒ 整条命令死拒;现在它照常落一条
434
477
  # STREAM_PENDING 行等人,人经 GET /v1/approvals / 壳队列 / 重连开流补投三条通道回来答,答完子代原地续跑;
435
478
  # 窗满才走既有 park/deny 出路。**有活流的 ask 一毫秒不受影响**(仍是 STREAM_ASK_WINDOW_MS)。
@@ -453,12 +496,12 @@ UNREACHED_ASK_TTL_MS=3600000
453
496
  core 走 headless 缺省,零 ask 行)、`UNATTENDED_APPROVAL_POLICY=deny`(零活流的 ask 铸造时刻即拒,
454
497
  悬挂路径结构上不可达)。三形下该键即使与短 orphan TTL 组合也照常起服 —— 一批从未选择本能力的既有
455
498
  部署不因升级引入的 1h 缺省当场拒启。上一条(`STREAM_ASK_WINDOW_MS` 版)保持**无条件**,与既有行为逐字一致。
456
- - **断连时活卡怎么办(#245/[3777] 成文)**:客户端**优雅断开**(server 收到 socket close)时,流内未决 ask
457
- 立即强转 durable park(7.16.0 #241),恢复后 attach 重放会重呈卡。客户端**网络中断**(TCP half-open,
499
+ - **断连时活卡怎么办**:客户端**优雅断开**(server 收到 socket close)时,流内未决 ask
500
+ 立即强转 durable park(7.16.0 ),恢复后 attach 重放会重呈卡。客户端**网络中断**(TCP half-open,
458
501
  server 侧 close 事件不触发)是已知盲窗:SSE heartbeat 帧只会堆进 TCP 重传队列,server 要等 OS 级重传
459
502
  耗尽(十几分钟量级)才感知——这段时间内真正的兜底是**活卡窗到期转 park**(`STREAM_ASK_WINDOW_MS`,
460
503
  默认 5min),所以把窗改大等于把断连场景的最坏无人区拉长,改小前先看上面的跨旋钮不变量。half-open 的
461
- 主动探测(写水位判死)在攻关排期([3777] S2)。
504
+ 主动探测(写水位判死)在攻关排期。
462
505
  - **无 durable 前置的部署(五合取不满足:local backend / 无 checkpoint 能力)**:流内协议整个不上场
463
506
  (启动一行 `stream_approval_disabled` 点名缺哪项),ask 走旧活卡腿——断连场景的最坏结局是活卡 TTL
464
507
  窗满**自动 deny(fail-closed)后 run 继续**,不会死锁在等一张没人能回的卡上;代价是断连期间用户的
@@ -469,7 +512,7 @@ UNREACHED_ASK_TTL_MS=3600000
469
512
  (代价是超限的 ask 走 park,要有人去审批队列捞);库压大 → 调小 `STREAM_APPROVAL_RECONCILE_BATCH`
470
513
  (代价是收敛变慢,靠队列轮转保证下轮接着扫)。
471
514
 
472
- **无人值守政策(`UNATTENDED_APPROVAL_POLICY`,7.34.0 起 / #280 R-13)**
515
+ **无人值守政策(`UNATTENDED_APPROVAL_POLICY`,7.34.0 起)**
473
516
 
474
517
  ```bash
475
518
  # 闭集 park|deny,缺省 park。拼错词**启动期响亮拒**(不静默回缺省)
@@ -508,11 +551,12 @@ READ_DENY_PATTERNS='[{"pattern":"**/secrets/**"},{"pattern":".ssh","caseSensitiv
508
551
  一律启动期点名拒——一个手滑的 deny 集比没有 deny 集更糟(以为挡了其实没挡)。
509
552
  - **缺席不铸**:两个键不设就**不传给引擎**——引擎抬默认/扩内建集那天,存量部署自动跟上(与重试上限
510
553
  同款「引擎当家」纪律)。
511
- - **leader 车道同席(S-28,7.54 起)**:`LEADER_ENABLED` 的 leader 编排(planner / worker / repair / conflict 四类
554
+ - **leader 车道同席(7.54 起)**:`LEADER_ENABLED` 的 leader 编排(planner / worker / repair / conflict 四类
512
555
  任务)与主车道吃**同一份**四键(连同 `MEMORY_DELEGATION_EVIDENCE` / `MEMORY_PROVENANCE` / `MEMORY_CAPTURE_POLICY`
513
556
  与委派入口 caps;取值点唯一 = `buildDeploymentPostureSeats`,leader 不另读 env)。此前(≤7.53)那条腿一席都不带:
514
- deny 追加对 leader worker **静默不生效**(core 内建 deny 表仍在)。仍缺 tracer / usageWindows / promptSource 三席
515
- (#311 扩面,另车)
557
+ deny 追加对 leader worker **静默不生效**(core 内建 deny 表仍在)。扩面(7.56 起)再补三席:计费/遥测
558
+ `tracer`(连同后台腿的 principal 归因)、治理窗 `USAGE_WINDOWS` 两键(连同 `POST /v1/leader` 的准入门)、
559
+ center 发布的提示词目录 —— 治理窗那一席的细节见 §9.2。
516
560
  - **组织下发腿(config-center,7.29 起)**:四键(`READ_FACE` / `READ_DENY_PATTERNS` /
517
561
  `READ_DENY_BUILTIN_TIERS` / `READ_DENY_BUILTIN_EXCLUDE`)也可由 config-center 的 `readFace` 域下发
518
562
  (`{face?, denyPatterns?, denyBuiltinTiers?, denyBuiltinExclude?}`)。三条规则:
@@ -525,7 +569,7 @@ READ_DENY_PATTERNS='[{"pattern":"**/secrets/**"},{"pattern":".ssh","caseSensitiv
525
569
  ⚠️ 本地 `config.d/` 车道今天**接不上**这一域(registry-core 的可移植域表里没有它),只有
526
570
  `CONFIG_PROVIDER=remote` 的部署走得通。
527
571
 
528
- - **MCP 撤销腿(7.39 起,core design/338)**:MCP 服务器 mount 仍是 restart-to-apply(新增/改址要
572
+ - **MCP 撤销腿(7.39 )**:MCP 服务器 mount 仍是 restart-to-apply(新增/改址要
529
573
  restart 才生效),但**从 enabled 集删除一个服务器即时生效**——refresh 拍收货后,被删服务器的
530
574
  MCP 调用当场按 coded 拒绝 `mcp.server_revoked` 结算(known-not-executed,引擎每次派发前探询
531
575
  台账,不缓存)。因此 `/health` 的 `restart.reasons` 对**纯删除向**不再报 `mcp`(签了就是为一个
@@ -651,9 +695,12 @@ ENGINE_LINGER_MS=60000 # 可选,默认 60s,下界 1000
651
695
 
652
696
  **布尔旋钮的取值与极性(运维必读)**
653
697
 
654
- 布尔 env **只认 `true` / `false` 两个字面量**。写成 `1` / `yes` / `TRUE` 该旋钮退回自己的缺省值,并在启动时
655
- 报一条 `config_env_invalid_using_default`(`env` + `raw` 字段)。以前这是**静默**退默认的,`LSP_ENABLED=1`
656
- 这种在人眼里是"开"、在代码里是""的写法查不出来。
698
+ 布尔 env **收 `true` / `false` / `1` / `0` / `yes` / `no` / `on` / `off`**(大小写不敏感;词表自 7.16.0
699
+ 起放宽)——`LSP_ENABLED=1` 这种人眼读作"开"的写法,现在代码里也
700
+ 读作"开",不再是此前"看着开、其实关"的陷阱。词表**外**的值(如 `enabled`、`TRUE1`)⇒ 进程**直接
701
+ 拒启**,报错点名该 env 名/收到的原始值/可接受词表(fail-loud,不留静默默认)。旧的「`config_env_
702
+ invalid_using_default` 警告 + 退回缺省值」这条臂随词表放宽一并退役,不再覆盖布尔旋钮——该事件现在
703
+ 只用于其余种类的越界配置(如某个数值旋钮被夹到边界值)。
657
704
 
658
705
  缺省值**推不出来**——同一个 `*_ENABLED` 后缀底下有三种极性。所以 `LOG_LEVEL=debug` 时服务每个旋钮打一行
659
706
  `config_knob_polarity`:`knob` / `polarity` / `value` / `source`,直接告诉你这台机器上每个开关此刻是什么、为什么:
@@ -721,7 +768,7 @@ ENGINE_LINGER_MS=60000 # 可选,默认 60s,下界 1000
721
768
  | `sessionId` | — | 续聊:带上次返回的 `sessionId`,服务端自动 wake 历史 |
722
769
  | `images` | — | 图文输入 `[{data,mimeType}|{url}]`(模型需支持 vision) |
723
770
  | `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 形走本地文件店。 |
724
- | `scenario` | — | `default`(默认)/ `code-review`(见 §5)/ `scan`(同 §5 的 repo 只读工具但**中性无框架提示词**——objective+中心下发 skill 全权主导输出,OA 扫描类用;与 `code-review`/`discuss` 同属 clone-free 无执行环境场景,见 §5 末)/ **配置控制面可声明任意新场景**(`{name, toolset: none\|repo-readonly, prompt?}`,组合即配置、能力写死在部署;restart-to-apply;center 可覆盖内建名,boot 日志 `config_center_scenarios.shadowsBuiltin` 可审计) |
771
+ | `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`/`discuss` 同属 clone-free 无执行环境场景,见 §5 末)/ **配置控制面可声明任意新场景**(`{name, toolset: none\|repo-readonly, prompt?}`,组合即配置、能力写死在部署;restart-to-apply;center 可覆盖内建名,boot 日志 `config_center_scenarios.shadowsBuiltin` 可审计) |
725
772
  | `repo` / `council` / `debate` | — | `repo` 为 `code-review`/`scan` 必填;`council`/`debate` 仅 `code-review`,见 §5 |
726
773
  | ~~model / tools / prompt~~ | 🚫 | **不接受**——服务端注入 |
727
774
 
@@ -736,13 +783,13 @@ ENGINE_LINGER_MS=60000 # 可选,默认 60s,下界 1000
736
783
  |---|---|
737
784
  | `result` | 答案文本 |
738
785
  | `sessionId` | 续聊用——下次带回 |
739
- | `status` | `completed` / `blocked` / `failed` / `timeout` |
786
+ | `status` | `completed` / `blocked` / `failed`(终局闭集;`timeout` 已于 core 5.8.0/server 6.0.0 退役,不会再出现——见 §9) |
740
787
  | `errorCode` | 程序化分支:如 `"conflict"`(乐观锁丢失,可重试) |
741
788
  | `blockedReason` | `status=blocked` 时:为什么做不了(缺信息/权限) |
742
789
 
743
790
  > ⚠️ **任务成败以 `status`/`errorCode` 为准,勿以 HTTP 状态码判**:同步腿 200=提交受理成功(任务可能
744
791
  > `failed`+`errorCode`);`POST /v1/runs` 202=已受理(执行期结局落在终态 run 记录/`done` 帧)。执行期
745
- > 错误结构上无法用 HTTP 状态码承载——流的响应头在 run 开跑前就发完了([2812]②b 口径,详见
792
+ > 错误结构上无法用 HTTP 状态码承载——流的响应头在 run 开跑前就发完了(详见
746
793
  > `docs/ASSISTANT-WIRE-CONTRACT.md` 附录 A)。
747
794
 
748
795
  ---
@@ -837,8 +884,8 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
837
884
  | `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) |
838
885
  | `GET /v1/capabilities/scenarios/<name>` | 单个场景的只读详情:工具面、提示词概览、**本部署现在跑不跑得动**(见下) |
839
886
  | `POST /v1/admin/drain` `{"reason":"…"}` | **停机因由显式声明**(7.35.0+;operator-only —— principal 须在 `OPERATOR_PRINCIPALS` 内,空名单 = 谁都不是 operator ⇒ 恒 403)。编排壳在发 SIGTERM **之前**调它;server 记进程内 `drainState.reason`,draining 期由 `/health` 的 `drainReason` 与 503 `draining` 体的 `reason` 透出(未声明 = 两面键缺席)。声明是**覆盖式**、无 TTL、随进程重启清零 ⇒ 每次停机流程内都要重新声明。`reason` 非空、trim 后 ≤256 字符,坏值 400。🔴 **写口是特权的,读口不是**:`/health` 免鉴权(见下一行)且缺省全网卡监听 —— 任何能连到这个端口的人都读得到你写的那句话。**别在 reason 里写工单号、内部主机名或任何内部标识**。 |
840
- | `GET /v1/config/catalog` | **配置目录自描述**(DESIGN-278 §5 S2,S-32;operator-only —— `explicitOperatorOk`,空名单=谁都不是)。逐 env 键一行:`{name, domain, type, semantics, writeLanes, effectiveLane, envHeld, envTiming, staticDefault?/derivedDefaultNote?, effectiveValue, valueClass, danger, center?{domain,path,precedence,timing,restartSlice?,domainExists,managedNow,effectiveNow}}`;顶层带 `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`。 |
841
- | `POST /v1/admin/config/refresh` | **配置手动刷新**(#322 批1;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。 |
887
+ | `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?,domainExists,managedNow,effectiveNow}}`;顶层带 `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`。 |
888
+ | `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。 |
842
889
  | `GET /health` | 健康(无需鉴权)。7.37.0+ 配置管道在场时带世代账键:`configTargetVersion`(本副本**最后见到**的配置版本,身份标)、`configAppliedVersion`(最后一次真落地的版本)、`configTargetOrdinal`/`configAppliedOrdinal`(7.38.0-rc.2 起,**收货序数**——`CONFIG_PROVIDER=local` 下 version 是内容哈希不承诺序,「改了没生效」看序数对不对齐)、`configApplyStaleMs`(target≠applied **持续**时长;收敛时键缺席)—— 编排器据此摘掉「配置持续落后」的副本 |
843
890
  | `GET /metrics` | Prometheus 指标(有 token 时需带) |
844
891
 
@@ -850,9 +897,9 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
850
897
  > 两面共用同一份判据,所以列表上画得出的场景点下去不会再突然 501。请按 `unavailableReason` 的**键**分支,
851
898
  > 不要去匹配英文文案——文案会改,键不会(新增取值只会追加,现役键不改语义)。
852
899
 
853
- > **operator 鉴权(审批队列)**:`OPERATOR_PRINCIPALS=ops:alice,ops:bob`(CSV)= 谁能当 operator——列任意 owner 待办 + 决议(批/否)。**单租户部署不设=旧行为**(握 service token 即 operator,向后兼容);设了之后,非名单 principal 列待办只看自己的、且**不能决议**(403,防"请求方批自己的高危操作"跳过 F4 闸)。⚠️ **多租户形拒启**(#157-①):`DURABLE_APPROVAL=true` + `REQUIRE_PRINCIPAL=true` 而 `OPERATOR_PRINCIPALS` 空 ⇒ 进程启动失败并点名修法——否则空名单会让任一已验证租户读到其他租户的待批队列(读面 true-for-all)。设名单,或确属单租户则不设 `REQUIRE_PRINCIPAL`。
900
+ > **operator 鉴权(审批队列)**:`OPERATOR_PRINCIPALS=ops:alice,ops:bob`(CSV)= 谁能当 operator——列任意 owner 待办 + 决议(批/否)。**单租户部署不设=旧行为**(握 service token 即 operator,向后兼容);设了之后,非名单 principal 列待办只看自己的、且**不能决议**(403,防"请求方批自己的高危操作"跳过 F4 闸)。⚠️ **多租户形拒启**:`DURABLE_APPROVAL=true` + `REQUIRE_PRINCIPAL=true` 而 `OPERATOR_PRINCIPALS` 空 ⇒ 进程启动失败并点名修法——否则空名单会让任一已验证租户读到其他租户的待批队列(读面 true-for-all)。设名单,或确属单租户则不设 `REQUIRE_PRINCIPAL`。
854
901
  >
855
- > **部署治理三声明(7.21.0 起,design/170 件B/C/D)**——三根都是**配置期**旋钮,「谁能改它们」≡「谁能改
902
+ > **部署治理三声明(7.21.0 )**——三根都是**配置期**旋钮,「谁能改它们」≡「谁能改
856
903
  > 部署配置」,不引入新鉴权面;三根**都不设 = 现行为逐字不变**,坏值一律**拒启**(安全轴上的旋钮不静默失效)。
857
904
  > - `COMPLIANCE_PROFILE=hipaa|zdr`(+ 可选 `COMPLIANCE_ADDITIONAL_DENIES=mcp_servers,workflows,web_fetch,org_memory_mount`,
858
905
  > **只能在档位内建 floor 上加禁,不能减**):合规档位否决。档位在场时被禁能力不挂载,任务**显式请求**被禁能力
@@ -890,7 +937,7 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
890
937
  > 且**状态变更与审计行同一个事务**(不存在"解冻了但账上没有")。
891
938
  > - `GET /v1/ops/retention/audit?domain=&limit=&before=` —— 审计读面(keyset 分页,游标形同名册面)。
892
939
  > - 能力位 `capabilities.retention` = `{mode, maxAgeDays}`(lane 开着)或 `null`(关着)。
893
- > - **子代转录(placed 分区)的留存天数没有独立旋钮座**(S-03/#292②,设计判定 2026-08-30):placed 转录的
940
+ > - **子代转录(placed 分区)的留存天数没有独立旋钮座**(设计判定 2026-08-30):placed 转录的
894
941
  > 删除权**独家**归联合 reap(`reapDurableAgents`:行赢删才 release 转录;上面这条 lane 的候选谓词显式排除
895
942
  > placed,见 `plugins/retention-store-sql.ts` 第五腿)——生效视界 = `BG_AGENT_RETENTION_MS`(缺省 7 天,
896
943
  > 毫秒形,同时是 agent 行的留存旋钮;分区孤儿腿的 `olderThanMs` 同源)。core 的
@@ -985,7 +1032,7 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
985
1032
  > (或用 `REQUIRE_PRINCIPAL` 关掉该面),同样只对**此后**新铸的行生效。
986
1033
  >
987
1034
  > **🔧 升级到 7.44.0 前:`PERMISSION_RULES_ENABLED=true` 的部署必须删库重建 `permission_rule_approval`
988
- > 表(BREAKING)。** #340 的卡编辑面(自由文本规则臂)给这张表新增 `command`/`edited_json` 两列,而三条
1035
+ > 表(BREAKING)。** 卡编辑面(自由文本规则臂)给这张表新增 `command`/`edited_json` 两列,而三条
989
1036
  > 记录语句(`get`/`create`/`cas`)无条件引用它们。本服务的 schema 契约 = 启动 DDL 是唯一真源、不发
990
1037
  > `ALTER` 增量 seam,建表语句是 `CREATE TABLE IF NOT EXISTS` ⇒ 对已存在的旧表**一字不改**:新两列不在,
991
1038
  > 升级后**整条审批记录面**(包括修前就有的候选臂)在这台机器上是坏的——不是「少一个新功能」。
@@ -995,7 +1042,7 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
995
1042
  > 与历史记录轨迹;真正持久化的规则本身在 `permission_rule` 表,不受影响、不需要重建。
996
1043
  > 探针只在规则车道真被装配时跑(`PERMISSION_RULES_ENABLED=false` 的部署一次都不碰这张表,不受影响)。
997
1044
  >
998
- > **同批顺带:场景旧名 `team` / 工具旧名 `run_team` 退役(#342,BREAKING)。** #273 过渡窗的别名层
1045
+ > **同批顺带:场景旧名 `team` / 工具旧名 `run_team` 退役(BREAKING)。** 此前过渡窗的别名层
999
1046
  > (`SCENARIO_ALIASES`)整体撤销:请求体里显式传 `scenario:"team"` 从「静默归一到 `discuss`」变成
1000
1047
  > **未知名响亮 400**(`scenario_unknown`);指派/部署配置里悬空写着的旧名走**现有**
1001
1048
  > unknown→default 回退(与任何其他未知名同待遇,不是新逻辑)。prompts 里写死旧工具名 `run_team` 的
@@ -1106,7 +1153,9 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
1106
1153
  | HTTP `403` / `404`(session/run) | 不是该 principal 的资源 | 用正确身份 |
1107
1154
  | HTTP `409`(`/v1/runs`) | 同 session 已有活跃 run | 等它完成 / 用返回的 `activeTaskId` |
1108
1155
  | HTTP `429` | 限流 | 看 `Retry-After` 退避 |
1109
- | HTTP `429` + `errorCode:"usage.window_exhausted"` | 部署级 token 治理窗耗尽(`USAGE_WINDOWS`) | 看响应体 `retryAfterSec` 退避;窗滑动/桶到期后放行 |
1156
+ | HTTP `429` + `errorCode:"limit.cost_quota_exceeded"` | per-principal 累计成本配额越顶(`MAX_PRINCIPAL_COST_USD`;**进场门**,不打断在跑的 run) | `Retry-After` / 体 `retryAfterSec` 退避;窗滚过或调高上限后放行 |
1157
+ | HTTP `429` + `errorCode:"usage.window_exhausted"` | 部署级治理窗耗尽(`USAGE_WINDOWS`,token 或 $ 天花板先满者) | 看响应体 `retryAfterSec` 退避;窗滑动/桶到期后放行 |
1158
+ | `status:"failed"` + `errorCode:"usage.window_exhausted"` | 已受理的 run 在 **turn 边界**撞上治理窗且无法 durable 挂起 | 同上退避后重投;配好 checkpoint 基建则改为 `suspended` 等窗自动续跑 |
1110
1159
  | HTTP `501`(`/v1/runs`) | 内存模式不支持异步 | 配 MySQL 协议存储(`SESSION_BACKEND=mysql`) |
1111
1160
 
1112
1161
  **机器码**:每个 4xx/5xx 响应体都带一个 `errorCode`(与人类文案 `error` 并列),这是**唯一**该拿来做
@@ -1125,7 +1174,7 @@ tag 词表与每条的最坏后果见 `docs/FAIL-OPEN-CENSUS.md`。
1125
1174
  `source:"limit_approach"`)。批处理场景嫌噪声:`limits:{approachNotice:false}` 关闭;调阈:
1126
1175
  `limits:{approachNotice:{at:[0.5,0.9]}}`(两分数 (0,1] 且 r1<=r2,相等合法;坏形 400 点名)。
1127
1176
 
1128
- ### 9.1b total_tokens 余量提醒帧(RB-318,缺省翻面 —— core 5.65 系起)
1177
+ ### 9.1b total_tokens 余量提醒帧(缺省翻面 —— core 5.65 系起)
1129
1178
 
1130
1179
  声明了 token 上限(`limits.maxTokens` 或资源切片余量)的任务,**每个带工具调用的边界**现在会多一行
1131
1180
  CC 逐字节形的提醒帧:`<total_tokens>N tokens left</total_tokens>`(**缺省 ON**;此前需显式 opt-in)。
@@ -1137,7 +1186,7 @@ CC 逐字节形的提醒帧:`<total_tokens>N tokens left</total_tokens>`(**缺
1137
1186
  - **分辨依据**(消费端/日志过滤):该帧内文**恒以 ` tokens left` 结尾**;`Infinite` 常量臂(`infinite`
1138
1187
  档)是唯一例外,单独放行——它不带数字、不依赖上限,且只在显式 `true` 下可达。
1139
1188
 
1140
- ### 9.2 部署级治理窗(USAGE_WINDOWS,6.1.0 起;$ ceiling=S-18/core 7.0.0 起)
1189
+ ### 9.2 部署级治理窗(USAGE_WINDOWS,6.1.0 起;$ ceiling=core 7.0.0 起)
1141
1190
 
1142
1191
  跨任务的滑动预算(core `usageWindows`),缺省不设=关。单键 JSON 数组;每窗两只**独立** ceiling
1143
1192
  (`maxTokens` token 数 / `maxCostUsd` 绝对美元)——each optional、**至少其一**(双缺拒启),
@@ -1156,8 +1205,15 @@ USAGE_WINDOWS='[{"windowMs":18000000,"maxTokens":5000000,"maxCostUsd":10,"anchor
1156
1205
  - 耗尽:提交面立即 `429 usage.window_exhausted`(体 `retryAfterSec` + `Retry-After` 头);已受理的
1157
1206
  任务撞窗时,基建合格(checkpoint+durable approval)⇒ suspend(reason `usage_window`)等窗放行,
1158
1207
  不合格 ⇒ 响亮失败同码落终态记录。
1159
- - 计量覆盖:主任务、具名/委托 subagent、hook 内部任务全计(部署真花销都进窗)
1160
- - **$ 窗(S-18,core 7.0.0 起)**:`maxCostUsd` 要求 run **可计价**——模型既无 pricing 条目也无
1208
+ - 计量覆盖:主任务、具名/委托 subagent、hook 内部任务全计(部署真花销都进窗)。**leader 车道自 7.56 起同覆盖**
1209
+ (`LEADER_ENABLED` planner / worker / repair / conflict 四类任务与主车道同席同店,且**记到提交者那口锅**
1210
+ ——leader 的每条 TaskSpec 都带提交者身份,匿名部署落 GLOBAL 锅;`POST /v1/leader` 的四门集合、门序与拒的
1211
+ 字节与 `/v1/tasks`、`POST /v1/runs` 一致 —— 窗耗尽同样 `429 usage.window_exhausted`,且**在起后台腿之前**拒,
1212
+ 被拒的提交一个字节都不会推到远端)。≤7.55 这条腿两面皆缺:既不受窗约束,花销也不入窗。
1213
+ ⚠️ 一处**如实登记的差异**:`POST /v1/runs` 的幂等重放预读在窗门之前,leader 的幂等裁决在门之后 ⇒ 窗满期间
1214
+ 用**同一个** `Idempotency-Key` 重投拿到的是 429 而不是原来那张 202 收据。这不丢 run:429 **不消费键**,窗一
1215
+ 放行,同键重投照旧回原 `leaderRunId` 且不会起第二条后台腿(第二条 = 第二次真 `git push`)。
1216
+ - **$ 窗(core 7.0.0 起)**:`maxCostUsd` 要求 run **可计价**——模型既无 pricing 条目也无
1161
1217
  `Model.cost` 声明时,cost 不是 0 而是**不存在**,$ 窗对它不可求值 ⇒ 任务门口拒
1162
1218
  (`config.usage_window_unpriced`),绝不记 0 让天花板静默失效。MIXED 声明(部署里同时有 $ 窗与纯
1163
1219
  token 窗)下共享账本行统一携带 cost;全部署零 $ 窗时账本行与此前逐字节同形。
@@ -77,7 +77,7 @@ registry:
77
77
  nodePort: 30300
78
78
  storageSize: 2Gi
79
79
  storageClass: "" # 空=集群默认 SC
80
- # [4824] S10 P1:默认管理员名必须是中性值——个人标识串作生产默认值=内部标识外溢(cli 点名案)。
80
+ # 默认管理员名必须是中性值——个人标识串作生产默认值=内部标识外溢(cli 点名案)。
81
81
  adminUser: admin
82
82
  adminPassword: "" # REQUIRED(kube-up 注入)
83
83
  sessionSecret: "" # REQUIRED