@sema-agent/server 7.128.0 → 7.129.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 (96) hide show
  1. package/MIGRATION.md +118 -0
  2. package/USAGE.md +89 -18
  3. package/dist/approval-card.d.ts +53 -26
  4. package/dist/approval-card.js +40 -11
  5. package/dist/approval-crash-convergence.js +4 -4
  6. package/dist/bake-runner/main.js +2 -1
  7. package/dist/boot/memory-consolidation.d.ts +46 -16
  8. package/dist/boot/memory-consolidation.js +15 -8
  9. package/dist/boot/side-query-lane.d.ts +7 -20
  10. package/dist/boot/side-query-lane.js +0 -24
  11. package/dist/boot/stage-01-config.js +3 -0
  12. package/dist/boot/stage-06-runners.js +1 -1
  13. package/dist/boot/stores.js +2 -2
  14. package/dist/boot/web-admin.js +17 -10
  15. package/dist/boot/webfetch-summarize-lane.d.ts +10 -7
  16. package/dist/boot/webfetch-summarize-lane.js +2 -3
  17. package/dist/brain.js +6 -9
  18. package/dist/capabilities/builtin-tools.d.ts +2 -2
  19. package/dist/capabilities/engine-capabilities.d.ts +14 -0
  20. package/dist/capabilities/engine-capabilities.js +12 -0
  21. package/dist/capability-facts.d.ts +3 -1
  22. package/dist/capability-facts.js +1 -0
  23. package/dist/config-types.d.ts +4 -3
  24. package/dist/egress-redirect.d.ts +12 -0
  25. package/dist/egress-redirect.js +7 -0
  26. package/dist/entry-mode-clamp-notice.d.ts +30 -0
  27. package/dist/entry-mode-clamp-notice.js +13 -0
  28. package/dist/fleet/fleet-bus.d.ts +7 -0
  29. package/dist/fleet/fleet-bus.js +1 -0
  30. package/dist/hooks/hook-llm.js +6 -2
  31. package/dist/host-lsp-manager.d.ts +3 -3
  32. package/dist/http/admission.js +5 -2
  33. package/dist/http/resume-legs.js +46 -27
  34. package/dist/http/route-ctx.d.ts +1 -1
  35. package/dist/http/routes/approvals-assistant.js +6 -4
  36. package/dist/http/routes/capabilities.js +1 -0
  37. package/dist/http/routes/session-sync.js +3 -4
  38. package/dist/http/routes/sessions.js +4 -17
  39. package/dist/http/send.d.ts +1 -1
  40. package/dist/http/send.js +4 -3
  41. package/dist/http/wire-types.d.ts +3 -2
  42. package/dist/index.d.ts +1 -1
  43. package/dist/index.js +1 -1
  44. package/dist/observability/fail-open.d.ts +7 -3
  45. package/dist/observability/fail-open.js +7 -3
  46. package/dist/observability/secret-env-scrub.d.ts +37 -3
  47. package/dist/observability/secret-env-scrub.js +2 -2
  48. package/dist/parked-decide.js +5 -2
  49. package/dist/plugins/memory-embedder.js +5 -0
  50. package/dist/plugins/one-time-migrations.d.ts +180 -9
  51. package/dist/plugins/one-time-migrations.js +315 -3
  52. package/dist/plugins/pg-pool.js +3 -1
  53. package/dist/plugins/remote-env-host.d.ts +17 -20
  54. package/dist/plugins/remote-env-host.js +13 -25
  55. package/dist/plugins/session-policy-store-sql.d.ts +16 -4
  56. package/dist/plugins/session-policy-store-sql.js +17 -20
  57. package/dist/plugins/session-rules-column.d.ts +16 -0
  58. package/dist/plugins/session-rules-column.js +6 -0
  59. package/dist/plugins/tidb-pool.js +4 -1
  60. package/dist/read-face-posture.d.ts +2 -1
  61. package/dist/request-value-closure.js +1 -1
  62. package/dist/retired-card-words.d.ts +17 -0
  63. package/dist/retired-card-words.js +49 -0
  64. package/dist/rule-entry-list.js +2 -0
  65. package/dist/run-local.js +10 -3
  66. package/dist/server-secret-env.d.ts +77 -85
  67. package/dist/server-secret-env.js +40 -26
  68. package/dist/session-policy-wire.d.ts +61 -124
  69. package/dist/session-policy-wire.js +59 -121
  70. package/dist/session-sync.d.ts +8 -6
  71. package/dist/session-sync.js +6 -5
  72. package/dist/spec-fields.d.ts +3 -2
  73. package/dist/standing-answer-grant.d.ts +17 -9
  74. package/dist/standing-answer-grant.js +11 -8
  75. package/dist/task-settings.d.ts +6 -1
  76. package/dist/task-settings.js +1 -1
  77. package/dist/tool-approval.d.ts +14 -7
  78. package/dist/tool-approval.js +23 -18
  79. package/dist/trace/core-keyset-guard.d.ts +3 -3
  80. package/dist/trace/engine-notice-wire.d.ts +1 -1
  81. package/dist/trace/engine-notice-wire.js +1 -0
  82. package/dist/trace/project.d.ts +7 -4
  83. package/dist/trace/project.js +6 -75
  84. package/dist/trace/tool-output-media.d.ts +2 -1
  85. package/dist/trace/tool-output-media.js +3 -25
  86. package/dist/web-admin-client/apply-effective.js +28 -11
  87. package/dist/web-admin-client/facade.d.ts +1 -1
  88. package/dist/web-admin-client/facade.js +1 -1
  89. package/dist/web-admin-client/read-face.d.ts +73 -20
  90. package/dist/web-admin-client/read-face.js +76 -36
  91. package/dist/web-admin-client/skills-mcp.d.ts +1 -1
  92. package/dist/web-admin-client/skills-mcp.js +1 -1
  93. package/dist/web-admin-client/types.d.ts +3 -3
  94. package/package.json +3 -3
  95. package/dist/mcp-tool-axes.d.ts +0 -62
  96. package/dist/mcp-tool-axes.js +0 -47
package/MIGRATION.md CHANGED
@@ -7,6 +7,124 @@
7
7
  > ⚠️ 完整清单在仓库根 `CHANGELOG.md`——它**不随 npm tarball 出包**(本文件随包)。看完整迁移窗的
8
8
  > 权威姿势是源码 tag diff:`git diff v<旧>..v<新>`(每版都推 `v<版本>` tag);npm 包页也镜像 CHANGELOG。
9
9
 
10
+ ## 7.129.0 —— 引擎 7.51.2 提货 · 会话规则一种文法与列改名(🔴 先停旧副本再起新)· 卡词换与存量一次性改写 · 读拒三席热生效 · 直连不跟重定向 · 两只告警标签改名
11
+
12
+ 1. **引擎 7.51.2(精确钉;7.51.0 的硬破坏 + 7.51.1 / 7.51.2 两补丁)**:本服务随引擎的硬破坏在下列第 2–13 条;引擎自己的收窄直接落到部署上的见第 14 条。DDL 只有第 2 条的一处列改名(SQL 后端,由起服一次性迁移做,不用手跑);第 3 条的存量改写同样由起服一次性迁移做。
13
+ 2. **会话规则:一种文法、两只拒码、装规则的列改名(BREAKING,调用方 + 持久面;🔴 升级序硬约束)**:规则体只剩三只桶 `toolAllow` / `toolDeny` / `allowDirs`;
14
+ `toolAllow` / `toolDeny` 装**规则条目** —— 工具名、`mcp__<server>`、工具自己文法的规则(`toolDeny: ["Bash(rm:*)"]` 按每一段命令的首词匹配 `rm`(连接链任一段的首词是 `rm` 即拒);写成路径的 `/bin/rm`
15
+ 不是这个名字);`commandAllow` / `commandDeny` 退役。
16
+ - **写**(`PUT /v1/sessions/:id/policy`、sync/import 的 `policy` 面):`commandDeny: ["rm"]` 的新写法是 `toolDeny: ["Bash(rm:*)"]` —— 它不拒旧桶还拒的两类:写成路径的
17
+ `/bin/rm …`、读不成一条命令名的命令行(管道、`&&`、命令替换);要这两类也拒,整只拒 shell(`toolDeny: ["Bash"]`)。`commandAllow` 没有对应写法(没有「只收窄 shell」的条目:
18
+ 要么列出允许的工具,其中 shell 写成 `Bash(<命令>:*)` 条目,要么不收窄)。拒码换成两只:
19
+ 规则体形不对(不是对象、有不是桶的键 —— 含退役命令桶 —— 桶不是一列字符串、空白目录)⇒ `400 rules.invalid_shape`,体顶层 `field` / `got`(值的类型);
20
+ 条目读成「什么都不是」(退役工具名、无前缀 `__` 名、铸不出的协议段、`*`、`mcp__*`、`Monitor(…)`、空串)⇒ `400 rules.inert_entry`,体顶层 `field` / `entry` / `reason` /
21
+ `verdict?` / `guidance?` / `currentName?`。**`rules.legacy_tool_name` 退役、无别名**(体里的 `name` 改叫 `entry`);`rules` 缺席 / 不是对象不再是 `request.body_shape`,
22
+ 桶形问题不再是 `request.field_invalid`。按旧码分支的消费端改按这两码分支(`rules.` 前缀族判仍成立)。
23
+ - **存量(SQL 后端)**:升级后第一次起服,一次性迁移 M17 把装规则的列 `session_policy.rules` 改名 `rule_entries`。带 `commandAllow` / `commandDeny` 的行:退役桶**不改写**、
24
+ 一个字节不动 —— 新文法写不出上面那两类拒,替你改写就是放宽 —— 这些行读回 `500 corrupt`(该会话的规则面 500、新任务起不来;引擎文件店对同一形同判),每次起服一行 warn
25
+ `session_rule_row_retired_bucket`(点名 `policyKey` / `sessionId` / `buckets`)。**出路**:在库里把那一行的 `rule_entries` 改写成新文法(`rev` / `gen` 不动;按上面「写」那条的
26
+ 配方,并决定那两类要不要整只拒 shell),或删会话(`DELETE /v1/sessions/:id`,规则随之删)。升级前想先处理:在 7.128.x 上用 operator 的 `PUT` 把命令桶换成
27
+ `toolDeny` 里的 `Bash` 与 `Monitor`(两版都执行:整只拒 shell,比旧桶严),升级后再按需要放成 `Bash(<命令>:*)` 条目 —— **不要**在 7.128.x 上直接写 `Bash(rm:*)`:
28
+ 7.128.x 收这串字样但不执行它,换掉命令桶的那一刻起到升级之前 `rm` 不再被拒。找这些行:
29
+ `SELECT policy_key, session_id FROM session_policy WHERE CAST(rule_entries AS CHAR) LIKE '%"commandDeny"%' OR CAST(rule_entries AS CHAR) LIKE '%"commandAllow"%'`
30
+ (PG:`rule_entries::text LIKE …`;升级前列名还是 `rules`)。
31
+ - **存量里旧文法写下的条目(同一次改名里改写;行自带文法戳)**:7.129.0 起每一行会话规则自带文法戳 `grammar`(写口判官盖、店整只存、读回原样交出;`GET` / `PUT`
32
+ 回包与同步导出的记录里多一个键 `grammar`,本版写下的每一行都是 `1`)。没有戳的行是戳之前写下的行,引擎按它存下时的文法(0)读:那种文法把 `toolAllow` 的条目当整串工具名比,
33
+ `Bash(rm:*)` / `Edit(src/**)` / `Bash(*)` 这类条目匹配不到任何工具 = 不放行任何东西;7.129.0 的文法把它们读成规则。引擎自己守住这一点(无戳行里这类 allow 条目照旧什么都
34
+ 不放行,并通告一次),M17 在改列名的同一步里把每一行改写成当前文法的同一句话:行交给引擎在它自己的文法里读,引擎读成「名了不存在的工具」的 `toolAllow` 条目删掉(名单**保持在场**:
35
+ 删到空就是空名单 = 什么都不放行,与升级前同判),其余条目原样,**每一行**盖 `grammar: 1`,`rev` / `gen` 不动、按原文逐字节比对后回写。改写之后库里没有无戳行,引擎的旧文法读法与通告
36
+ 在本服务上不再触发。`toolDeny` 里的条目一个字节不动、不点名:引擎的文法只修订 allow 方向的读法,deny 方向的 `Bash(rm:*)` 在两种文法下都是一条拒 `rm` 的规则(7.128.x 上它不拒任何东西,
37
+ 升级后拒 —— 收紧,与写的人的本意同向;要知道哪些会话带这类条目,读行即可)。`toolAllow` 里的 `*`:7.128.x 把它当一个匹配不到任何工具的名字、名单里其余条目照放;引擎 7.51.2 读无戳行时(文法 0)仍把带 `*` 的 allow 名单**整张**读成作废(引擎的已知限制,已向引擎请托修正),
38
+ 这遮住了同一名单里其余条目在旧文法下各算什么 —— 所以改名那一拍对这类行**不改写、不盖戳**,只各打一条 warn `session_rule_row_old_grammar_entries`(`wildcardMasked: true`,点名键与会话)。升级后这类会话每个调用都拒(fail-closed 方向),
39
+ 要恢复:运维(operator)去掉 `*` 重写那一行(非 operator 删 `*` 是放宽,写口拒;重写前复核同一名单里 `Bash(rm:*)` 这类条目 —— 重写后它们成为新文法的放行规则);引擎修正后这些留在原文法里的行自动按 7.128.x 的意思读。deny 名单里的 `*` 只自己作废、其余条目照拒。有被删条目的行改名那一次起服各打一条 warn
40
+ `session_rule_row_old_grammar_entries`(`policyKey` / `sessionId` / `toolAllowDropped`;之后起服不再报;改名那次起服中途失败的,见下面的收据一条)。被删的条目若本意就是新文法的规则,
41
+ 升级后用 `PUT` 按新文法写回那一行。带退役命令桶的行同样过这一步(桶本身仍原样、读回仍 `corrupt`;其余条目照判、照盖戳)—— 人按上一条出路删掉桶的那一刻,行已是当前文法的真话。
42
+ 终名 `rule_entries` 只在每一行都改写完之后出现:列先从 `rules` 改成进行中的名 `rule_entries_old_grammar`,改写在它下面跑,改完再改成终名。改写没走完(出错或进程被杀)⇒ 列停在
43
+ 进行中的名、这次起服拒;下一次起服从它续改(已带戳的行零动作),改完才出终名 —— 没有一拍会把没改的行留在终名之下。
44
+ **收据随改写落,不随成功落**:一行有被删条目的改写提交的那一刻它的收据(键与被删的条目)就记下;这次起服之后的任何一步失败 —— 改写半途、终名改名、改名后的扫描、后续迁移 ——
45
+ 拒因都带着已记下的收据(结构化在错误对象上,句子里也有一份),起服日志在抛之前先打出来:一行 `one_time_migrations_interrupted` 加每个有被删条目的行一条
46
+ `session_rule_row_old_grammar_entries`(只盖戳的改写不记:戳不丢信息,重来再判零动作)。
47
+ **起服日志里 M17 收据只在改写发生的那一拍出现,保存它**:改写过的行下一次起服再判零命中(行已是新形),不会再出现 —— 两次起服的收据并起来才是全部。
48
+ 进程恰在改写与那一行日志之间被杀、或一条改写的确认在连接上丢了,没有别处可查被删的条目(收据没有耐久载体);有此顾虑的部署升级前备份 `session_policy`。
49
+ - **写口上的文法戳**:`PUT` / 同步导入的规则值若带着 `rev` 却没有 `grammar`(回传了 7.128.x 读回的行再加条目、或导入一份无戳的导出),引擎按存量行的文法读它 ——
50
+ 括号形 allow 条目拒 `400 rules.inert_entry`(`reason: "earlier_grammar"`,理由词闭集的新成员),出路是只写三只桶(不带 `rev`;写口盖当前文法的戳)或去掉那条条目;
51
+ `grammar` 坏 / 比引擎新 ⇒ `400 rules.invalid_shape`(`field: "grammar"`)。本服务没有发布过把无戳行写进 `rule_entries` 列的版本(7.129.0 是第一个引擎 7.51.x 版),
52
+ 所以不存在「升级前有意写下的括号形 allow 行被收紧」一说;若部署曾手工把 7.51.0 / 7.51.1 引擎的行写进库 —— 不是本服务的任何版本写的 —— 这类无戳行按文法 0 读、括号形 allow
53
+ 条目不放行、起服每层一次通告,运维按通告重写。**回滚**:带戳的行回到 ≤7.128.x 本来就读不到(列名不同,见下面「回滚」);列名改回之后,`grammar` 对 ≤7.128.x 是一个它不读的多余键,不另加回滚步骤。
54
+ - **升级序**:7.128.x 及以前的副本读不出文法戳、按整串工具名读规则 —— 它把 `Bash(rm:*)` 读成一个不存在的工具名、那条命令拒在它上面**不生效**。列改名就是为了让
55
+ 旧副本读不到新文法的行:第一只 7.129.0 副本起服之后,还在跑的 ≤7.128.x 副本的规则读写在库里找不到列、响亮失败(它上面的会话任务起不来、规则面 500;在跑的腿
56
+ 留着已执行的规则快照,不放宽),≤7.128.x 二进制对着这只库起服即拒(`config.schema_mismatch`:缺 `rules`、多 `rule_entries`)。**先停掉全部 ≤7.128.x 副本,再起 7.129.0**
57
+ —— 这样没有失败窗;滚动升级的代价是上面那段失败,直到旧副本被换掉。
58
+ - **回滚**:≤7.128.x 不能对着迁过的库起服。确要回滚:先把列名改回(PG `ALTER TABLE session_policy RENAME COLUMN rule_entries TO rules`;MySQL 协议
59
+ `ALTER TABLE session_policy CHANGE COLUMN rule_entries rules JSON NOT NULL`;列停在进行中的名 `rule_entries_old_grammar` 时从那个名改回),并接受本版写下的
60
+ `Bash(…)` / `Edit(…)` 这类条目在旧版上读成不存在的工具名、**不生效**(改回旧桶写法,或在回滚前删掉这些会话规则)。
61
+ - **local 后端**:规则在引擎自己的文件店里,不改列名、不改写:带命令桶的规则文件读回 `corrupt`(该会话任务起不来),出路在引擎的拒句里(改文件,或删掉它 = 放弃那份规则)。
62
+ 回滚同样会让 `Bash(…)` 条目在旧版上不生效。引擎文件店**没有**上面那一步改写,也不需要:引擎 7.51.2 按行自带的文法读,7.128.x 写下的(无戳)文件按文法 0 读 —— `toolAllow` 里 `Bash(rm:*)` 这类条目照旧什么都不放行(**不放宽**),
63
+ 引擎起服每层通告一次;要让它们成为新文法的规则,升级后按通告重写那份规则(写口盖戳)。`toolDeny` 里的同类条目升级后开始拒(收紧)。无需升级前删条目。
64
+ - **停驻的 run**:checkpoint 文档里的祖先会话规则快照(`state.inheritedGate.ancestorRules`)由**引擎**在停驻时写下,本服务不铸、不改写 —— 它是引擎行摘要覆盖的成员,
65
+ 改它续跑就被拒 `requirements_damaged`,所以上面两条的剔除**不及**停驻行。快照若带命令桶,续跑时引擎对每个调用都拒;快照同样是无戳行,引擎续跑时按文法 0 读 ——
66
+ `toolAllow` 里旧文法写下的 `Bash(rm:*)` 这类条目照旧什么都不放行(**不放宽**;`toolDeny` 的同类开始拒)。只有带命令桶的快照仍是每个调用都拒,那一种的出路才是升级前决掉或取消。
67
+ 3. **卡词 / 类词换(BREAKING,消费方)**:`ruleOffersAbsence` 取 `mandated` / `allow_outranked` / `ask_rule_matched` / `lane_cannot_speak`(`shadowed` 退役);`askClass` / `classes` 不再出现
68
+ `governance_unavailable`(并入 `rule_unreadable`)。消费方按新闭集重排分支,认不出的词按缺席处置(本服务读活卡帧的读法同)。
69
+ - **存量卡与停驻行(一次性迁移 M18,零动作;停驻行不改写)**:7.128.x 及以前落库、带 `shadowed` 或 `governance_unavailable` 的审批卡(`approval_ask.card_json`,本服务自己写的)
70
+ 起服时改写一次(SQL 两后端;`shadowed` 删键,`governance_unavailable` 换成 `rule_unreadable`、类集去重),起服日志行 `one_time_migrations_applied` 的
71
+ `retiredCardWordAskRowsRewritten` / `retiredCardWordRowsUnreadable` 是读数(后者非零 = 有卡行的 JSON 读不懂,那些行照旧按坏行处置)。
72
+ **待决停驻行(`checkpoint` 文档)是引擎写的,本服务一个字节不改**:引擎的行摘要覆盖停驻行上的类对 `askClass` / `classes`,改写它们会让续跑被拒(`requirements_damaged`);引擎续跑也不按今天的闭集
73
+ 重判它们,带着旧词的停驻行照常续跑。本服务读停驻行(运维队列行、耐久 `/decide` 的 remember 判据)时按同一只改写读 —— SQL 后端与 local 车道引擎 File 店的所有停驻行同此 ——
74
+ 每读到一次计一次 `server.approval-card.retired-card-word-read`(载体 `parked row`):那些行被决掉 / 收割之前,同一行每读一次都计,计数涨不代表有新行。
75
+ 起服改写够不着的卡(滚动升级窗里 7.128.x 副本新写的、local 车道的文件 ask 账)读时同样按这一只改写读,载体 `card_json`(SQL 后端上若还有 ≤7.128.x 副本在跑,
76
+ 它们新写的旧词卡也在这里计数;先停旧副本再起新即可避开)。窗(本版发布日 + 30 天,登记在 `src/plugins/one-time-migrations.ts`)关后读侧改写与起服改写一起删,
77
+ 之后仍带旧词的卡整卡读不出、仍带旧词的停驻行按坏行处置(运维队列行缺那一位、remember 按强制 = 只放行这一次),停驻行的旧词对引擎续跑始终无害。
78
+ 跳版升级越过删码那一版之前,先在带本条的版本上起服一次(管存卡)。
79
+ - **回滚**:M18 改写出来的卡形(删键 / `rule_unreadable`)7.128.x 读得懂;读不懂的是本版之后新铸的词 `allow_outranked` / `ask_rule_matched` —— 带它们的卡回滚后
80
+ 整卡读不出(`.strict()` 读面:断连重放跳过、幂等重入走回落),带它们的停驻行在 7.128.x 上读成「按强制」(只放行这一次)。回滚前先把待批卡处置完。
81
+ 停驻行没被本版改写过,回滚后仍是 7.128.x 引擎自己写下的字节。
82
+ - **「本会话放行」读引擎的绑定词**:卡(活卡 / 停驻行 / 运维队列行)的可记性只读引擎 7.51.0 在卡上盖的 `boundGrantDigest` / `boundGrantAbsence`(新上活卡帧、inbox 行、
83
+ 队列行三面,`card_json` 不带)。**升级前停驻**的卡两者都不说 ⇒ `remember:"session"` 只放行这一次(回执 `rememberApplied:false`,队列行 `rememberRefused:true`)——
84
+ 答一次即可;之后停驻的卡照常可记。
85
+ - **门记录回放(读面)**:`tool_end.gate` 按引擎的档案读法回放 —— 上一代引擎写下的词原样到达(`governance_unavailable`、放行理由 `rule_unreadable`、出身 `unresolvable`),
86
+ 任何一代引擎都没铸过的词整条不上帧。消费方对门记录里认不出的词按通用渲染,不要按今天的词表丢整条。
87
+ 4. **`routePairing` 去掉 `ok:unpinned`(BREAKING,读面)**:没声明根的部署凭据配对现在读 `broken:credential_mismatch`。本服务的部署凭据只随显式网关根装配,常规部署零动作。
88
+ 5. **工具名单里没人答应的名照跑(行为变化)**:`allowTools` / `excludeTools` / 子代 `denyTools` / `allowTools` 里没有任何工具答应的名字不再拒 run(7.126.0–7.128.x 拒
89
+ `config.tool_face_control` / `config.agent.unknown_tool`);条目原样保留,运维行 `runner_config_advisory` 告知一次并带该写的拼法。两码只剩任何工具名都不可能有的拼法。
90
+ 6. **媒体块准入(零动作)**:引擎 7.51.0 起 MCP 服务器与宿主自定义工具交回的媒体块同样过引擎准入;随包 USAGE 里「这些块不验(引擎下一 minor 收)」一句由此作废,已改写。
91
+ 被拒的块在 `tool_end.output` 上是一段文字占位;运维行 `tool_result.media_refused`。
92
+ 7. **缺省部署自动整理恢复(零动作)**:7.127.2 节第 3 条披露的「缺省部署上每条整理都被引擎拒」由引擎修好,自动腿真起跑。
93
+ 8. **模型端点回重定向 ⇒ 不跟(行为变化)**:引擎的模型请求、hook 直连腿(会话标题器等)与记忆 embedder 都不再跟随重定向,端点回 3xx ⇒ 该次调用失败
94
+ (`endpoint answered with a redirect (HTTP <n>); it was not followed …`)。升级前把会重定向的根(`http` → `https`、搬过家的网关、尾斜杠)
95
+ 换成最终端点:`MODEL_GATEWAY_BASEURL` / `MODEL_GATEWAY_FALLBACK_URLS` / `ANTHROPIC_BASE_URL` / 目录条目的 `baseUrl` / `MEMORY_EMBEDDER_ENDPOINT`。
96
+ 9. **网关 key 换值 = 重启(零动作)**:`MODEL_API_KEY` 在启动时读一次(以前同样如此,只是装配源码写成了「每次请求现读」);目录条目自己的凭据照旧热应用(两处例外 —— 降级壳目标、记忆整理席 —— 见 USAGE 模型路由段)。
97
+ 10. **`createPerModelKeyBrain` 公面导出删除(BREAKING,嵌入方)**:把 per-model 凭据交给引擎的座位 —— 摘要器 `createWebFetchSummarizer(brain, model, { getApiKeyAndHeaders })`、
98
+ 任务 `TaskSpec.getApiKeyAndHeaders`、side-query 用 `createSideQueryLane`;不要再在 brain 外面包一层注入 `options.apiKey`。
99
+ 11. **手动整理阀门的写判官是引擎那一只,债计数改名(零动作;告警标签要改)**:阀门的写判官座由本服务代答「无异议」改为引擎导出的同一只构造器;7.127.0 节第 4 条写的 fail-open 计数
100
+ `server.memory-consolidation.operator-valve-unjudged` 退役,换成 `server.memory-consolidation.operator-valve-layers-unfed`(含义收窄:判官已被问,但人的规则 / 部署策略 / 项目根三层没喂)——
101
+ 你的告警若按旧标签配,改成新标签。手动阀门的放行范围与 7.128.x 相同:人的持久路径规则、`APPROVAL_DENY` 之类的部署名单都拦不住它;这些只约束自动整理腿。
102
+ 12. **中心下发的读拒规则下一拍热生效(行为修复,零动作)**:`readFace` 域的 `denyPatterns` / `denyBuiltinTiers` / `denyBuiltinExclude` 发布与撤回在下一拍 refresh 当场落配并换进全部 boot Runner
103
+ (下一个任务按新规则判;在飞的任务保留它准备时的那一份)。`/health` 不再为这三席签 `read-face` 重启理由 —— 按该理由滚动 fleet 的编排器对 deny 成员不会再收到它,因为变更已经生效;本机 env 写过的键仍恒赢。
104
+ 13. **续跑腿失败后卡回到人手里(行为变化,读面)**:一条没停驻的失败续跑腿之后,卡是否回到待决只按店里那一行判(此前按三个失败码判)。plan_review / 抢占续跑 / wake 的续跑腿
105
+ 任何失败都重开(引擎只对工具审批记「批过的那一步开跑了」):`GET /v1/runs/:id` 读回 `needs_review` / `suspended` 而不是 `failed`,卡可再决;被取消的续跑腿按取消结算(卡过期、行终局)。
106
+ 按「失败码在三码名单里」判重开的消费方改读 park 事件的 `reopened` 键或 `GET /v1/approvals` 列不列。
107
+ 14. **随引擎 7.51.0 落到部署上的收窄(升级前逐条看,本服务不改它们的语义)**:
108
+ - `auto` 模式下读不出目标的删除(`rm -rf $UNSET/` 这类)不再交分类器放行,改为出审批卡,没有审批人则拒 —— 依赖分类器放行这类删除的 `auto` 部署:把目标写成字面,或答卡。
109
+ - MCP 条目的静态头与 `principalHeader` 同名(不分大小写)或静态头同名多拼写 ⇒ 该 MCP 服务器在连接时被拒(`config.protocol_header_collision`,不挂)—— 升级前从条目里删掉那只静态头;
110
+ 配了中心的部署先用控制台的撞名普查 / 预检脚本过一遍。
111
+ - A2A 对端卡片点名的 RPC 源不在声明里 ⇒ 带头 / 带主体的请求被拒(`config.protocol_endpoint_unbound`),且该对端的请求不再跟重定向 —— 把 RPC 源声明为 `url`、卡片位置为 `cardUrl`。
112
+ - 续跑一条停驻的 shell 调用时,读站若仍答不出(沉默)⇒ 拒,句子含 `This approval is spent` —— 重新提交该调用。
113
+ - 会话 `toolAllow` 是收窄姿态:一行命令的**每一段**都要被某条目点名(`cat x` 与 `git status && cat x` 同拒,句含 `no entry covers "cat x"`)。
114
+ - `config.permission_mode_unavailable` 的 `detail.requested` 读作「被折的那个词」(子腿什么都没请求时 = 它继承的词),子腿任何一臂的折叠都另铸一份到父会话的流 —— 按 `childSessionId` 去重,不要按「每子一行」计数。
115
+ 15. **本服务自己的凭据在任何 `inheritEnv` 词下不进模型驱动的 shell(安全,行为修复;零 DDL、零旋钮)**:起服把配置目录全部 `type:"secret"` 名 + 名单型旋钮名向引擎
116
+ 声明一次为自有凭据;`inheritEnv:"all"`(单用户 host 上 cli / desktop 交互 lane 每次提交发它)= 整份用户环境**减**它们 —— 用户自己的 `GH_TOKEN` / `NPM_TOKEN` 照进,
117
+ 本服务的 `SERVICE_AUTH_TOKEN(S)` / `APPROVAL_HMAC_KEYS` / `MODEL_API_KEY` 从此不进(此前逐字进:模型一条 `curl` 可自批自己的审批卡);`"scrub"` 读数不变。
118
+ 名单还含生效配置里每条模型条目的 `apiKeyEnv`(本服务替该模型从自己环境取钥匙的变量名):中心热应用新增 / 改名条目时在模型目录换代前重声明(名单只随落地的世代
119
+ 增长 —— 改名后旧名照扣到重启;没落地的候选不留痕);条目的 `apiKeyEnv` 若是引擎扣不住的名(含空白 / `PATH` 这类运行时回填名)⇒ 该候选整拒、不半落(起服落 LKG /
120
+ env,刷新拍下一拍重判;`run-local` 一个面都不采,拒行 `config_apply_refused`)。
121
+ - **受伤方**:靠 `"all"` 让模型 shell 拿到本服务凭据的流程。出路:把那枚令牌另放一个不在配置目录里、也不是任何模型条目 `apiKeyEnv` 的名(`SERVICE_AUTH_TOKENS` 表里另加一条、值同时放在 `MY_SVC_TOKEN`),
122
+ `"all"` 下照给;或显式授予(请求体 `settings.env`)。
123
+ - **嵌入方**:`RemoteHostExecutionEnv` 的 `inheritEnv` 选项型改为引擎的 `InheritEnvPolicy`;白名单点到本服务自有凭据名 ⇒ **构造时**抛(拒句带名),spawn 时再拒;
124
+ `inheritProcessEnv` 整键删(改写 `inheritEnv:"all"` / `[]`;在场即构造拒、拒句指路 —— 不读它,`false` 不会静默退回缺省 scrub)。
125
+ - **能力位**:`GET /v1/capabilities` 顶层新键 `inherit_env.own_credentials_withheld: true`(引擎能力词原样;值 = 最近一次成功声明的名单 ⊇ 当前生效配置应声明的集合)。壳按它闸发不发 `"all"`;老 worker 上这一键缺席 ⇒ 别发 `"all"`。
126
+ - **告警标签**:`secret_env_scrubbed_total{kind}` 的 `config-catalog` / `name-list-knob` 两词退役,读数进引擎的 `own-credential` 桶 —— 按旧标签写的规则改标签。
127
+
10
128
  ## 7.128.0 —— 本地 `config.d/skills.json` 生效 · 部署侧 MCP `toolAxes` 进引擎(坏形整条不挂)· 宿主 shell 两行运维行 · 7.127.2 第 1 条两句订正(升级零动作)
11
129
 
12
130
  1. **本地部署的 `config.d/skills.json` 开始生效(行为修复,零 DDL、无配置变化)**:`CONFIG_PROVIDER=local` 的部署此前只装出厂技能,`config.d/skills.json` 的条目从不进名册(零提示;
package/USAGE.md CHANGED
@@ -108,6 +108,14 @@ ANTHROPIC_API_KEY=sk-ant-… # 可选:ANTHROPIC_BASE_URL / ANTHROPIC_VERSION /
108
108
  只设 `MODEL_GATEWAY_FALLBACK_URLS` 不设主网关同拒(`config.gateway_fallback_without_primary`)。只走 Anthropic 路由的部署
109
109
  不设网关合法;此时 `MODEL_API_KEY`(网关的凭据)不装载、不发往任何路由(启动通知 `model_gateway_key_unpaired`)。
110
110
  中心 / config.d 下发的候选目录里有解析不到的条目 ⇒ 候选的模型面这一组被拒、上一代模型面继续服务(同候选其余组照落)。
111
+ - **凭据什么时候生效**:网关 key `MODEL_API_KEY`(以及 `ANTHROPIC_API_KEY` / `ANTHROPIC_AUTH_TOKEN`)在启动时读一次,**换值 = 重启本服务**
112
+ —— 没有热应用通道(以前的版本同样如此:这几把 key 只来自启动时的环境变量;引擎 7.51.0 起模型路由的构造配置也只读一次)。
113
+ 目录条目**自己的**凭据(`apiKeyEnv` 点名的变量、`sealedApiKey`)跟着中心 / config.d 的模型面热应用走,不用重启:新起的任务与调用
114
+ 用新的一代(在跑的任务用它起跑时那一代)。两处例外:降级壳焊住的目标连凭据身份一起按 `degrade-route` 重启片走;**记忆整理席**的模型在启动时定
115
+ (重启生效),它的凭据却跟着热应用 —— 同名条目把端点和凭据一起换掉时,重启之前整理请求会带**新**凭据打**旧**端点,这种换法之后请重启本服务。
116
+ - **模型端点回重定向 ⇒ 不跟、该次调用响亮失败**(引擎 7.51.0 起的模型请求;hook 直连腿 —— 会话标题器等 —— 与记忆 embedder 同一条):请求只去被判过的那个根,
117
+ 句子 `endpoint answered with a redirect (HTTP <n>); it was not followed and no credential was sent anywhere else — …`。出路:把最终端点写进 base URL
118
+ (`http` 根被重定向到 `https`、网关搬家、尾斜杠重定向都是这一形)。
111
119
  - 🔴 **目录里的 anthropic 条目要本机真有 anthropic 路由**(7.101.0 起):`provider:"anthropic"` 的目录条目只在本服务配了
112
120
  `ANTHROPIC_API_KEY` / `ANTHROPIC_AUTH_TOKEN` 时可用;没配 ⇒ 该条目**不进可选目录**(`GET /v1/models` 不列、不当缺省,缺省顺延到下一条
113
121
  可用条目),启动 / 刷新日志一行 `web_admin_model_no_brain`(带码 `config.model_no_brain` 与出路),显式点它(`body.model` /
@@ -414,7 +422,7 @@ MODEL_CODE_ROLES=default,subagent # 不设=全中立;仅这些角色在「
414
422
 
415
423
  | env | 缺省 | 说明 |
416
424
  |---|---|---|
417
- | `MEMORY_EMBEDDER_ENDPOINT` | 缺省 | OpenAI 兼容服务的**基址**(`https://host/v1`)或**完整**端点(`https://host/v1/embeddings`),两种写法都收(query 原样保留,Azure 形可用)。必须是 http(s) 合法 URL 且**不得带 `user:pass@` 凭据**(会被传输层错误/日志/记忆冲突文案反复复制;要 auth 走下面的 API_KEY),否则拒启 |
425
+ | `MEMORY_EMBEDDER_ENDPOINT` | 缺省 | OpenAI 兼容服务的**基址**(`https://host/v1`)或**完整**端点(`https://host/v1/embeddings`),两种写法都收(query 原样保留,Azure 形可用)。必须是 http(s) 合法 URL 且**不得带 `user:pass@` 凭据**(会被传输层错误/日志/记忆冲突文案反复复制;要 auth 走下面的 API_KEY),否则拒启。端点回重定向 ⇒ 这次 embedding 失败、不跟(请求体与 `Authorization` 只去这里声明的端点;写最终端点) |
418
426
  | `MEMORY_EMBEDDER_MODEL` | 缺省 | 模型名,原样进请求体 `{model, input}` |
419
427
  | `MEMORY_EMBEDDER_DIM` | 缺省 | 声明维度(**正整数**,如 `1024`)。必须与模型真实维度一致 |
420
428
  | `MEMORY_EMBEDDER_TIMEOUT_MS` | `30000`(`[1000, 300000]`) | 单次 embed 的活性上限。挂死的端点会把整条记忆写入路径吊住 |
@@ -1109,11 +1117,13 @@ READ_DENY_PATTERNS='[{"pattern":"**/secrets/**"},{"pattern":".ssh","caseSensitiv
1109
1117
  ① **本机 env 显式恒赢**(逐键判)——env 是这台机器的部署主权,下发值不会静默盖掉它,被忽略的键留一条
1110
1118
  `web_admin_read_face_env_wins` warn;② **坏值整域拒**——任何一个成员过不了引擎自己的校验器就整域不落、
1111
1119
  保留 env 前值 + `web_admin_read_face_invalid` warn(半应用一份 READ 姿态比不应用更危险);
1112
- ③ **一席热、三席重启**(server 7.69.0 起,引擎侧交付了 read-face 的活体座席):`face` 是**热换席** ——
1113
- 下发或撤回一个档位,下一拍 refresh(≤60s,或手动 `POST /v1/admin/config/refresh`)当场换进全部 Runner,
1114
- **不重启、也不再签 `read-face` 重启理由**(已在飞的任务保留它准备时的档位,新任务读新档);三个 deny 键
1115
- (`denyPatterns`/`denyBuiltinTiers`/`denyBuiltinExclude`)**仍是 boot-baked**(core 的可换席闭集只收了
1116
- `readFace` 一席),下发改动仍经 `/health` 的 `restart.reasons` 带 `read-face` 通告、由编排器重启兑现。
1120
+ ③ **四席全热**(`face` 自 server 7.69.0 起;三个 deny 键自 core 7.51.0 起 —— 引擎把读拒三席也交付为活体座席):
1121
+ 下发或撤回 `face` / `denyPatterns` / `denyBuiltinTiers` / `denyBuiltinExclude` 任一成员,下一拍 refresh
1122
+ (≤60s,或手动 `POST /v1/admin/config/refresh`)当场落进配置并换进全部 Runner,**不重启、也不签 `read-face` 重启理由**
1123
+ (已在飞的任务保留它准备时的读拒判,新任务读新规则;**撤回**一个成员 = 该席回到本机 env / 引擎缺省的姿态,不会停在上一份发布上)。
1124
+ 哪几席是热的由引擎逐席答(`SWAPPABLE_DEP_SEATS`),落配、换席与重启理由读同一个答案;引擎没有活体座的席才会走
1125
+ `/health` 的 `restart.reasons` 带 `read-face` 通告、由编排器重启兑现。每次换席引擎在各 boot Runner 上各留一条 operator 通告
1126
+ `config.read_deny_swapped`,经运维日志 `engine_notice` 行落地(不上会话流);本服务另留 `read_face_swapped` 行(只报换了哪几席)。
1117
1127
  当前生效档可从 `GET /v1/capabilities` 的 `readFace` 位读
1118
1128
  (`"open"`/`"roots"`/`null`=本部署未钉,引擎默认当家;deny 表内容属策略内容,**不上**能力面);
1119
1129
  **来源**(env / center / posture / engine-default)在 operator 面 `GET /v1/diagnostics/wiring` 的 `readFace.source`。
@@ -1457,14 +1467,18 @@ invalid_using_default` 警告 + 退回缺省值」这条臂随词表放宽一并
1457
1467
 
1458
1468
  **🔴 模型可驱动的 host shell 没有点名放回旋钮(刻意,7.104.0 写明)**:凭据形变量不交给模型能驱动的 shell —— 这条腿恒走缺省剥除
1459
1469
  (本服务不给它传点名名单),部署配置里没有任何键能改它的继承模式;`inheritEnv:[names]` 是**嵌入方**用本包构造执行环境时的选项
1460
- (下一段),不是 sema-server 部署方的旋钮(引擎 CHANGELOG 里把它说成部署方出路的那一句,对 sema-server 部署不成立)。被剥变量的
1470
+ (下一段),不是 sema-server 部署方的旋钮(引擎 CHANGELOG 里把它说成部署方出路的那一句,对 sema-server 部署不成立)。7.129.0 起这条腿的三个继承词只在引擎
1471
+ 一处判(`inheritedHostEnv`),本服务自己的凭据名起服向引擎声明一次、任何词都不交(下面「请求级 `inheritEnv`」段)。被剥变量的
1461
1472
  出路**按用途分**:git 凭据走 git 配置文件 / credential helper(隔离全局配置的两条 git 腿 —— project-memory 读仓、center-plugins 克隆 ——
1462
1473
  另见 `docs/DEPLOY-PREREQS.md` 7.103.2 那一节的「按腿分」);带口令的出网代理走不带口令的形(`~/.gitconfig` 里只带用户名的 `http.proxy` + credential helper,见
1463
1474
  `docs/DEPLOY-PREREQS.md`「带口令的出网代理」);语言服务器 / bake-runner 走各自的名单 `LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW`;沙箱车道走
1464
1475
  `E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV` / `K8S_SANDBOX_ENV`。要不要给 host shell 加部署级名单属于能力面变更(安全轴),不在文档里顺手给。
1465
1476
 
1466
1477
  **请求级 `inheritEnv`(7.106.0 起)—— 调用方声明,不是部署旋钮**:提交体顶层键 `inheritEnv:"all"` 让**这一条请求**的模型驱动 host shell
1467
- 继承整份用户环境(CC 交互形,凭据形变量在内);`"scrub"` / 缺席 = 上面的缺省剥除。🔴 **`"all"` 只在单用户部署兑现**:`REQUIRE_PRINCIPAL=true`
1478
+ 继承整份用户环境(CC 交互形,用户自己的凭据形变量在内)—— 🔴 **但不含本服务自己的凭据**(7.129.0 起,引擎 7.51.1):配置目录全部 `type:"secret"` 名
1479
+ (`SERVICE_AUTH_TOKEN(S)` / `APPROVAL_HMAC_KEYS` / `MODEL_API_KEY` …)与名单型旋钮名起服已向引擎声明为本进程自有凭据,生效配置里每条模型条目的 `apiKeyEnv` 也在名单里
1480
+ (中心热应用新增 / 改名条目时在模型目录换代前重声明,名单只随落地的世代增长 —— 改名后旧名照扣到重启,没落地的候选不留痕;条目的 `apiKeyEnv` 是引擎扣不住的名 ⇒ 该候选整拒),`"all"` = 整份环境**减**它们,任何继承词都不把它们
1481
+ 交给模型 shell(能力位 `inherit_env.own_credentials_withheld` = 最近一次声明 ⊇ 当前生效配置应声明的集合;真要让模型 shell 持某枚令牌 ⇒ 另放一个不在配置目录、也不是任何模型条目 `apiKeyEnv` 的名,或经 `settings.env` 显式授予);`"scrub"` / 缺席 = 上面的缺省剥除。🔴 **`"all"` 只在单用户部署兑现**:`REQUIRE_PRINCIPAL=true`
1468
1482
  ⇒ 422 `request.host_posture_refused`(射程与写容纳面那条腿同一只谓词,`TASK_WRITE_FACE_ESCALATION` 不管它);非 host 车道带键 ⇒ 400。部署方
1469
1483
  **没有**旋钮能替调用方打开它,也没有旋钮能在单用户部署上关掉它(单用户部署的用户把他自己的环境交给他自己驱动的 shell);起服一行 info
1470
1484
  `task_inherit_env_honored {honored}` 告诉你这台机器认不认。上面「部署级名单不给模型驱动 shell」那条不变 —— 两件事是两条轴(部署名单 vs 调用方 × 单用户射程)。
@@ -1489,7 +1503,8 @@ bake-runner 要用 ⇒ `LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW` 点名放回。
1489
1503
  `E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV` / `K8S_SANDBOX_ENV`)另走同一只名单解析:形错拒启点名第几段、不回显原值,点名任一只名单型旋钮拒启,
1490
1504
  本进程环境里没有的名按序号一行 `config_env_entry_rejected`,旋钮原文按凭据类对待(配置目录只回在场位、不进任何子进程)。
1491
1505
  几处只在**没点到的键**上不同:语言服务器在缺省剥除的结果之上放回点名键(没点到的照剥,非密钥键照常在);host shell 的
1492
- `inheritEnv:[names]` 与三只沙箱名单是 hermetic 的(没点到的一律不交,与引擎 `NodeExecutionEnv` 同名选项同义)。
1506
+ `inheritEnv:[names]` 与三只沙箱名单是 hermetic 的(没点到的一律不交,与引擎 `NodeExecutionEnv` 同名选项同义;7.129.0 起 host shell 的名单由引擎读 ——
1507
+ 点到本服务自有凭据名 ⇒ 执行环境构造时即拒,拒句带名)。
1493
1508
  两处都不向对方看齐:host shell 改成「剥后放回」等于在模型可驱动的 shell 上放宽,语言服务器改成 hermetic 会让它当场找不到自己的工具链。
1494
1509
 
1495
1510
  **沙箱带外 env 名单(`E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV` / `K8S_SANDBOX_ENV`,前两只 7.101.0 起、`K8S_SANDBOX_ENV` 7.126.0 起,同上一段的解析与谓词)**:沙箱里的工具要用本服务
@@ -1679,7 +1694,9 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
1679
1694
  > `GET /v1/runs/:id/events`(异步 run 的活流与重连面)上它同样先于子代的首个 `task_progress`。
1680
1695
  > - **`tool_end.output` 两形**:串,或块数组。块里可有图像 / 文档 `{ type, data, mimeType }` —— 本流上整块逐字(`data` 是字节编码,不过文本脱敏),
1681
1696
  > 一帧最大约为部署媒体预算的 4/3(缺省约 40 MiB),请按块 `data` 长界帧;读得慢的连接(本帧媒体 + 还没送出去的字节 > 64 MiB)上本帧媒体降成描述符。
1682
- > 媒体块 `data` 的内容由引擎准入负责:引擎本版只对自己产出的块验字节(读图像 / PDF、提交的图像、用户附件),MCP 服务器与宿主自定义工具交回的块不验(引擎下一 minor 收);本服务不在 wire 上对 `data` 做任何改写。
1697
+ > 媒体块 `data` 的内容由引擎准入负责:引擎 7.51.0 起工具结果席只有一只媒体准入 —— 引擎自己的工具、MCP 服务器、宿主自定义工具与 hook 替换交回的块一律过它
1698
+ > (不是 base64、媒体类型不在闭集、超上限、字节没有所称类型的签名 ⇒ 拒;被拒的块在结果里换成一段点名工具与拒因的文字占位,从不带字节;运维行记一条 `tool_result.media_refused`)。
1699
+ > 本服务零动作,不在 wire 上对 `data` 做任何改写。
1683
1700
  > 账本面不存媒体字节 —— 账本行与 `GET /v1/runs/:id/events`(断线重连面;对 `POST /v1/runs` 提交的异步 run 它**就是活流**,所以这类客户端拿不到整图,整图只在本流、子代 tail 流与会话存档 `GET /v1/sessions/:id` 上)
1684
1701
  > 里,带 `data` 的媒体块换成 `{ type, mimeType?, bytes, truncated: true }`(帧级 `truncated: true` 与 `totalChars` = 原值真实大小 —— 文本块字符数 + 媒体块 `data` 串长,引擎已截断的帧保留引擎给的值);无 `data` 且 `truncated: true` 的块渲染成「未送达」。
1685
1702
  > **同一条律也管 `tool_end.structured`**:Read 读图像 / PDF 的卡 `{ type: "image" | "document", file: { base64?, type, originalSize, … } }` 里 `file.base64` 是同一份字节的第二份编码(引擎只在 ≤48K 字符时随卡),
@@ -1825,9 +1842,15 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
1825
1842
  > 规则点名库目录 ⇒ 在任何占位与模型调用之前拒,运维行 `engine_notice` 一条 `memory.consolidation_refused`(`detail.refusalCode: "memory.consolidation_write_refused"`);
1826
1843
  > 只点名产物文件 ⇒ 应用时拒、什么都不写,一条 `memory.consolidation_incomplete`(`stop: "refused"`);用户任务不受影响。没有主体的 run 读不到任何人的持久规则(只剩部署策略那一层)。
1827
1844
  > ⚠️ **引擎 7.50.x 上这条腿在缺省部署上每一次都被拒**:只要部署的敏感路径守卫在场(`SENSITIVE_WRITE_PATTERNS` 未设即在场),引擎问写判官时递给部署策略的是一只已中止的信号,守卫判不了库目录的真实落点、按 `target_unresolved` 拒 —— 与规则、权限模式无关(`memory.consolidation_refused`,人读句只带拒因前 200 字)。修复随引擎提货;要整理用手动阀门,不要为此设 `SENSITIVE_WRITE_PATTERNS=off`(见 MIGRATION 7.127.2 第 3 条)。
1828
- > - **手动阀门**(`POST /v1/admin/memory/consolidation/run`)没有腿,拿不到引擎的那只判官;本服务不另造一只 ⇒ 它的写判官答「无异议」(与 7.126.0 该口行为逐字同),
1829
- > 每次被问记一次 fail-open 计数 `server.memory-consolidation.operator-valve-unjudged`(计数 + 探针行 + 一次性告警)。驱动的每一条通告 —— 非收敛停的 `memory.consolidation_incomplete`、
1830
- > 问判官**之前**推不出库家落点时同码拒的 `memory.consolidation_refused`(生产上可达,与判官答什么无关)—— 都经同一只 `engine_notice` 转发器进运维行;早问拒仍按路由的码臂答 `409 memory.consolidation_refused`。
1845
+ > - **手动阀门**(`POST /v1/admin/memory/consolidation/run`)没有腿,但它的写判官座与自动腿是**同一只**引擎判官构造器(此前由本服务代答「无异议」)。喂给它的部署事实是部署的合成规则店与
1846
+ > `~/` 行的 home(`REMOTE_EXEC_HOME_DIR`;未声明时 host / in-process 车道取本机 home、e2b 取 `/home/user`,k8s / ssh 等车道缺席)。**这两样在手动阀门上今天都读不到人的行**:
1847
+ > 阀门不带主体(没有来源能回答「这个库属于谁」),引擎读的是匿名地板、不读任何人的持久路径行 —— 所以「`~/` 收紧行无处锚定 ⇒ 拒闭」这条引擎规则在生产上走不到
1848
+ > (只有调用方自己带着主体去喂这只构造器时才走得到),与下一句的「三层没喂」是同一件事的两面。
1849
+ > **三层没喂,所以手动阀门今天仍不受它们约束**:人的持久路径规则(阀门没有来源能回答「这个库属于谁」)、部署的审批名单 / 自治档 / 敏感路径守卫(`APPROVAL_DENY=Write` 拦不住手动阀门)、
1850
+ > 项目根(相对行无处锚定)。这些只约束自动整理腿。每次被问记一次 fail-open 计数 `server.memory-consolidation.operator-valve-layers-unfed`(计数 + 探针行 + 一次性告警)—— 这是一笔显式登记的欠账,
1851
+ > 不是常态;读谁的规则、怎么喂部署策略待设计,落地后本句改写。
1852
+ > 驱动的每一条通告 —— 非收敛停的 `memory.consolidation_incomplete`、问判官**之前**推不出库家落点时同码拒的 `memory.consolidation_refused`(生产上可达,与判官答什么无关)——
1853
+ > 都经同一只 `engine_notice` 转发器进运维行;早问拒仍按路由的码臂答 `409 memory.consolidation_refused`。
1831
1854
  > - **怎么让整理写**:删掉或收窄点名记忆库路径的规则;只想禁止自动整理就设 `MEMORY_AUTO_CONSOLIDATION=off`。
1832
1855
  > - `MEMORY_CONSOLIDATION_SCOPES=user:local[,org:acme]`:阀门的 scope 表 = 这台 worker **声明**它会折叠哪些库。
1833
1856
  > **恰好一条**时它是两个口省略 `scope` 的唯一缺省;零条或多条时省略 `scope` 一律 400(在两个库之间替人挑
@@ -2041,7 +2064,7 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
2041
2064
  | HTTP `429` + `errorCode:"limit.cost_quota_exceeded"` | per-principal 累计成本配额越顶(`MAX_PRINCIPAL_COST_USD`;**进场门**,不打断在跑的 run) | 看 `Retry-After` / 体 `retryAfterSec` 退避(体 `windowResetsAt` = 当代窗滚过的时刻);窗滚过或调高上限后放行 |
2042
2065
  | HTTP `400` + `errorCode:"request.id_invalid"` | 调用方给的 id 形不对。**会话 id 的形门只有一只** = 任意版本 canonical uuid(壳预设的 v4 也过门):会话口 `:id`(fork / 抹除 / 会话策略 / 2c 同步)与提交口(`POST /v1/tasks` / `/v1/tasks/stream` / `/v1/runs` 体里的 `sessionId`,A2A 的 `contextId` 同)同判,提交口的句子在句首之后点名「改用 uuid、自己记映射」;`GET /v1/sendfile-links?before=` 游标与 `body.taskId` 幂等键要求 uuidv7(时间序 / 幂等键契约) | 按 `errorCode` 分支;句子各自点名要求的形(`must be a canonical uuid` / `must be a uuidv7 …`),只作展示、别锚文案 |
2043
2066
  | HTTP `400` + `errorCode:"request.field_invalid"`(`PATCH /v1/sessions/:id` / 2c 导入的 `title`) | 标题不合形:1–120 个 Unicode 字符、**首尾不得有空白**(服务端不代为 trim,先于 C0 判)、不得含 C0 控制字符;`title: null` 清回自动标题;空串是 `request.body_shape` | 客户端送出前自行 trim;按 `errorCode` 分支,句子只作展示 |
2044
- | HTTP `400` + `errorCode:"config.tool_face_control"`(7.126.0 起;流式 / 后台腿是 `failed` 终局带同码) | 请求的 `allowTools` / `excludeTools` 里有一个这台部署上**没有任何工具答应**的名字(或 `allowTools` 写了 `mcp__*`、`excludeTools` 写了 `*`);引擎开跑前拒:主模型零调用、标题器零调用(7.127.0 起;7.126.x 上标题器仍会为这次提交调一次模型)。这次提交留下的会话无标题、零消息 | 只列这台部署挂得出的工具名;句子点名那一条与它在哪张表里 |
2067
+ | HTTP `400` + `errorCode:"config.tool_face_control"`(7.126.0 起;流式 / 后台腿是 `failed` 终局带同码) | 请求的 `allowTools` / `excludeTools` 里有一个**任何工具名都不可能有**的拼法(空白、括号不配、空名)(或 `allowTools` 写了 `mcp__*`、`excludeTools` 写了 `*`);引擎开跑前拒。7.129.0 起(引擎 7.51.0)这台部署上**没人答应**的名字不再拒:照跑,条目原样留在表里(今天什么都不准 / 不拒,之后挂上同名工具即生效),运维行 `runner_config_advisory` 告知一次。拒时:主模型零调用、标题器零调用(7.127.0 起;7.126.x 上标题器仍会为这次提交调一次模型)。这次提交留下的会话无标题、零消息 | 只列这台部署挂得出的工具名;句子点名那一条与它在哪张表里 |
2045
2068
  | HTTP `429` + `errorCode:"usage.window_exhausted"` | 部署级治理窗耗尽(`USAGE_WINDOWS`,token 或 $ 天花板先满者) | 看响应体 `retryAfterSec` 退避;窗滑动/桶到期后放行 |
2046
2069
  | `terminal:{kind:"failed", code:"usage.window_exhausted"}` | 已受理的 run 在 **turn 边界**撞上治理窗且无法 durable 挂起 | 同上退避后重投;配好 checkpoint 基建则改为 `suspended` 等窗自动续跑 |
2047
2070
  | HTTP `501`(`/v1/runs`) | 内存模式不支持异步 | 配持久存储(`DB_BACKEND=mysql` / `pg` + 坐标;7.106.0 起给了坐标必须点名 `DB_BACKEND`) |
@@ -2064,8 +2087,9 @@ tag 词表与每条的最坏后果见 `docs/FAIL-OPEN-CENSUS.md`。引擎装配
2064
2087
  按 `code` / `classification` / `level` 打标;其中 `shell-gate-off` 在本 session 显式声明 `permissionMode:
2065
2088
  "bypassPermissions"` 时是调用方要的姿态,降为 **info** 并标 `expected`——只有没表态落到引擎默认 off 的无壳直连
2066
2089
  形才 warn。子代 `agents[].denyTools` / `allowTools` 的**名字**归引擎开跑前一只判(7.126.0 起与根 run 的 `allowTools` / `excludeTools` 是同一只工具选择器):
2067
- 没有任何工具答应的名字(无人挂的名、退役拼法 / 大小写孪生 `bash` / `BASH`)**开跑前拒**(`config.agent.unknown_tool`;委派工具声明了逐次注入工具的名册,引擎列得出
2068
- 子代工具池),名字按工具名原拼写 `Bash`。这两张表管子代可挂载的**全部**工具 —— 委派池、内建 hands(`Read` / `Edit` / `Write` / `NotebookEdit` / `Bash` / `Grep` /
2090
+ 7.129.0 起(引擎 7.51.0)没有任何工具答应的名字(无人挂的名、退役拼法 / 大小写孪生 `bash` / `BASH`)**不再拒跑**:条目原样保留(今天什么都不拒 / 不准,之后挂上同名工具即生效),
2091
+ 运维行 `runner_config_advisory` 每条告知一次(句子带该写的拼法;委派工具声明了逐次注入工具的名册,引擎列得出子代工具池);仍开跑前拒(`config.agent.unknown_tool`)的只剩
2092
+ 任何工具名都不可能有的拼法(空白、括号不配、空名)与非字符串条目。名字按工具名原拼写 `Bash` —— `denyTools:["bash"]` 拒不了 `Bash`。这两张表管子代可挂载的**全部**工具 —— 委派池、内建 hands(`Read` / `Edit` / `Write` / `NotebookEdit` / `Bash` / `Grep` /
2069
2093
  `Glob` …)、引擎自己的工具(`Monitor` / `Artifact` / `Skill` / 记忆工具 …)与父的 MCP 挂载:`denyTools:["Write"]` ⇒ 子代没有 `Write`,`denyTools:["Monitor"]` ⇒ 子代没有
2070
2094
  `Monitor`;`allowTools` 只列调用方工具 ⇒ 子代只剩那几只与它这条腿的协议工具(要什么就点名什么,`mcp__<srv>` 点整台服务器)。内建只读子代(Explore / Plan)没有
2071
2095
  `Edit` / `Write` / `NotebookEdit`。要按路径 / 命令拦而保留工具,写调用方权限策略的 deny 规则。
@@ -2352,7 +2376,8 @@ GET /v1/capabilities?permissionMode=auto (AUTONOMY=plan 的部署)
2352
2376
  运维 `AUTONOMY=plan` 与模型自己调 `EnterPlanMode`,落到的都是同一个模式词。提交之后,一条 run 的模式词只有一个源 —— 存储的提交体顶层
2353
2377
  `permissionMode`;写它的只有提交入口与下面的 `plan_review` 写回。运维 `AUTONOMY=plan` 是**提交入口的模式词地板**:受理时盖进提交体
2354
2378
  (请求不带模式词 ⇒ `plan`;带任何词 ⇒ 钳成 `plan`;bundle 的 `defaultMode` 不动,顶层赢),之后不再按段续跑重钳;热改 `AUTONOMY` 只影响之后提交的任务,
2355
- 在飞的 run 保持它的入口词。工具表与 `default` 同面(写手、委派工具 `Agent` 都在),
2379
+ 在飞的 run 保持它的入口词。请求的词真被折了(带了比 `plan` 松的词)⇒ 7.129.0 起这条会话的流(`POST /v1/tasks/stream` / `POST /v1/runs` 的事件账)
2380
+ 在开口那一拍多一帧 `engine_notice` `permission.entry_mode_clamped`,`detail: { requested, effective, ceiling, sessionId }`;没带词、带的就是 `plan` ⇒ 不发。工具表与 `default` 同面(写手、委派工具 `Agent` 都在),
2356
2381
  写与非只读调用在**调用时**由引擎的 plan 门问(有人可答)/ 拒(无人可答)—— 问是一张审批卡,人批了就写,
2357
2382
  **射程是写面根围栏之内**(任务 `cwd`、引擎宣告的可写目录〔记忆目录、会话 scratchpad〕、请求的 `additionalDirectories`):批准只清模式门,引擎文件工具自己的根围栏不受批准影响,
2358
2383
  围栏外的路径批了也不写(`tool_end.errorCode` = `path_not_in_root`,拒句点名 roots;写面声明为 open 的任务除外);委派的子代以 plan 为天花板继承同一道门。所以请求体带
@@ -2480,6 +2505,7 @@ WebSearch 工具的 `tool_result` 错误文本,回到模型的对话里,**不是
2480
2505
 
2481
2506
  `mcpServers[].transport`(`kind:"http"`)上有两个「用哪个头送身份」的可选键,**只能设一个**:`principalHeader`(runner 把该任务已认证的终端用户 principal 注入这个头)与 `assertionHeader`(托管连接器断言所用的头**名**)。
2482
2507
  - 请求体(`POST /v1/tasks` · `/v1/tasks/stream` · `/v1/runs`)与部署声明(中心下发 / 本地 `config.d/mcp.json`)判据相同:两键同设 ⇒ 请求体新提交腿 `400 request.body_shape`(拒句点名条目与两键,与契约包解析期的拒句逐字相同);`assertionHeader` 不是字符串 ⇒ `400 request.field_invalid`;部署声明里同设的那一条不进。耐久 resume 腿重放存量请求体时不拒、也不摘 server:server 照挂,`principalHeader` 胜、`assertionHeader` 不生效,一行 warn `mcp_assertion_header_ignored`(`server` / `reason` / `note`)。
2508
+ - `assertionHeader` 的**值**是头名,本服务不铸、也不校验具体词(只判是字符串且与 `principalHeader` 互斥);头名由引擎(上游)定为 **`connector-assertion`**(无 `x-` 前缀;引擎发送座落地后本服务只认这一个值),条目填别的名 ⇒ 连接器侧 401。
2483
2509
  - 本版只承载、不发送断言:声明了 `assertionHeader` 的 server 照常挂载,但不带断言头;每台这样的 server 一行 info `mcp_assertion_header_not_sent`(`server` / `note`)。探测口 `POST /v1/capabilities/mcp/probe` 对声明了该键的条目整条拒 `400 request.field_invalid`(探测不拨一份少了断言头的配置)。
2484
2510
 
2485
2511
  ### 9.6 trace 脱敏的射程:与出站请求体无关
@@ -2690,7 +2716,10 @@ web-admin 拉取 / fleet 公告 / 凭证续签、WebSearch、对象存储、OTLP
2690
2716
  回溯到钉的那条链,每一环的清单都从目标 tag 的树里读(`git -C ../sema-core show v<目标>:wire-changes/core@<ver>.json`),取并集 —— 跳版提货
2691
2717
  不再只读目标那一份;链断了(某环清单读不出、本地 tag 树缺了那一版)⇒ exit 2 点名。门会先 `git fetch --tags` 那个 core 克隆(不切换 checkout)。
2692
2718
  当前钉缺省取本仓上一发版 tag 的 `package.json`,可用 `--from <x.y.z>` 显式给;`--core-repo` 必须是 core 仓的 git 根
2693
- (安装树 `node_modules/@sema-agent/core` 不再接受)。
2719
+ (安装树 `node_modules/@sema-agent/core` 不再接受)。命中的处置写进**随版认领表** `docs/core-consumption/ACK-core@<目标>.json`(维护者文件,不随包):
2720
+ 门按 `section` + `key` 整串全等逐键放行被人核过的命中,条目的 `where` 须把这一命中现在的读点列全,读数行 `acked=` 如实带出放行条数;
2721
+ 表里有而读数里没有的条目、读点变了的条目按过期红(`stale=`),表自报的版本或区间下界与本次不符、形坏都是 exit 2;
2722
+ 没有通配、没有全局放行旗标,放行面只此一张表(细则在门脚本头注)。
2694
2723
  - **core RC 探针**(维护者裁定,2026-09-28):core 出 RC tarball 时直讯本仓(`npm pack` 产物路径 + shasum + `wire-changes/core@<ver>.json` 的 commit),
2695
2724
  当拍跑:
2696
2725
  ```bash
@@ -2882,3 +2911,45 @@ UPDATE session_policy_rev_mark SET rev = 0, gen = gen + 1 WHERE policy_key = '<w
2882
2911
 
2883
2912
  解封之后这把键没有规则,下一次写从 `rev 1` 开始,世代是新的一代,持有旧快照的读者因此不会把它读成延续。**不要**删掉这条记号:
2884
2913
  删掉会把这把键的世代交回 0,正好与那些旧快照同代。要恢复规则,解封后照常 `PUT /v1/sessions/:id/policy`(或重跑那次 2c 导入)。
2914
+
2915
+ ### 9.12 会话规则:写法、两只拒码与存量行(7.129.0 起)
2916
+
2917
+ `PUT /v1/sessions/:id/policy` 与 `POST /v1/sessions/:id/sync/import` 的 `policy` 面写的是同一种规则体:一只对象,只有三只桶。
2918
+
2919
+ - `toolDeny` / `toolAllow`:**规则条目**的列表 —— 工具名(`Read`)、协议工具的命名空间(`mcp__<server>`)或工具自己文法的规则(`Bash(git push:*)`、`Edit(src/**)`、
2920
+ `WebFetch(domain:<host>)`)。`toolDeny: ["Bash(rm:*)"]` 按命令名拒 `rm`(连接链里任何一段跑到它都算;写成路径的 `/bin/rm` 不是这个名字 —— 要整只拒 shell 写 `Bash`)。
2921
+ `toolAllow` 是收窄名单:没有一条条目放行的调用被拒。
2922
+ - `allowDirs`:写工具只许落在这些目录里(不能按路径收住的写类工具,例如 shell,在它生效时整只被拒)。
2923
+ - 7.128.x 及以前的 `commandAllow` / `commandDeny` 已退役:`commandDeny: ["rm"]` 的新写法是 `toolDeny: ["Bash(rm:*)"]`,但旧桶还拒写成路径的 `/bin/rm …` 和读不成一条命令名的
2924
+ 命令行(管道、`&&`),新写法不拒这两类;`commandAllow` 没有「只收窄 shell」的写法,要收窄就在 `toolAllow` 里列出允许的工具(shell 写成 `Bash(<命令>:*)` 条目)。
2925
+
2926
+ **两只拒码**(判官是引擎的那一只,两条写口与店里的拒同码、同一份拒体;同步导入的写口在句首加记录坐标 `policy: [i].rules: `;店里一个字节都不落):
2927
+
2928
+ - `400 rules.invalid_shape` —— 规则体形不对:`rules` 缺席或不是对象、有一个不是桶的键(含退役的命令桶,**不会被静默忽略**)、一只桶不是一列字符串、`allowDirs` 里有空目录。
2929
+ 体顶层 `field`(哪个键;整体不是对象时是空串)与 `got`(那个值的类型,不回显值),`error` 是引擎的句子。
2930
+ - `400 rules.inert_entry` —— 一条条目读成「什么都不是」,写进去什么都不拒 / 不许:退役的工具名、带 `__` 却没有协议前缀的名、协议名里铸不出的段、`*`、`mcp__*`、工具没有的规则形
2931
+ (`Monitor(touch:*)`)、文法不成的写法(空串、`Bash(`)。体顶层 `field` / `entry` / `reason`,外加 `verdict` / `currentName` / `guidance`(有就带)。按 `reason` 改写那一条再写。
2932
+
2933
+ 一条什么都不拒的 deny 已经在库里时,会话属主自己删得掉(删它不算放宽)。
2934
+
2935
+ **行自带文法戳。** 7.129.0 起每一行会话规则自带规则文法戳 `grammar`(写口判官盖,本版写下的每一行都是 `1`;店整只存、读回原样交出)—— `GET` / `PUT` 回包的 `rules` 与同步导出的
2936
+ 记录里多这一个键。客户端改规则时只写三只桶,不要把读回的 `rev` / `gen` / `grammar` 一起回传:带着 `rev` 却没有 `grammar` 的值引擎按存量行的文法(0)读,括号形 allow 条目答
2937
+ `400 rules.inert_entry`(`reason: "earlier_grammar"`;出路在句子里);`grammar` 坏或比引擎新 ⇒ `400 rules.invalid_shape`(`field: "grammar"`)。
2938
+
2939
+ **存量行(`DB_BACKEND=mysql|pg`)。** 7.129.0 第一次起服时一次性迁移把装规则的列 `session_policy.rules` 改名为 `rule_entries`。带 `commandAllow` / `commandDeny` 的行:退役桶
2940
+ **不改写**、原样留着(新写法拒不了旧桶拒的那两类,替你改写就是放宽):读它的请求答 `500 corrupt`,这个会话上的新任务起不来,每次起服打一条 warn
2941
+ `session_rule_row_retired_bucket`(`policyKey` / `sessionId` / `buckets`)。出路是在库里把那一行改写成上面的写法(`rev` / `gen` 不动),或删掉会话(删会话即删规则)。
2942
+ 同一次改名里每一行改写成当前文法的同一句话:没有戳的行是 7.128.x 写的,引擎按它存下时的文法读 —— 那种文法把 `toolAllow` 的条目当整串工具名比,`Bash(rm:*)` / `Edit(src/**)` / `Bash(*)`
2943
+ 这类条目匹配不到任何工具、不放行任何东西,7.129.0 读成规则;引擎读成「名了不存在的工具」的 `toolAllow` 条目删掉,名单保持在场(删到空 = 什么都不放行,与升级前同判),其余条目原样,
2944
+ 每一行盖 `grammar: 1`,`rev` / `gen` 不动。`toolDeny` 里的条目一个字节不动、不点名(deny 规则在两种文法下同义;7.128.x 上不拒,升级后拒)。`toolAllow` 里的 `*` 不删:引擎在任何文法下都把
2945
+ 带 `*` 的 allow 名单在引擎 7.51.2 的旧文法读法下整张作废(7.128.x 其余条目照放;引擎已知限制,已请托修正)—— 改名那一拍对这类行不改写、不盖戳、只点名(`wildcardMasked`),升级后这种会话每个调用都拒,operator 去掉 `*` 重写即恢复。有被删条目的行改名那一次起服各一条 warn `session_rule_row_old_grammar_entries`
2946
+ (`policyKey` / `sessionId` / `toolAllowDropped`),之后不再报;被删的条目要按新文法生效,升级后 `PUT` 写回。改写在它下面跑的那一拍列叫
2947
+ `rule_entries_old_grammar`,改完才改成 `rule_entries`:起服在改写中途或其后的任何一步(终名改名、扫描、后续迁移)失败 ⇒ 拒因带有被删条目的行的收据、起服日志先打
2948
+ `one_time_migrations_interrupted` 与逐行的 `session_rule_row_old_grammar_entries` 再抛(这些收据只在改写发生的那一拍的起服日志里出现,请保存;下一次起服不再有;只盖戳的改写不记);
2949
+ 失败或被杀时列停在进行中的名,下一次起服续改;回滚时从那个名改回(`grammar` 对旧版是一个它不读的多余键)。
2950
+
2951
+ **升级与回滚。** 7.128.x 及以前的副本按整串工具名读规则,读不懂 `Bash(rm:*)` 这类条目。改名之后它们在这只库上找不到规则列:还在跑的旧副本读写规则的请求失败、会话任务起不来,
2952
+ 旧二进制起服被拒(`config.schema_mismatch`)—— 不会把新写法读成「没有这条拒」。所以先停掉全部旧副本再起 7.129.0。回滚到旧版之前要先把列名改回,
2953
+ 而且本版写下的规则条目在旧版上不生效(见 `MIGRATION.md` 7.129.0 节)。`DB_BACKEND=local` 的规则在引擎自带的文件店里,不改列名:带命令桶的规则文件读回 `corrupt`,出路在引擎的拒句里。
2954
+ 文件店也没有上面那一步判读,也不需要:引擎按行自带的文法读,7.128.x 写下的无戳文件里 `toolAllow` 的 `Bash(rm:*)` 这类条目照旧什么都不放行(不放宽),升级后按引擎通告重写。停驻的 run 里引擎写下的
2955
+ 祖先规则快照同理(本服务不改写),升级前决掉或取消待决的停驻 run。
@@ -21,7 +21,7 @@
21
21
  * 没有方法、没有捕获的行为。
22
22
  */
23
23
  import { z } from "zod";
24
- import { ALLOW_RULE_MEMBERS, READ_ROOT_CANDIDATE_DIR_MAX, type AskClass, type AskEvidenceAbsence, type AskRequest as CoreAskRequest, type PendingAction as CorePendingAction, type ReadRootGrantCandidate, type RuleOffer, type SteerReplayFacet } from "@sema-agent/core";
24
+ import { ALLOW_RULE_MEMBERS, READ_ROOT_CANDIDATE_DIR_MAX, type AskClass, type AskEvidenceAbsence, type PendingAction as CorePendingAction, type ReadRootGrantCandidate, type ResumeStartupCause, type RuleOffer, type RuleOffersAbsence as CoreRuleOffersAbsence, type SteerReplayFacet } from "@sema-agent/core";
25
25
  import type { AskRow } from "./plugins/approval-ask-store-sql.js";
26
26
  /** 模型自由文本(`sourceAgentName` / `delegation.agentName`)的限长(设计稿 §6.2)。设计 §3.1 把
27
27
  * 「限长 + 脱敏」写成 **server 新增责任**(引擎无此层):spawning model 挑的名字是自由文本,
@@ -384,22 +384,19 @@ export declare function readAllowRuleShadowed(req: unknown): AllowRuleShadowProj
384
384
  */
385
385
  export declare function readRuleEvidence(req: unknown): RuleEvidenceProjection | undefined;
386
386
  /**
387
- * [ref] 第五单(core [ref] 修②)—— `ruleOffers` 缺席因由的**闭三词集**。
387
+ * [ref] 第五单(core [ref] 修②)—— `ruleOffers` 缺席因由的**闭集**。
388
388
  *
389
- * 🔴 **型从 core 派生,不手抄**(S-249):core 刻意**不导出**这个名字,也不导出运行期常量
390
- * (`permission-rule-lanes.d.ts` 顶注逐字:「Deliberately not exported: this is a wire vocabulary, and its
391
- * two faces declare it literally … so a consumer reads the closed set on the type it is holding」)——
392
- * 于是本仓**必须**自持一份运行期词表(zod 要字面元组),但**不必**自持一份型。做法:型 =
393
- * `AskRequest["ruleOffersAbsence"]` 的联合(那正是 core 要我们读的那张脸),运行期词表另立一行,
394
- * 两者之间架**两向编译围栏** ⇒ core 加词 / 改词 / 退役任一,本文件当场 tsc 红。
395
- * (对照 {@link AskEvidenceAbsenceSchema}:那一族 core **给了**运行期常量,所以那边零词表、用 `z.custom`。
396
- * 两族的姿势不同不是分歧,是「上游给什么就用什么」的同一条纪律的两种兑现。)
389
+ * 🔴 **型与运行期词表都读 core 的导出**(core 7.51.0 #1515 起 core 导出 `RULE_OFFERS_ABSENCES` 与 `RuleOffersAbsence`):
390
+ * 此前 core 刻意不导出这张词表,本仓只能自持一份手抄运行期表(zod 要字面元组)+ 两向编译围栏;core 给了常量之后
391
+ * 那份手抄表与围栏退役(一个事实一个来源)—— 与 {@link AskEvidenceAbsenceSchema}「上游给了运行期常量就零词表」同一条纪律。
392
+ * 7.51.0 的词:`mandated` / `allow_outranked` / `ask_rule_matched` / `lane_cannot_speak`(`shadowed` 退役、拆成中间两词)。
397
393
  */
398
- export type RuleOffersAbsence = NonNullable<CoreAskRequest["ruleOffersAbsence"]>;
394
+ export type RuleOffersAbsence = CoreRuleOffersAbsence;
399
395
  export declare const RuleOffersAbsenceSchema: z.ZodEnum<{
400
396
  mandated: "mandated";
397
+ allow_outranked: "allow_outranked";
398
+ ask_rule_matched: "ask_rule_matched";
401
399
  lane_cannot_speak: "lane_cannot_speak";
402
- shadowed: "shadowed";
403
400
  }>;
404
401
  /**
405
402
  * `ruleOffersAbsence` 座的**三态读数**(S-663 codex r1 起;此前是 {@link readRuleOffersAbsence} 函数体内的两步)。
@@ -464,6 +461,9 @@ type ToolApprovalPark = Extract<CorePendingAction, {
464
461
  * `AskRequest.origin`(盖章点即 core 的筛点),照旧只过「非空串」形门(`tool-approval.ts` 帧键 `origin` 顶注)。
465
462
  * · `classifierNoRuling`(core 7.43.0 #1353)—— 分类器没给判词的成因词,core `AUTO_MODE_FALLBACK_CAUSES` 闭集一词(筛 = `isAutoModeFallbackCause`)。
466
463
  * 读者:活卡帧读 `AskRequest.classifierNoRuling`(`tool-approval.ts`);inbox 行读 core `CheckpointSummary` 已筛过的同名位、直投不经本函数。
464
+ * · `cause`(core 7.51.0 #1448)—— **载体是引擎续跑回执的 `details`**(不是 ask 的三个载体之一),值是起跑失败的成因词,core `RESUME_STARTUP_CAUSES` 闭集一词
465
+ * (成员表取 core 常量;core 的 `resumeStartupCauseOf` 从失败的**码**读出)。读者:`parked-decide.ts` 的 422 `parked_resume.startup_failed` 体(`cause`)。
466
+ * 回执没说 ⇒ 缺席(不计数,「分不出」是一等状态);集外词 / 非串 ⇒ 按缺席(键不上 wire)+ 计一次,与卡上的闭集词同一只计数。
467
467
  * · `differs`(core 7.44.0 #1374)—— **载体是引擎拒 steer 重放那只错的 `detail`**(不是 ask 的三个载体之一),值是 core `STEER_REPLAY_FACETS` 的**非空子集**
468
468
  * (core:「恒非空」;筛 = 本文件 {@link isSteerReplayDiffers},成员表取 core 常量)。读者:`runs.ts` 的 409 `steering.duplicate_input_id` 体(`detail.differs`)。
469
469
  * 集外成员 / 空数组 / 非数组 ⇒ 按缺席(不铸 `detail`,码仍是契约)+ 计一次;detail 只报形名(`differs=<array>` —— 数组从不展开成员,见 {@link outOfSetDetail})。
@@ -480,12 +480,14 @@ type ToolApprovalPark = Extract<CorePendingAction, {
480
480
  * 只读值不调用值)。
481
481
  */
482
482
  export declare function readClosedWordSeat<S extends ClosedWordSeat>(carrier: object, seat: S): ClosedWordOf<S> | undefined;
483
- /** ask 载体上的闭集词座名:三个 core 载体上**同名**(`AskRequest` / 寄存行 `tool_approval` 臂 / `CheckpointSummary`)—— 任一处改名 / 退役 ⇒ 下面的围栏编译红。 */
484
- type AskClosedWordSeat = "mandate" | "origin" | "classifierNoRuling";
483
+ /** ask 载体上的闭集词座名:三个 core 载体上**同名**(`AskRequest` / 寄存行 `tool_approval` 臂 / `CheckpointSummary`)—— 任一处改名 / 退役 ⇒ 下面的围栏编译红。
484
+ * S-1493(core 7.51.0 #1688)起第四座 `boundGrantAbsence`:卡为什么没有授予绑定摘要(`not_bindable` / `unreadable`)。 */
485
+ type AskClosedWordSeat = "mandate" | "origin" | "classifierNoRuling" | "boundGrantAbsence";
485
486
  /** 闭集词座的名 = ask 载体上的三座 + 引擎拒 steer 重放那只错的 `detail.differs`(载体不同、读法同一只)。 */
486
- type ClosedWordSeat = AskClosedWordSeat | "differs";
487
- /** 一座的值型:ask 三座 = core 寄存行上那一座的型(去掉可选位的 `undefined`);`differs` = core 闭集 `SteerReplayFacet` 的数组(core `detail.differs` 的型)。 */
488
- type ClosedWordOf<S extends ClosedWordSeat> = S extends AskClosedWordSeat ? NonNullable<ToolApprovalPark[S]> : readonly SteerReplayFacet[];
487
+ type ClosedWordSeat = AskClosedWordSeat | "differs" | "cause";
488
+ /** 一座的值型:ask 座 = core 寄存行上那一座的型(去掉可选位的 `undefined`);`differs` = core 闭集 `SteerReplayFacet` 的数组(core `detail.differs` 的型);
489
+ * `cause` = core 的 `ResumeStartupCause`(续跑回执 `details.cause` 的型)。 */
490
+ type ClosedWordOf<S extends ClosedWordSeat> = S extends AskClosedWordSeat ? NonNullable<ToolApprovalPark[S]> : S extends "differs" ? readonly SteerReplayFacet[] : ResumeStartupCause;
489
491
  /**
490
492
  * 🔴 **S-934 ②(core 7.37.0 #1214)—— ask 类对的唯一窄读**:`askClass`(一问的类 = `classes` 的首员)与 `classes`(这一问属于的全部类,core
491
493
  * `ASK_CLASSES` 序)。三个载体上同名:`AskRequest`(活卡帧 / 呈卡帧的卡读它)、寄存行 `PendingAction.tool_approval`(运维队列行经
@@ -525,8 +527,11 @@ export interface AskClassPair {
525
527
  * bypass 姿态下按 `ask_rule` / `hook` 照问的那条壳规则无从落地。缺席 = 老行(core 7.5.0 之前 park 的)/ 集外词(计 S-249 tag)。
526
528
  * S-934 ②(core 7.37.0 #1214)第六 / 七位 `askClass` / `classes`:这一问的类对(经 {@link readAskClassPair};两位同在同缺,与活卡帧 / 呈卡帧的卡
527
529
  * 同名同义同值)—— durable 审批席按类答的依据。缺席 = 老行(core 7.37.0 之前 park 的,续跑不补)/ 读不出(计 S-249 tag)。
530
+ * S-1493(core 7.51.0 #1688)第八位 `boundGrantAbsence`:这张卡为什么没有授予绑定摘要(经 {@link readClosedWordSeat};与活卡帧 / inbox 行同名同义同值)——
531
+ * `not_bindable` = 这一问不绑任何授予(「本会话放行」可按工具记)。缺席 = 技能卡(带摘要;摘要不上队列行,见 ⑧b)/ 7.51.0 之前停驻的行(core inbox 投
532
+ * `unreadable`,队列行如实缺席)/ 集外词(计 S-249 tag)。
528
533
  */
529
- export type ParkRowFacts = Pick<ToolApprovalPark, "requiresRealApproval" | "ruleOffersAbsence" | "mandated" | "mandate" | "origin" | "askClass" | "classes">;
534
+ export type ParkRowFacts = Pick<ToolApprovalPark, "requiresRealApproval" | "ruleOffersAbsence" | "mandated" | "mandate" | "origin" | "askClass" | "classes" | "boundGrantAbsence">;
530
535
  /**
531
536
  * S-700 —— park 行事实的**唯一投影**(`pendingAction` 是寄存行上 core 铸的那一只,读自持久层 ⇒ `unknown`)。
532
537
  *
@@ -553,7 +558,7 @@ export type ParkRowFacts = Pick<ToolApprovalPark, "requiresRealApproval" | "rule
553
558
  * `ruleOffersAbsence` 推「强制」的**权限**判据需要的是三态(读不出 ⇒ 按强制,fail-closed),方向相反,所以权限面
554
559
  * 不许拿本函数的产物去推强制 —— 两者共用的是 schema,不是折叠。
555
560
  */
556
- export declare function parkRowFacts(pendingAction: unknown): ParkRowFacts;
561
+ export declare function parkRowFacts(stored: unknown): ParkRowFacts;
557
562
  /** S-114(core 7.4.0 [ref])—— 回落卡的计数与窗。`limit` 是**闭二词**(core d.ts 逐字),两个计数是
558
563
  * 非负整数,`autoDenyAfterMs` 是 core 契约里的 `0..2147483647` 非负整数(`0` = 本卡不武装窗:部署把
559
564
  * 旋钮关了,或 TOTAL 档的卡按 [ref]① 恒 0 等人)。四成员**全必填**——core 的 `DenialLimitFallback` 上
@@ -670,8 +675,9 @@ export declare const ApprovalCardSchema: z.ZodObject<{
670
675
  }, z.core.$strict>]>>>;
671
676
  ruleOffersAbsence: z.ZodOptional<z.ZodEnum<{
672
677
  mandated: "mandated";
678
+ allow_outranked: "allow_outranked";
679
+ ask_rule_matched: "ask_rule_matched";
673
680
  lane_cannot_speak: "lane_cannot_speak";
674
- shadowed: "shadowed";
675
681
  }>>;
676
682
  denialLimitFallback: z.ZodOptional<z.ZodObject<{
677
683
  consecutive: z.ZodNumber;
@@ -707,8 +713,8 @@ export declare const ApprovalCardSchema: z.ZodObject<{
707
713
  }, z.core.$strict>>>;
708
714
  personalRuleDotsAbsent: z.ZodOptional<z.ZodCustom<AskEvidenceAbsence, AskEvidenceAbsence>>;
709
715
  }, z.core.$strict>>;
710
- askClass: z.ZodOptional<z.ZodCustom<"mode" | "hook" | "org_rule" | "write_protection" | "target_unresolved" | "content_question" | "ask_rule" | "denial_limit_fallback" | "ancestor" | "dangerous_delete" | "unverifiable_delete" | "unverifiable_shell_text" | "transcript_write" | "governance_unavailable" | "rule_unreadable" | "classifier_no_ruling" | "requires_user_interaction" | "operator_forced" | "shell_floor" | "probe" | "tool_marks_mcp" | "tool_marks_a2a" | "policy_rra" | "artifact_shared_read" | "unstated" | "ordinary", "mode" | "hook" | "org_rule" | "write_protection" | "target_unresolved" | "content_question" | "ask_rule" | "denial_limit_fallback" | "ancestor" | "dangerous_delete" | "unverifiable_delete" | "unverifiable_shell_text" | "transcript_write" | "governance_unavailable" | "rule_unreadable" | "classifier_no_ruling" | "requires_user_interaction" | "operator_forced" | "shell_floor" | "probe" | "tool_marks_mcp" | "tool_marks_a2a" | "policy_rra" | "artifact_shared_read" | "unstated" | "ordinary">>;
711
- classes: z.ZodOptional<z.ZodArray<z.ZodCustom<"mode" | "hook" | "org_rule" | "write_protection" | "target_unresolved" | "content_question" | "ask_rule" | "denial_limit_fallback" | "ancestor" | "dangerous_delete" | "unverifiable_delete" | "unverifiable_shell_text" | "transcript_write" | "governance_unavailable" | "rule_unreadable" | "classifier_no_ruling" | "requires_user_interaction" | "operator_forced" | "shell_floor" | "probe" | "tool_marks_mcp" | "tool_marks_a2a" | "policy_rra" | "artifact_shared_read" | "unstated" | "ordinary", "mode" | "hook" | "org_rule" | "write_protection" | "target_unresolved" | "content_question" | "ask_rule" | "denial_limit_fallback" | "ancestor" | "dangerous_delete" | "unverifiable_delete" | "unverifiable_shell_text" | "transcript_write" | "governance_unavailable" | "rule_unreadable" | "classifier_no_ruling" | "requires_user_interaction" | "operator_forced" | "shell_floor" | "probe" | "tool_marks_mcp" | "tool_marks_a2a" | "policy_rra" | "artifact_shared_read" | "unstated" | "ordinary">>>;
716
+ askClass: z.ZodOptional<z.ZodCustom<"mode" | "hook" | "org_rule" | "write_protection" | "target_unresolved" | "content_question" | "ask_rule" | "denial_limit_fallback" | "ancestor" | "dangerous_delete" | "unverifiable_delete" | "unverifiable_shell_text" | "transcript_write" | "rule_unreadable" | "classifier_no_ruling" | "requires_user_interaction" | "operator_forced" | "shell_floor" | "probe" | "tool_marks_mcp" | "tool_marks_a2a" | "policy_rra" | "artifact_shared_read" | "unstated" | "ordinary", "mode" | "hook" | "org_rule" | "write_protection" | "target_unresolved" | "content_question" | "ask_rule" | "denial_limit_fallback" | "ancestor" | "dangerous_delete" | "unverifiable_delete" | "unverifiable_shell_text" | "transcript_write" | "rule_unreadable" | "classifier_no_ruling" | "requires_user_interaction" | "operator_forced" | "shell_floor" | "probe" | "tool_marks_mcp" | "tool_marks_a2a" | "policy_rra" | "artifact_shared_read" | "unstated" | "ordinary">>;
717
+ classes: z.ZodOptional<z.ZodArray<z.ZodCustom<"mode" | "hook" | "org_rule" | "write_protection" | "target_unresolved" | "content_question" | "ask_rule" | "denial_limit_fallback" | "ancestor" | "dangerous_delete" | "unverifiable_delete" | "unverifiable_shell_text" | "transcript_write" | "rule_unreadable" | "classifier_no_ruling" | "requires_user_interaction" | "operator_forced" | "shell_floor" | "probe" | "tool_marks_mcp" | "tool_marks_a2a" | "policy_rra" | "artifact_shared_read" | "unstated" | "ordinary", "mode" | "hook" | "org_rule" | "write_protection" | "target_unresolved" | "content_question" | "ask_rule" | "denial_limit_fallback" | "ancestor" | "dangerous_delete" | "unverifiable_delete" | "unverifiable_shell_text" | "transcript_write" | "rule_unreadable" | "classifier_no_ruling" | "requires_user_interaction" | "operator_forced" | "shell_floor" | "probe" | "tool_marks_mcp" | "tool_marks_a2a" | "policy_rra" | "artifact_shared_read" | "unstated" | "ordinary">>>;
712
718
  fromSubagent: z.ZodOptional<z.ZodLiteral<true>>;
713
719
  sourceTaskId: z.ZodOptional<z.ZodString>;
714
720
  sourceAgentName: z.ZodOptional<z.ZodString>;
@@ -804,8 +810,9 @@ export declare const ApprovalCardEnvelopeSchema: z.ZodObject<{
804
810
  }, z.core.$strict>]>>>;
805
811
  ruleOffersAbsence: z.ZodOptional<z.ZodEnum<{
806
812
  mandated: "mandated";
813
+ allow_outranked: "allow_outranked";
814
+ ask_rule_matched: "ask_rule_matched";
807
815
  lane_cannot_speak: "lane_cannot_speak";
808
- shadowed: "shadowed";
809
816
  }>>;
810
817
  denialLimitFallback: z.ZodOptional<z.ZodObject<{
811
818
  consecutive: z.ZodNumber;
@@ -841,8 +848,8 @@ export declare const ApprovalCardEnvelopeSchema: z.ZodObject<{
841
848
  }, z.core.$strict>>>;
842
849
  personalRuleDotsAbsent: z.ZodOptional<z.ZodCustom<AskEvidenceAbsence, AskEvidenceAbsence>>;
843
850
  }, z.core.$strict>>;
844
- askClass: z.ZodOptional<z.ZodCustom<"mode" | "hook" | "org_rule" | "write_protection" | "target_unresolved" | "content_question" | "ask_rule" | "denial_limit_fallback" | "ancestor" | "dangerous_delete" | "unverifiable_delete" | "unverifiable_shell_text" | "transcript_write" | "governance_unavailable" | "rule_unreadable" | "classifier_no_ruling" | "requires_user_interaction" | "operator_forced" | "shell_floor" | "probe" | "tool_marks_mcp" | "tool_marks_a2a" | "policy_rra" | "artifact_shared_read" | "unstated" | "ordinary", "mode" | "hook" | "org_rule" | "write_protection" | "target_unresolved" | "content_question" | "ask_rule" | "denial_limit_fallback" | "ancestor" | "dangerous_delete" | "unverifiable_delete" | "unverifiable_shell_text" | "transcript_write" | "governance_unavailable" | "rule_unreadable" | "classifier_no_ruling" | "requires_user_interaction" | "operator_forced" | "shell_floor" | "probe" | "tool_marks_mcp" | "tool_marks_a2a" | "policy_rra" | "artifact_shared_read" | "unstated" | "ordinary">>;
845
- classes: z.ZodOptional<z.ZodArray<z.ZodCustom<"mode" | "hook" | "org_rule" | "write_protection" | "target_unresolved" | "content_question" | "ask_rule" | "denial_limit_fallback" | "ancestor" | "dangerous_delete" | "unverifiable_delete" | "unverifiable_shell_text" | "transcript_write" | "governance_unavailable" | "rule_unreadable" | "classifier_no_ruling" | "requires_user_interaction" | "operator_forced" | "shell_floor" | "probe" | "tool_marks_mcp" | "tool_marks_a2a" | "policy_rra" | "artifact_shared_read" | "unstated" | "ordinary", "mode" | "hook" | "org_rule" | "write_protection" | "target_unresolved" | "content_question" | "ask_rule" | "denial_limit_fallback" | "ancestor" | "dangerous_delete" | "unverifiable_delete" | "unverifiable_shell_text" | "transcript_write" | "governance_unavailable" | "rule_unreadable" | "classifier_no_ruling" | "requires_user_interaction" | "operator_forced" | "shell_floor" | "probe" | "tool_marks_mcp" | "tool_marks_a2a" | "policy_rra" | "artifact_shared_read" | "unstated" | "ordinary">>>;
851
+ askClass: z.ZodOptional<z.ZodCustom<"mode" | "hook" | "org_rule" | "write_protection" | "target_unresolved" | "content_question" | "ask_rule" | "denial_limit_fallback" | "ancestor" | "dangerous_delete" | "unverifiable_delete" | "unverifiable_shell_text" | "transcript_write" | "rule_unreadable" | "classifier_no_ruling" | "requires_user_interaction" | "operator_forced" | "shell_floor" | "probe" | "tool_marks_mcp" | "tool_marks_a2a" | "policy_rra" | "artifact_shared_read" | "unstated" | "ordinary", "mode" | "hook" | "org_rule" | "write_protection" | "target_unresolved" | "content_question" | "ask_rule" | "denial_limit_fallback" | "ancestor" | "dangerous_delete" | "unverifiable_delete" | "unverifiable_shell_text" | "transcript_write" | "rule_unreadable" | "classifier_no_ruling" | "requires_user_interaction" | "operator_forced" | "shell_floor" | "probe" | "tool_marks_mcp" | "tool_marks_a2a" | "policy_rra" | "artifact_shared_read" | "unstated" | "ordinary">>;
852
+ classes: z.ZodOptional<z.ZodArray<z.ZodCustom<"mode" | "hook" | "org_rule" | "write_protection" | "target_unresolved" | "content_question" | "ask_rule" | "denial_limit_fallback" | "ancestor" | "dangerous_delete" | "unverifiable_delete" | "unverifiable_shell_text" | "transcript_write" | "rule_unreadable" | "classifier_no_ruling" | "requires_user_interaction" | "operator_forced" | "shell_floor" | "probe" | "tool_marks_mcp" | "tool_marks_a2a" | "policy_rra" | "artifact_shared_read" | "unstated" | "ordinary", "mode" | "hook" | "org_rule" | "write_protection" | "target_unresolved" | "content_question" | "ask_rule" | "denial_limit_fallback" | "ancestor" | "dangerous_delete" | "unverifiable_delete" | "unverifiable_shell_text" | "transcript_write" | "rule_unreadable" | "classifier_no_ruling" | "requires_user_interaction" | "operator_forced" | "shell_floor" | "probe" | "tool_marks_mcp" | "tool_marks_a2a" | "policy_rra" | "artifact_shared_read" | "unstated" | "ordinary">>>;
846
853
  fromSubagent: z.ZodOptional<z.ZodLiteral<true>>;
847
854
  sourceTaskId: z.ZodOptional<z.ZodString>;
848
855
  sourceAgentName: z.ZodOptional<z.ZodString>;
@@ -912,8 +919,28 @@ export interface ApprovalCardSource {
912
919
  * 头注。)
913
920
  */
914
921
  export declare function buildApprovalCard(source: ApprovalCardSource, req: unknown, requiresRealApproval: boolean): ApprovalCard;
915
- /** 落库信封的纯构造(写侧;读侧 = `ApprovalCardEnvelopeSchema.safeParse`,**同一个 schema**)。 */
922
+ /** 落库信封的纯构造(写侧;读侧 = {@link readStoredCardEnvelope},**同一个 schema**)。 */
916
923
  export declare function buildApprovalCardEnvelope(approvalId: string, card: ApprovalCard): ApprovalCardEnvelope;
924
+ /**
925
+ * 🔴 **存下的卡的唯一读者入口**(S-1562)—— `approval_ask.card_json` 的每一个读点(断连重放 {@link buildReplayFrame}、回决口的「要不要人」
926
+ * {@link storedCardDemandsAPerson}、幂等重入与规则兑付两处(`tool-approval.ts`)、local 车道的启动收敛(`approval-crash-convergence.ts`))都经这一只,
927
+ * 不各自 `safeParse`(修前五处各写一遍 —— 读法的任何一次改动都要改五处,漏一处就是两种读法)。
928
+ *
929
+ * 读法 = **一次性迁移 M18 的读侧半场 + 同一只 schema**:7.128.x 写下的卡上两座可能带引擎 7.51.0 退役的词(`ruleOffersAbsence: "shadowed"` /
930
+ * 类对里的 `governance_unavailable`),起服改写够不着的行(滚动升级窗里旧副本新写的、local 车道的文件 ask 账)读时过同一只改写
931
+ * (`retired-card-words.ts`),**响亮**:每改写一次计一次 `server.approval-card.retired-card-word-read`(计数 + probe + 一次 warn,detail 只报座名)。
932
+ * 窗关(登记表 M18)后读侧半场随起服改写一起删,本函数回到「只 `safeParse`」。读不出(真坏形)⇒ `undefined`,各读点的处置不变(跳过 + warn / park /
933
+ * 按要人 / 素材缺席 / 收敛成因按缺省)。
934
+ */
935
+ export declare function readStoredCardEnvelope(cardJson: unknown): ApprovalCardEnvelope | undefined;
936
+ /**
937
+ * 🔴 **停驻行 `tool_approval` 臂的读侧入口**(S-1562;一次性迁移 M18 的读侧半场)—— 读它两座的两个读者(运维队列行 {@link parkRowFacts}、
938
+ * 耐久 `/decide` 的 remember 判据 `tool-approval.ts` `parkRowStandingAnswerFacts`)先经这一只:7.50.x 停驻的行上两座的退役词按 M18 那一只改写读,
939
+ * 响亮(同一个 tag)。停驻行是**引擎写的文档**,没有起服改写这一半:引擎的行摘要覆盖类对 `askClass` / `classes`,本服务改写 = 续跑判官判 `damaged`、拒续跑 ⇒ **所有**停驻行
940
+ * (SQL 店的、local 车道引擎 File 店的)都只在这里读时改写,交的是浅拷贝,被读的行一个字节不动。
941
+ * 改写之后读者只读 core 的一张闭集,「读不出 ⇒ 按强制」只会遇到真坏形。别的臂 / 非对象 ⇒ 原样。窗关随 M18 一起删。
942
+ */
943
+ export declare function currentParkArm<T>(pendingAction: T): T;
917
944
  /**
918
945
  * [ref] §3.1 的呈卡帧(设计稿 §4.1)—— **live 铸造**形(重放形见 {@link buildReplayFrame},
919
946
  * 两者投的是同一种帧,区别只在 `card` 的来源:这里是刚投影出来的,那里是从行上回读的)。