@sema-agent/server 7.149.0 → 7.150.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 (194) hide show
  1. package/MIGRATION.md +122 -9
  2. package/README.md +1 -1
  3. package/README.zh-CN.md +1 -1
  4. package/USAGE.md +39 -15
  5. package/dist/approval-card.d.ts +1 -1
  6. package/dist/approval-gate-currency.d.ts +1 -1
  7. package/dist/audit.d.ts +1 -1
  8. package/dist/audit.js +2 -2
  9. package/dist/background-session-reap.d.ts +1 -1
  10. package/dist/boot/deferred-sandbox-path-env.d.ts +5 -3
  11. package/dist/boot/deferred-sandbox-path-env.js +1 -2
  12. package/dist/boot/parked-revive-gate.d.ts +5 -5
  13. package/dist/boot/parked-revive-gate.js +2 -3
  14. package/dist/boot/resolve-spec.d.ts +4 -5
  15. package/dist/boot/resolve-spec.js +61 -25
  16. package/dist/boot/retention-lane.d.ts +1 -1
  17. package/dist/boot/session-faces.js +21 -21
  18. package/dist/boot/stage-03-store-backend.js +2 -2
  19. package/dist/boot/stage-06-runners.js +1 -2
  20. package/dist/boot/stage-07-capability-layer.js +1 -1
  21. package/dist/boot/stage-10-http-server.d.ts +33 -1
  22. package/dist/boot/stage-10-http-server.js +9 -4
  23. package/dist/boot/web-admin.d.ts +1 -1
  24. package/dist/capability-facts.d.ts +65 -56
  25. package/dist/capability-facts.js +42 -35
  26. package/dist/config-catalog.js +1 -1
  27. package/dist/config-types.d.ts +12 -11
  28. package/dist/deployment-governance.d.ts +7 -28
  29. package/dist/deployment-governance.js +1 -14
  30. package/dist/device-ws-hub.d.ts +1 -0
  31. package/dist/device-ws-hub.js +64 -28
  32. package/dist/device-ws-protocol.d.ts +20 -6
  33. package/dist/device-ws-protocol.js +9 -2
  34. package/dist/engine-identity.d.ts +7 -1
  35. package/dist/engine-identity.js +5 -0
  36. package/dist/field-conflict.d.ts +2 -2
  37. package/dist/fleet/engine-signal.d.ts +14 -1
  38. package/dist/fleet/engine-signal.js +5 -1
  39. package/dist/fleet/fleet-reconciler.d.ts +1 -1
  40. package/dist/fleet/serving-lease.d.ts +18 -3
  41. package/dist/fleet/serving-lease.js +81 -35
  42. package/dist/fleet-client.d.ts +13 -11
  43. package/dist/fleet-client.js +53 -33
  44. package/dist/history-anchor.js +1 -1
  45. package/dist/hitl-delivery.d.ts +1 -3
  46. package/dist/hitl-delivery.js +1 -1
  47. package/dist/http/active-run-conflict.d.ts +3 -7
  48. package/dist/http/active-run-conflict.js +2 -2
  49. package/dist/http/admission.js +2 -2
  50. package/dist/http/approval-sweeps.js +2 -1
  51. package/dist/http/caller-trust.d.ts +2 -2
  52. package/dist/http/caller-trust.js +3 -3
  53. package/dist/http/checkpoint-ctx.d.ts +18 -63
  54. package/dist/http/checkpoint-ctx.js +1 -24
  55. package/dist/http/claim-heartbeat.d.ts +4 -4
  56. package/dist/http/claim-heartbeat.js +3 -3
  57. package/dist/http/dispatch.js +2 -2
  58. package/dist/http/durable-run-leg.js +4 -4
  59. package/dist/http/owner-access.d.ts +243 -19
  60. package/dist/http/owner-access.js +184 -5
  61. package/dist/http/readiness.d.ts +4 -2
  62. package/dist/http/resume-ctx-read.js +16 -1
  63. package/dist/http/resume-legs.js +18 -14
  64. package/dist/http/route-ctx.d.ts +23 -11
  65. package/dist/http/routes/a2a-serve.js +15 -14
  66. package/dist/http/routes/approvals-assistant.d.ts +3 -2
  67. package/dist/http/routes/approvals-assistant.js +8 -9
  68. package/dist/http/routes/capabilities.js +4 -3
  69. package/dist/http/routes/config-catalog.d.ts +1 -1
  70. package/dist/http/routes/mcp-reconnect.js +2 -2
  71. package/dist/http/routes/memory-origin.d.ts +1 -1
  72. package/dist/http/routes/notify-wake.js +2 -3
  73. package/dist/http/routes/projects.d.ts +1 -0
  74. package/dist/http/routes/projects.js +5 -3
  75. package/dist/http/routes/retention-ops.d.ts +1 -1
  76. package/dist/http/routes/run-permission-mode.d.ts +1 -1
  77. package/dist/http/routes/run-permission-mode.js +2 -2
  78. package/dist/http/routes/runs.d.ts +1 -1
  79. package/dist/http/routes/runs.js +23 -30
  80. package/dist/http/routes/session-memory-sync.js +4 -5
  81. package/dist/http/routes/session-sync.js +3 -7
  82. package/dist/http/routes/sessions.d.ts +1 -1
  83. package/dist/http/routes/sessions.js +18 -44
  84. package/dist/http/routes/tasks.js +9 -8
  85. package/dist/http/routes/trace-usage.js +14 -14
  86. package/dist/http/routes/workflows.d.ts +1 -0
  87. package/dist/http/routes/workflows.js +20 -38
  88. package/dist/http/server.d.ts +8 -4
  89. package/dist/http/server.js +5 -1
  90. package/dist/http/stored-body-replay.d.ts +3 -24
  91. package/dist/http/stored-body-replay.js +2 -12
  92. package/dist/http/submit-ctx.d.ts +1 -2
  93. package/dist/http/submit-ctx.js +3 -2
  94. package/dist/http/submit-replay.d.ts +15 -2
  95. package/dist/http/submit-replay.js +10 -4
  96. package/dist/http/wire-types.d.ts +6 -3
  97. package/dist/leader/endpoint.js +9 -8
  98. package/dist/leg-facts.d.ts +8 -2
  99. package/dist/leg-facts.js +1 -0
  100. package/dist/mcp-authorization-face.d.ts +18 -14
  101. package/dist/mcp-authorization-face.js +3 -2
  102. package/dist/memory-store-refusal.d.ts +1 -1
  103. package/dist/observability/corrupt-read-seat.d.ts +1 -1
  104. package/dist/observability/fail-open.d.ts +5 -5
  105. package/dist/observability/fail-open.js +5 -5
  106. package/dist/observability/logger.d.ts +7 -0
  107. package/dist/observability/logger.js +3 -1
  108. package/dist/observability/run-terminal.d.ts +1 -1
  109. package/dist/owner-faces.d.ts +27 -3
  110. package/dist/owner-faces.js +2 -0
  111. package/dist/peer-seat.d.ts +1 -1
  112. package/dist/peer-seat.js +52 -59
  113. package/dist/plugins/exec-path-law.d.ts +34 -0
  114. package/dist/plugins/exec-path-law.js +13 -0
  115. package/dist/plugins/file-run-store.js +1 -1
  116. package/dist/plugins/local-checkpoint-store.d.ts +4 -1
  117. package/dist/plugins/local-checkpoint-store.js +3 -0
  118. package/dist/plugins/memory-run-store.js +1 -1
  119. package/dist/plugins/one-time-migrations.d.ts +74 -49
  120. package/dist/plugins/one-time-migrations.js +134 -69
  121. package/dist/plugins/pg-pool.js +4 -0
  122. package/dist/plugins/posix-shell-fs.js +4 -2
  123. package/dist/plugins/remote-env-adb.js +3 -2
  124. package/dist/plugins/remote-env-device.js +8 -6
  125. package/dist/plugins/remote-env-e2b.d.ts +3 -1
  126. package/dist/plugins/remote-env-e2b.js +11 -8
  127. package/dist/plugins/remote-env-host.d.ts +1 -1
  128. package/dist/plugins/remote-env-host.js +20 -11
  129. package/dist/plugins/remote-env-k8s.js +3 -2
  130. package/dist/plugins/remote-env-local-docker.js +3 -2
  131. package/dist/plugins/remote-env-ssh.js +4 -3
  132. package/dist/plugins/remote-shell.d.ts +3 -1
  133. package/dist/plugins/remote-shell.js +3 -3
  134. package/dist/plugins/replica-registration.d.ts +35 -0
  135. package/dist/plugins/replica-registration.js +48 -0
  136. package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
  137. package/dist/plugins/run-store-sql.d.ts +1 -1
  138. package/dist/plugins/run-store-sql.js +2 -2
  139. package/dist/plugins/scenario-prefix-switch-m42.d.ts +6 -12
  140. package/dist/plugins/scenario-prefix-switch-m42.js +7 -5
  141. package/dist/plugins/schema-consistency.d.ts +7 -5
  142. package/dist/plugins/schema-consistency.js +29 -16
  143. package/dist/plugins/schema-drift-sentence.js +1 -1
  144. package/dist/plugins/serving-requirement.d.ts +49 -0
  145. package/dist/plugins/serving-requirement.js +53 -0
  146. package/dist/plugins/stop-then-start-sentence.d.ts +42 -3
  147. package/dist/plugins/stop-then-start-sentence.js +44 -0
  148. package/dist/plugins/store-backend.d.ts +1 -1
  149. package/dist/plugins/store-contracts.d.ts +12 -2
  150. package/dist/plugins/store-contracts.js +3 -0
  151. package/dist/plugins/tidb-pool.js +4 -0
  152. package/dist/process-instance.d.ts +3 -0
  153. package/dist/process-instance.js +7 -0
  154. package/dist/project-context.d.ts +66 -0
  155. package/dist/project-context.js +70 -0
  156. package/dist/question.d.ts +9 -21
  157. package/dist/question.js +28 -19
  158. package/dist/run-cancel-card.d.ts +1 -1
  159. package/dist/run-cancel-card.js +2 -1
  160. package/dist/security.d.ts +1 -1
  161. package/dist/session-agent.d.ts +1 -10
  162. package/dist/session-agent.js +0 -6
  163. package/dist/session-incarnation.d.ts +18 -4
  164. package/dist/session-incarnation.js +4 -1
  165. package/dist/session-notify-door.d.ts +4 -4
  166. package/dist/session-notify-door.js +5 -6
  167. package/dist/session-rules-seat.d.ts +4 -3
  168. package/dist/session-rules-seat.js +3 -11
  169. package/dist/session-sync.js +3 -3
  170. package/dist/stores-sql/checkpoint-store-sql.d.ts +21 -2
  171. package/dist/stores-sql/checkpoint-store-sql.js +106 -18
  172. package/dist/stores-sql/retention-store-sql.d.ts +1 -1
  173. package/dist/task-cwd.d.ts +4 -3
  174. package/dist/task-settings.d.ts +13 -0
  175. package/dist/task-settings.js +13 -7
  176. package/dist/tool-approval.d.ts +7 -6
  177. package/dist/tool-approval.js +41 -37
  178. package/dist/trace/engine-notice-wire.d.ts +14 -0
  179. package/dist/trace/engine-notice-wire.js +18 -1
  180. package/dist/trace/project.d.ts +4 -3
  181. package/dist/trace/project.js +3 -2
  182. package/dist/trace/projection-table.d.ts +33 -2
  183. package/dist/trace/self-minted-wire.d.ts +150 -17
  184. package/dist/trace/self-minted-wire.js +54 -16
  185. package/dist/trace/tool-card.d.ts +3 -0
  186. package/dist/trace/tool-card.js +24 -0
  187. package/dist/trace/tool-output-media.d.ts +2 -1
  188. package/dist/unattended-permission-mode.d.ts +1 -1
  189. package/dist/web-admin-client/apply-effective.d.ts +1 -1
  190. package/dist/web-admin-client/effective-keys.d.ts +1 -1
  191. package/dist/web-admin-client/skills-mcp.d.ts +1 -1
  192. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +57 -29
  193. package/package.json +2 -2
  194. package/wire-changes/server@7.150.0.json +335 -0
package/MIGRATION.md CHANGED
@@ -4,10 +4,124 @@
4
4
  > (内部多 AI 协作节奏,当日通告+实解)。**生产部署请锁精确版本**;GA 后 2.0 起严格 semver
5
5
  > (BREAKING → major)。本文件只记录会影响存量部署行为的变更。
6
6
  >
7
- > ⚠️ 完整清单在仓库根 `CHANGELOG.md`——它**不随 npm tarball 出包**(本文件随包)。看完整迁移窗的
8
- > 权威姿势是源码 tag diff:`git diff v<旧>..v<新>`(每版都推 `v<版本>` tag);npm 包页也镜像 CHANGELOG。
7
+ > ⚠️ 升级要做的动作都在本文件里:从哪一版升到哪一版,就把中间每一版的节从上往下读完(每节的「升级前做」「出路」「回滚」就是那一版要你做的)。
8
+ > 本文件只记会影响存量部署行为的变更:某一版没有节,即那一版没有记入这里的升级动作;完整的变更流水账(修复、文字订正、内部重构)是内部记录,不随 npm 包出。
9
9
  > 文中的「契约 §N」「契约附录 X」指服务端 wire 契约的节号(内部记录,不随包),只作查证坐标。
10
10
 
11
+ ## 7.150.0 —— 1–12 条见下
12
+
13
+ **升级清单(本节唯一的一张;下面各条只讲为什么,不再各自重述升级步骤)** —— 一次读清本版对一只存量部署要做的全部动作。每版同形:① 手工 DDL(逐表逐句,两方言各一份)② 起服自建与一次性迁移 ③ 停-迁-起 ④ 顺序;后续每版的段头照此写,新增一句 `ALTER`、一条一次性迁移、一条停-迁-起时,先改这里,再写条目。
14
+ 已发各节(7.149.0 及以前)逐字只读,不在原处补汇总 —— 跨过它们的部署读下面「跨过 7.149.0」一块。起服拒句是同一张清单的运行期形:缺列拒、单表探针拒、一次性迁移的判活拒,每一句都带「这只库此刻还差哪些 `ALTER`、哪些迁移要先停光旧副本、先后顺序」,一次起服读全,不必补一句再试一次。
15
+
16
+ - **从 7.149.x 升级到本版**:
17
+ - 手工 DDL:**无**。
18
+ - 起服自建(运维零手工):`replica_registration` · `serving_requirement`(两张新表,`CREATE TABLE IF NOT EXISTS`;第 7 条「副本能力登记与起服准入两张常设表」)。
19
+ - 一次性迁移:M52 停-迁-起证据读法的心跳一半(不改库;同一条)。
20
+ - 到期移除的一次性迁移:**M12**(停驻行模式词读法记号的拒臂;7.122.0 引入,窗 2026-11-08 关)本版删码(第 1 条「一次性迁移 M12 退役」)—— 从 ≤ 7.121.x(M12 起始版 7.122.0 之前)直升本版的部署,**须先经过含 M12 的版本**(7.122.0–7.149.x 任一版)起服并让那几版上停驻的 run 走完或决掉,再升本版;直升的话,`AUTONOMY=plan` 部署上 7.121.x 及以前写下、此刻仍停驻的行不再被拒,按体里的原词续跑。从 7.122.0–7.149.x 升级的部署零动作。
21
+ - 停-迁-起:**无新增**,本版条目没有一条要求停光旧副本。已有几条(M40 · M41 · M42 · M51)的守卫证据本版改读「判活窗内心跳过、持有者没登记为知道这条迁移的 `running` run」(同一条);停光旧副本这一步不变,仍是运维的第一道。
22
+ - device 车道(`REMOTE_EXEC=device`):**先把设备执行器升到声明设备协议修订 5 的版本,再换服务端版本** —— 本版只受理修订 5,声明 4 及以下的执行器在 hello 被拒、连不上(第 12 条)。
23
+ - 顺序:① 备份 ② device 车道先升执行器 ③ 换版本(可滚动)。
24
+ - **跨过 7.149.0(从 ≤ 7.148.x 直升本版)另加 7.149.0 节段头升级清单的全部动作**(那一张已含「跨过 7.148.0」一块):手工 DDL、停光旧副本与起服顺序照那一张做;守卫的证据按本版的读法判(上一块「停-迁-起」一行)。
25
+
26
+ 1. **一次性迁移 M12 退役:续跑重放不再问「停驻行有没有模式词读法记号」;`AUTONOMY=plan` 的部署上,7.121.x 及以前写下、此刻仍停驻的行不再被拒,按体里的原词续跑(持久数据面;行为变更;运维日志的 `kind` 少一个取值;wire 零新键、零新码,`checkpoint.unsupported_version` 少一个自铸臂;嵌入方包根导出无变化;部署零动作,除非 `AUTONOMY=plan` 的部署上还留着这类停驻行)**:
27
+ 7.122.0 引入的一条一次性迁移,窗(发布日 2026-10-09 + 30 天)于 2026-11-08 关,本版删了它的码:模式词读法记号的判据与拒臂(那句「this task was suspended by an earlier server version that re-applied this deployment's AUTONOMY on every resume …」不再出现)、提交入口与 `plan_review` / 在飞切档两处写回对记号的写、续跑上下文上的记号位。
28
+ 旧形:7.122.0–7.149.x 每一次新的持久化写都在续跑上下文上盖记号;本部署设了 `AUTONOMY=plan`、而行上没有记号(7.121.x 及以前写下)、体的生效词被地板钳会变 ⇒ 续跑重放前 409 `checkpoint.unsupported_version`(行字节不动,运维行 `resume_refused_stored_body` 带 `kind: "permission_mode_reading"`)。
29
+ 新形:不再问这一句。续跑照体里的词读:本版写下的行,体词在提交入口已盖过地板,读到的就是它;7.121.x 及以前写下的停驻行体里是请求原词,运维的 plan 不再被按段重钳 —— 不经 `plan_review` 的续跑腿(`/decide` · `/resume` · `/wake`)上 `acceptEdits` / `bypassPermissions` 一类原词直接生效(`plan_review` 的驳回仍由引擎留在 plan,写撞 plan 门)。存量行上已写下的记号键原样留着、无人读;新写的行不再带它。没有响亮的起服臂。
30
+ 谁需要:无。**谁受伤**:`AUTONOMY=plan` 的部署上仍有 7.121.x 及以前写下的停驻行(把 `APPROVAL_TERMINAL_BACKSTOP_MS` 调长的部署,或从 ≤ 7.121.x 直接升到本版的库)—— 这类行续跑时不再被拒,按原词重放;按那句 409 或 `kind: "permission_mode_reading"` 做告警的运维。
31
+ 补偿:升级前把这些停驻的 run 决掉 / 取消,或升级窗内设 `AUTONOMY=read-only`(按段的只读围栏);常规从 7.149.x 升级的部署无须动作。回滚(本版 → 7.122.0–7.149.x):本版写下、体词已被 `plan_review` 批准松开的行不带记号,在回滚后设了 `AUTONOMY=plan` 的部署上会被那一版的模式词问拒(409 `checkpoint.unsupported_version`;出路 = 取消后作为新任务重提)。
32
+
33
+ 2. **项目上下文继承:提交的 run 从本部署的项目登记簿继承项目简报与工作目录,只在登记带中心内容证明 `contentAttestedAt` 时生效(能力位 `projectInheritance`;行为加性;wire 加性:能力位一键、两类拒的坐标并集多 `projectId`;零新码;部署零动作)**:
34
+ 规则:继承读这条腿交给引擎的项目(提交带的 `projectId`,否则会话行上声明过的)。登记对提交者在 `GET /v1/projects/:id` 读得到(同一只可见性判)且带内容证明 ⇒ `goal` / `notes` / `note` 条目折成一段项目简报,进系统提示的追加折叠(序 `appendSystemPrompt` → 简报 → `settings.outputStyle`,共一只 16384 帽);能力位 `projectContext` 为真的部署(单主 host)上另列 `folder` / `file` 条目的名字(只列名字,不读、不附),第一条 `folder` 填缺席的请求 `cwd`。新提交时判一次,随 run 落进续跑重建输入,续跑只重放。契约 §G.9g.1。
35
+ 谁受伤与出路:① 端已自行把项目内容拼进 `appendSystemPrompt` ⇒ 重复 —— 读到 `projectInheritance: true` 就停拼;② 请求自带的追加段接近 16384 ⇒ 加上简报超帽,400 `request.field_conflict`(`reason: "system_prompt_fold"`,`fields` 含 `projectId`)—— 缩短追加段、带 `projectMemory: "disabled"`,或在中心缩短登记的 goal / notes;③ 升级前写下的项目内容不带内容证明 ⇒ 不生效,直到在中心按 publisher 角色重存一次;④ 多租部署上可见性只认 `registeredBy`,团队成员拿不到管理员登记的项目的简报;⑤ 改登记 ⇒ 之后提交的系统提示前缀变一次;⑥ 会话先前显式给过 `cwd`、这一次提交不带 ⇒ 这一次落到项目的第一条 folder —— 每次提交都带 `cwd`;⑦ 继承来的 `cwd` 落在会话 scratchpad ⇒ 400 `request.field_conflict`(`reason: "scratchpad_root"`,`fields` = `["projectId"]`)—— 提交带一个 scratchpad 之外的 `cwd`,或把登记的第一条 folder 挪出来。
36
+ 回滚(本版 → 上一版):续跑重建输入多一格 `projectContext`;上一版开它时原样保留、不读 ⇒ 本版停驻的 run 在上一版上续跑没有项目简报,继承来的工作目录也不重登记(新进程上落到部署缺省目录)。续跑照跑、不拒。要它们带着项目上下文续跑完,先在本版上排空停驻再回滚。
37
+
38
+ 3. **服务租约围栏:心跳答复没有 `capabilities` 键(老中心 `{ ok: true }`、网关 / 维护页 `{}`)不再读成「中心不宣告」—— 已授予的租约照旧、钟照走;只有中心的显式声明对象才开关围栏(依赖 settings-schema 25.0.0;行为收紧;wire 零变化;部署零动作,除非心跳打到不带键的后端)**:
39
+ 旧形:这样的答复读成 `undeclared` ⇒ 交「没宣告」⇒ 已授予 / 已过期的租约当拍回到 `none`(零续租、零 503),outage 钟随之失效 —— 一个指错的活性基址或回滚前的中心就能解除围栏。
40
+ 新形:它与读不出的答复(空体 / 截断 / 非对象 / 形坏的 `capabilities`)同一只臂 —— 不是读数,租约的状态与钟原样(到期之前 `granted`,过期后 `expired`,满 `FLEET_LEASE_OUTAGE_MAX_MS` 后新工作 503 `state.lease_expired`),每拍计 `server.fleet.announce-reply-unreadable`,一段连拍一行 warn `fleet_announce_reply_unreadable`(`why: capabilities-absent`)。中心显式声明而不带 `servingLease`(`{ capabilities: {} }`)⇒ 照旧回到 `none`。从没收到过声明的成员见第 6 条(起步即推定在围栏内)。
41
+ 补偿:让心跳路径答到带 `capabilities` 键的中心(升级它,或修正 `SEMA_WEB_ADMIN_LIVE_URL`);不要围栏的成员去掉 `FLEET_GENERATION`。
42
+
43
+ 4. **会话历史里的 MCP 授权记录只剩 `{ reason }`;授权记录的每一个投影面同读一张处置表**(安全轴收窄;行为变更;部署零动作)
44
+ 改了什么:`GET /v1/sessions/:id`(整读 / `?tail` / `?before` / `?blobCap` / `?message=N`)里,工具结果卡 `details` 是 MCP 授权失败卡(`code: "unauthorized"` 且带 `authorization`)时,
45
+ `authorization` 只剩 `{ reason }`(词出闭集 ⇒ 这一位缺席);远端 MCP 服务器在 401 / 403 挑战头里自报的 `resourceMetadataUrl` 不再回显。卡上其余成员照旧。
46
+ `tool_end.structured` 上挂着授权记录的自注册卡同读。状态行 / 重拨回执 / 面板 / 能力面 / 通告上这条记录本来就只有 `reason`,不变。
47
+ 谁需要:读会话历史的一切客户端与运维 —— 不再把一只远端选的地址摆到人面前。
48
+ 谁受伤:从会话历史 `details.authorization` 读 `resourceMetadataUrl` 的消费方;在 `code: "unauthorized"` 卡上放别义 `authorization` 的自注册工具(被按授权记录窄读)。
49
+ 补偿:那只地址远端在每次挑战里都会再答;没有旋钮(安全轴收窄)。存档字节不动(读口投影);会话同步拉取口照旧逐字复制存档。
50
+
51
+ 5. **AskUserQuestion 的落点只由「这一次投递有没有经这条腿的活面把卡呈出」定(问被上游门放行、到达投递 / 耐久处理之后;`permissionMode: "dontAsk"` 的问在上游门就被拒、到不了投递,自成一形 G):呈出过而窗走完没人答 = 一次失败的问(不停驻、不改道);没呈出 = 没拿到答(durable 部署停驻、可经 decide 补答);续跑腿的新问也先投卡、窗内可答;卡面 `reason` 由 `declined_unavailable` 换 `callback_failed`、coded 码由 `question.human_unavailable` 换 `question.human_channel_failed`(行为变更;wire 零新键、零新帧型、零新码;部署零动作)**:
52
+ 射程:问题席在场(`ASK_QUESTION_ENABLED`;单用户缺省开,多租户显式开的同读)的部署。durable 部署(`DURABLE_APPROVAL=true` + checkpoint 能力的 backend)的落点见「新形」各形;无 durable 的部署:没呈出 ⇒ 自答续跑(一字不变),呈出过而窗走完 ⇒ 同「新形」的失败臂(只换卡面 `reason` / coded 码)。问题席不在场的部署一字不变。带 `permissionMode: "dontAsk"` 的 run 不走落点:见「新形」里的形 G 与其后的 `dontAsk` 一段。
53
+ 旧形(7.149.0 及以前):durable 部署上落点按「判决那一刻这条腿的投递面可不可达」分形 —— 后台腿(`POST /v1/runs`)、连着的 `/v1/tasks/stream`、判决时还连着的 detach 流上,问题工具当场执行,`QUESTION_TTL_MS` 窗到期没人答 ⇒ 模型拿到「没人可问,自己判」的续跑卡继续跑(连着的流上是 coded 工具失败 `question.human_unavailable`),不铸 checkpoint、答案补不回;开帧投不出 / 撞并发帽 / 撞总量帽 ⇒ 同形当场续跑;只有判决时就够不着人的腿(断连已被确认的 detach 流、同步 `/v1/tasks`)停驻;续跑腿上的新问当场停驻、不投卡、`POST /v1/questions/:id/respond` 答 404;principal 带 `forceDurableGate` 的 run 在有上下文的腿上照样当场执行问题工具。无 durable 的部署:卡面 `reason: declined_unavailable` 的自答续跑(posture interactive 的连着的流为 coded `question.human_unavailable`),卡呈出过的也一样。
54
+ 新形:落点只读这一次投递的结局,本服务不在投递前预判「有没有人」。卡先经本腿的人在环传输投出(流腿:有 durable run 行时账本 + 活面,否则只有活面;后台 / 续跑腿:账本),窗内 `POST /v1/questions/:id/respond` 答到即交引擎;窗走完没人答时,由一个事实挑落点 —— 这一次投递有没有经这条腿的活面(直写 SSE)把卡呈出(审批族的呈卡登记读同一位)。呈出过 ⇒ 一次失败的问:不停驻、不交给第二个决断者,run 接着跑;没呈出 ⇒ 没拿到答:durable 部署停驻(`done(suspended)` + `pendingGate`),经 `POST /v1/approvals/:sessionId/decide` 带 `answer` 补答,无 durable 的部署自答续跑。durable 部署 × 问题席在场,各形(只列没在窗内答到的):
55
+ · 形 A 连着的 `/v1/tasks/stream`:呈出 ⇒ 窗走完抛,不停驻。
56
+ · 形 B `x-detach-on-disconnect` 流,卡出现时直写面接下、断开发生在其后:呈出 ⇒ 窗走完抛,不停驻。
57
+ · 形 C `x-detach-on-disconnect` 流,断开发生在卡出现之前(卡只进 run 账本):没呈出 ⇒ 窗走完停驻。
58
+ · 形 D 后台腿(`POST /v1/runs`)与续跑腿(流式续跑;卡进本腿账本、窗内可 respond):卡只进 run 账本 ⇒ 窗走完停驻。
59
+ · 形 E 不投卡的形 —— 没有问题上下文的腿(同步 `/v1/tasks`、verify / cascade 的续跑腿)· principal 带 `forceDurableGate` · 开帧投不出 · 撞并发帽(缺省 2)/ 总量帽 · 脱敏后序列化的载荷超 16384 个字符:当场停驻,不等窗。
60
+ · 形 F 开帧一直悬着、窗先走完:没呈出 ⇒ 停驻;run 取消、非 detach 腿断连、腿退场清扫:腿中止,不停驻、不抛。
61
+ · 形 G 自成一形、不在落点表内(落点之前:上游门拒,问到不了投递;**不归形 E**:E 是没有卡可答而当场停驻,这一形是拒、没有停驻行):durable 部署上 `permissionMode: "dontAsk"` 的 run —— 见下面 `dontAsk` 一段。
62
+ 抛的那一臂,工具结果在 posture interactive 的流(连着、未声明 `x-detach-on-disconnect`、部署有活体审批席、请求没把 `approvalReach` 声明成 `none` —— 本服务只在这几条都成立时向引擎声明 interactive)上是 coded `question.human_channel_failed`,其余(形 B、不声明 interactive 的连着的流)是「按你的判断继续」的续跑卡 `reason: callback_failed`;两句都引本服务的原话「the question was shown on this run's live stream and nobody answered it within its <窗>s answer window」。`question_complete{unanswered}` 照发;窗走完之后 respond 恒 404。
63
+ 同 ALS 的**同步**委派子代(`Agent` 不带 `run_in_background`)读父腿同一张卡:父腿是形 A / B ⇒ 同一个失败臂(卡面随父腿的姿态);父腿是形 D ⇒ 没有停驻车道(引擎不给同步子代转交 durable 声明):卡照投、窗走完子代得到引擎的拒(工具未执行、拒句让模型按自己的判断继续;子代接着跑、不铸 checkpoint)。此前这两形读的是自答续跑卡(连着的 interactive 流上是 coded `question.human_unavailable`),两版都不停驻。
64
+ detach 流上断开的先后只通过一件事起作用:卡出现那一刻直写面接没接下(接下 = 形 B,没接下 = 形 C),落点本身只读这一件事;`UNATTENDED_APPROVAL_POLICY` 只管审批问,`deny` 部署上问题照读各形。verify / cascade 组合入口的续跑腿没有人在环上下文(三族同,两版同)。
65
+ 例外(两版同):run 被取消或非 detach 腿断连 ⇒ 腿中止、不停驻;停驻环帽已满 ⇒ 不铸 checkpoint。`question_complete` 是尽力投递的收帧,停驻以 run 的 `suspended` 终局为准(窗到期到停驻铸好之间有一段 respond 已 404、`/v1/approvals` 还没有这一行的空档)。
66
+ **`permissionMode: "dontAsk"`(形 G,落点表之外;两形之差)**:旧形(7.149.0 及以前):durable 部署上那只服务端预判门放行问题工具,后台腿与连着的流上带 `dontAsk` 的 run 里问题卡照投、窗内可答。新形:预判门撤掉后,问题工具经引擎原形的问答策略答 `ask`,引擎模式表对这一只 `content_question` ask 判拒(hook 自己的 ask 除外)—— 零问题行、零卡、不停驻、不铸 checkpoint,工具未执行,模型读到 `isError` + `notExecuted` 的 dontAsk 拒句。非 durable 部署不变:今天照引擎的 live 问形送卡(live 形里问题工具不铸 ask、执行时直接问问题席,模式表读不到它),随引擎让 live 形也铸 ask 翻面后同答「拒」;本服务不在下游再造一只模式判官。**谁受伤**:durable 部署上在 `dontAsk` 里仍指望问题卡送达的提交方。**出路**:要人答问换 `permissionMode: "default"` / `"acceptEdits"`;不要问题工具带 `interactiveTools: false`;零新旋钮。
67
+ **谁需要**:durable 部署上的人 —— 后台没在窗内答的问,run 停下等人而不是按猜往下跑;续跑之后的新问也有卡可答;连着的人看过卡、窗内没答的问不再又交给第二个决断者,也不再被说成「没呈给人 / 没人可达」。
68
+ **谁受伤**:依赖「问了没人答也会接着跑」的无人值守提交方(后台 run、开帧投不出 / 撞帽的腿)—— run 现在停在 `suspended`,要有人去 decide;续跑腿上的新问多等一个窗才停驻;`declined_unavailable` / `question.human_unavailable` 的分诊(壳与告警按它判「窗到期」)在卡呈出过的那一形上读不到这两个词了;同步委派子代的模型读到的工具结果换了(父腿是形 D 时由自答续跑卡换成引擎的拒)。
69
+ **出路**:不想让问题挡住 run 的提交方在请求体带 `interactiveTools: false`(逐 run;模型根本不问);`QUESTION_TTL_MS` 调窗长(上限 1 小时);要「回头再答」改用 `POST /v1/runs`(卡只进账本 ⇒ 停驻、`GET /v1/approvals` 取回、`decide` 带 `answer` 补答);按码分诊的消费方把 `callback_failed` / `question.human_channel_failed` 读作「呈出过、没拿到答」;部署级不要任何停驻 ⇒ `DURABLE_APPROVAL=false`。
70
+ **升级前做**:无(零配置、零存储形变化;升级前停驻的问题行照常可赎)。**升级前自查**:durable 部署上提交 `permissionMode: "dontAsk"` 又指望 AskUserQuestion 的卡送达的调用方(7.149.0 的后台腿与连着的流上卡照投)—— 本版起零卡、模型读到拒句,按上面 `dontAsk` 一段的出路改。
71
+ **回滚**(本版 → 7.149.0):回到「旧形」一段写的 7.149.0 的形 —— 那一版的服务端预判门在这条腿有问题上下文、且直写面此刻可达时放行,窗走完协调器对一切没拿到答的问都答 `unavailable`,引擎自答续跑、不铸 checkpoint:后台腿与连着的流上没人答的问不再停驻、答案补不回;卡呈出过而窗走完的问回到 `declined_unavailable` / coded `question.human_unavailable`;续跑腿的新问回到当场停驻、零卡、respond 404;principal 带 `forceDurableGate` 的 run 在有上下文的腿上又照样执行问题工具。无存储形变化:本版停驻着、还没人 decide 的问题行回滚后仍是停驻行,7.149.0 的赎回腿对这类行同样判 ask、经 decide 带 `answer` 补答。`permissionMode: "dontAsk"` 的 durable run 回滚后回到卡照投、窗内可答(预判门放行)。
72
+
73
+ 6. **fleet 成员起步即在服务租约围栏内(推定),不再因「从没听到中心」而永久不受围栏(只影响设了 `FLEET_GENERATION` 的部署;行为收紧;wire 零新键、零新码 —— 变的是 `leaseState` 的取值时机与 `state.lease_expired` 的触发条件;部署零动作,除非心跳一直到不了带键的中心)**:
74
+ 旧形:心跳从起步起一直得不到中心的能力声明(中心不可达 / 401 / 404 / 400 / 2xx 而不是声明)⇒ `leaseState` 恒 `none`、零续租、零 503(心跳失败零计数;答复不是声明那一形另有一只计数,但成员同样不受围栏)—— 被吊销后重启、换节点而卷不随、没有数据根的实例照常接新工作,永不受围栏。
75
+ 新形:没有任何声明之前按「围栏在、还没授予」处置 —— `leaseState: "expired"`,失联钟从本进程起点算,过 `FLEET_LEASE_OUTAGE_MAX_MS` 新工作 503 `state.lease_expired`(`Retry-After: 60`;取消 / HITL 应答 / 续跑 / 读面照放行,`/readyz` 不翻,在飞不动;拒体与续租不通那一臂逐字同)。推定期不续租。第一条声明定推定:宣告了 `servingLease` ⇒ 照常续租(钟起点仍是本进程起点);声明不带位(`{ capabilities: {} }`)⇒ `none`,与修前同。只有显式的 `capabilities` 声明对象是中心的话;心跳 401 / 404 / 400 / 不可达 / 超时 / 答复没有 `capabilities` 键都是「没听到」,推定照旧。听不到的每一拍计 `server.fleet.serving-lease-unconfirmed`(`why=heartbeat:…`;与续租没确认同一段、同一行 warn `fleet_serving_lease_unconfirmed`,宣告不收尾、续租判决或声明不带位才收尾,一行 `fleet_serving_lease_recovered`,带 `by`)。推定的终结(第一条声明)、续租判决、被拒引起的租约态翻转即刻再公告一拍心跳(首拍心跳因此是 `expired`,数据根记着拒的成员是 `denied`;第一条声明 / 第一次授予一到即刻再报 `none` / `granted`);推定终结之后心跳答复引起的位翻转(新老副本交替答心跳)与授予到期随心跳节拍。
76
+ 谁需要:靠围栏吊销 / 退役 fleet 成员的部署 —— 被吊销后重启的实例也受 `T_max` 定界。
77
+ 谁受伤:① 心跳从起步起一直不通的成员(活性基址错、令牌错、中心拒公告体)过 `T_max`(缺省 24 h)拒新工作(修前静默照接),多租形里额度谈判照通也一样(额度门与围栏门是两只门、两条基址);② 成员首拍心跳报 `leaseState: "expired"`(修前 `none`),答复 / 授予一到即刻再公告 —— 按首拍计数的告警只在一个往返的窗里看到它;③ 每个进程各自一段 `T_max`(钟不跨重启,重启即重计);中心一条显式的「不带位」声明照旧解除围栏、之后不限时;④ 心跳实际打到的路径答没有 `capabilities` 键的 2xx(活性基址单独路由到别的后端、回滚到带键之前的中心、网关 / 维护页答 `200 {}`、手配成员 × 老中心)的成员恒推定,满 `T_max` 拒新工作。
78
+ 补偿:`FLEET_LEASE_OUTAGE_MAX_MS`(缺省 24 h,地板 60 s)定这段容忍;到点之前逐拍计数 + 一段一行 warn(运维行 `fleet_announce_failed` 带中心拒句)+ `/health.leaseState: "expired"`;中心一答即恢复(声明不带位 ⇒ `none`,宣告 + 授予 ⇒ `granted`);让心跳路径答到带 `capabilities` 键的中心;不要围栏的单机形去掉 `FLEET_GENERATION`。
79
+ 嵌入方:`createHttpServer` 收到租约读数 `servingLease` 而 `config.fleetGeneration` 缺席 ⇒ 构造期拒启(「createHttpServer: servingLease is wired but config.fleetGeneration is not: … refusing to start rather than ignore the lease reading」)—— 成员身份只有代际一个开关,读数在、代际不在的装配里读数永远读不到、准入永远不按租约拒(7.149.x 上这一形准入照按租约拒而 `/health` 不报)。补 `config.fleetGeneration`(本实例的代际),或不注入读数;本服务自己的入口只给成员装租约客户端,部署零变化。
80
+ 勘误:7.121.0 节第 4 条「租约只在中心于心跳答复里宣告 `capabilities.servingLease: true` 之后生效;没宣告 ⇒ `leaseState: "none"`」自本版起只对中心读得出的声明成立 —— 从没听到声明的成员是 `expired`(推定);同条「那只文件写不进 ⇒ 重启后在第一次续租答复之前会接单」的窗,在心跳一直不通时以 `T_max` 为界(每个进程一段)。
81
+
82
+ 7. **停-迁-起守卫的旧副本证据改按「持有者知不知道这条迁移」判:副本能力登记与起服准入两张常设表(SQL 后端起服自建);拒因词 `live_runs` 改名 `older_replica`;新拒启码 `config.serving_requirement_unmet`(行为变更;机读词硬断;持久面:两张新表;部署零动作)**:
83
+ 旧形:四只停-迁-起守卫(M40 · M41 · M42 · M51)与撤回出口的启用标记(M43)判「更早版本的副本还在服务吗」只看判活窗(`REAP_RUN_STALE_SEC`)内有没有 `running` run 在心跳,不分持有者 —— 本版副本自己的 run 也被读成「可能是旧副本」,撤回出口的标记在整天有 run 的部署上落不下。
84
+ 新形:每只走 SQL 一次性迁移段的进程(本服务,与走公开序列 `createStoreBackend()` → `ensureSchema()` → `runOneTimeMigrations()` 的嵌入方)在段头、取迁移锁之前往 `replica_registration` 登记一行:进程身份(与它写在 run 行上的持有者、`/health` 的 `instanceId` 同一只)→ 本构建的能力名单 + 发布线 + 主机名 / pid + 起服时刻;只写一次、不续期、停下不删,登记时删掉 90 天前别人的行。
85
+ 守卫的证据只有一只读法:判活窗内心跳过的 `running` run,减去登记为知道该条迁移的持有者 —— 剩下的才挡起服;拒句点名没登记的 run 数,并逐只点名登记了而没有这项能力的持有者(实例 / 发布线 / 主机 / pid)。
86
+ 起服准入:段头锁外读一次 `serving_requirement`、取到迁移锁之后在锁连接上再读一次;库上记着本构建没有的能力(`state` 两词闭集 `pending` / `in_force`,读出别的词按 `in_force`)⇒ 拒 `config.serving_requirement_unmet`(`capability_missing`),一步迁移不跑;两张表不在 ⇒ 同码拒(`table_absent`,拒句点名公开序列)。本版不写 `serving_requirement`(表空 ⇒ 准入恒过):它为之后的停-迁-起迁移预装。
87
+ 机读词硬断:三只拒句的拒因词 `live_runs` 改名 `older_replica`(`config.session_ext_move_refused` / `config.scenario_prefix_switch_refused` / `config.session_deleting_switch_refused`;零别名;`copy_mismatch` / `stored_prefix` 不变)。
88
+ 看不见的边(如实,这一句订正 7.147.0 / 7.148.0 / 7.149.0 节「守卫只看心跳」诸句的射程,不改它们的结论):登记不续期、不观察,**不是围栏**。早于本版的副本不登记 —— 空闲时(零 `running` run)照旧看不见,M40–M51 要防的旧副本全在这一类(终局边界;停光旧副本仍是运维的第一道,见本节段头「停-迁-起」一行);停顿 / 与库断开的旧副本迟到的写,任何观察都关不上。M41 拷贝 → 核对 → 删列三步之间的旧写丢失是 7.147.0 节已披露的边,本版不改。准入挡的是起服者,不挡已在服务的副本。
89
+ 谁需要:多副本滚动升级的 SQL 部署 —— 撤回出口在繁忙部署上终于启用(这一收益只活到 M43 删码:那之后撤回出口无条件启用);之后的停-迁-起迁移有准入可读。谁受伤:按拒因词 `live_runs` 告警或分支的运维规则;跳过 `ensureSchema()` 直接调 `runOneTimeMigrations()` 的嵌入方(拒 `table_absent`);每次起服在迁移段头多两句写(删 90 天前别人的登记行、缺席才落本进程一行)。空闲旧舰队上的滚动升级此后会被写了准入要求的停-迁-起迁移拒启 —— 以前可能「意外滚成」,这是预期行为。补偿:拒句点名持有者;嵌入方按公开序列开店;按升级清单停光再起。回滚:上一版不读这两张表,表与行留着无害。
90
+
91
+ 8. **`listening` 启动行不再随 `LOG_LEVEL` 消失,并多一键 `boundAddress`(行为变更 + wire 加性;部署零动作)**:
92
+ `listening` 是启动协议行 —— 起本进程的宿主从 stdout 读它取口与实绑地址 —— 档位只滤诊断行,`LOG_LEVEL=warn` / `error` 下这一行照出(每次起服一行,行上 `level` 仍是 `info`)。
93
+ 同一行多一键 `boundAddress` = 内核实际绑上的地址(`::` / `0.0.0.0` / `127.0.0.1` / `::1` 原样),与请求绑的地址 `bindHost`(键名与值不变)分开两格。
94
+ **谁需要**:自起本机引擎、按「实绑地址 + 口」认归属的宿主,与在任何档位下读口的嵌入方;**谁受伤**:把档位调到 warn / error 并断言 stdout 为空的部署 / 脚本(多这一行),
95
+ 按整行逐字节匹配 `listening` 行的脚本(多一键);**补偿**:按 `level` 字段筛日志的管道照旧筛得掉它(`level` 为 `info`);按键读的消费方零动作;不设关它的旋钮。
96
+
97
+ 9. **起服拒句的升级清单第 2 步按库实况列:只列这只库还欠的停-迁-起迁移;一条都不欠(如起过 7.148.x 的库)⇒ 没有这一步、不说「不可滚动」(运维文案变;守卫的判据、拒码与出路不变;wire 零新键、零新码;部署零动作)**:
98
+ 旧形(7.149.x):缺列拒 / 单表探针拒 / 判活守卫拒句尾的 `Upgrade checklist` 第 2 步按登记表列本构建带的全部停-迁-起迁移(M40 · M41 · M42 · M51)并说 "this release cannot be rolled across replicas of both versions",不判这只库欠不欠(7.149.0 节段头与部署前提文档的升级清单一节如实写了这一保守句)。
99
+ 新形:第 2 步逐条问那条迁移的守卫自己的判据(M42 / M51:标记行在不在;M40:带 `execution_lane` 列的表上还有没有退役词;M41:`session_meta` 上还有没有部署格列),只列还欠的;一条不欠 ⇒ 清单只有「执行语句 → 起服」两步。某一条读不出欠不欠 ⇒ 照列它并在那一行说没读到(多停一次,不漏停)。
100
+ 谁需要:升级运维 —— 只漏了 DDL 的库照第 1 步加列即可滚动,拒句与段头说同一件事。谁受伤:按清单全文或 "cannot be rolled" 字面匹配的脚本。补偿:按码判别(码不变)。回滚:上一版照旧全列。
101
+
102
+ 10. **活卡上的 `rememberRefused` 改读「这一问 + 这条腿」:没有会话的腿、读不出会话住客令牌的腿(会话店不带执行席 —— 引擎缺省的内存会话店)上,活卡帧、呈卡帧的卡、`livePending` 行也带 `rememberRefused`(行为修复;wire:这一位在场的情形变多,键与值域不变;零新码;部署零动作)**:
103
+ 旧形(7.116.0–7.149.x):卡面的 `rememberRefused` 只问这一问本身记不记得住;那两形腿上点 `allow_session` 回执 `rememberApplied:false`(7.149.0 第 37 条),卡面却不带这一位 —— 壳照渲「本会话放行」,点了只放行一次、下一问照样出卡;契约用「本位不管那两形」一句托住。
104
+ 新形:卡面与回执读同一只判官 —— 卡带 `rememberRefused` ⇔ 点 `allow_session` 回执 `rememberApplied:false`(写失败 / 提交歧义是落定之后才知道的,仍只在回执上说)。契约那句例外删。读不出住客的腿上客户端照样发 `allow_session` 时,那一行 warn `session_grant_refused_occupant_unreadable` 照留。
105
+ 谁需要:用引擎缺省内存会话店的嵌入方、无会话的 adhoc 腿上的壳用户。谁受伤:无。补偿:要「本会话放行」生效就给会话店带执行席(`sessionStorage.executionPlacement`;SQL 两方言与 local 后端恒带)。回滚:上一版卡面不带。
106
+
107
+ 11. **订正 7.149.0 节与 7.147.0 节的三处文字(文字订正;零行为变化;wire 零变化;部署零动作;已发节不改字节,本条即订正)**:
108
+ ① **7.149.0 节第 1 条**把 `/metrics*` 与 `GET /v1/tasks/source-summary` 合写成一句「`401 auth.unauthorized`(运维面句;配了 `METRICS_TOKEN` 时出示它照放,没带它的请求先答 `401`、不先读主体头)」。
109
+ **订正**:两只只读凭据各认各的口 —— `METRICS_TOKEN` 只开指标口(`/metrics*`),`TRACE_TOKEN` 只开 `GET /v1/tasks/source-summary`,互不通用(出示 `METRICS_TOKEN` 读 `source-summary` 照答 `401`,出示 `TRACE_TOKEN` 读 `/metrics*` 同);
110
+ 「没带它的请求先答 `401`、不先读主体头」是配了 `METRICS_TOKEN` 的 dev-open 部署上指标口的读法,`source-summary` 的判据里没有 `METRICS_TOKEN` 这一格。该条里「报了主体的请求对这两口答 `401 auth.unauthorized`」一句不变,401 句本身(「METRICS_TOKEN for the metrics endpoints, TRACE_TOKEN for the task source summary」)本来就分开写。
111
+ ② **7.149.0 节第 15 条「升级前自查」**的例句(`E2B_SANDBOX_ENV=GIT_TOKEN`,而 `GIT_TOKEN` 与 `GIT_API_TOKEN` 同值 ⇒ 本版拒启)**只对 `REMOTE_EXEC=e2b` 的部署成立(e2b 车道)**:三只沙箱名单(`E2B_SANDBOX_ENV` / `DOCKER_SANDBOX_ENV` / `K8S_SANDBOX_ENV`)只在自己的车道被选中时才求值,
112
+ 别的车道上它们是无关配置(不校验、不拒启)。host 车道上自查的是 `LSP_ENV_ALLOW` / `HOOK_ENV_ALLOW`(两只在单用户姿态下照判,多租户姿态下名单被忽略)。其余自查与出路不变。
113
+ ③ **7.147.0 节第 18 条末尾「引擎已知限度(随版发)」一项**里括号点名的三枚短号是引擎的内部编号,读者无从查证、也不需要 —— 三条限度本身的说明都在同一项的正文里;那一节由发版链门逐字守着、字节不改,本条即披露(此后随包文字不再带这一类编号)。
114
+ **谁需要**:按 ① 配了 `METRICS_TOKEN`、想用它读 `source-summary` 的运维,与按 ② 的例句在 host 车道上自查名单的运维(例句在 host 车道上不会拒启,不必为它改名单)。**谁受伤**:按订正前的句子拿 `METRICS_TOKEN` 读 `source-summary` 的人 —— 实现一直答 `401`,从没放过,没有行为可退。
115
+ **补偿**:`source-summary` 用 `TRACE_TOKEN`(只读)或不带主体头的 service token(在部署让它代自己说话时);指标口用 `METRICS_TOKEN`。
116
+
117
+ 12. **device 执行器协议修订 5:指令里的路径只锚不折,服务端只受理修订 5 —— 声明修订 4 及以下的执行器在 hello 收 `protocol_version_unsupported`、注册被拒(wire:`deviceExecutor.protocolVersion` 与 challenge 帧的值 4 → 5,受理下沿 1 → 5,`helloReject` 加一键 `message`;零新码;**部署动作:device 车道先升执行器**)**:
118
+ 本版起服务端交给设备的每条路径(`args.path` / `args.paths` / `cwd`)绝对的原样、相对的锚在会话工作目录上,`.` / `..` / 空段一律不折,由设备的文件系统解(协议文档 §2.1 修订号表第 5 行、§3.1、执行器义务第 6 条)。
119
+ 写给修订号 4 及以下的执行器认的是折过的路径(已发的 cli 执行器对 `.` / `..` / 空段一律拒收),所以受理下沿同版抬到 5:它们在 hello 收 `{ "t": "helloReject", "code": "protocol_version_unsupported", "minProtocolVersion": 5, "message": "device protocol revision 4 is below the lowest revision this server accepts (5): since server 7.150.0 every path an instruction carries (args.path, args.paths, cwd) arrives unfolded, with \".\" / \"..\" / empty segments kept for the device's filesystem to resolve; upgrade the executor to one that declares revision 5 or above" }`,
120
+ 服务端同一句进 warn 行 `device_hello_version_rejected`(`declared` / `minProtocolVersion` / `protocolVersion` / `message`)并计 `device_hello_rejected_total{code="protocol_version_unsupported"}`。修订号律随之恢复「语义变更升号」:一处变更旧执行器守不住 ⇒ 同版抬受理下沿,一次响亮拒,不逐调用失败。
121
+ **谁需要**:device 车道的部署 —— 路径语义变了,旧执行器在注册时就被拒、拒句说清缘由,而不是含 `.` / `..` 的文件工具调用逐个失败(或被按字符串前缀判根的执行器放过 `<root>/../etc`)。
122
+ **谁受伤**:先升了服务端、执行器还是修订 4 及以下的 device 部署 —— 设备连不上,落在该设备上的 run 读到设备不可用;按 `device_hello_rejected_total` 只看 `auth_failed` 的告警多一个 `code` 取值。
123
+ **补偿**:先升执行器、再升服务端(协议文档 §1.2:实现修订 5 的执行器对上沿 4 的老服务端声明 4,先升执行器不断连);执行器一时升不了 ⇒ 服务端留在 7.149.x。零旋钮、零折叠臂(下沿不可调:调低 = 把未折路径发给读不了它的执行器)。回滚:回到 7.149.x,修订 5 的执行器照旧按修订 4 连上;无存储形变化。
124
+
11
125
  ## 7.149.0 —— 1–40 条见下
12
126
 
13
127
  **升级清单(本节唯一的一张;下面各条只讲为什么,不再各自重述升级步骤)** —— 一次读清本版对一只存量部署要做的全部动作。每版同形:① 手工 DDL(逐表逐句,两方言各一份)② 起服自建与一次性迁移 ③ 停-迁-起 ④ 顺序;后续每版的段头照此写,新增一句 `ALTER`、一条一次性迁移、一条停-迁-起时,先改这里,再写条目。
@@ -5554,7 +5668,7 @@
5554
5668
  `ensureSchema()` 自身先探后建,与本服务自己的起服路径是同一条路;7.126.0 上同一序列遇退役 schema 是在 `runOneTimeMigrations()` 里拒,本版前移到 `ensureSchema()` 里拒(同一只 `RetiredSchemaPresentError`,码 `config.schema_mismatch`,拒之前零 DDL),
5555
5669
  包根不新增也不需要任何别的建连动词;按 `code` / `instanceof RetiredSchemaPresentError` 分派的代码不用改。
5556
5670
  - **补偿**:无需动作,也无旋钮。
5557
- 10. **升级顺序的唯一出处(跨仓)**:控制台 / 编排器 / 引擎的升级顺序只在 sema-web-admin 的 `DEPLOYMENT.md` §1「升级顺序(唯一出处)」一张表上(按版本加例外行)。本文件自本版起**不再重述顺序句**;此前各节里的顺序句(7.124.0 第 1 条、7.126.0 节头)是当时的快照,与该表不一致时以该表为准。
5671
+ 10. **跨组件的升级顺序不由本文件另立一张表**:控制台 / 编排器 / 引擎三者之间谁先升,本服务这一侧对顺序的要求(先停光旧副本、先升读者再放写、先客户端后中心 ……)写在对应那一版的节里 —— 按你要升到的那一版的节读;本文件自本版起**不再重述顺序句**。此前各节里的顺序句(7.124.0 第 1 条、7.126.0 节头)是当时的快照,与较新版本节里的升级序不一致时,以较新的为准。
5558
5672
 
5559
5673
  ## 7.126.0 —— 🔴 会话 id 只收 canonical uuid(提交口 / A2A / run-local;自选名改成自留映射)· 🔴 团队编排面退役(请求键 `council` `debate` / `discuss` 场景 / `councilRuns`,零别名)· 🔴 角色词六个 · 🔴 审批卡词 `contentKind` → `approvalCard` · 🔴 `allowTools` 未知名拒跑 · 存量 SQL 库:`task_run` 加一列(幂等体指纹)+ 新表 `workspace_space_commit`(建表腿自动;期限列随行)· 会话删除 / 留存 sweep 回收空间快照对象(桶生命周期规则只作兜底)· 一次性迁移 M1–M3 退役:从没在 7.125.x 起过服的旧库起服即拒 ⇒ 先过 7.125.x 一次 · `WORKSPACE_PERSIST` 缺省 `off` → `space` · 记忆店根搬家 ⇒ 拒启 · 远端车道相对 `cd` 后删除问人 · 升级序:web-admin 先(council 键退役)、再 server;核心 7.49.5 / settings-schema 12.2.1 两包精确钉
5560
5674
 
@@ -7574,8 +7688,7 @@ wire 面(同步提交撞 park 的 200 体、`GET /v1/runs/:id` 行形、同一
7574
7688
  `mounted: true` ≠ shell 可达。operator 面的 `configFingerprint` **一次性变值**
7575
7689
  (同装配各腿仍相等)—— 持久化 / 钉了指纹的读者会见一次跳变。
7576
7690
  - **`GIT_CONFIG_KEY_<i>` 不再被剥**(core 7.33.1):宿主子进程(host shell / git 腿 / 语言服务器 / bake-runner)里,普通的 git env
7577
- 配置组原样到达;只有某个 KEY 的值带 `<scheme>://<userinfo>@`(令牌 insteadOf)时这一条扣下(7.103.0 是本仓整组扣;7.103.1 起判定全在 core 7.33.6,见本段末条)。🔴 **安全轴披露(core
7578
- KL-1129a)**:`GIT_CONFIG_VALUE_<i>` **不在** core 的凭据形状判据里 —— 组里 KEY 是 `http.extraHeader`、VALUE 是 `Authorization: Bearer …`
7691
+ 配置组原样到达;只有某个 KEY 的值带 `<scheme>://<userinfo>@`(令牌 insteadOf)时这一条扣下(7.103.0 是本仓整组扣;7.103.1 起判定全在 core 7.33.6,见本段末条)。🔴 **安全轴披露(core 的已知限度)**:`GIT_CONFIG_VALUE_<i>` **不在** core 的凭据形状判据里 —— 组里 KEY 是 `http.extraHeader`、VALUE 是 `Authorization: Bearer …`
7579
7692
  这一形会**原样**进宿主子进程(含模型能驱动的 host shell),并在 `isolateConfig` 的 git 腿上作为命令级配置**生效**(`GIT_CONFIG_GLOBAL /
7580
7693
  SYSTEM=/dev/null` 挡不住 env 这一层)。**迁移**:不要把凭据放进服务进程 env 的 git 配置组;要给 git 带凭据,走 credential helper / ssh key /
7581
7694
  写在 `durableRemote` 里。⤷ **7.103.0 已随 core 7.33.2 修掉**(车PA 提货):core 给凭据侧加了**值族表**,
@@ -7587,10 +7700,10 @@ wire 面(同步提交撞 park 的 200 体、`GET /v1/runs/:id` 行形、同一
7587
7700
  其后任意位置有 `@` 或 `%40` 即剥;proxy 地址键下值里有 `@` / `%40` 即剥(密码含未转义 `/`、括号形、单斜杠 `https:/u:t@h`、`ssh://t%40h/…` 等此前
7588
7701
  漏过的写法一并剥 ⇒ 这一对扣下)。**谁受伤**:git env 配置组的键或值里 `:/` 之后带 `@` / `%40` 的(含 `@scope` 包路径写法、`%40` 编码形;不限 URL ——
7589
7702
  scp 绝对路径 `host:/repo@v2.git`、`http.extraHeader = Referer: https://…/@scope`、`includeIf.gitdir:/home/a@corp/.path` 同)那一对不到宿主子进程
7590
- (多剥,core KL-1129b;7.103.0 是整组不到,7.103.1 起同组其余条目照到)。**出路**(只对不带凭据的这类配置;凭据照旧走 credential helper / ssh key / `durableRemote`,`~/.gitconfig`
7703
+ (多剥,core 的已知限度;7.103.0 是整组不到,7.103.1 起同组其余条目照到)。**出路**(只对不带凭据的这类配置;凭据照旧走 credential helper / ssh key / `durableRemote`,`~/.gitconfig`
7591
7704
  模型可读):不隔离全局配置的腿(host shell / 语言服务器 / bake-runner / leader 推送)读宿主全局 git 配置,写进 `~/.gitconfig` 即可;center-plugins
7592
- 克隆腿每次在新目录克隆、且隔离全局 / 系统配置,这类配置在该腿上**没有替代通道**,只能改写成 `:/` 之后不带 `@` / `%40` 的形,或候 core 收窄
7593
- KL-1129b;project-memory 只跑 `git log` / `git status`,不读这类配置。不受影响:值里没有 `:/` 的 scp 相对路径(`git@host:org/repo.git`)与 `:/`
7705
+ 克隆腿每次在新目录克隆、且隔离全局 / 系统配置,这类配置在该腿上**没有替代通道**,只能改写成 `:/` 之后不带 `@` / `%40` 的形,或候 core 把这条「多剥」
7706
+ 收窄;project-memory 只跑 `git log` / `git status`,不读这类配置。不受影响:值里没有 `:/` 的 scp 相对路径(`git@host:org/repo.git`)与 `:/`
7594
7707
  之后没有 `@` / `%40` 的值;7.103.1(core 7.33.6)起 `ssh://` / `git+ssh://` / `git://` 的 userinfo 只是登录名(`ssh://git@github.com/…`,键的子节或整条值)
7595
7708
  也不再扣。
7596
7709
  ⤷ **7.103.1(core 7.33.6 精确提货;车PG 退役本层、车PK 提 7.33.6):git env 配置组的判定全在 core,本仓那一层退役**。7.103.0 的「整组扣」让同组的
@@ -7610,7 +7723,7 @@ wire 面(同步提交撞 park 的 200 体、`GET /v1/runs/:id` 行形、同一
7610
7723
  `secret_env_git_config_pairs_withheld_total` / warn `git_config_pair_withheld` 从未发布、随层删除。
7611
7724
  **点名放回**(`LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW`)按名叠在剥除结果之上:一条被扣的配置把 `GIT_CONFIG_KEY_<i>` 与 `GIT_CONFIG_VALUE_<i>` **两只一并点名** ⇒
7612
7725
  这一条原样交出(COUNT 本就原样,点不点同;v7.103.0 文档写的出路 —— 点名全部 KEY / VALUE —— 照旧成立);只点一只 = 与另一只的占位半条配成一对。
7613
- **迁移**:凭据照旧不进 env 配置组。KL-1129b 多剥的无凭据配置,core 7.33.5 Pickup 段(git 2.54 真验)给的可用写法:`includeIf.gitdir:` 路径里的 `@` 写成
7726
+ **迁移**:凭据照旧不进 env 配置组。上面「多剥」的无凭据配置,core 7.33.5 Pickup 段(git 2.54 真验)给的可用写法:`includeIf.gitdir:` 路径里的 `@` 写成
7614
7727
  glob `?`;`safe.directory` 写进宿主自有的全局配置文件(`GIT_CONFIG_GLOBAL`;仓库级配置对它无效);`@scope` 包 URL 写进仓库级配置。(把 `@` 写成 `%40`
7615
7728
  同样触发判据 —— 不可用。)**已知边界**(core 7.33.6):① 子进程看得见占位行 `sema.withheld=`(见上);② 运行时才拼出来的口令(`$(…)` / `$VAR` / `read` 取值、
7616
7729
  由展开给出的程序名)core 读不到、照原样交出;③ `http.extraHeader` 下的**非凭据**头(trace / 路由头)照样按键扣,要它到达写进 git 配置文件。
package/README.md CHANGED
@@ -210,7 +210,7 @@ The server is configured entirely through environment variables. The most import
210
210
 
211
211
  | Variable | Default | What it does |
212
212
  |----------|---------|--------------|
213
- | `PORT` | `8090` | HTTP listen port. `0` = let the OS pick one: the `listening` log line (stdout, info level) and `/health.port` report the port actually bound |
213
+ | `PORT` | `8090` | HTTP listen port. `0` = let the OS pick one: the `listening` startup line (stdout, one JSON line at info level; a startup protocol line, not filtered by `LOG_LEVEL`) and `/health.port` report the port actually bound; the same line's `boundAddress` is the address the kernel actually bound |
214
214
  | `BIND_HOST` (alias `HOST`) | see note | Listen address. An explicit `BIND_HOST` **always wins**. Default: `127.0.0.1` when the write face is unauthenticated (`ALLOW_UNAUTHED_WRITES=true` **and** no service token configured), otherwise all interfaces — deployments with a token are unaffected. Since 3.15.0 the `HOST` alias is **not** fully equivalent: shells commonly set `HOST` to the machine name without the operator knowing, so when the narrowing condition above holds it wins over an inherited `HOST` (logged as `bind_host_from_HOST_env_overridden`). To expose the write face, configure a service token — on a single-user deployment (`REQUIRE_PRINCIPAL` off) an explicit non-loopback `BIND_HOST` with an unauthenticated write face refuses to boot (invariant `single-user-anonymous-boundary`); on a multi-tenant deployment an explicit `BIND_HOST` still exposes it. |
215
215
  | `MODEL_GATEWAY_BASEURL` | **no default** | OpenAI-compatible gateway base URL (without `/chat/completions`). **No factory default**: every route that rides the openai-compatible lane must resolve to an explicit endpoint — the model's own catalog `baseUrl`, or this knob — else the server refuses to boot, naming the models and printing the explicit local spelling (`MODEL_GATEWAY_BASEURL=http://127.0.0.1:8000/v1`). An Anthropic-only deployment may leave it unset. Setting `MODEL_GATEWAY_FALLBACK_URLS` without it is refused too |
216
216
  | `MODEL_ID` | **required unless a catalog supplies the default** | Default model id — **no factory default since 3.0.0**. Since 7.101.0 the check runs **after** the catalog is applied: `MODEL_ID` **or** a usable catalog default (`config.d/models.json` / the web-admin) is enough; with neither the server refuses to boot (`config.default_model_missing`, naming both ways out). A catalog that is adopted replaces the env model table, so `MODEL_ID` is then not served: the boot is not refused (even with no gateway, when the catalog entries carry their own `baseUrl`) and logs one `config_env_set_but_ineffective` line naming the catalog default that serves (exactly once, at the moment the catalog is first adopted — at boot, or later when it arrives after the boot fetch budget / on a refresh). The old baked-in default was an internal-only model name, so every external deployment failed later and further from the cause: a gateway `400` plus a cascade of title-hook warnings |
package/README.zh-CN.md CHANGED
@@ -164,7 +164,7 @@ curl -s localhost:8090/v1/tasks -H "Authorization: Bearer <SERVICE_AUTH_TOKEN>"
164
164
 
165
165
  | 变量 | 默认 | 说明 |
166
166
  |------|------|------|
167
- | `PORT` | `8090` | HTTP 监听端口。`0` = 让 OS 分一个:`listening` 日志行(stdout,info 级)与 `/health.port` 报实际绑到的口 |
167
+ | `PORT` | `8090` | HTTP 监听端口。`0` = 让 OS 分一个:`listening` 启动行(stdout 一行 JSON,info 级;启动协议行,不随 `LOG_LEVEL` 滤掉)与 `/health.port` 报实际绑到的口;同一行的 `boundAddress` 是内核实际绑上的地址 |
168
168
  | `BIND_HOST`(别名 `HOST`) | 见说明 | 监听地址。显式 `BIND_HOST` **恒生效**。缺省:写面无鉴权时(`ALLOW_UNAUTHED_WRITES=true` **且**未配任何 service token)= `127.0.0.1`,否则全接口——配了 token 的部署不受影响。自 3.15.0 起 `HOST` 别名**不再完全等效**:shell 常把 `HOST` 设成机器名而 operator 并不知情,于是上述收窄条件成立时,收窄压过继承来的 `HOST`(告警事件 `bind_host_from_HOST_env_overridden`)。要暴露写面,配一个 service token —— 单用户部署(`REQUIRE_PRINCIPAL` 未开)上写面无鉴权时显式非回环 `BIND_HOST` 起服拒(不变量 `single-user-anonymous-boundary`);多租部署上显式 `BIND_HOST` 照旧暴露。 |
169
169
  | `MODEL_GATEWAY_BASEURL` | **无缺省** | OpenAI 兼容网关地址(不带 `/chat/completions`)。**无出厂缺省**:每一条落 openai 兼容腿的路由都必须解析到显式端点 —— 目录条目自己的 `baseUrl`,或本键 —— 否则拒启,拒因点名模型并给出本机写法(`MODEL_GATEWAY_BASEURL=http://127.0.0.1:8000/v1`)。anthropic 单路由部署可不设。只设 `MODEL_GATEWAY_FALLBACK_URLS` 不设本键同拒 |
170
170
  | `MODEL_ID` | **必填(目录给出缺省时可省)** | 缺省模型 id ——**3.0.0 起无出厂缺省**。7.101.0 起判据挪到目录应用**之后**:`MODEL_ID` **或**目录里一条可用的缺省(`config.d/models.json` / 配置控制面)任一在场即可;两者皆无 = 拒启(码 `config.default_model_missing`,指路二选一)。目录一旦被采纳就整张换掉 env 表,`MODEL_ID` 此时不服务:起服不拒(目录条目自带 `baseUrl` 时没有网关也能起),并说一行 `config_env_set_but_ineffective` 点名在服务的目录缺省(目录第一次被采纳的那一拍恰好说一次 —— 启动时,或目录晚到 / 刷新时才第一次采纳)。旧的烤死缺省是内网模型名,外部部署必炸且炸在离根因最远处:网关 `400` + 标题 hook 连环告警 |
package/USAGE.md CHANGED
@@ -634,8 +634,14 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
634
634
  7.105.0 上这类嵌入方的顶层 `sql` 是有值的)。要 caps 说真话就把你装出来的后端事实经 `storePosture` 递进来(形 = `src/store-posture.ts` 的 `StorePosture`;7.148.0 起多两格必填 `durability` 三态与逐根明细 `roots`,自己判不了介质就写 `"unknown"`,不要写 `"durable"`)。
635
635
  - **库形嵌入的 SQL 后端不跑列册对账**:嵌入方的公开序列(`createStoreBackend()` → `ensureSchema()` → `runOneTimeMigrations()`)不跑列册对账(`assertSchemaMatchesCode` / `projectPendingShape`),
636
636
  一次性迁移的守卫尾因此保留;本服务的起服序才跑对账(排在一次性迁移段之前)。
637
- - **`PORT=0` = 让 OS 分口,口由引擎自己报**:引擎自己绑内核分给它的口,`listening` 启动日志行(stdout,info 级;`LOG_LEVEL` 高于 info 会滤掉它)的 `port` 与 `/health.port` 报**实际绑定口**
637
+ - **库形嵌入的一次性迁移段头**:SQL 后端的 `runOneTimeMigrations()` 先在 `replica_registration` 登记本进程一行(身份 · 能力名单 · 发布线),再读 `serving_requirement`(锁外一次、取到迁移锁之后再一次):
638
+ 库上记着本构建没有的能力 ⇒ 拒 `config.serving_requirement_unmet`,一步迁移不跑。两张表由 `ensureSchema()` 建 —— 跳过它直接调 `runOneTimeMigrations()` 的嵌入方拒启(`table_absent`)。
639
+ - **`PORT=0` = 让 OS 分口,口由引擎自己报**:引擎自己绑内核分给它的口,`listening` 启动行(stdout 一行 JSON,行上 `level` 是 info)的 `port` 与 `/health.port` 报**实际绑定口**
638
640
  (`PORT` 非 0 时两者与配置值相等)。要用 OS 分的口起引擎的宿主:子进程以 `PORT=0` 起,读 stdout 那一行 `listening` 的 `port`,再用 `/health` 的 `pid` / `dataRoot` 验答话的是自己起的那个进程;
641
+ - **`listening` 是启动协议行(7.150.0+)**:**不随 `LOG_LEVEL`** —— 档位只滤诊断行,`LOG_LEVEL=warn` / `error` 下这一行照出(仍经同一只出口脱敏;行上 `level` 照写 info,按 `level` 字段筛日志的管道照旧筛得掉它)。
642
+ 宿主不必、也不该替部署把档位压回 info。同一行带两格绑址,一格一个事实:`bindHost` = 本进程**请求**绑的地址(`BIND_HOST` 经无鉴权自收窄之后的那一只 —— 不设 `BIND_HOST` 而写面无鉴权时是 `127.0.0.1`,另带 `bindHostNote`;请求的是全接口时是 `"0.0.0.0/::(all interfaces)"` 这句说明),
643
+ `boundAddress` = 内核**实际**绑上的地址(`::` / `0.0.0.0` / `127.0.0.1` / `::1` 原样;`BIND_HOST=localhost` 时是解析出的那一只)。自起引擎的宿主要认「这个口上的是不是我起的那只」:
644
+ 读同一行的 `boundAddress` + `port` 去连,再以 `/health` 的 `pid` / `dataRoot` 验身份;别拿配置值去推实绑地址。行上的顶层键集登记在 wire 变更清单(`selfMinted.listeningLineKey`)。
639
645
  **不要**先在外面挑一个空闲口、关掉、再交给子进程去绑 —— 关与子进程真正 `listen` 之间有窗,并行负载下别的进程能抢走这个号。`configHash` 仍按**配置值**铸(配置指纹不随 OS 分的口漂)。
640
646
  - `/health` 身份字段承诺:`pid`(= 引擎进程)、`startedAt`(**7.67.0+**,= 引擎进程起点的 epoch ms,
641
647
  一次铸)与 `dataRoot`(= 生效数据根,解析恒回退 `~/.ai-agent`,与 DB_BACKEND 无关)**恒在**(**7.148.0+**:配了服务凭据的引擎上,
@@ -679,6 +685,8 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
679
685
  标记在而根里已没有「存档在、格缺」的会话(收齐之后、删之前停了)⇒ 无事可收:打开它的版本在锁内、抬高水位之前删掉它、照起。
680
686
  带那条迁移的版本不判这一道:它在本次起服里收(没收完的那一次留下标记,见上);起动时会话存档目录读不出 ⇒ 打开即拒,不抬高水位。
681
687
  钉:`test/local-engine-high-water.test.ts` · `test/local-root-one-row-store.test.ts`(跳版拒启臂)· `test/archive-token-adoption-duty.test.ts`(收令牌欠账)。
688
+ - **已知限制:停-迁-起守卫只认登记过的副本**:守卫判「库上还有不知道这条迁移的副本在服务吗」读的是判活窗内心跳过的 run 与持有者的能力登记;早于 7.150.0 的副本不登记,空闲时(没有在跑的 run)看不见,
689
+ 登记也不是围栏(停顿 / 断连后迟到的写挡不住)。停光旧副本是运维的第一道 —— 动作与顺序见随包 MIGRATION.md 当前版段头的「停-迁-起」一行,这里不另写一份。
682
690
  - **已知限制:混版回退 —— 7.147.0 起写下的会话行,在 ≤7.146.x 的版本上续跑不保证**:7.147.0 起会话行上本服务记的十二格搬出了引擎的座表(SQL 后端住旁表 `session_ext`、`session_meta` 只剩引擎的列,一次性迁移 M41 停-迁-起;local 数据根另有上面的引擎版本高水位 marker)。
683
691
  把部署回退到 ≤7.146.x、再续跑升级之后写下或改写过的会话,结果**不保证**:可能照常续到终答(一次续跑返回 200),也可能被拒 —— 两种都不在支持范围内,本服务不承诺其中任何一种,也不为它加兼容读法(一条旧版读法就是第二份读法)。
684
692
  **出路**:回退前先排空 —— 让待续跑的停驻 run 先收完(完成或取消),再回退;随包的排空检查(`scripts/drain-check-local.mjs`〔`DB_BACKEND=local`〕/ `scripts/drain-check.mysql.sql` / `scripts/drain-check.pg.sql`,只读)是读出停驻行存量的工具;或不跨版回退。回退的唯一支持形是恢复升级前的数据根 / 库备份。
@@ -746,6 +754,10 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
746
754
  - 数据驻留提示:`DB_BACKEND=local` 下显式 `SESSION_BACKEND=memory` 会被收编为 local 文件店
747
755
  (1.292+ 裸 boot 默认 local;/health 的 `sessionBackend` 报 `local`)——session 行落盘在
748
756
  数据根下,清数据/隐私预期要按「sessions 在 engine-data 里」来做,不要按「只在内存」。
757
+ - **`DB_BACKEND=local` 没有会话同步的文件历史 blob 交换组**:local 的文件历史店没有 blob 的取 / 放面(只有 SQL 两后端有),所以 `PUT /v1/sessions/:id/sync/blobs/:hash` 与
758
+ `GET /v1/sessions/:id/sync/history/blobs/:hash` 两口恒拒,答 `501 capability.snapshot_store_required`(句子点名 `capabilities.sessionSyncFileHistory` 在这里是 `false`)—— 这是设计,不是故障。
759
+ 同一位在能力位上先说了「不」:推送端读到 `capabilities.sessionSyncFileHistory` 为 `false` 就别推带引用 blob 的 `fileHistory`、别 PUT blob,只同步对话(对话那一半的清单 / 条目 / 导入照常;
760
+ `MIGRATION.md` 的 7.116.0 节写同一读法);SQL 后端上同一位为真。
749
761
  - **后台子代的转录住哪、活多久(按车道)**:规则一条 —— 一只后台子代的持久记录在,它的转录就是宿主会话店委派分区里的一只分区会话:活过重启、随记录同亡;
750
762
  进程内短 TTL 店只装没有持久记录指着的会话(普通子任务、前台子代)。`subagent_type: "fork"` 的后台子代不在这条规则里(各车道同):它的转录是从父会话分叉出来、
751
763
  住宿主会话店的一只普通会话,记录被收割时保留(归会话留存管),也不能经 `SendMessage` 复活(工具结果是错误,`revive.fork_unsupported`)—— 下表的居所与续话两列不适用于它。
@@ -1023,12 +1035,15 @@ FLEET_ADVERTISE_ADDRESS=http://<本机可达IP>:8090 # 可选:设了才启公
1023
1035
  # FLEET_GENERATION=7 # 7.121.0:fleet 成员的代际(配置渲染器写,手配只在你自己就是渲染器时)。规范十进制串(空串 / 前导零 /
1024
1036
  # 符号 / 空白拒启);缺席 = 非 fleet 成员(单机形:不续服务租约、不受围栏)。回声到心跳与 /health 的
1025
1037
  # generation(连同 leaseState / leaseUntil);排空口按它比 expectedGeneration。
1026
- # 服务租约只在中心于心跳答复里宣告 capabilities.servingLease 之后生效;没宣告 ⇒ leaseState none,
1027
- # 零续租零 503;位撤掉再宣告不重置 T_max 的钟(最后一次授予的到期 / 本进程第一次见到位的时刻照算)。
1038
+ # 成员起步即推定在服务租约围栏内:还没听到中心的任何声明 ⇒ leaseState expired,T_max 的钟从本进程起点走,
1039
+ # 推定期不续租。中心在心跳答复里宣告 capabilities.servingLease ⇒ 续租(钟起点不动);声明里不带位 ⇒
1040
+ # leaseState none,零续租零 503;位撤掉再宣告不重置 T_max 的钟(最后一次授予的到期 / 围栏在本进程第一次
1041
+ # 生效的时刻照算)。心跳 401 / 404 / 400 / 不可达 / 答复不是声明 = 没听到,推定照旧。
1028
1042
  # 🔴 设了它就必须配齐中心三件(URL + 凭证 + SEMA_WEB_ADMIN_WORKER)与 FLEET_ADVERTISE_ADDRESS
1029
1043
  # (不公告就听不到能力位),缺一 ⇒ 起服拒启 config.fleet_member_unfenced —— 成员不许悄悄脱离围栏。
1030
1044
  # 续租打 POST /api/fleet/live/serving-lease(契约 FLEET_PATHS.servingLease;活性基址见 SEMA_WEB_ADMIN_LIVE_URL)。
1031
- # 心跳答复体读不出 / 不是 JSON 对象 = 没有能力读数(位与租约钟原样,warn fleet_announce_reply_unreadable)。
1045
+ # 心跳答复体读不出 / 不是 JSON 对象 / 没有 capabilities 键 = 没有能力读数(推定、位与租约钟原样,
1046
+ # warn fleet_announce_reply_unreadable)。只有显式的 capabilities 对象是中心的声明。
1032
1047
  # FLEET_SCOPE=acme # 7.121.0:本引擎所在的**租户 scope**(配置渲染器对每台受管引擎写,缺省 scope 也写 = 它的 per-worker 令牌绑的 scope)。缺席 =
1033
1048
  # 缺省 scope global;在场必须是 scope id 形(小写字母 / 数字 / 内部连字符,1–64;空串拒启
1034
1049
  # config.fleet_scope_invalid)。与 FLEET_GENERATION 不联动。只进排空口的身份比对:请求的 scope 与它比,
@@ -1036,7 +1051,8 @@ FLEET_ADVERTISE_ADDRESS=http://<本机可达IP>:8090 # 可选:设了才启公
1036
1051
  # FLEET_LEASE_OUTAGE_MAX_MS=86400000 # 7.121.0:一台实例**活着而不服务的最长时长**(缺省 24 h;< 60000 / 0 / 非整数拒启)。两处读它:
1037
1052
  # ① 服务租约中心不可达的容忍上限 —— 租约过期(中心不可达 / 答 unknown)后照常服务,过期满这么久 ⇒
1038
1053
  # 新工作 503 state.lease_expired(Retry-After 60;取消 / HITL 应答 / 续跑 / 读面照放行,在飞不动,
1039
- # /readyz 不翻);中心恢复授予即恢复。从没被授予过的,从本进程第一次见到能力位起算。
1054
+ # /readyz 不翻);中心恢复授予即恢复。从没被授予过的,从围栏在本进程生效那一刻起算 —— 一直没听到
1055
+ # 中心的成员(心跳不通 / 令牌被拒 / 答复不是声明)就是本进程起点,每个进程各一段(重启重计)。
1040
1056
  # ② 7.122.0 起排空口 POST /v1/admin/drain 的期限上界:deadlineAt 离此刻超过它 ⇒ 400
1041
1057
  # request.field_invalid(非 fleet 成员同样生效 —— 这一席无条件解析)。
1042
1058
  # 中心以正面证据拒(被取代 / 已退役)不受它管:当拍排空、到期自停,数据根记一只
@@ -1096,19 +1112,24 @@ QUESTION_MAX_TOTAL_PER_RUN=20
1096
1112
  QUESTION_TTL_MS=300000
1097
1113
  ```
1098
1114
  - 三个值都有出厂缺省(即上面写的值),**不设=行为不变**;越界值**启动期响亮拒**(不静默夹取)。
1099
- - 窗用尽 / 到期 **不是替人作答**:服务只报「此刻无人可答」,落点由引擎选——**durable 部署**(`DURABLE_APPROVAL=true` 且这条腿没有活的流)把问题 **park** 成待办,运维随后经审批面带答案补答;**无 durable 的部署**则让模型被告知"没人在"后按自己的判断继续(run 永不挂死)。
1115
+ - 窗用尽 / 到期 **不是替人作答**:服务只报「这一问没拿到答」,落点只看**这一次投递有没有经这条腿的活面(直写 SSE)把卡呈出**(问被上游门放行、到达投递之后;`permissionMode: "dontAsk"` 的问在引擎模式表就被拒、到不了投递,见下面「`permissionMode: "dontAsk"`」一条;7.150.0 起;此前按「判决那一刻这条腿够不够得着人」分形:后台腿与连着的流上自答续跑,只有判决时够不着人的腿停驻):
1116
+ - **呈出过而窗走完没人答**(连着的 `/v1/tasks/stream`;`x-detach-on-disconnect` 流里卡出现时还连着、随后才断开):这是一次失败的问,**不停驻**(卡已经摆到连着的人面前过,同一问不再交给第二个决断者),run 接着跑;模型读到的工具结果说明「卡在活的流上呈出过、窗内无人答」(引本服务的原话)—— 连着的 `/v1/tasks/stream`(posture interactive,前提见下面那条「声明 interactive」)上是 coded 失败 `question.human_channel_failed`,其余(`x-detach-on-disconnect` 流、不声明 interactive 的连着的流)是「按你的判断继续」的续跑卡(`reason: callback_failed`);同步委派子代随父腿。durable 与无 durable 的部署同读。
1117
+ - **没呈出**(卡只进了 run 账本 —— 后台腿 `/v1/runs`、续跑腿、detach 流断开之后才问;没有问题上下文的腿;开帧投不出;撞并发帽 / 总量帽;窗先走完而开帧还悬着):**durable 部署**(`DURABLE_APPROVAL=true` + checkpoint 能力的 backend)把 run **停驻**(`done(suspended)` + `pendingGate`),运维随后经 `POST /v1/approvals/:sessionId/decide` 带 `answer` 补答;**无 durable 的部署**则让模型被告知"没人在"后按自己的判断继续(`declined_unavailable`;run 永不挂死)。后台腿与续跑腿是先投卡、窗内 `POST /v1/questions/:id/respond` 可答、窗走完才停驻;没有问题上下文的腿、投不出、撞帽当场停驻、不等窗。
1118
+ - 例外:run 被取消或非 detach 腿断连 ⇒ 腿中止,不停驻;verify / cascade 的续跑腿没有问题上下文(当场停驻、不投卡);同步委派子代没有停驻车道 —— 连着的流上随父腿读同一张卡(上面第一条),后台腿上窗到期子代收到引擎的拒、接着跑,不停驻。
1119
+ - **`permissionMode: "dontAsk"`**(上面各形之外 —— 问在引擎模式表就被拒,到不了投递):durable 部署上 ⇒ 零 `question` 行、零卡、不停驻,工具未执行,模型读到 dontAsk 拒句(7.149.x:卡照投、窗内可答);非 durable 部署今天卡照送,随引擎让 live 形也铸 ask 翻面后同答「拒」。要人答问换 `permissionMode: "default"` / `"acceptEdits"`;不要问题工具 ⇒ `interactiveTools: false`。
1120
+ - 不想让问题挡住 run(无人值守的提交方)⇒ 在请求体带 `interactiveTools: false`:这条 run 不挂 AskUserQuestion,模型根本不问(比「等一个窗再猜」更直接);部署级不要任何停驻 ⇒ `DURABLE_APPROVAL=false`。
1100
1121
  - 想让人有更长时间答就调大 `QUESTION_TTL_MS`;想让 agent 少打扰人就调小 `QUESTION_MAX_TOTAL_PER_RUN`。
1101
1122
  - **问题卡随 run 账本回放**:`question` / `question_complete` 在每条腿都落进 run 账本(`/v1/tasks/stream` 腿在有 durable run 行时先落账、再写 SSE;`/v1/runs` 腿本来就只有账本)。
1102
1123
  页面重载 / 重连后,经 `GET /v1/runs/:id/events` 取回仍在等的卡(待答 = 没有同 `questionId` 的 `question_complete` 且 `expiresAtMs` 未到)。
1103
1124
  **答题口不变**:`POST /v1/questions/:id/respond` 只在受理这一问的副本内、窗(`QUESTION_TTL_MS`)内有效;没有「列出待答问题」的读口。无 durable run 行的部署没有账本,仍只有 live 帧。
1104
- - 🔴 **`/v1/tasks/stream` 的开帧投递失败是响亮的(逐腿,不是部署级)**:这条腿(**且仅这条腿**)向引擎
1125
+ - 🔴 **`/v1/tasks/stream` 的开帧投递失败是响亮的(逐腿,不是部署级;开帧投不出这一形只对无 durable 的部署起作用 —— durable 部署上没呈出的问停驻,见上;卡在活的流上呈出过、窗走完那一形两种部署同读,落失败臂:posture interactive 的流上是 coded `question.human_channel_failed`,其余是 `callback_failed` 自答指令卡)**:这条腿(**且仅这条腿**)向引擎
1105
1126
  声明 `interactionPosture: "interactive"` —— 人就在这条 SSE 上,一次开帧投递失败(壳断连/流被撕裂)
1106
1127
  不再合成「没有人可问,你自己判」的续跑卡,而是让那次工具调用返回 coded 失败
1107
1128
  `question.human_unavailable`。**其余每一条腿行为字节不变**:`/v1/tasks`(非流)、`/v1/runs`、A2A、
1108
1129
  resume、cron / wake、`verify` / `cascade` 一律**不声明** ⇒ 续跑 + 披露卡 + 既有计数照旧
1109
1130
  (那些腿本来就没人可问,拒掉它们只会把无人值守任务打死)。
1110
1131
  声明本身有前提:活体问答面与活体审批席**两个席位都在**、且调用方没有把 `interactiveTools` 声明成
1111
- `false` —— 缺任何一项就不声明(引擎的 posture 门是**拒整条腿**的,声明一个够不着人的姿态会让每个
1132
+ `false`、没有把这条 run 的 `approvalReach` 声明成 `"none"` —— 缺任何一项就不声明(引擎的 posture 门是**拒整条腿**的,声明一个够不着人的姿态会让每个
1112
1133
  任务在准备阶段当场失败)。**发了 `x-detach-on-disconnect: true` 的请求同样不声明** —— 那个头逐字是
1113
1134
  「我可能随时走开,别杀 run」,正是 interactive 的反面。
1114
1135
  ⚠️ **射程如实**:引擎的姿态解析是三腿的,**从这条腿委派出去的子代会继承根腿的姿态**(引擎的设计,
@@ -1137,7 +1158,7 @@ QUESTION_TTL_MS=300000
1137
1158
 
1138
1159
  ```bash
1139
1160
  # 协议总开关。默认 true;显式 =false 是**本协议面**的干净还原键(不发 approval_request / approval_complete、改发旧族帧、不落 ask 行、
1140
- # 不起收敛器腿)。⚠️ 它**不**回滚 7.34.0 的 R-13 窗到期语义——那一条的
1161
+ # 不起收敛器腿)。⚠️ 它**不**回滚 7.34.0 的「窗到期走 park」语义——那一条的
1141
1162
  # 回滚键是下面的 UNATTENDED_APPROVAL_POLICY=deny(两根旋钮正交,别当一根用);它也**不**再回滚窗长
1142
1163
  # (窗是活卡件不是协议件,见下一键——缺省值与旧硬编码窗同为 300000,没配过就仍然字节不变)
1143
1164
  STREAM_APPROVAL_ENABLED=true
@@ -1256,6 +1277,8 @@ UNATTENDED_APPROVAL_POLICY=park
1256
1277
  ```
1257
1278
  - 管的是**同一个问题**:一只需要人批的 ask 到了,而此刻**没有任何人**能答(窗走完没人点、连接全断、
1258
1279
  运维把窗关成 0、bg/headless 腿本来就没有活流、未决卡撞上准入帽)。
1280
+ - 只管**审批**问;AskUserQuestion 的问不读本旋钮 —— 落点只看卡有没有经这条腿的活面呈出(没呈出的 durable 部署停驻、呈出过而窗走完按失败结算,见上「AskUserQuestion 的裁量窗」;`deny` 部署也一样),
1281
+ 不想让问题挡住 run 用请求体 `interactiveTools: false`(见上「AskUserQuestion 的裁量窗」)。
1259
1282
  - `park`(缺省)⇒ 服务报「此刻无人可答」,**durable 部署**(`DURABLE_APPROVAL=true` + checkpoint 能力的
1260
1283
  backend)把 run **挂起候人**(`done(suspended)` + `pendingGate`),人回来经审批面补批就接着跑;
1261
1284
  没有 park 设施的部署由引擎 fail-closed 拒绝并告诉模型「没有可停靠的 durable 审批门」。
@@ -1276,9 +1299,9 @@ UNATTENDED_APPROVAL_POLICY=park
1276
1299
  - 纯部署级:**不看**客户端的任何表态/权限模式。协调器整体关掉(`TOOL_APPROVAL_ENABLED` 关)时这个旋钮
1277
1300
  没有施加对象。`APPROVAL_REQUIRE` 名单只决定「这只工具要不要审批」,与本旋钮无关;协调器在场时,名单工具的
1278
1301
  审批与其余 ask 走同一条路、同一个政策(`deny` 部署上同样当场拒)。
1279
- - 🔴 **与 `STREAM_APPROVAL_ENABLED` 正交**:把流内协议开关关掉**不会**回滚 R-13 —— 它只是不发
1302
+ - 🔴 **与 `STREAM_APPROVAL_ENABLED` 正交**:把流内协议开关关掉**不会**回滚 7.34.0 的「窗到期走 park」—— 它只是不发
1280
1303
  `approval_request`、不落 ask 行、不起收敛器腿,活卡窗到期照样走 park(park 的承载是引擎的 checkpoint,
1281
- 不是 server 的 ask 行,「有 durable 设施但没开协议」正是 R-13 要救的那类部署)。要回到旧的「窗满即拒」
1304
+ 不是 server 的 ask 行,「有 durable 设施但没开协议」正是「窗到期走 park」要救的那类部署)。要回到旧的「窗满即拒」
1282
1305
  就配 `UNATTENDED_APPROVAL_POLICY=deny`。
1283
1306
 
1284
1307
  **「本会话不再询问」对引擎的问也生效(7.106.0 起)**
@@ -1298,7 +1321,7 @@ UNATTENDED_APPROVAL_POLICY=park
1298
1321
  - 已知边界(两只家并存):活卡上对**引擎的问**点的「本会话放行」记在审批席的进程内表(按副本、随进程;能力桶:`Write` 盖 `Edit` / `NotebookEdit`),**不在**上面那只豁免店里 ——
1299
1322
  `GET /v1/approvals/:sessionId/exemptions` 列不出它、`DELETE …/exemptions/:toolName` 撤不到它(回 `revoked:true` 只说耐久那一行没了)、删会话与留存清理也清不掉它的表项,换副本 / 重启即失。
1300
1323
  它**绑会话这一位住客**(7.149.0 起):键含这条腿的会话此刻的住客令牌,所以会话删了(`DELETE /v1/sessions/:id`、留存清理、别的副本)再用同一个 id 重新登记之后,
1301
- 上一位住客点的放行读不中,新一位住客的第一次调用照样出卡;读不出住客令牌的腿(会话店不带执行席 —— 引擎缺省的内存会话店;SQL 与 local 后端恒带)点「本会话放行」只放行这一次(回执 `rememberApplied:false`,日志一行 `session_grant_refused_occupant_unreadable`)。
1324
+ 上一位住客点的放行读不中,新一位住客的第一次调用照样出卡;读不出住客令牌的腿(会话店不带执行席 —— 引擎缺省的内存会话店;SQL 与 local 后端恒带)上卡面带 `rememberRefused`(壳不渲「本会话放行」;7.150.0 起,与回执同一只判官),照样点了也只放行这一次(回执 `rememberApplied:false`,日志一行 `session_grant_refused_occupant_unreadable`)。
1302
1325
  名单的问点的「本会话放行」与停驻之后 `remember:"session"` 记的放行在豁免店里,列表与撤销都管。读豁免店的两道策略层门(`APPROVAL_REQUIRE` 名单门、`run_workflow` 问句门)经同一只读口:
1303
1326
  两秒内读不到(店慢 / 店坏)⇒ 照问;店答的不是布尔真值也不放行。命中时的审计行是 `approval_exempted`(字段 `seat`:`deployment_list` / `workflow_gate`;`run_workflow` 问句此前的事件名 `workflow_gate_exempted` 已退役,按它告警的运维改读这一条)。
1304
1327
 
@@ -1414,7 +1437,7 @@ READ_DENY_PATTERNS='[{"pattern":"**/secrets/**"},{"pattern":".ssh","caseSensitiv
1414
1437
  升级后**读得到**根内的 `.npmrc` / `.ssh` / `.aws` / `.claude/settings.json` 这类名字,且整个递归读族
1415
1438
  零卡执行。**要老姿态就显式写** `READ_DENY_BUILTIN_TIERS=credentials,browser,wallet,agent-config`。`open` 与这张表仍是**两个并列机制**:
1416
1439
  `open` 放开的是「工作区外」那道围栏,内建表判的是名字。
1417
- 🔴 **7.106.0(core 7.34.0 R-2)再翻一次**:未设 = **`credentials` + 家目录锚定的 `agent-config` 两档在岗**(`browser` / `wallet` /
1440
+ 🔴 **7.106.0(core 7.34.0)再翻一次**:未设 = **`credentials` + 家目录锚定的 `agent-config` 两档在岗**(`browser` / `wallet` /
1418
1441
  `shell-history` 仍是选装;显式 `READ_DENY_BUILTIN_TIERS=none` 仍是空姿态)⇒ 未配置的机器升级后**读不到**根内的 `.npmrc` / `.ssh` / `.aws` 这类名字
1419
1442
  (每种模式都拒、不出卡)。agent-config 的十五行改写成 `~/…`(`~/.claude/settings.json`、`~/.sema/settings.json` …)、只在执行环境的 home 正下方
1420
1443
  命中 —— 工作区里的 `.claude/settings.json` 不再是内建行;🔴 `READ_DENY_BUILTIN_EXCLUDE` 里写旧行名(`.claude/settings.json`)的部署**起服拒**
@@ -1647,7 +1670,7 @@ MCP_ELICITATION_TTL_MS=300000 # 一张没人填的表单挂多久后释放(
1647
1670
  - 两帧同样**随 run 账本回放**(与上面的问题卡同一条规则:`/v1/tasks/stream` 腿有 durable run 行时先落账再直写,`/v1/runs` 腿只有账本),重载后经 `GET /v1/runs/:id/events` 取回表单卡;
1648
1671
  `elicitation` 开帧带 `expiresInMs` / `expiresAtMs`,待答判法与问题卡同一把。**答题口不变**:`POST /v1/elicitations/:id/respond` 只在受理那一张表单的副本内、窗内有效。
1649
1672
 
1650
- **可选 — 把这台 worker 挂进别人的 A2A 拓扑(server-as-peer,DESIGN-269 车2):默认关**
1673
+ **可选 — 把这台 worker 挂进别人的 A2A 拓扑(server-as-peer):默认关**
1651
1674
 
1652
1675
  别家 orchestrator 把这台 sema-server 当成它拓扑里的一个 **A2A agent** 来调。开了才有两条对外路由;
1653
1676
  关着(默认)它们**都不存在**(404)——不是「发卡但拒调」。
@@ -2044,7 +2067,8 @@ bake-runner 要用 ⇒ `LSP_ENV_ALLOW` / `BAKE_ENV_ALLOW` 点名放回。
2044
2067
  > **dev-open 部署的主体头是自报的,不是隔离。** 没接任何身份来源(没配 `SERVICE_AUTH_TOKEN(S)`、没装中心鉴权桥、没开直连门 —— 即 `/health.auth.kinds` 为空)、
2045
2068
  > 也没配 `TRACE_TOKEN`、没开 `REQUIRE_PRINCIPAL` 的部署上,`x-agent-principal` 谁都能自报。它说的是「这次请求为谁」,每一口同读(7.149.0 起;与提交续用别人会话被拒同一读法):
2046
2069
  > 带了头的请求只看得到、动得到这位主体的会话与 run(会话列表 / 单会话读写口 / 规则口 `…/policy` / `…/notify` / fork / run 轮询 —— 别人的与不存在的同答 404;删除别人的会话答 `200 {"deleted":false}`,与删不存在的同),
2047
- > `/metrics` 族与 `GET /v1/tasks/source-summary` 对它答 `401`(配了 `METRICS_TOKEN` 时出示它照放);
2070
+ > `/metrics` 族对它答 `401`(配了 `METRICS_TOKEN` 时出示它照放);
2071
+ > `GET /v1/tasks/source-summary` 对它同样答 `401`(这一口认的是只读的 `TRACE_TOKEN` —— 配了才有,出示它照放;`METRICS_TOKEN` 在这一口不算);
2048
2072
  > **不带头**(也不带这台部署验不过的 Bearer —— 带了它的请求没有可信身份,不代部署本人)的请求代部署本人:列表与单会话读写口看得到、动得到全部会话,run 轮询看得到全部 run,
2049
2073
  > `/metrics` 族不要凭据(没配 `METRICS_TOKEN` 时);run 的驱动口(取消 / steer …)与提交续用别人的会话照旧按属主判,对它同样不开。它也决定新会话记在谁名下、记忆按谁的 scope 走。
2050
2074
  > 头谁都能自报 ⇒ 这给的是一致的视图,不是隔离;要按用户隔离,给部署设上面几者之一(典型:service token + 写主体头的网关)。≤7.148.x:带不带头都看得到全部会话(只有提交续用按头判)。
@@ -408,7 +408,7 @@ type ClosedWordOf<S extends ClosedWordSeat> = S extends AskClosedWordSeat ? NonN
408
408
  * {@link parkRowFacts} 读它);`CheckpointSummary` 不回显(core 原句「read the row」)⇒ inbox 行不带。
409
409
  *
410
410
  * 判据(display,不是裁决输入 —— 裁决在 core 的仲裁点,本仓无人值守腿的按类作答读的是 core 交来的 `AskRequest.classes` 本身,不经本函数):
411
- * · **两键都缺席 ⇒ 缺席**(不计数):老引擎(core 7.37.0 之前)、老行(续跑不补,KL-1214-b)、不盖 `origin` 的站(KL-1214-a)。缺席从不编默认类;
411
+ * · **两键都缺席 ⇒ 缺席**(不计数):老引擎(core 7.37.0 之前)、老行(续跑不补,[ref])、不盖 `origin` 的站([ref])。缺席从不编默认类;
412
412
  * · **在场 ⇒ 读法就是 core 的屏** `screenAskClassMembers`([ref] ⑥,core 7.38.0 [ref] 导出;引擎在写停车行前用的同一只):`askClass` 在集、`classes`
413
413
  * 非空数组且每员在集、无重复、按 `ASK_CLASSES` 规范序取首员 === `askClass`;屏收 ⇒ 投屏给的规范序副本。屏拒(半对 / 集外词 / 首员不符 /
414
414
  * 重复员)⇒ **两键都不投**(卡上少一行类 ≪ 卡上出现生词或半只对)+ 计一次 `server.approval-card.closed-word-out-of-set`([ref] 族,detail 前缀
@@ -61,7 +61,7 @@
61
61
  * 行没了 ⇒ `unknown`(`row_absent`;应答口 PARKED 臂的 404 `not_found.approval` 同判)。
62
62
  *
63
63
  * ## 盲区(core 的筛仍不判的一处,如实写下)
64
- * · 种类 × 当事方的配对(结算种类与 `settlement.who` 的当事方是否相配)—— 筛不判(core KL-1265-b),本判官也不核:读的是种类。只有坏行 / 手改行才走得到。
64
+ * · 种类 × 当事方的配对(结算种类与 `settlement.who` 的当事方是否相配)—— 筛不判(core [ref]),本判官也不核:读的是种类。只有坏行 / 手改行才走得到。
65
65
  * (时刻与方向两处自 core 7.56.0 [ref] 起由筛判:I10 / I11。trace 投影读同一种记录、经同一只筛,零改即得 I10 —— 坏时刻在两个读者那里是同一个答法。)
66
66
  */
67
67
  import { type Checkpoint, type CheckpointToken, type SettlementKind } from "@sema-agent/core";
package/dist/audit.d.ts CHANGED
@@ -36,7 +36,7 @@ export interface SessionAudit {
36
36
  * 真源 = `resume_anchor`(锚店 `listBySession` 的读数),本表只是它对**一位读者**的一次投影。
37
37
  */
38
38
  export type RewindHandles = ReadonlyMap<string, string>;
39
- /** 会话视图读口(`ServiceDeps.sessionAudit`)的读者参数:`reader` = 读这张视图的**已验身份**(无身份 = `null`)。给了 ⇒ 这是一次**视图读**:用户消息行投回退手柄、体顶层投 `agent`(`session-agent.ts` 的 `readSessionAgent`);
39
+ /** 会话视图读口(`ServiceDeps.sessionAudit`)的读者参数:`reader` = 读这张视图的**已验身份**(无身份 = `null`)。给了 ⇒ 这是一次**视图读**:用户消息行投回退手柄、体顶层投 `agent`(`http/owner-access.ts` 的 `readSessionAgent`);
40
40
  * 不给(只要归属的调用方:memory-status / 自助抹除)⇒ 两样投影的读都不付。 */
41
41
  export interface SessionAuditReadOpts {
42
42
  readonly reader: Subject | null;
package/dist/audit.js CHANGED
@@ -4,7 +4,7 @@ import { PgSessionStorage } from "./stores-sql/pg-session-storage.js";
4
4
  import { LocalSessionWindow } from "./plugins/local-session-window.js";
5
5
  import { redactModelText } from "./observability/run-terminal.js";
6
6
  import { redactDeepWith } from "./trace/redact.js";
7
- import { projectToolStructured } from "./trace/tool-output-media.js";
7
+ import { projectToolCard } from "./trace/tool-card.js";
8
8
  import { INJECTED_BLOCK_RE, isAttachmentFrame } from "./user-authored-text.js";
9
9
  import { ownerKeyOf, sameKey } from "./owner-faces.js";
10
10
  export function buildRewindHandles(anchors, reader) {
@@ -68,7 +68,7 @@ function withRewindHandles(messages, entries, handles) {
68
68
  }
69
69
  const deepScrub = (v) => redactDeepWith(v, redactModelText);
70
70
  const textSlot = (v) => (typeof v === "string" ? redactModelText(v) : deepScrub(v));
71
- const readToolCard = (v) => projectToolStructured(v, deepScrub);
71
+ const readToolCard = (v) => projectToolCard(v, deepScrub);
72
72
  function readLeaf(treatment, v) {
73
73
  switch (treatment) {
74
74
  case "text": return textSlot(v);
@@ -48,7 +48,7 @@ export interface SessionHardReap {
48
48
  * 两处调用(见文件头 ①),都 `await`:E21 删除级联 `includeRetained:false`(显式活服务照留);停止口照传请求体。应答在硬面之后发,
49
49
  * 代价 = 有 shell 行刚被软停的会话,DELETE / 停止口的应答慢到最后一行的「软停 + 宽限」(通常一次宽限;操作员旋钮 `HOST_BG_KILL_GRACE_MS`,缺省 1000;要几乎不等写 `1` ——
50
50
  * `numEnvOr` 把 `0` 读作缺席、回落缺省,与它供的其余执行车道旋钮同一条语义)。
51
- * 受委托帮手的 shell 挂在帮手自己的会话上,本函数碰不到(core KL-1107a / [ref],候 core)。
51
+ * 受委托帮手的 shell 挂在帮手自己的会话上,本函数碰不到(core [ref] / [ref],候 core)。
52
52
  * 注册表是**进程内**的:只收本副本上的行(多副本部署里别的副本上的行由那个副本自己的出口收)。
53
53
  */
54
54
  export declare function reapSessionBackgroundHard(sessionId: string, scope: string, opts: {
@@ -17,7 +17,9 @@
17
17
  *
18
18
  * ## 三条不可动的裁定
19
19
  *
20
- * 1. **`absolutePath` 一面不转发**,沿用词法规则(POSIX 绝对形归一 / NUL 拒 / 非绝对形 err)。
20
+ * 1. **`absolutePath` 一面不转发**,只读执行缝路径律(`plugins/exec-path-law.ts`):POSIX 绝对形**原样**交出(`..` / `.` 不折 ——
21
+ * 不折才与工具同读:七条车道的工具也原样把它交给沙箱的文件系统,`..` 由那块盘解;门的真身判决随后经下面四腿转发问的正是这只拼法)/
22
+ * NUL 拒 / 非绝对形 err。
21
23
  * 真 env 的 `absolutePath` 会按 **env 自己的 cwd** 解析相对路径,而工具真写处按 engine 跟踪的
22
24
  * cwd 解析——两个基准不同,转发它等于拿错基准铸 canonical key,而**错误的 allow 比没有门更糟**。
23
25
  * 相对形 / `~` / 盘符形因此照旧 err ⇒ `canonicalizeTarget` 失败 ⇒ 写门 `ask`(fail-closed)。
@@ -115,7 +117,7 @@ export interface DeferredSandboxPathEnvOptions {
115
117
  logger?: SandboxPathEnvLogger;
116
118
  }
117
119
  /**
118
- * 写门用的 ExecutionEnv 代理(见文件头)。fs **读**原语转发 slot 里的真 env,`absolutePath` 保留词法形,
120
+ * 写门用的 ExecutionEnv 代理(见文件头)。fs **读**原语转发 slot 里的真 env,`absolutePath` 绝对形原样(不折,见裁定 1),
119
121
  * 其余一切(写面 / shell 面 / listDir …)继承 `StubExecutionEnv` 的 `not_supported`。
120
122
  *
121
123
  * **继承而非逐一手写**是刻意的:core 日后给 `ExecutionEnv` 加必填面时,新面会随 `StubExecutionEnv`
@@ -130,7 +132,7 @@ export declare class DeferredSandboxPathEnv extends StubExecutionEnv {
130
132
  constructor(opts: DeferredSandboxPathEnvOptions);
131
133
  /** 取真 env;取不到就地铸 fail-closed 错误(并留痕一次)。 */
132
134
  private bound;
133
- /** 词法一面,**不**转发 —— 理由见文件头裁定 1。 */
135
+ /** 只读路径律的一面,**不**转发 —— 理由见文件头裁定 1。绝对形原样:门与工具读同一只拼法,`..` 交给沙箱的文件系统(经四腿转发的真身判决)。 */
134
136
  absolutePath(path: string): Promise<Result<string, FileError>>;
135
137
  exists(path: string, abortSignal?: AbortSignal): Promise<Result<boolean, FileError>>;
136
138
  canonicalPath(path: string, abortSignal?: AbortSignal): Promise<Result<string, FileError>>;
@@ -1,5 +1,4 @@
1
1
  import { FileError, StubExecutionEnv, err, ok } from "@sema-agent/core";
2
- import { normalizeAbsolutePathLexically } from "../task-cwd.js";
3
2
  const MAX_BOUND_SESSIONS = 4096;
4
3
  export class SandboxPathEnvSlots {
5
4
  bySession = new Map();
@@ -128,7 +127,7 @@ export class DeferredSandboxPathEnv extends StubExecutionEnv {
128
127
  if (!path.startsWith("/")) {
129
128
  return Promise.resolve(err(new FileError("not_supported", "this gate cannot resolve a non-absolute path (the sandbox working directory is not the gate's to guess)", path)));
130
129
  }
131
- return Promise.resolve(ok(normalizeAbsolutePathLexically(path)));
130
+ return Promise.resolve(ok(path));
132
131
  }
133
132
  async exists(path, abortSignal) {
134
133
  const b = this.bound(path, "exists");
@@ -1,7 +1,7 @@
1
1
  import { type BackgroundAgentRecord, type RunnerDeps, type ToolExecuteContext } from "@sema-agent/core";
2
2
  import type { ServiceConfig } from "../config.js";
3
3
  import type { RunnerDepsOnAsk } from "./runner-deps.js";
4
- import { type ApprovalBaselineConfigView, type DeploymentGovernanceConfigView, type LiveQuestionFace } from "../deployment-governance.js";
4
+ import { type ApprovalBaselineConfigView, type DeploymentGovernanceConfigView } from "../deployment-governance.js";
5
5
  import { type SandboxPathEnvSlots } from "./deferred-sandbox-path-env.js";
6
6
  import type { ApprovalExemptionStore } from "../plugins/approval-exemption-store.js";
7
7
  /** 本腿读的配置切面(`ServiceConfig` 结构满足)。两个构造口各自的切面 + lane 判别式读的 `remoteExec`。 */
@@ -15,9 +15,9 @@ export interface ParkedReviveGateConfigView extends DeploymentGovernanceConfigVi
15
15
  export interface ParkedReviveGateDeps {
16
16
  /** ⚠️ **活引用**:热改字段(autonomy/commandPolicy/守卫集/审批四旋钮)每次赎回现读,见下方「禁 memoize」。 */
17
17
  readonly config: ParkedReviveGateConfigView;
18
- /** [ref]:活体 AskUserQuestion 面。赎回腿今天无 ALS ctx ⇒ 判 ask,与原形同判。
19
- * ⚠️ 它同时是**决议链元数据** `contentMandate` 的唯一判据(见 {@link mandatePostureOf})。 */
20
- readonly question: LiveQuestionFace | undefined;
18
+ /** 问题席在场(与 resolve-spec 的 `questionSeat` 同一位,装配点递的是同一只协调器的在场性)。
19
+ * 它是**决议链元数据** `contentMandate` 的唯一判据(见 {@link chainMetadataOf})。 */
20
+ readonly questionSeat: boolean;
21
21
  /**
22
22
  * 部署级活体审批席 —— `RunnerDeps.onAsk` 的**同源铸法**(main.ts 递 `createRunnerDepsOnAsk(toolApproval)`,
23
23
  * 单一属主同 boot/runner-deps.ts)。
@@ -90,7 +90,7 @@ export type RebuiltParentConstraint = NonNullable<RebuiltInheritedGate["parentCo
90
90
  /**
91
91
  * 赎回腿的父约束链重建([ref] 件二)。
92
92
  *
93
- * 语义:**同/跨副本一致地重建「部署 ⊇ 操作员」两层完整链**——审批基线(durable question 门 + F4 高危写
93
+ * 语义:**同/跨副本一致地重建「部署 ⊇ 操作员」两层完整链**——审批基线(core 的问答策略 + F4 高危写
94
94
  * 审批门 + 部署名单那一问的豁免读面)作 base 的 `toolPolicy` 座,经**折叠属主** `applyRuntimeGovernance`
95
95
  * (→ core `tightenTaskSpec`)叠上部署治理段(autonomy / commandPolicy / MANUAL_MODE_SHELL_GATE /
96
96
  * 守卫集),取其 `toolPolicy` 装进单层 `parentConstraints`。