@sema-agent/server 7.151.0 → 7.152.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 (286) hide show
  1. package/MIGRATION.md +266 -0
  2. package/README.md +15 -3
  3. package/README.zh-CN.md +10 -2
  4. package/USAGE.md +93 -64
  5. package/deploy/sema-up/README.md +5 -4
  6. package/deploy/sema-up/cluster-up.sh +6 -6
  7. package/deploy/sema-up/kube-up.sh +5 -5
  8. package/deploy/sema-up/lib/flagshape.sh +36 -8
  9. package/deploy/sema-up/machines.example.yaml +3 -1
  10. package/deploy/sema-up/sema-up.sh +22 -16
  11. package/dist/approval-ask-audit-store.d.ts +1 -1
  12. package/dist/approval-ask-machine.d.ts +4 -10
  13. package/dist/approval-ask-retention.d.ts +1 -1
  14. package/dist/approval-card.d.ts +10 -12
  15. package/dist/approval-gate-currency.d.ts +8 -8
  16. package/dist/approval-gate-currency.js +3 -4
  17. package/dist/approval-hmac.d.ts +17 -1
  18. package/dist/approval-hmac.js +7 -1
  19. package/dist/approval-queue-kind.d.ts +1 -1
  20. package/dist/approval.d.ts +2 -2
  21. package/dist/approval.js +2 -2
  22. package/dist/bind-host.d.ts +1 -3
  23. package/dist/bind-host.js +2 -3
  24. package/dist/boot/coordinators.d.ts +1 -1
  25. package/dist/boot/coordinators.js +9 -10
  26. package/dist/boot/device-lane.js +2 -0
  27. package/dist/boot/execution-env.d.ts +1 -0
  28. package/dist/boot/execution-env.js +31 -5
  29. package/dist/boot/leader.d.ts +31 -30
  30. package/dist/boot/leader.js +38 -28
  31. package/dist/boot/leg-path-adjudication.d.ts +40 -0
  32. package/dist/boot/leg-path-adjudication.js +18 -0
  33. package/dist/boot/parked-revive-gate.js +3 -15
  34. package/dist/boot/reapers.d.ts +1 -1
  35. package/dist/boot/reapers.js +1 -1
  36. package/dist/boot/resolve-spec.js +12 -26
  37. package/dist/boot/runner-deps.d.ts +39 -35
  38. package/dist/boot/runner-deps.js +92 -89
  39. package/dist/boot/shutdown.d.ts +6 -0
  40. package/dist/boot/shutdown.js +10 -4
  41. package/dist/boot/stage-02-web-admin-pull.js +4 -5
  42. package/dist/boot/stage-05-execution-env.js +2 -1
  43. package/dist/boot/stage-06-runners.d.ts +2 -0
  44. package/dist/boot/stage-06-runners.js +10 -4
  45. package/dist/boot/stage-07-capability-layer.d.ts +1 -0
  46. package/dist/boot/stage-07-capability-layer.js +1 -1
  47. package/dist/boot/stage-08-reapers.d.ts +1 -0
  48. package/dist/boot/stage-09-leader.d.ts +1 -0
  49. package/dist/boot/stage-09-leader.js +2 -2
  50. package/dist/boot/stage-10-http-server.d.ts +8 -0
  51. package/dist/boot/stage-10-http-server.js +11 -15
  52. package/dist/boot/stores.js +7 -4
  53. package/dist/boot/web-admin.js +2 -7
  54. package/dist/capabilities/approvals-push.d.ts +3 -3
  55. package/dist/capabilities/scenarios.d.ts +28 -13
  56. package/dist/capabilities/scenarios.js +2 -2
  57. package/dist/capability-facts.d.ts +22 -20
  58. package/dist/capability-facts.js +18 -34
  59. package/dist/card-rule-offers.d.ts +32 -0
  60. package/dist/card-rule-offers.js +130 -0
  61. package/dist/config-catalog.js +12 -11
  62. package/dist/config-dir-trust.js +4 -2
  63. package/dist/config-invariants.d.ts +18 -7
  64. package/dist/config-invariants.js +51 -8
  65. package/dist/config-lkg.d.ts +5 -19
  66. package/dist/config-lkg.js +8 -62
  67. package/dist/config-provider.d.ts +1 -1
  68. package/dist/config-root-migration.d.ts +2 -2
  69. package/dist/config-types.d.ts +28 -38
  70. package/dist/config.d.ts +7 -5
  71. package/dist/config.js +55 -16
  72. package/dist/control-socket.d.ts +51 -0
  73. package/dist/control-socket.js +115 -0
  74. package/dist/deployment-governance.d.ts +2 -1
  75. package/dist/device-ws-hub.d.ts +7 -0
  76. package/dist/device-ws-hub.js +7 -1
  77. package/dist/elicitation.js +2 -2
  78. package/dist/entry-problems.d.ts +40 -3
  79. package/dist/entry-problems.js +3 -1
  80. package/dist/execution-env-refusal.d.ts +8 -2
  81. package/dist/execution-env-refusal.js +3 -0
  82. package/dist/fleet/engine-signal.d.ts +12 -29
  83. package/dist/fleet/engine-signal.js +1 -7
  84. package/dist/fleet-client.js +2 -5
  85. package/dist/gate-settlement.d.ts +1 -8
  86. package/dist/gate-settlement.js +0 -4
  87. package/dist/history-anchor.js +1 -1
  88. package/dist/hitl-delivery.d.ts +2 -3
  89. package/dist/hitl-delivery.js +0 -2
  90. package/dist/hooks/hook-llm.js +4 -2
  91. package/dist/hooks/hook-runner.d.ts +3 -2
  92. package/dist/hooks/hook-runner.js +16 -1
  93. package/dist/host-decision.d.ts +11 -11
  94. package/dist/http/active-run-conflict.d.ts +35 -2
  95. package/dist/http/active-run-conflict.js +48 -19
  96. package/dist/http/approval-sweeps.d.ts +1 -1
  97. package/dist/http/approval-sweeps.js +5 -2
  98. package/dist/http/checkpoint-ctx.d.ts +2 -0
  99. package/dist/http/checkpoint-ctx.js +1 -0
  100. package/dist/http/dispatch.d.ts +6 -2
  101. package/dist/http/dispatch.js +25 -5
  102. package/dist/http/durable-run-leg.js +0 -1
  103. package/dist/http/health-reader.d.ts +16 -9
  104. package/dist/http/health-reader.js +2 -1
  105. package/dist/http/health-self-report.js +1 -1
  106. package/dist/http/owner-access.d.ts +2 -3
  107. package/dist/http/owner-access.js +3 -4
  108. package/dist/http/principal-gate.d.ts +49 -21
  109. package/dist/http/principal-gate.js +61 -11
  110. package/dist/http/request-channel.d.ts +23 -0
  111. package/dist/http/request-channel.js +9 -0
  112. package/dist/http/request-guards.js +17 -2
  113. package/dist/http/resume-ctx-read.js +20 -3
  114. package/dist/http/resume-legs.d.ts +2 -2
  115. package/dist/http/resume-legs.js +111 -41
  116. package/dist/http/route-ctx.d.ts +73 -7
  117. package/dist/http/route-ctx.js +17 -0
  118. package/dist/http/route-table.d.ts +4 -0
  119. package/dist/http/route-table.js +4 -0
  120. package/dist/http/routes/admin-config-refresh.js +3 -3
  121. package/dist/http/routes/admin-drain.d.ts +3 -0
  122. package/dist/http/routes/admin-drain.js +7 -7
  123. package/dist/http/routes/admin-memory-optout.js +3 -3
  124. package/dist/http/routes/adoption.js +3 -5
  125. package/dist/http/routes/approvals-assistant.d.ts +2 -1
  126. package/dist/http/routes/approvals-assistant.js +95 -48
  127. package/dist/http/routes/capabilities.d.ts +4 -0
  128. package/dist/http/routes/capabilities.js +30 -5
  129. package/dist/http/routes/config-catalog.js +2 -2
  130. package/dist/http/routes/devices.js +1 -1
  131. package/dist/http/routes/diagnostics.d.ts +3 -3
  132. package/dist/http/routes/diagnostics.js +19 -7
  133. package/dist/http/routes/images.js +10 -10
  134. package/dist/http/routes/memory-bundle.js +3 -3
  135. package/dist/http/routes/memory-compliance.js +3 -3
  136. package/dist/http/routes/memory-consolidation.js +2 -2
  137. package/dist/http/routes/memory-origin.js +3 -3
  138. package/dist/http/routes/memory-policy.js +2 -2
  139. package/dist/http/routes/observability.js +3 -3
  140. package/dist/http/routes/retention-ops.js +3 -3
  141. package/dist/http/routes/rules.js +5 -5
  142. package/dist/http/routes/run-permission-mode.d.ts +2 -1
  143. package/dist/http/routes/run-permission-mode.js +2 -2
  144. package/dist/http/routes/runs.d.ts +4 -9
  145. package/dist/http/routes/runs.js +73 -33
  146. package/dist/http/routes/session-sync.d.ts +18 -6
  147. package/dist/http/routes/session-sync.js +38 -36
  148. package/dist/http/routes/sessions.js +20 -9
  149. package/dist/http/routes/tasks.js +28 -37
  150. package/dist/http/routes/workflows.js +1 -1
  151. package/dist/http/server.d.ts +9 -3
  152. package/dist/http/server.js +15 -3
  153. package/dist/http/submit-ctx.d.ts +1 -1
  154. package/dist/http/submit-ctx.js +2 -0
  155. package/dist/human-facing-args.d.ts +33 -0
  156. package/dist/human-facing-args.js +33 -0
  157. package/dist/index.d.ts +1 -1
  158. package/dist/k8s-api-credentials.d.ts +29 -0
  159. package/dist/k8s-api-credentials.js +25 -0
  160. package/dist/lane-home-probe.d.ts +1 -1
  161. package/dist/lane-home-probe.js +2 -1
  162. package/dist/leader/diffup.d.ts +19 -0
  163. package/dist/leader/diffup.js +7 -0
  164. package/dist/leader/leader.d.ts +114 -10
  165. package/dist/leader/leader.js +69 -5
  166. package/dist/leader/repair-wire.js +2 -2
  167. package/dist/leader/run-entry.d.ts +2 -1
  168. package/dist/leader/run-entry.js +1 -1
  169. package/dist/leader/wire.d.ts +42 -132
  170. package/dist/leader/wire.js +81 -67
  171. package/dist/memory-scope.d.ts +20 -9
  172. package/dist/memory-scope.js +15 -3
  173. package/dist/model-entry-headers.d.ts +9 -10
  174. package/dist/model-entry-headers.js +0 -4
  175. package/dist/model-entry-refusal.d.ts +7 -4
  176. package/dist/model-entry-refusal.js +11 -5
  177. package/dist/model-plane-face.d.ts +1 -1
  178. package/dist/model-plane-face.js +3 -1
  179. package/dist/model-route-endpoint.d.ts +13 -7
  180. package/dist/model-route-endpoint.js +14 -11
  181. package/dist/model-select.d.ts +22 -0
  182. package/dist/model-select.js +9 -0
  183. package/dist/model-unreachable-notice.d.ts +11 -0
  184. package/dist/model-unreachable-notice.js +4 -4
  185. package/dist/object-store-buckets.d.ts +640 -6
  186. package/dist/object-store-buckets.js +166 -0
  187. package/dist/observability/fail-open.d.ts +20 -16
  188. package/dist/observability/fail-open.js +20 -16
  189. package/dist/observability/logger.js +3 -1
  190. package/dist/operator-roster.d.ts +68 -0
  191. package/dist/operator-roster.js +32 -0
  192. package/dist/orchestration/workflow-park-decision.d.ts +1 -1
  193. package/dist/outbound-dispatcher.d.ts +71 -8
  194. package/dist/outbound-dispatcher.js +48 -7
  195. package/dist/owner-faces.d.ts +1 -1
  196. package/dist/parked-decide.d.ts +2 -2
  197. package/dist/permission-mode-admission.d.ts +59 -26
  198. package/dist/permission-mode-admission.js +37 -10
  199. package/dist/plugins/approval-ask-store-file.d.ts +4 -0
  200. package/dist/plugins/approval-ask-store-file.js +3 -0
  201. package/dist/plugins/approval-ask-store-memory.d.ts +4 -0
  202. package/dist/plugins/approval-ask-store-memory.js +7 -1
  203. package/dist/plugins/approval-ask-store-sql.d.ts +26 -2
  204. package/dist/plugins/approval-ask-store-sql.js +12 -1
  205. package/dist/plugins/blob-backend.d.ts +83 -7
  206. package/dist/plugins/blob-backend.js +57 -6
  207. package/dist/plugins/checkpoint-index-columns.d.ts +20 -16
  208. package/dist/plugins/checkpoint-index-columns.js +3 -6
  209. package/dist/plugins/checkpoint-store-face.d.ts +119 -61
  210. package/dist/plugins/checkpoint-store-face.js +134 -55
  211. package/dist/plugins/landed-attendants.d.ts +2 -2
  212. package/dist/plugins/local-checkpoint-store.d.ts +13 -3
  213. package/dist/plugins/local-checkpoint-store.js +38 -49
  214. package/dist/plugins/one-time-migrations.d.ts +51 -21
  215. package/dist/plugins/one-time-migrations.js +67 -45
  216. package/dist/plugins/pg-pool.js +11 -9
  217. package/dist/plugins/remote-env-host.d.ts +29 -10
  218. package/dist/plugins/remote-env-host.js +99 -19
  219. package/dist/plugins/remote-env-k8s.d.ts +68 -2
  220. package/dist/plugins/remote-env-k8s.js +107 -24
  221. package/dist/plugins/replica-registration.d.ts +1 -1
  222. package/dist/plugins/replica-registration.js +1 -1
  223. package/dist/plugins/s3-presign.d.ts +23 -0
  224. package/dist/plugins/s3-presign.js +37 -3
  225. package/dist/plugins/scheduler-support.d.ts +2 -2
  226. package/dist/plugins/scheduler-support.js +0 -3
  227. package/dist/plugins/schema-indexes.js +2 -0
  228. package/dist/plugins/send-file-ledger.d.ts +2 -2
  229. package/dist/plugins/send-file-ledger.js +2 -2
  230. package/dist/plugins/send-user-file.d.ts +3 -4
  231. package/dist/plugins/send-user-file.js +56 -52
  232. package/dist/plugins/session-blob-claim-token.d.ts +15 -0
  233. package/dist/plugins/session-blob-claim-token.js +20 -0
  234. package/dist/plugins/session-blob-namespace.d.ts +46 -0
  235. package/dist/plugins/session-blob-namespace.js +116 -0
  236. package/dist/plugins/snapshot-bucket-presence.d.ts +25 -2
  237. package/dist/plugins/snapshot-bucket-presence.js +8 -2
  238. package/dist/plugins/sql-json-column.d.ts +1 -1
  239. package/dist/plugins/store-backend.d.ts +44 -2
  240. package/dist/plugins/store-backend.js +75 -53
  241. package/dist/plugins/task-attachment-store.d.ts +2 -1
  242. package/dist/plugins/task-attachment-store.js +5 -1
  243. package/dist/plugins/tidb-pool.js +19 -24
  244. package/dist/plugins/workspace-custody.d.ts +4 -3
  245. package/dist/question.d.ts +2 -1
  246. package/dist/question.js +5 -13
  247. package/dist/request-form.d.ts +6 -1
  248. package/dist/request-form.js +4 -1
  249. package/dist/rule-issue-reading.d.ts +1 -1
  250. package/dist/rules-consent.d.ts +1 -1
  251. package/dist/run-local.js +8 -9
  252. package/dist/runs.d.ts +17 -8
  253. package/dist/runs.js +15 -2
  254. package/dist/runtime-governance.d.ts +2 -2
  255. package/dist/sema-config-dir.d.ts +2 -1
  256. package/dist/session-sync.d.ts +27 -11
  257. package/dist/session-sync.js +15 -7
  258. package/dist/stores-sql/blob-index-sql.d.ts +30 -9
  259. package/dist/stores-sql/blob-index-sql.js +40 -21
  260. package/dist/stores-sql/checkpoint-store-sql.d.ts +54 -27
  261. package/dist/stores-sql/checkpoint-store-sql.js +70 -146
  262. package/dist/stores-sql/file-history-store-sql.d.ts +64 -16
  263. package/dist/stores-sql/file-history-store-sql.js +100 -28
  264. package/dist/stores-sql/retention-store-sql.js +3 -9
  265. package/dist/task-settings.d.ts +6 -5
  266. package/dist/task-settings.js +3 -3
  267. package/dist/tool-approval.d.ts +342 -518
  268. package/dist/tool-approval.js +498 -1207
  269. package/dist/trace/core-keyset-guard.d.ts +1 -1
  270. package/dist/trace/ledger-events.d.ts +3 -3
  271. package/dist/trace/project.d.ts +2 -3
  272. package/dist/trace/redact.js +3 -2
  273. package/dist/trace/self-minted-wire.d.ts +287 -157
  274. package/dist/trace/self-minted-wire.js +93 -48
  275. package/dist/web-admin-client/apply-effective.js +2 -2
  276. package/dist/web-admin-client/read-warnings.d.ts +2 -2
  277. package/dist/web-admin-client/read-warnings.js +1 -1
  278. package/dist/web-admin-client/types.d.ts +1 -1
  279. package/dist/workspace-space.d.ts +10 -4
  280. package/dist/workspace-space.js +4 -1
  281. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +1 -1
  282. package/package.json +6 -2
  283. package/scripts/embed-minimal.mjs +3 -3
  284. package/wire-changes/server@7.152.0.json +900 -0
  285. package/dist/config-d-migration.d.ts +0 -37
  286. package/dist/config-d-migration.js +0 -60
package/USAGE.md CHANGED
@@ -156,7 +156,7 @@ ANTHROPIC_API_KEY=sk-ant-… # 可选:ANTHROPIC_BASE_URL / ANTHROPIC_VERSION /
156
156
  - **凭据什么时候生效**:网关 key `MODEL_API_KEY`(以及 `ANTHROPIC_API_KEY` / `ANTHROPIC_AUTH_TOKEN`)在启动时读一次,**换值 = 重启本服务**
157
157
  —— 没有热应用通道(以前的版本同样如此:这几把 key 只来自启动时的环境变量;引擎 7.51.0 起模型路由的构造配置也只读一次)。
158
158
  目录条目**自己的**凭据(`apiKeyEnv` 点名的变量、`sealedApiKey`)跟着中心 / config.d 的模型面热应用走,不用重启:新起的任务与调用
159
- 用新的一代(在跑的任务用它起跑时那一代)。启动时焊死模型的两只座是一条规则:降级壳焊住的目标、**记忆整理席**解到的条目,凭据**身份**随焊住的模型一起冻结
159
+ 用新的一代(在跑的任务用它起跑时那一代;leader 车道与 agent 钩子同读 —— 跑在一只执行器上的腿用那只执行器出生时那一代)。启动时焊死模型的两只座是一条规则:降级壳焊住的目标、**记忆整理席**解到的条目,凭据**身份**随焊住的模型一起冻结
160
160
  (`apiKeyEnv` 点名的变量名照旧每次按名现读值 —— 同名轮换不用重启;重贴 `sealedApiKey` / 换变量名 = 新身份),同名条目把端点和凭据一起换掉之后、重启之前,
161
161
  这两只座打的仍是焊住的旧端点、带的是旧一代的凭据(绝不把新凭据发往旧端点);身份或端点变了 ⇒ `/health` 报 `restartRequired`(`degrade-route` / `memory-consolidation`)。
162
162
  - **`agents[].model` 写成内联 Model 对象时,这一只模型的请求带哪条凭据**:本服务只验 `id` 非空,引擎对内联对象原样信任、不查目录;每次请求只发**一条**凭据 —— 凭据词头与部署钥互斥,只发赢家。
@@ -528,6 +528,13 @@ MODEL_CODE_ROLES=default,subagent # 不设=全中立;仅这些角色在「
528
528
  拒句点名控制面记下的路径与现在解析到的路径,并给出两条出路:**① 把目录放回原处**;**② 在本服务的目录里跑一次重锚**:
529
529
  `node --input-type=module -e 'import("@sema-agent/core").then(m => m.reanchorMemoryStoreRoot("<path>", { controlDir: "<controlDir>" }))'`
530
530
  (两个坐标拒句里逐字给出),然后再起服。`MEMORY_ENGINE_BACKEND=pg|mysql` 与 `MEMORY_ENGINE=off` 的部署不经这一判。
531
+ - **file 记忆控制面账本的形只在开档判一次;运行中被外部写坏,本服务不检测**:起服装配(`run-local` 的装配同)在构造 file 记忆后端之前,经引擎的只读口问一次「账本是什么形」(只读、不升级、不加锁、不建文件):
532
+ 答「引擎拒读的形」⇒ 一行 warn `memory_ledger_malformed`(`hint` 带出路),**不拒启**;读口自己抛(账本路径是目录 / 无权读 / 环路)⇒ 一行 warn `memory_ledger_check_unavailable`,照常起服。
533
+ **之后本服务不再判它,也没有定时复查**:账本在服务跑着时被手改、被别的进程写坏、被磁盘故障弄坏,**不会有告警** —— 跑着的服务对它的反应只取决于引擎下一次读到它的那一刻:
534
+ 读到坏文件的记忆面答 `500 internal.memory_control_plane_corrupt`,没碰到它的读不受影响,所以症状可能只是「没有报错、读到的还是旧数据」。
535
+ 怎么读日志:起服日志里有 `memory_ledger_malformed` ⇒ 账本的形在开档时就不对;有 `memory_ledger_check_unavailable` ⇒ 开档时没判成,**不能据此分先后**;两行都没有而之后才出 500 ⇒ 账本形在开档时判过关,坏是之后的事,或坏在账本之外的控制面文件(开档这一判只管账本的形)。
536
+ **出路**:修好或还原控制面账本后**重启**(重启即再开档,按上面的开档律判;账本是 5.33.0 起没被任何引擎开过的「绑定之前」旧形时,出路是「先用上一版引擎开一次再升级」—— warn 的 `hint` 逐字给出这句话,出处与细节见 MIGRATION 7.146.0 节第 23 条)。
537
+ 不经这一判的部署形(file 记忆引擎在那里不接线、或根本没有 file 账本):`MEMORY_ENGINE_BACKEND=pg|mysql`、`MEMORY_ENGINE=off`、多租户(`REQUIRE_PRINCIPAL=true`),以及 file 记忆平面与 worker 分家而未显式放行的远端车道形。
531
538
  - **数据根的写豁免只有一个来源(7.126.0 / 引擎 7.49.0 起)**:路径守卫对「本服务自己的数据根」的豁免,只读引擎的 `memoryEngineDir` 一处 —— 本服务把记忆引擎根
532
539
  **恒**交给它,**`MEMORY_ENGINE=off` 的部署也交**(否则豁免根缺席,数据根下的写会被当成普通受保护路径拒)。运维零动作。
533
540
  - **写目标的真落点读不出 ⇒ 有守卫行的面拒这次写(7.126.0 / 引擎 7.49.0 起,所有权限模式含 `bypassPermissions`)**:`Write` / `Edit` / `NotebookEdit` 的目标
@@ -626,8 +633,8 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
626
633
  `run-local` 声明 `host`);自建 HTTP 装配递包根导出的 `HTTP_SERVICE_LANE_ENTRY`(全部车道、不替部署声明);不走文件覆写的装配直接调同一份判据 `assertExecutionLaneInvariants(config)`。
627
634
  `ctx` 的型 `ExecutionLaneComposeCtx` 也从包根导出(自己的装配里要写它的签名 / 抽 helper 时直接 import,不必手抄一份形)。
628
635
  读口场景门 `scenarioGate` 是 `createHttpServer` 的必填件(7.131.0+):用包根导出的 `createScenarioGate({ config, scenarios, principalCaps, logger }, "peek")` 铸(与任务提交腿同一个工厂,型 `ScenarioGate` / `ScenarioGateLeg` / `ScenarioGateSources` 一并导出),没递就是 tsc 红;绕过类型(JS 嵌入)没递或形不对,`createHttpServer` 构造即拒并点名这一席。
629
- **模型路由端点的拒启同理判在目录应用之后**:`judgeServedModelRoute(config, { envTableIsPlaceholder })`(包根导出,HTTP 服务与 `run-local` 调的就是它)只判将要服务的那张表(目录表或 env 表,句子随视图);
630
- 只调 `loadConfig()` 的装配不再在读配置那一拍拿到路由拒启 —— 在**自己的**配置源应用之后调它一次,有判决就拒启(`reason` 是给运维的原句)。严格名册布防(env 表是占位)传 `envTableIsPlaceholder: true`,没有名册门的装配传 `false`。
636
+ **模型路由端点的拒启同理判在目录应用之后**:`judgeServedModelRoute(config, { envTableIsPlaceholder, webAdminIsConfigSource })`(包根导出,HTTP 服务与 `run-local` 调的就是它)只判将要服务的那张表(目录表或 env 表,句子随视图);
637
+ 只调 `loadConfig()` 的装配不再在读配置那一拍拿到路由拒启 —— 在**自己的**配置源应用之后调它一次,有判决就拒启(`reason` 是给运维的原句)。严格名册布防(env 表是占位)传 `envTableIsPlaceholder: true`,没有名册门的装配传 `false`;`webAdminIsConfigSource`(必填)= web-admin 是不是这个装配的配置源,没有配置源的装配传 `false`,只决定 env 表拒因句里「让目录缺省来服务」那条出路提不提 web-admin。
631
638
  随包的可运行参照:`node scripts/embed-minimal.mjs`(在包目录下跑)按上面这条链装一台最小服务 —— 包根工厂、引擎的 `Runner` 与自写的一行 `resolveSpec` 凑齐 `createHttpServer` 的九个必填席,
632
639
  host 车道、一次性数据根、内核分口的回环口,起到监听就打一行 `EMBED-MINIMAL ok port=<n>`;`--omit <席名>` 少给一席,说清 `createHttpServer` 是构造即拒(`refused`,退 2)还是照收(`accepted`,退 3)。
633
640
  构造期判且拒句点名席名的是 `runnerFor` / `authorize` / `deploymentMods` / `peerDesk` / `scenarioGate` 五席(省掉 `config` 抛一句不点名的 `TypeError`);`runner` / `sideQuery` / `resolveSpec` 只有型是提示,绕过类型的嵌入省掉它们构造期照收。
@@ -644,15 +651,29 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
644
651
  宿主不必、也不该替部署把档位压回 info。同一行带两格绑址,一格一个事实:`bindHost` = 本进程**请求**绑的地址(`BIND_HOST` 经无鉴权自收窄之后的那一只 —— 不设 `BIND_HOST` 而写面无鉴权时是 `127.0.0.1`,另带 `bindHostNote`;请求的是全接口时是 `"0.0.0.0/::(all interfaces)"` 这句说明),
645
652
  `boundAddress` = 内核**实际**绑上的地址(`::` / `0.0.0.0` / `127.0.0.1` / `::1` 原样;`BIND_HOST=localhost` 时是解析出的那一只)。自起引擎的宿主要认「这个口上的是不是我起的那只」:
646
653
  读同一行的 `boundAddress` + `port` 去连,再以 `/health` 的 `pid` / `dataRoot` 验身份;别拿配置值去推实绑地址。行上的顶层键集登记在 wire 变更清单(`selfMinted.listeningLineKey`)。
654
+ **监听不开端口复用**(契约 §0.4 的同名条):同一具体地址同口只有一个监听者;绑全接口时引擎不独占回环地址(macOS 上同机进程能用更具体的回环地址在同口截走回环连接,Linux 上被引擎监听范围覆盖的更具体绑定被拒、没被覆盖的异族 / 异址仍可)——
655
+ 所以 `boundAddress` 是具体地址时只连它的逐字地址,不连 `localhost`、不换地址族;是全接口时任何回环地址上有应答都不算「是我起的那只」的证据,TCP 上证不出归属。
647
656
  **不要**先在外面挑一个空闲口、关掉、再交给子进程去绑 —— 关与子进程真正 `listen` 之间有窗,并行负载下别的进程能抢走这个号。`configHash` 仍按**配置值**铸(配置指纹不随 OS 分的口漂)。
648
657
  - `/health` 身份字段承诺:`pid`(= 引擎进程)、`startedAt`(**7.67.0+**,= 引擎进程起点的 epoch ms,
649
658
  一次铸)与 `dataRoot`(= 生效数据根,解析恒回退 `~/.ai-agent`,与 DB_BACKEND 无关)**恒在**(**7.148.0+**:配了服务凭据的引擎上,
650
- 这些键只给出示了凭据的读者 —— 宿主验身份时带起引擎用的**同一枚** `Authorization: Bearer <SERVICE_AUTH_TOKEN>` 读;没出示凭据的读者只拿 `status` / `auth` / `operator` / `center` 四键;
651
- 没配服务凭据的嵌入形照旧匿名全键,见契约 §0.4「读者分档」)——宿主用
659
+ 这些键只给出示了凭据的读者与经本机控制 socket 到达的读者 —— 宿主验身份时带起引擎用的**同一枚** `Authorization: Bearer <SERVICE_AUTH_TOKEN>` 读,或经 socket 读(7.152.0+,见下一条);
660
+ 没出示凭据、经 TCP 到达的读者只拿 `status` / `auth` / `operator` / `center` 四键,对端是回环也一样;没配服务凭据的嵌入形照旧匿名全键,见契约 §0.4「读者分档」)——宿主用
652
661
  它们验证「这个端口上的 /health 是不是我起的那个引擎」。`startedAt` 变了 = 这个端口后面换了一条命
653
662
  (另一个宿主重启了共享引擎):壳据此把「引擎换代」当**一等事实**处理,不必等某个带鉴权的请求先撞一次
654
663
  401 才发现(`/health` 无鉴权,心跳恒绿)。⚠️ 只给这一个数:service token 本体与代际计数**不上**这一面。
655
664
  钉:`test/health-identity-contract.test.ts`。
665
+ - **同机附着方怎么读身份(本机控制 socket;7.152.0+;只 `DB_BACKEND=local`)**:同一台机器上的附着方(壳、桌面宿主 —— 自己起的引擎或别的进程起的引擎)读引擎身份走**控制 socket**,
666
+ 不从引擎日志反推、也不把凭据发给一只回环 TCP 口(macOS 上全接口绑址时本机别的进程绑更具体的回环地址就能答掉回环读者;Linux 上被引擎监听范围覆盖的更具体绑定被拒、没被覆盖的异族 / 异址仍可 —— 见契约 §0.4「监听不开端口复用」)。三句:
667
+ ① **socket 在哪**:`<数据根的绝对物理路径>/control/engine.sock`(自起者直接读引擎 `listening` 行的 `controlSocket`;没起它的附着方按绝对数据根推导 —— 用相对数据根起的引擎只有自起者能用)。
668
+ 连之前核:`<数据根>/control` 是目录而不是符号链接、属主是你自己的 OS 用户、没有组或他人位;`engine.sock` 是 socket、属主是你自己 —— 任一不过就当这台没有 socket。
669
+ 然后经它读 `GET /health`(例:`curl --unix-socket <数据根>/control/engine.sock http://localhost/health`),`pid` / `startedAt` / `instanceId` / `version` / `dataRoot` 全在(`port` 缺席:socket 不是 TCP 口)。
670
+ ② **读到之后仍要比**:照样与你记下的锚点比 `pid` + `startedAt` + `instanceId` —— socket 只证「对面是本 OS 用户」,不证「是你起的那台」(同一 OS 用户的别的进程能删掉 socket 重绑)。
671
+ 带凭据的调用也走这只 socket(凭据照带;socket 不是凭据,配了服务凭据的引擎上不带凭据的写、审批、只读口照旧 `401`);**这条路上证明的归属只算这条路**:不要拿它去放行 TCP 上的凭据出示。
672
+ ③ **没有 socket 的宿主**:`listening` 行带 `controlSocketUnavailable`(或文件不在)时这条路不在 —— Windows(`unsupported_platform`:Windows 上不开这只口,一律走 `listening` 行)、
673
+ SQL / 内存后端(`data_root_not_exclusive`)、数据根路径过长(`path_too_long`:换短一点的数据根;macOS 上 socket 的绝对路径最多 104 字节)、`control/` 被放宽或属主不对(`dir_not_private`:修目录的属主与模式)、
674
+ 路径上有一只不是本用户 socket 的东西(`path_occupied`)、绑失败(`bind_failed`)。这时读 `listening` 行(独占 `127.0.0.1` 的 `boundAddress` + `port` + `instanceId`)证归属;
675
+ 只有引擎**精确绑在** `127.0.0.1` 且这一行证得出归属时,才向那只 TCP 口出示凭据 —— 证不出就不出示。
676
+ 部署前提(数据根与上级不可被别的用户写、不被 ACL 放宽;socket 不得被代理)见部署前提文档「本机控制 socket 与远端读者」一节。钉:`test/control-socket.test.ts`。
656
677
  - **数据根引擎版本高水位 marker(7.147.0+;只 `DB_BACKEND=local`)**:`<数据根>/engine-high-water.json`(`/health.dataRoot` 那只目录的顶层),
657
678
  形 = **恰一键** `{"highWater":"x.y.z"}` —— 打开过这只数据根的最高引擎(本包)版本(引擎判过之后、往根里写第一个字节之前就抬,所以它是「可能写过这只根的最高版本」,不是服务成功的记录)。`highWater` 是纯 `x.y.z`(无前导零、无 `v` 前缀、无预发布 / 构建后缀、
658
679
  每段是安全整数),三段按数比。宿主(壳侧判官)与引擎读**同一只文件、同一读法**,不另记第二份高水位:
@@ -693,10 +714,15 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
693
714
  把部署回退到 ≤7.146.x、再续跑升级之后写下或改写过的会话,结果**不保证**:可能照常续到终答(一次续跑返回 200),也可能被拒 —— 两种都不在支持范围内,本服务不承诺其中任何一种,也不为它加兼容读法(一条旧版读法就是第二份读法)。
694
715
  **出路**:回退前先排空 —— 让待续跑的停驻 run 先收完(完成或取消),再回退;随包的排空检查(`scripts/drain-check-local.mjs`〔`DB_BACKEND=local`〕/ `scripts/drain-check.mysql.sql` / `scripts/drain-check.pg.sql`,只读)是读出停驻行存量的工具;或不跨版回退。回退的唯一支持形是恢复升级前的数据根 / 库备份。
695
716
  - **已知限制:`REMOTE_EXEC=host` 车道上请求不带 `cwd` 时,进程重起之后才被批准的停驻后台子代,续跑的命令落在已删目录**:请求不带 `cwd` 时,host 车道为这条任务铸一个一次性的工作区 `<HOST_WORKSPACE_BASE,缺省 os.tmpdir()>/sema-host-<id>-<rand>`,
696
- 由落在里面的执行环境(父 run 与按父的工作目录落根的委派子代)共同持有,最后一只放手的删它 —— 同一进程里父 run 先收了场、停驻在审批门上的后台子代之后被批准,续跑接着在那只目录里跑,子代最后一条腿收场时目录才删。
717
+ 由工作目录落在里面的执行环境(父 run 与委派子代)共同持有;删之前那一拍按文件系统当下的答案判「此刻谁的工作目录在里面」(经符号链接落进来的也算),有人在就交给它、最后离场的删它,判不出就留到进程退出 —— 同一进程里父 run 先收了场、停驻在审批门上的后台子代之后被批准,续跑接着在那只目录里跑,子代最后一条腿收场时目录才删(删时先在同目录改名成一只隐藏墓名再删)。
697
718
  **进程退出时**(正常退 / 未捕获异常 / 有处理器的信号)退出兜底删掉本进程铸的全部临时目录,含仍被停驻子代持有的;重起之后再批准,续跑的命令落在已删目录上 —— 命令没有运行,引擎的拒句是 `…the task root directory no longer exists (or is no longer a directory): <路径>`,
698
719
  而续跑提醒仍写成命令已执行(以拒句为准)。停驻中被取消 / 过期的后台子代持有的临时目录活到进程退出、由退出兜底删。
699
720
  **出路**:提交时带 `cwd` —— 带了 `cwd` 的请求不走一次性工作区,没有这个删除(`sema` CLI 恒带;SDK 只有 launch 助手默认传 `process.cwd()`,直接调提交接口的要自己带)。
721
+ - **已知限制:进程被硬杀(`SIGKILL` / 内存杀手 / 容器被强停)之后,`REMOTE_EXEC=host` 车道上本服务起的前台命令的进程组不被回收 —— 它们成孤儿,活到自己退出或被外部限额拦下**:host 车道的前台命令(命令在本机跑)记在**进程内存**里的一本簿上,进程退出时由退出边界把簿上每个组整组硬杀;但这道边界挂在 `exit` 事件上,而 `exit` 事件在进程被信号**默认终止**时**不触发** ——
722
+ `SIGKILL`(`kill -9`、OOM killer、容器被强停)之下没有一行收尾代码会跑。那些命令的进程组于是成孤儿(父进程变 init),照常往盘上的输出暂存文件继续写,直到它们自己退出、或被外部限额(磁盘配额 / `ulimit` / cgroup)拦下。
723
+ **覆盖与不覆盖**:**覆盖** —— 正常退出、未捕获异常的崩溃出口、装了处理器的信号(HTTP 车道的 `SIGTERM` / `SIGINT` / `SIGHUP` 收尾;`run-local` 的 `SIGINT` / `SIGTERM`,退出码取 128 + 信号号):退出前对簿上每个组硬杀。**不覆盖** —— 被硬杀的那一代留下的组:簿只在进程内存里、不落盘,**重起之后的新进程不知道前代起过哪些组,也不接管**;起服时的孤儿回收(`DB_BACKEND=local`:把前代孤立的在跑 run 翻成 `failed`、放掉会话占用)作用在 run 行与会话占用上,不作用于进程。远端车道(沙箱 / ssh / adb / device)里的命令跑在远端环境,不在这一条射程内。
724
+ **影响**:被硬杀过的 host 部署上,机器上可能留着父进程为 init 的长跑命令(构建 / 监听 / 不退出的脚本之类),持续写盘、占 CPU,本服务不再跟踪它们。
725
+ **出路**:本服务**不提供**起服回收,也不登记前代的进程组。运维侧:① 把服务放进容器或 cgroup,让「服务被杀 ⇒ 整树被收」由容器运行时 / 进程管理器兜底,并设磁盘配额 / `ulimit` 之类外部限额;② 硬杀之后手工清:找父进程为 init、工作目录或输出路径对得上本服务数据根 / 工作区的进程组,核对后整组 `kill`;③ 要停服务走 `SIGTERM` 或排空口(`POST /v1/admin/drain`),让退出边界跑得起来,不用 `SIGKILL`。
700
726
  - `/health` 活体 store 探测(5.14.0,SQL 后端专属 additive):后台探针(`STORE_PROBE_INTERVAL_MS`,
701
727
  默认 15000;0=关;负/非数启动响亮拒)缓存一份 DB 真往返结果,`storeProbe:{live,ageMs,error?}` 在
702
728
  探针接线时恒在,顶层告警键 `storeLive:false` 只在死时出现。**status 保持 "ok"**(liveness≠readiness
@@ -713,19 +739,19 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
713
739
  逐字同一份),`/readyz` 503 `{"ready":false,"reasons":[…]}` + `Retry-After: 5`(体只这两键,不带身份字段)。
714
740
  ⚠️ 7.102.0 起 **draining 也是未就绪**:此前 draining 形的 `/health` 只有 `draining:true`、不带 `ready` 键(读成
715
741
  「就绪」),与同一刻计费提交的 503 自相矛盾。`storeLive:false` **不进**就绪判(见上一条:披露不代裁)。fleet 心跳的
716
- `ready` 同一只判(名册未落时心跳 `ready:false`,落地当刻即再报一次)。fleet 心跳另带 **`restart`**(**7.102.0+**):
717
- 与 `/health.restart` 同一只取值口、逐字同一个对象(形 = settings-schema `RestartSignal`:`reasons` / `version` / `since`;
742
+ `engineSignal.ready` 同一只判(名册未落时 `ready:false`,落地当刻即再报一次)。fleet 心跳的 `engineSignal.restart`(**7.102.0+**;7.152.0 起只在
743
+ `engineSignal` 里):与 `/health.restart` 同一只取值口、逐字同一个对象(形 = settings-schema `RestartSignal`:`reasons` / `version` / `since`;
718
744
  在场即「须重启才能应用配置」,缺席即不需要 —— 中心整行替换);重启信号出现 / 消失 / 变化的当刻即再报一次,不等下一拍心跳。
719
745
  **7.106.0+**:候选**部分应用**(某组被整组拒、其余组已提交)的拍也照同一律算这一信号 —— 修前那一拍不签;被拒的是审批组 / 模型面组时
720
746
  (新进程 boot 重判可能拒启)信号扣下、只出 warn `restart_signal_withheld_boot_may_refuse`(点名组与差异)。
721
747
  钉:`test/health-readyz.test.ts` / `test/health-readiness-single-source.test.ts`。
722
748
  - **引擎信号 `engineSignal`(7.123.0+,`/health` 与 fleet 心跳都带,恒在场)**:引擎态整只,型 = settings-schema `EngineSignal`
723
- (`@sema-agent/settings-schema/fleet`;字段以契约包为准)。不带 `instanceId`(身份只在体顶层)。与扁平键**同一次读数**:
724
- `engineSignal.draining`(排空事实 `{actionId, since, deadlineAt}`)在场 ⇔ 扁平 `draining: true`,`drainingSince` = `engineSignal.draining.since`;
725
- `ready` / `restart` / 成员三键 / `/health` 世代账两版本与嵌套里的同名键同值。缺席 = 更早的引擎。⚠️ `/health` 无鉴权而排空中
749
+ (`@sema-agent/settings-schema/fleet`;字段以契约包为准)。不带 `instanceId`(身份只在体顶层)。**一次读数**:
750
+ `engineSignal.draining`(排空事实 `{actionId, since, deadlineAt}`)在场 = 排空中,`since` = 排空起点;
751
+ `/health` 顶层的就绪键 / `restart` / 成员三键 / 世代账两版本与嵌套里的同名键同值(心跳体顶层不带任何引擎态键)。缺席 = 更早的引擎。⚠️ `/health` 无鉴权而排空中
726
752
  `engineSignal.draining.actionId` 是本次排空的动作号:引擎自起的排空是引擎的闭集词,经 `POST /v1/admin/drain` 进的是调用方自取的幂等键原样
727
- —— 用不透明 id(推荐 `manual:<uuid>`),别放秘密或人名。扁平布尔对 `draining` / `drainingSince`
728
- 在 settings-schema 12.0.0 删、本引擎 7.124.0+ 删发 —— 读它们的编排脚本 / 壳改读 `engineSignal.draining`。
753
+ —— 用不透明 id(推荐 `manual:<uuid>`),别放秘密或人名。8.0 扁平方言(心跳体顶层 `ready` / `draining` / `restart` / 成员三键,
754
+ `/health` 的布尔 `draining` + `drainingSince`)随 settings-schema 26.0.0 退役,本引擎 **7.152.0** 起停发 —— 读它们的编排脚本 / 壳改读 `engineSignal`。
729
755
  - **配置接力位 `configHandoff`(7.121.0+,只在 `/health`,不上心跳)**:`{ state: "missing", version, since }`,**在场 ⇔ 部署声明了耐久接力(`CONFIG_LKG_DURABLE=true`
730
756
  ∧ LKG 启用)∧ 应用着的那一代没在下一次起服将用的凭证身份的 LKG 桶里确认耐久落盘** —— 中心断着时重启按**此刻的凭证身份**选 LKG 桶,那只桶里没有这一代就回到
731
757
  别的配置。三种形:LKG 发布失败(保留候选、启动期技能债)、部分应用(某组被整拒、其余组已热应用,LKG 停在上一个完整世代)、凭据换了人(凭证文件换成别的身份,
@@ -969,7 +995,7 @@ MINIO_ACCESS_KEY=… MINIO_SECRET_KEY=… # 可选 MINIO_REGION;外接 S3
969
995
  S3_PUBLIC_ENDPOINT=https://files.example.com # 用户可达的 S3/MinIO 公网 base(反代必须透传 Host——SigV4 绑定 Host)
970
996
  S3_PUBLIC_BUCKET=sema-public # 永久轨匿名 GET 桶(默认 sema-public)
971
997
  SESSION_SNAPSHOT_BUCKET=session-snapshots # 限时轨私桶(与 snapshot 面共用;sendfile/ 前缀内隔离)
972
- SEND_USER_FILE_URL_TTL=0 # 缺省 ttl 秒:0=永久(默认);1..604800=限时签名链接;非法值回落 0
998
+ SEND_USER_FILE_URL_TTL=0 # 缺省 ttl 秒:0=永久(默认);1..604800=限时签名链接;其余(超限 / 不是十进制整数:1h、-5、3600.5、1e3)起服拒
973
999
  SEND_USER_FILE_SANDBOX_PUT_ENDPOINT=… # 可选:沙箱直传 PUT 的端点(默认=S3_PUBLIC_ENDPOINT;
974
1000
  # k8s 集群内 pod 可指内网 MinIO 省公网带宽)
975
1001
  ```
@@ -977,6 +1003,7 @@ SEND_USER_FILE_SANDBOX_PUT_ENDPOINT=… # 可选:沙箱直传 PUT 的端
977
1003
  - **🔴 云形(`DB_BACKEND=mysql|pg`)快照 blob 强制对象存储(1.295+,clay 拍)**:缺 MinIO 三键 ⇒ **boot 拒启**——单行 SQL blob 写会撞 TiDB `txn-entry-size-limit`(默认 6MiB,真库实测矮墙)/ mysql 协议 `max_allowed_packet`,大字节归对象存储(与 D-1 附件面同裁定)。单机/测试台显式逃生:`SNAPSHOT_BLOB_ALLOW_SQL_BYTES=true`(SQL 店此时带 per-blob 帽,MySQL 协议(InnoDB 与 TiDB)默认 = **写入那条数据库连接**报的两堵已知墙取最小:单行上限减 1 KiB(blob 行自己的余量:哈希键 + 长度 + 时间戳;TiDB 才有)与包墙 `max_allowed_packet` 扣 1 KiB 语句余量后折半(⌊(包墙 − 1 KiB) ÷ 2⌋:文本协议把 blob 写成十六进制,语句至少是它的 2 倍);任一读数读不出时按该库族文档缺省并留一行运维日志 `fail_open`(tag `server.sql.packet-cap-unread` / `server.sql.tidb-row-cap-unread`),超限 PUT 413 `blob_too_large_for_sql`,句子按帽的出处分三形(运维旋钮 / 库报的墙 / 读不出而按文档缺省假定的墙),机读位 `wallSource` 同词 `operator` / `reported` / `assumed`。包墙是每条连接自己的会话值(7.133.0 起按连接判):DBA 调大 `max_allowed_packet` 之后,池里的旧连接在它第一次按包墙拒 blob 时自换(运维日志一行 `sql_connection_packet_wall_stale`,那一笔在新连接上重判),不必重启副本;`GET /v1/diagnostics/wiring` 的 `sqlEngine.maxRowBytes` / `sqlEngine.maxPacketBytes` 是最近一次初始化的那条连接的读数;帽可用 `SNAPSHOT_BLOB_SQL_MAX_BYTES` 按部署真实限值覆写,两方言生效;pg 默认无帽)。
978
1004
  - **per-file rewind 历史的边界上限**(与 workspace 浏览面同一个整树快照纪元退役批留下的保留条款):`FILE_HISTORY_RETENTION_KEEP` —— 每个 scope(会话)存活的**最新** boundary 数,**每次 boundary 提交后自剪**(最旧者先亡,刚提交者恒存活;失引用的版本行级联删,blob 字节走异步 grace 窗 GC;v1 基线永存)。未设=core 缺省 **100**(由 core 的 `resolveFileHistoryRetention` 兑现,server 不复制那个数);`unbounded` 是**唯一**的「不剪」拼法(整店自剪关闭,GC 归部署自排 `reap`);`0`/负数/小数/非数 ⇒ **boot 拒启**(码 `config.retention_policy_invalid`,与 core 内置后端同码同判——**省略这个键不等于不剪**,它选的是缺省 100)。三条车道(TiDB / PG / local 文件店)同旋钮同判。⚠️ 升级即生效:存量超 100 boundary 的 scope 在下一次提交时被剪到 100;要保全量先写 `unbounded`。被剪 entry 的键**已花掉**(再次 snapshot 同 entry 典型拒,不复铸)。排序=DB 分配的 per-scope 发布序号 `file_history_boundary.publish_seq`(不是墙钟):**自 7.50.x 有存量 `file_history_boundary` 的部署升级本版会在 boot 期被 `assertFileHistorySchema` 拒启,拒因打印这张表缺的列的 `ALTER TABLE … ADD COLUMN …`,由部署者自己执行**(列集对账与拒启句只打印语句、永不改库;per-file rewind 历史照留,会话与其他持久面不动)。⚠️ `publish_seq` 是 `NOT NULL` 且无缺省的列(要值列:已有行的值只有部署者知道),拒启句对它打印**三步配方**而不是一句 ADD COLUMN:先 `ADD COLUMN publish_seq BIGINT;`(可空加列),再一行 `-- ` 注释(回填 `UPDATE file_history_boundary SET publish_seq = … WHERE publish_seq IS NULL;`,值由部署者按 per-scope 的发布顺序补;注释行不会被误执行),最后收紧(PostgreSQL `ALTER COLUMN … SET NOT NULL`;MySQL 协议先开 `STRICT_ALL_TABLES` 再 `MODIFY COLUMN`)。打印的语句全部生效之前起服一直拒 —— 只执行了第一句(列已在、仍可空)同样拒,并只再给后两步;空表上打印的语句逐字全部可执行。
979
1005
  - **会话同步重推(同图)要读 blob**(7.136.0 起):`POST /v1/sessions/:id/sync/import` 的 identical 重推臂,对云端「已握着同一张文件历史图」的目标,也先把信封引用的每一枚 blob 经云端自己的店读一遍、核哈希(不回写),再答成功 —— 对象存储里的对象若在图发布之后丢了,这一腿答 `422 missing_blob`(`PUT` 缺的 blob 再推;图不动),不再答 `200 identical`。代价:无变化会话的频繁重推多付对象存储读流量(每枚引用的对象一次下载,正比于引用的 blob 字节);没有旋钮。
1006
+ - **会话同步的文件历史 blob 按落点可见**(7.152.0 起;SQL 后端):一枚 blob 只对「这只会话的历史图引用它(会话在时)」或「在这只会话 id、同一调用方身份下 `PUT …/sync/blobs/:hash` 传过它」的推送 / 拉取可见 —— 别的主体 / 别的会话传过的同一份字节不让你的推送过预检(`422 missing_blob`,与谁都没传过同答),也读不出来(`404 not_found.blob`)。字节仍全局去重一份;「谁传过」记在 `file_history_blob_grant`(起服自建,随会话删除清,收割腿在 blob 孤儿回收的 1 h 宽限之后清掉没导入的上传,还记着的上传不当孤儿回收)。客户端照契约在第一阶段之前逐枚 `PUT` 即不受影响;上传与导入身份不同、或相隔超过 1 h,会多一轮 422 → 补 `PUT`。升级之前已写进图的引用维持原样,核查跨属主共享的 blob 用 MIGRATION 7.152.0 节那一句只读查询。
980
1007
  - **文件历史捕获总开关**(7.97.0 立,7.104.0 改语义):`FILE_HISTORY_CAPTURE`(缺省 `true`;布尔词表 true/false/1/0/yes/no/on/off,未知词 ⇒ boot 拒启;TiDB / PG / local 三条车道同义)。`false` ⇒ **只关捕获**:历史店照接,引擎拿到部署捕获位 `RunnerDeps.fileHistoryCapture: "off"`,改动文件的首次快照**一个都不捕获**、不铸回合边界(`wiring_manifest.fileHistory` 报 `"disabled"`);**已存历史照常可用** —— `resumeAt`+`restoreFiles` / `rewindFilesTo` 照常还原、2c session-sync 照常导出 / 导入。`GET /v1/capabilities` 的 `fileHistoryCapture` 报 `"off"`,而 `restoreFiles` / `rewindFilesTo` / `sessionSync` 仍为 `true`(缺省部署报 `"on-optional"` = 捕获中且请求可按 run 关掉,见上一段)。🔴 **7.97.0–7.103.x 的旧语义**:`false` = 不把店交给引擎,连已存历史也读不了(还原走 `rewind.store_unconfigured`、sync 501)—— 把本旋钮当成「整个文件历史功能关掉」来用的部署,升级后已存历史**重新可以被还原 / 同步**。要让已存历史也不可用,今天的出路只有**逐会话** `DELETE /v1/sessions/:id`(擦除面不受本旋钮管)与 SQL 后端的留存清扫(`RETENTION_MODE=enforce` + `RETENTION_MAX_AGE_DAYS` + `RETENTION_SWEEP_INTERVAL_SEC`,按会话年龄删,local 车道没有留存车道);**没有**「整库历史一键不可用」的旋钮。⚠️ **关掉 ≠ 删掉**:翻旋钮不批量删除存量历史;删会话照删已捕获的历史(三条车道同读:local 车道自 7.146.0 / 引擎 7.64.0 起也走整会话清理动词 —— 边界、版本记录、v1 基线与只被这只会话引用的 blob 一起删;此前 local 走 `reap`,v1 基线留存)。关着时 `FILE_HISTORY_RETENTION_KEEP` 没有效果(不再有边界提交,自剪不会触发),值仍照常校验。没有存储后端的部署(env-only worker)本来就没有历史店,旋钮在那里无事可关、不下发捕获位(引擎对「off 但没店」拒 prepare)。
981
1008
  - **🔴 公桶策略必须 GetObject-only**:`mc anonymous set download` 会**连带打开 ListBucket**——匿名 `GET /<bucket>/?list-type=2` 能枚举全部不可猜 key,能力链接设计即告失效。正确姿势=`mc anonymous set-json`,policy 只含 `Action:["s3:GetObject"]` on `arn:aws:s3:::<bucket>/*`(验证:对象 GET 200、桶 LIST 403)。
982
1009
  - **两条轨**:ttl=0(默认)→ 匿名 GET 公桶下 `uuidv7/<name>` 不可猜 key,链接永久、可回收(删对象);0<ttl≤7 天 → 私桶 + SigV4 限时签名链接(7 天是 SigV4 物理上限,更久用 ttl=0)。
@@ -1151,32 +1178,27 @@ QUESTION_TTL_MS=300000
1151
1178
  要在活流腿上保留旧的自答续跑行为,目前的办法是把该请求改走 `/v1/tasks`(非流)或 `/v1/runs`,
1152
1179
  或带上 `x-detach-on-disconnect: true`。
1153
1180
 
1154
- **流内审批协议(`approval_request` 帧族)—— 总开关 + 九个调优钮**
1181
+ **流内审批协议(`approval_request` 帧族)—— 十个调优钮(没有总开关)**
1155
1182
 
1156
- > 🔴 **默认已是 ON**(clay 裁 2026-08-08;7.3.0/7.4.0 的**已发布**线上仍是 OFF,翻转落在其后的下一个发布)。
1157
- > 开关只是发帧五合取里的一项:还要**活卡腿开着 ∧ 非 local 的持久 store backend ∧ park 设施在场**
1158
- > (`DURABLE_APPROVAL=true` + checkpoint 能力的 backend)。所以一台什么都没配的默认 worker 仍是零帧 +
1159
- > 启动一行 `stream_approval_disabled`;而**已经配齐那套前置的部署,升级后不必再显式开这个开关就会开始发帧**
1160
- > —— 壳/SDK 不实现消费就等于用户看不到审批卡。🔴 **7.133.0 起一条腿一个帧族**:协议上场的 worker(`GET /v1/capabilities` 的
1161
- > `streamApproval` 为真)只发 `approval_request` / `approval_complete` / `approval_revoke`,旧族 `tool_approval` / `tool_approval_complete`
1162
- > 不再发;协议不上场的 worker 只发旧族。帧格式、消费端契约、五合取全表在
1183
+ > 🔴 **7.152.0 起没有总开关**:协议上不上场只看这台 worker 的装配 —— **审批席(`TOOL_APPROVAL_ENABLED`)∧ store backend ∧ park 设施在场**
1184
+ > (`DURABLE_APPROVAL=true` + checkpoint 能力的 backend)。三项任一不成立 ⇒ 起服不装审批席:零帧、零卡(审批席开着而后两项缺一 ⇒ 启动一行 warn
1185
+ > `approval_cards_unavailable` 说出缺哪项),受门调用的终局归引擎(有 park 设施 ⇒ 停驻;否则当场拒)。旧总开关 `STREAM_APPROVAL_ENABLED` 已退役:部署 env 里留着它(任何值)⇒ **拒启**
1186
+ > (契约包墓碑,拒句带 `config.env_key_retired`、点名这一键,不回显值),出路 = 删掉这一键 —— 它的「关」是回退发旧族卡,旧族已经没有了。
1187
+ > 壳/SDK 不实现呈卡族的消费就等于用户看不到审批卡。🔴 **只有一个帧族**:呈卡的 worker(`GET /v1/capabilities` 的
1188
+ > `streamApproval` 为真)只发 `approval_request` / `approval_complete` / `approval_revoke`;旧族 `tool_approval` / `tool_approval_complete`
1189
+ > 不再出现在任何流上。帧格式、消费端契约、五合取全表在
1163
1190
  > 契约 §4a-quint(内部记录;错误码 `feature.approval_ask_disabled` 在契约附录 A)。
1164
- > 🔴 **7.134.0 起卡只发给声明认得这一族的客户端**:提交体 `settings.approvalFrameFamilies: ("tool_approval" | "approval_request")[]` 声明这条 run 的客户端
1165
- > 渲染得了哪些族;一条 run 只在声明含 `GET /v1/capabilities` 顶层 `approvalFrameFamilies` 那个词时收到活卡。缺席 / 不含 ⇒ 这条 run 不呈卡(不换族、不双发)、
1191
+ > 🔴 **7.134.0 起卡只发给声明认得这一族的客户端**:提交体 `settings.approvalFrameFamilies: "approval_request"[]` 声明这条 run 的客户端
1192
+ > 渲染得了这一族(7.152.0 起写 `"tool_approval"` ⇒ `400 request.field_invalid`);一条 run 只在声明含 `GET /v1/capabilities` 顶层 `approvalFrameFamilies` 那个词时收到活卡。缺席 / 不含 ⇒ 这条 run 不呈卡(不换族、不双发)、
1166
1193
  > 开流不重放挂着的卡,每只审批问(会话里已记住的放行照旧先兑现)当场收场,结局看这台 worker 有没有耐久店(`DURABLE_APPROVAL` 开着):在场 ⇒ 交 `UNATTENDED_APPROVAL_POLICY`:`park`(缺省)⇒ 任务停驻、`/v1/approvals/:sessionId/decide` 作答;`deny` ⇒ 当场拒,拒因句点名缺的族与出路;
1167
1194
  > 不在场(`DURABLE_APPROVAL=false`,含 helm 图表关持久的形)⇒ 受门工具当场拒(缺省 `park` 政策下是引擎按 `approver_unavailable` 拒,结算当事方是「无人」,不是 `deny` 政策的结算,拒因句不点名缺的族与声明键;`deny` 政策下是部署政策拒)、run 跑完、无卡无 `/decide`(对 7.133.x 是有意的行为收窄:那类 worker 原本出活的 `tool_approval` 卡);两形都不出任何一个族的卡。
1168
- > 运维日志 `approver_unavailable` 的 `arm` 是 `frame_family_undeclared`。对两个族同一条(`STREAM_APPROVAL_ENABLED=false` 的 worker 呈 `tool_approval`,同样要声明)。
1195
+ > 运维日志 `approver_unavailable` 的 `arm` 是 `frame_family_undeclared`。
1169
1196
  > 续跑读持久化的提交体(声明随体重放);耐久事件流 `GET /v1/runs/:id/events` 的开流重放只列这条 run 名下的卡,声明由卡作证(没有通道的腿一张卡都不落;7.135.0 起不读落盘的声明,run 跑完之后名下仍在窗内的卡照样重放);与同步流开流共读同一只判据函数,入参不全对称:关窗臂里「贴 deadline ⇒ `window_zero`」由 sync 腿判,事件流只按窗判。坏形(非数组 / 词表外 / 重复)⇒ `400 request.field_invalid`(`field` / `reason: "bad_entry"` / `index`)。
1170
1197
  > 受理位 `taskSettings.approvalFrameFamilies`(恒真;老 server 对这个键 `400 request.body_shape` —— 见位才发)。读法全文在契约 §0.58。
1171
1198
 
1172
1199
  ```bash
1173
- # 协议总开关。默认 true;显式 =false 是**本协议面**的干净还原键(不发 approval_request / approval_complete、改发旧族帧、不落 ask 行、
1174
- # 不起收敛器腿)。⚠️ 它**不**回滚 7.34.0 的「窗到期走 park」语义——那一条的
1175
- # 回滚键是下面的 UNATTENDED_APPROVAL_POLICY=deny(两根旋钮正交,别当一根用);它也**不**再回滚窗长
1176
- # (窗是活卡件不是协议件,见下一键——缺省值与旧硬编码窗同为 300000,没配过就仍然字节不变)
1177
- STREAM_APPROVAL_ENABLED=true
1178
- # 活卡窗(毫秒)。**辖域=所有腿**:协议上不上场都按它计窗——无 checkpoint 店 / 关了协议的部署同样
1179
- # 生效(此前那几形上本键一个字节都不生效、窗恒 5min,且没有任何一行说出来)。
1200
+ # (协议总开关 STREAM_APPROVAL_ENABLED 已退役:设了任何值即拒启,删掉它。要回到「窗满即拒」配 UNATTENDED_APPROVAL_POLICY=deny。)
1201
+ # 活卡窗(毫秒)。**辖域=呈卡的每一条腿**(窗是活卡件,缺省值与旧硬编码窗同为 300000,没配过就仍然字节不变)。
1180
1202
  # 🔴 **缺省值是 posture 派生的,不是一个数**:单机 turnkey(`REQUIRE_PRINCIPAL` 未设)=
1181
1203
  # **86400000(24 小时)**,多租户形 = 300000(5min)。**7.81.0 起这根旋钮在单机上是要读的那一根**:
1182
1204
  # local 后端自此有持久 ask 账、流内协议上场,受门 ask 先发一张活卡、**窗满才转 park**(升级前是铸造
@@ -1229,10 +1251,10 @@ UNREACHED_ASK_TTL_MS=3600000
1229
1251
  **同一条不变量对悬挂窗再判一次**(7.52.0 案A):`UNREACHED_ASK_TTL_MS + STREAM_APPROVAL_ADHOC_GRACE_MS`
1230
1252
  同样必须严格小于 `STREAM_APPROVAL_ORPHAN_TTL_MS`(悬挂行的 `expiresAtMs ≈ 创建 + 悬挂窗)。两条分开判
1231
1253
  是为了让拒启文案点得出**是哪一个键**破了不等式;默认值(1h + 60s vs 7d)自然满足。
1232
- ⚠️ 悬挂窗这一条**不是无条件**:门挂在「这台部署会不会铸出悬挂行」上,三形豁免(配置期可知)——
1233
- `STREAM_APPROVAL_ENABLED=false`(协议不上场,零 ask 行)、`TOOL_APPROVAL_ENABLED=false`(协调器不构造,
1254
+ ⚠️ 悬挂窗这一条**不是无条件**:门挂在「这台部署会不会铸出悬挂行」上,两形豁免(配置期可知)——
1255
+ `TOOL_APPROVAL_ENABLED=false`(协调器不构造,
1234
1256
  core 走 headless 缺省,零 ask 行)、`UNATTENDED_APPROVAL_POLICY=deny`(零活流的 ask 铸造时刻即拒,
1235
- 悬挂路径结构上不可达)。三形下该键即使与短 orphan TTL 组合也照常起服 —— 一批从未选择本能力的既有
1257
+ 悬挂路径结构上不可达)。两形下该键即使与短 orphan TTL 组合也照常起服 —— 一批从未选择本能力的既有
1236
1258
  部署不因升级引入的 1h 缺省当场拒启。上一条(`STREAM_ASK_WINDOW_MS` 版)保持**无条件**,与既有行为逐字一致。
1237
1259
  - **断连时活卡怎么办**:客户端**优雅断开**(server 收到 socket close)时,流内未决 ask
1238
1260
  立即强转 durable park(7.16.0 起),恢复后 attach 重放会重呈卡。客户端**网络中断**(TCP half-open,
@@ -1240,21 +1262,12 @@ UNREACHED_ASK_TTL_MS=3600000
1240
1262
  耗尽(十几分钟量级)才感知——这段时间内真正的兜底是**活卡窗到期转 park**(`STREAM_ASK_WINDOW_MS`,
1241
1263
  默认 5min),所以把窗改大等于把断连场景的最坏无人区拉长,改小前先看上面的跨旋钮不变量。half-open 的
1242
1264
  主动探测(写水位判死)在攻关排期。
1243
- - **无 durable 前置的部署(四合取不满足:无 backend / 无 checkpoint 能力 / 显式关协议 / 无活卡腿;
1244
- ⚠️ **7.81.0 起 `local` backend 不再是其中一项** —— 它有了文件形 ask 账,配齐 `DURABLE_APPROVAL=true`
1245
- + checkpoint 店的单机部署上协议**照常上场**,`GET /v1/capabilities` 的 `streamApproval` 报 true)**:流内协议整个不上场
1246
- (启动一行 `stream_approval_disabled` 点名缺哪项),ask 走旧活卡腿——断连场景的最坏结局是活卡 TTL
1247
- 窗满**自动 deny(fail-closed)后 run 继续**,不会死锁在等一张没人能回的卡上;代价是断连期间用户的
1248
- 批准机会直接过期。(7.34.0 起这条 deny 由**引擎**在「没有可停靠的 durable 门」时给出——服务侧只报
1249
- 「此刻无人可答」,见上面的 `UNATTENDED_APPROVAL_POLICY`;结局不变,拒绝理由的文案更准确。)要「断连也不丢决策」的语义,配齐 durable 前置(持久 store + `DURABLE_APPROVAL=true`)。
1250
- ⚠️ **这条腿上的窗照样是 `STREAM_ASK_WINDOW_MS`**:此前它被硬编码成 5min、旋钮在这些部署上
1251
- 静默无效(慢组场景只能真等满 5 分钟);现在调它就是调这条腿。另:这类部署上**迟到决议**
1252
- (卡挂过窗、人才按 Yes)不能走 `POST /v1/tool-approvals/:id/respond`(没有持久 ask 行 ⇒ 恒 404
1253
- `not_found.ask`;7.147.x 及以前 `tool_approval.not_pending`、不带 `cause`),要走 `POST /v1/approvals/:sessionId/decide`
1254
- ——前提是配了 `DURABLE_APPROVAL=true`(local 车道有文件形 checkpoint 店),否则窗到期即 fail-closed 拒。
1255
- ⚠️ **上面这段「没有持久 ask 行 ⇒ 恒 404」自 7.81.0 起不再适用于 `DB_BACKEND=local` 本身**:local 的
1256
- ask 账已是文件形,配齐 park 设施的单机部署上迟到决议走 `respond` 的赎回席是通的;本段现在只说
1257
- 那几条**真的**不上场的部署形(无 backend / 无 checkpoint 能力 / 显式关协议)。
1265
+ - **无 durable 前置的部署(三项任一不成立:审批席 / store backend / park 设施;`local` backend 有文件形 ask 账,配齐
1266
+ `DURABLE_APPROVAL=true` + checkpoint 店的单机部署上协议照常上场)**:7.152.0 起不装审批席 —— 没有卡、没有活登记,
1267
+ 受门调用的终局归引擎:审批席关而有 park 设施 ⇒ 照常 durable park(经 `POST /v1/approvals/:sessionId/decide` 作答);审批席开而无
1268
+ store backend / 无 park 设施 ⇒ 引擎的无审批席臂当场拒(启动一行 warn `approval_cards_unavailable`),有问行的卡的两扇口
1269
+ (决断口 `POST /v1/tasks/:taskId/asks/:askId/decision` / 按问读口 `GET /v1/tasks/:taskId/asks/:askId`)答 `501 feature.approval_ask_disabled`。要「断连也不丢决策」,
1270
+ 配齐 durable 前置(持久 store + `DURABLE_APPROVAL=true`)。(7.151.x 及以前这类部署呈旧族活卡、窗满 fail-closed 拒;旧族已退役。)
1258
1271
  默认值(300s + 60s vs 7d)自然满足,只有显式改坏才会撞上。
1259
1272
  - **调参方向**:想让人有更长时间点审批卡 → 调大 `STREAM_ASK_WINDOW_MS`;卡太多刷屏 → 调小两个 `ADMIT_MAX_*`
1260
1273
  (代价是超限的 ask 走 park,要有人去审批队列捞);库压大 → 调小 `STREAM_APPROVAL_RECONCILE_BATCH`
@@ -1313,10 +1326,8 @@ UNATTENDED_APPROVAL_POLICY=park
1313
1326
  - 纯部署级:**不看**客户端的任何表态/权限模式。协调器整体关掉(`TOOL_APPROVAL_ENABLED` 关)时这个旋钮
1314
1327
  没有施加对象。`APPROVAL_REQUIRE` 名单只决定「这只工具要不要审批」,与本旋钮无关;协调器在场时,名单工具的
1315
1328
  审批与其余 ask 走同一条路、同一个政策(`deny` 部署上同样当场拒)。
1316
- - 🔴 **与 `STREAM_APPROVAL_ENABLED` 正交**:把流内协议开关关掉**不会**回滚 7.34.0 的「窗到期走 park」—— 它只是不发
1317
- `approval_request`、不落 ask 行、不起收敛器腿,活卡窗到期照样走 park(park 的承载是引擎的 checkpoint,
1318
- 不是 server 的 ask 行,「有 durable 设施但没开协议」正是「窗到期走 park」要救的那类部署)。要回到旧的「窗满即拒」
1319
- 就配 `UNATTENDED_APPROVAL_POLICY=deny`。
1329
+ - 🔴 **回到旧的「窗满即拒」只有这一根旋钮**:配 `UNATTENDED_APPROVAL_POLICY=deny`(曾经的协议总开关 `STREAM_APPROVAL_ENABLED` 已退役,
1330
+ 设了即拒启;它从来不回滚「窗到期走 park」—— park 的承载是引擎的 checkpoint,不是 server 的 ask 行)。
1320
1331
 
1321
1332
  **「本会话不再询问」对引擎的问也生效(7.106.0 起)**
1322
1333
 
@@ -2195,6 +2206,16 @@ PostgreSQL 硬上限 ⇒ 只有缩小;7.151.0 起 413 句与错误本身不再
2195
2206
  > `terminal.kind:"failed"`+`code`);`POST /v1/runs` 202=已受理(执行期结局落在终态 run 记录/`done` 帧)。执行期
2196
2207
  > 错误结构上无法用 HTTP 状态码承载——流的响应头在 run 开跑前就发完了。
2197
2208
 
2209
+ **同一把 `Idempotency-Key` 重投(`POST /v1/tasks` · `POST /v1/tasks/stream` · `POST /v1/runs`;契约 §0.7,内部记录)**:同一调用方、同一把键、**同一份体**才算同一次提交;体不同 ⇒ `409 conflict.idempotency_payload_mismatch`(要重试就重发同一份体,要发不同的请求换新键)。首发**已落定**之后的重投读行作答:同步腿 `200` + 与首答同一只结果信封的体(读行重放不带只属于首答那次请求的 `wiringManifest` / `notice` / `warnings`)+ `replay: true`,流式腿终帧 `done.result` 同体(`done.replay` 同位),`POST /v1/runs` 照首答的 `202 { taskId, sessionId, status }`。
2210
+ **首发还在跑的时候重投,同步 / 流式腿的回执有两形,取决于重投落在哪个进程**(这是现行行为,不是两个版本的差别):
2211
+
2212
+ | 重投落在哪 | 你拿到什么 | 等多久 |
2213
+ |---|---|---|
2214
+ | 首发所在的**同一个进程**(那条在飞的提交还挂在本进程的在飞表里) | **等**首发落定,拿同一份结局(与首答同体)+ `replay: true`;流式腿是终帧 `done.result` 同体;首发被拒则拿同样的拒 | 本层没有第二只超时,等到首发落定为止。**只有在飞表条目有期限**:缺省 15 分钟(不可配);首发 15 分钟后仍未落定,**之后才到的**同键重投不再合流、改读行(下一行) |
2215
+ | **另一个副本**、**服务重启之后**、或在飞表条目已过 15 分钟 | 读行:行上**还没有结果可交** ⇒ **立刻**答 `200` 轮询回执 `{ taskId, sessionId, status, replay: true }`(`status` 是那一行此刻的词,在跑 ⇒ `running`);行上已有结果 ⇒ 与首答同一只结果信封的体(同上,不含首答那次请求独有的三键)+ `replay: true` | 立刻返回,不等 |
2216
+
2217
+ 所以:带短超时的重试方,落在同一进程的重投看起来像「卡住」(它在等首发),落在别的副本的重投立刻拿到一份**没有结局**的回执。判别:体里**没有 `terminal` 键**、只有 `taskId` / `sessionId` / `status` / `replay` 的是轮询回执,**不是**结局,不要当终局读 —— 去 `GET /v1/runs/:taskId`(或 `GET /v1/runs?idempotencyKey=<原始键>`)轮询。`POST /v1/runs` 腿没有这两形:它的首答与重投恒是 `202` 回执,结局本来就要轮询。
2218
+
2198
2219
  ---
2199
2220
 
2200
2221
  ## 2. Hello world——同步发一个任务,拿到答案(最简单)
@@ -2314,14 +2335,14 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
2314
2335
  | 端点 | 用途 |
2315
2336
  |---|---|
2316
2337
  | `GET /v1/sessions/<id>` | 审计回溯:当前上下文 + 摘要(owner 校验);消息里模型 / 工具产出的文本与 live 帧、账本帧同一只脱敏(密钥形换占位,其余字节不变);Read 图像 / PDF 的媒体卡(工具结果消息的 `details`)的 `file.base64` 是字节编码,逐字。**委派子代的转录**也走这一口:`<id>` = 子代帧 / fleet 子代行上的 `transcriptId`(未压缩过的子代首条用户消息是委派 prompt;子代跑过的工具是 assistant 消息里的 `toolCall` 段 + 同 `toolCallId` 的 `toolResult` 行;整段逐字条目走 `…/sync/entries`)。体顶层加性键 `agent: { id?, parentSessionId?, rootSessionId? }`(**7.133.0+**)说这是哪只子代:只投影会话行上存下来的委派元组,`id` 与 fleet 行的 `id`、`GET /v1/agents/roster` 的 `handle` 同一个串;键缺席 = 普通会话 / 老 server(`DB_BACKEND=local` 到 7.140.x 也是)/ 会话店不存元组(`DB_BACKEND=memory`;嵌入方自建、不声明委派分区的店),读作「没存」、回落 fleet 行 / 名册上的 id。子代会话的属主是引擎判的委派树属主(**7.144.0+**:没人派生的 run 读自己那一行的属主、按化身绑一次,原样带给每一层子代;三只持久会话店〔MySQL 协议 / PostgreSQL / local〕建行那一笔原样记引擎交来的属主,`null` = 根匿名;停驻挂点只比对宿主行属主与引擎交来的属主,不认领、不推断。**7.133.0–7.143.x** 是本服务在建行那一笔按根 / 父推断:根会话的行在 ⇒ 它的属主,根行已不在 ⇒ 它记录的父会话的,都不在 ⇒ 哨兵 `_orphan:<id>`;7.132.x 及更早的 SQL 后端上子代会话无主、谁都读得到)⇒ 用发起子代的那位主体读;别的主体与不存在的 id 同一个 `404 not_found.session`。属主为空的委派子代行(根会话本就无主,或升级前写下、还没回填)只有 fleet 调用方读得到(出示 service 凭据、不带主体头;dev-open 部署不钉人),任何具名主体、没有 service 凭据的无身份调用方同样 404(存量无主会话互认臂 —— 单用户形下 `default` 主对无主的普通会话放行,fork / 删除 / `/sync/*`(同步族自 7.134.0 起)同走这一只 —— 对这些 placed 行同样不放行,审批豁免、notify 同样不放行;fork / 删除 / `/sync/*` 的 fleet 调用方与此处同一个判据:出示 service 凭据、不带主体头,带主体头的 service 凭据请求是那位主体,7.134.0 起);SQL 后端两店每次起服由一次性迁移 M23 在它的窗内回填升级前写下的无主 placed 行(按 7.133.0 起的读法:根的属主,根行不在取记录的父的,都不在写 `_orphan:<id>`;起服读数行 `placedSessionOwnersRemaining` 非零 = 还有没定下的,再起直到为 0),local 后端没有回填臂(**7.145.0+** local 后端建子代行先记属主、再建转录存档,进程崩在两笔之间留下的是一只没有转录的属主格 —— 不是子代行,不再新生无主子代行;更早版本崩在两笔之间留下的无主子代行照旧无主、不回填,出路是取消 / 删掉那只后台子代)。属主 `_orphan:<id>` 由 7.133.0–7.143.x 的建行推断与 M23 回填写下(7.144.0 起本服务不再推哨兵,建行原样记引擎交来的属主 —— 已有的哨兵属主随引擎的树属主原样带到它下面新建的子代行上):任何主体都不匹配,只有 fleet-wide / operator 读得到,前缀永久保留。停驻时宿主行属主与引擎交来的不等 ⇒ 停驻否决(那张待批卡作废、那一只子代以失败收尾) |
2317
- | `GET /v1/approvals?owner=user:42` | 高危写审批(durable,需 `DURABLE_APPROVAL=true`):**operator** 看待办队列(可按 owner 过滤);非 operator 只看自己的。队列里某一行的存储体读不出来、重试不会好时,答 `500` + `errorCode:"approvals.read_corrupt"`(**7.134.0+**;与订阅流同码、同一只判官;运维处置那一行后再读,别原样重试);此刻读不出队列(连接 / 超时 / 店抖动)⇒ `503` + `errorCode:"approvals.read_unavailable"`(**7.135.0+**,此前 500 `internal.error`;稍后重发,这一答不表示队列为空);本服务自己的程序错(代码缺陷 / 读腿漂移)⇒ `500` + `errorCode:"internal.error"`(**7.148.0+**,此前同落 503;重发不会好,看运维行 `request_error`)。`GET /v1/assistant/inbox` / `GET /v1/assistant/tasks` 读同一个队列,两种读失败同码同状态(**7.135.0+**)。一张停驻行的存储体读不出摘要(手改成 `{}`、整段缺 `state` 等)只坏它自己那一行(**7.145.0+**,MySQL 协议 / PostgreSQL 后端):收件箱与运维队列照列它、带引擎判官的 `rowVerdict`(只缺 `pendingAction` 的行判官答 `ok`,引擎侧已知缺口),任务视图照列那条 run,三个面都不再整张答 500 / 503;别的卡的决断照常,这张卡自己的决断答 409 带码拒,`error` 以行上 `rowVerdict.sentence` 开头(**7.146.0+**;此前存储体 `{}` 的卡被指向 plan_review 的口、存储体读不成检查点的卡只有店的诊断);`DB_BACKEND=local` 上列表面本版仍整张 503(决断已逐行)。开了工具审批时响应另带 `livePending[]`(本副本上的活卡,决议走 `POST /v1/tool-approvals/<approvalId>/respond`):**7.149.0+** 它与下面的按问读口同一只读法 —— 列出的每一张,读口对同一调用方都答 `pending`(反过来不然:具名主体的列表不含无主的卡,运维列表含读口不放行的卡),店读不出某张卡的行 ⇒ 这张不列(读口同时答 404 `not_found.ask`;`livePending` 为空不等于没有待批,可能是此刻读不出),行已被别处收走 ⇒ 不列,属主取行上当下的那一位(此前按本副本登记列,店抖动期照列而读口 404);店恢复后重列即回,每次列表对每个有行的不同 ask 多一次按 id 的点读。停驻的**技能卡**(对引擎 `Skill` 工具的调用)的行带 `boundGrantDigest` / `boundGrants`(批准它还授予的东西:技能名 + 两只工具清单;清单只在哈希等于摘要时才有,是部署文本,渲染要按上下文转义),分类器没给判词的卡带 `classifierNoRuling`(`error` / `timeout` / `parse_error`)—— **7.136.0+**,与 `GET /v1/assistant/inbox` 行逐字同值(此前只有收件箱行与活卡帧带);普通卡三位都缺席。`GET /v1/assistant/inbox` 行的 `objective` 是门所属 run 的**目标预览**(写入时已脱敏、截到约 120 字符,与 `GET /v1/assistant/tasks` 读同一列;委派子代的门读派它的那条 run;没有 run 账本 ⇒ `null`;**7.136.0+**,此前原样回显存着的任务目标,运维 `?owner=` 跨看也是原文)。停驻行带 `agent`(**7.145.0+**):行的会话在委派树里的位置 `{ id?, parentSessionId?, rootSessionId? }`,与 `GET /v1/sessions/<sessionId>` 体顶层的 `agent` 同一只读口、逐值同答 —— 后台委派子代停驻的行(`taskId` 为 `null`、`sessionId` 是子代自己的会话)凭它连回发起委派的会话(`rootSessionId` / `parentSessionId` = 本端会话 ⇒ 本端的卡);缺席 = 这台 server 没有说(宿主会话自己的停驻、同步委派的子代、`subagent_type: "fork"` 的后台子代、不存委派元组的店、那一读没读成 —— 只这一行缺键、列表照出),按原读法判,不读作「不是子代」 |
2338
+ | `GET /v1/approvals?owner=user:42` | 高危写审批(durable,需 `DURABLE_APPROVAL=true`):**operator** 看待办队列(可按 owner 过滤);非 operator 只看自己的。队列里某一行的存储体读不出来、重试不会好时,答 `500` + `errorCode:"approvals.read_corrupt"`(**7.134.0+**;与订阅流同码、同一只判官;运维处置那一行后再读,别原样重试);此刻读不出队列(连接 / 超时 / 店抖动)⇒ `503` + `errorCode:"approvals.read_unavailable"`(**7.135.0+**,此前 500 `internal.error`;稍后重发,这一答不表示队列为空);本服务自己的程序错(代码缺陷 / 读腿漂移)⇒ `500` + `errorCode:"internal.error"`(**7.148.0+**,此前同落 503;重发不会好,看运维行 `request_error`)。`GET /v1/assistant/inbox` / `GET /v1/assistant/tasks` 读同一个队列,两种读失败同码同状态(**7.135.0+**)。一张停驻行的存储体读不出摘要(手改成 `{}`、整段缺 `state` 等)只坏它自己那一行(**7.145.0+**,MySQL 协议 / PostgreSQL 后端):收件箱与运维队列照列它、带引擎判官的 `rowVerdict`(只缺 `pendingAction` 的行判官答 `ok`,引擎侧已知缺口),任务视图照列那条 run,三个面都不再整张答 500 / 503;别的卡的决断照常,这张卡自己的决断答 409 带码拒,`error` 以行上 `rowVerdict.sentence` 开头(**7.146.0+**;此前存储体 `{}` 的卡被指向 plan_review 的口、存储体读不成检查点的卡只有店的诊断);`DB_BACKEND=local` 上列表面本版仍整张 503(决断已逐行)。开了工具审批时响应另带 `livePending[]`(本副本上的活卡,决议走 `POST /v1/tasks/<taskId>/asks/<askId>/decision`;7.151.x 及以前另有已删的活卡应答口):**7.149.0+** 它与下面的按问读口同一只读法 —— 列出的每一张,读口对同一调用方都答 `pending`(反过来不然:具名主体的列表不含无主的卡,运维列表含读口不放行的卡),店读不出某张卡的行 ⇒ 这张不列(读口同时答 404 `not_found.ask`;`livePending` 为空不等于没有待批,可能是此刻读不出),行已被别处收走 ⇒ 不列,属主取行上当下的那一位(此前按本副本登记列,店抖动期照列而读口 404);店恢复后重列即回,每次列表对每个有行的不同 ask 多一次按 id 的点读。停驻的**技能卡**(对引擎 `Skill` 工具的调用)的行带 `boundGrantDigest` / `boundGrants`(批准它还授予的东西:技能名 + 两只工具清单;清单只在哈希等于摘要时才有,是部署文本,渲染要按上下文转义),分类器没给判词的卡带 `classifierNoRuling`(`error` / `timeout` / `parse_error`)—— **7.136.0+**,与 `GET /v1/assistant/inbox` 行逐字同值(此前只有收件箱行与活卡帧带);普通卡三位都缺席。`GET /v1/assistant/inbox` 行的 `objective` 是门所属 run 的**目标预览**(写入时已脱敏、截到约 120 字符,与 `GET /v1/assistant/tasks` 读同一列;委派子代的门读派它的那条 run;没有 run 账本 ⇒ `null`;**7.136.0+**,此前原样回显存着的任务目标,运维 `?owner=` 跨看也是原文)。停驻行带 `agent`(**7.145.0+**):行的会话在委派树里的位置 `{ id?, parentSessionId?, rootSessionId? }`,与 `GET /v1/sessions/<sessionId>` 体顶层的 `agent` 同一只读口、逐值同答 —— 后台委派子代停驻的行(`taskId` 为 `null`、`sessionId` 是子代自己的会话)凭它连回发起委派的会话(`rootSessionId` / `parentSessionId` = 本端会话 ⇒ 本端的卡);缺席 = 这台 server 没有说(宿主会话自己的停驻、同步委派的子代、`subagent_type: "fork"` 的后台子代、不存委派元组的店、那一读没读成 —— 只这一行缺键、列表照出),按原读法判,不读作「不是子代」 |
2318
2339
  | `GET /v1/approvals/stream` | 同一队列的 SSE 推送(范围同上):首拍快照 → `pending` / `resolved` 增量(开了工具审批时另有 `live_pending` / `live_resolved`),空闲时心跳,15 分钟上限(`STREAM_MAX_DURATION` 帧后重连)。**心跳只表示流活着,不表示这一拍读过队列**:队列读不出时每次读失败发一帧 `error`(`approvals.read_unavailable`,流留着、下一拍再读,恢复后同一条流继续);某一行的存储体读不出来、重试不会好时发 `approvals.read_corrupt` 并收流(运维处置那一行后再重连);本服务自己的程序错发 `internal.error` 并收流(**7.148.0+**,此前同落 `approvals.read_unavailable`、流不收)。15 分钟上限对读失败的拍同样算数。`live_pending` / `live_resolved` 与 `GET /v1/approvals` 的 `livePending[]` 同一只读函数(**7.149.0+** 同按问读口读行:读不出行 ⇒ 这一拍离场、店恢复后再入场一帧,推流每拍对每个有行的不同 ask 多一次点读;问账读不出而队列读得出的拍才发这一对帧)。`pending` 帧带的行与 `GET /v1/approvals` 同一只投影(含上面的技能卡三位,**7.136.0+**;含 `agent`,新订阅首拍补给的帧与之后推出的帧同值,**7.145.0+**) |
2319
2340
  | `POST /v1/approvals/<sessionId>/decide` `{"decision":"approve"|"deny","reason":"…"}` | 批/否并恢复挂起任务(CAS,重复决议 409);**仅 operator**(非 operator → 403)。5.0.0 起旧轮询腿 `POST /v1/approvals/<id>` 已退役。停驻的后台子代**复活不了**(行上没有根会话锚;未起名的后台子代不在此列 —— 引擎 7.57.0 起每只子代都有名字,升级前的无名行读作派生名,照常复活)⇒ `deny` 作废这张卡:`200 {taskId, status:"failed", decision:"deny"}`,卡离开待决队列、子代终局 `failed`;`approve` 答 `409 decide.row_unrevivable`、拒句指路 deny(**7.136.0+**;此前两个决定都 409,卡处理不掉)。有根会话锚的停驻子代卡上,`decide` 与删根会话 / 卡作废并发:写口放行之后卡已被作废 ⇒ `422 parked_resume.startup_failed`(卡已作废、什么都没复活;与 `409 decide.gate_moved` 并列为并发终局码,调用方须同时处理 409 / 422) |
2320
- | `GET /v1/tool-approvals/<approvalId>` | 按问读口(**7.133.0+**;与 `POST …/respond` 同门同身份,不是你的 ask 与查无同形 404 —— **7.148.0+** 两口与耐久决断口同码 `not_found.ask`、不带状态位,`tool_approval.not_pending` 只对属主出;**7.136.0+** 两扇决议口 —— `POST …/respond` 与耐久决断口 `POST /v1/tasks/<taskId>/asks/<askId>/decision` —— 与本口同一只属主判据:有行的问读行上当下的属主,收编之后新属主三口都认、前属主三口都 404;此前本副本上的活卡读的是铸卡那一刻记下的属主;**7.149.0+** 本口在店读不出时看不见这只问,答 404 `not_found.ask`(与查无同体;活卡与起服接管的登记同答 —— 7.136.0–7.148.x 对本副本自己铸的活卡回落登记,登记属主认你 ⇒ `pending`),店恢复后重读即回):一只审批问此刻在哪一态,`state` ∈ `pending` / `decided` / `parked` / `void`(闭集)。停驻卡被答过 ⇒ `decided`、停驻 run 被取消 ⇒ `void`(**7.134.0+**;7.133.x 恒答 `parked`);停驻行的门已结算却读不出可用的决议(含坏行:状态词 / 种类 / 当事方不在闭集、结算记录过不了引擎的不变量筛、缺必填成员、时刻 / 方向读不出)⇒ 404 `tool_approval.not_pending`,运维日志一行 error(`defect` 首条缺陷句 · `defectCount`)。停驻行的 `decided` 可回到 `parked`:续跑腿在被批准的动作开跑前失败时,引擎重开同一张门 —— 读口答 `parked` + `decidePath` 并另带 `reopened`(**7.135.0+**):`reason` 是引擎的重开因由词(`env_failed` 按原决议经 `decidePath` 再交一次 —— 本副本还留着首次受理回执时(缺省 1 小时内)`…/respond` 回放那张回执、不驱续跑;`tool_unavailable` / `constraint_rejected` 重新决;`config_refused` 改好配置再交或重新决),`decision` / `decidedAtMs` / `decisionNote?` 是重开之前的那次决议(读不出 ⇒ 只带 `reason`);再决之后回到 `decided`。续跑被取消 ⇒ 重开的门被收掉,读作 `void`。全文见契约 §4a-bis-read |
2341
+ | `GET /v1/tasks/<taskId>/asks/<askId>` | 按问读口(**7.152.0+** 在这一根,与耐久决断口 `POST /v1/tasks/<taskId>/asks/<askId>/decision` 同根同门同身份;**7.133.0–7.151.x** 在 `GET /v1/tool-approvals/<approvalId>`,它与活卡应答口 `POST /v1/tool-approvals/<approvalId>/respond` 一起 7.152.0 起已删,迁移对照见 MIGRATION)。不是你的 ask 与查无同形 404 —— **7.148.0+** 与耐久决断口同码 `not_found.ask`、不带状态位,`tool_approval.not_pending` 只对属主出;**7.136.0+** 决断口与本口同一只属主判据:有行的问读行上当下的属主,收编之后新属主两口都认、前属主两口都 404;此前本副本上的活卡读的是铸卡那一刻记下的属主;**7.149.0+** 本口在店读不出时看不见这只问,答 404 `not_found.ask`(与查无同体;活卡与起服接管的登记同答 —— 7.136.0–7.148.x 对本副本自己铸的活卡回落登记,登记属主认你 ⇒ `pending`),店恢复后重读即回):一只审批问此刻在哪一态,`state` ∈ `pending` / `decided` / `parked` / `void`(闭集)。停驻卡被答过 ⇒ `decided`、停驻 run 被取消 ⇒ `void`(**7.134.0+**;7.133.x 恒答 `parked`);停驻行的门已结算却读不出可用的决议(含坏行:状态词 / 种类 / 当事方不在闭集、结算记录过不了引擎的不变量筛、缺必填成员、时刻 / 方向读不出)⇒ 404 `tool_approval.not_pending`,运维日志一行 error(`defect` 首条缺陷句 · `defectCount`)。停驻行的 `decided` 可回到 `parked`:续跑腿在被批准的动作开跑前失败时,引擎重开同一张门 —— 读口答 `parked` + `decidePath` 并另带 `reopened`(**7.135.0+**):`reason` 是引擎的重开因由词(`env_failed` 按原决议再交一次 —— 经 `decidePath` 或决断口;`tool_unavailable` / `constraint_rejected` 重新决;`config_refused` 改好配置再交或重新决),`decision` / `decidedAtMs` / `decisionNote?` 是重开之前的那次决议(读不出 ⇒ 只带 `reason`);再决之后回到 `decided`。续跑被取消 ⇒ 重开的门被收掉,读作 `void`。全文见契约 §4a-bis-read |
2321
2342
  | `GET /v1/agents/roster` | 后台 agent **名册**(7.29 起,需 durable background-agent store,否则 501 `capability.background_agent_store_required`):调用方自己 scope 的 `a…` 行,新→旧。行只带 `handle`/`name`/`agentType`/`status`/`spawnedAt`/`updatedAt`/`settledAt`(content-free;子代产物正文走 `GET /v1/runs/<id>/subagents/<handle>/output`)。查询:`?limit=`(1..100,默认 20)、`?before=`(上一页的 `nextBefore` 游标)、`?status=`(running\|parked\|completed\|failed\|killed)、`?session=`(按会话树窄化)、`?scope=`(**仅** fleet 调用方:不带主体头的 service 凭据 / 只读 `TRACE_TOKEN`(只在没配 service 凭据的部署上够得着这一口)/ dev-open;带主体头的调用方 —— 含带 service 凭据的 —— 锁定自己)。回体 `{agents,total,nextBefore?,truncated?}`;`truncated:true` = 租户行数超过名册窗(500),`total` 此时是窗内数。非属主的行**不出现也不计数**(无存在性 oracle) |
2322
2343
  | `GET /v1/capabilities/scenarios/<name>` | 单个场景的只读详情:工具面、提示词概览、**本部署现在跑不跑得动**、附件供给、**技能清单**(见下) |
2323
2344
  | `GET /v1/capabilities/mcp[?scenario=<场景名>]` | **无需先跑 run 的逐台 MCP 状态**(现连、列工具、关掉;按调用方限速 + 30 秒复用)。答的是**某个场景**会挂哪些中心声明的 MCP 服务器:不带参数 = 这位主体 run 时的场景(**7.131.0+**:中心按主体指派的场景胜出,否则部署缺省场景;此前恒读部署缺省场景;指派悬空 ⇒ `503 state.scenario_assignment_unknown`,与提交任务时同一句;**7.147.0+** 部署接了中心而中心答不出这位主体的裁定(不可达 / 超时 / 答复读不出 / 指派两键形不对)⇒ 点名与不点名同答 `503 state.scenario_assignment_unavailable`(体无 `detail`、带 `permanence: "transient"`),与提交任务时同一句,不再按「无治理」读部署缺省场景;`GET /v1/sessions/:id/mcp` 与 `…/a2a` 两张会话面板同读这位主体 run 时的场景:a2a 面板悬空时同样整面 503,而 mcp 面板另有「这个会话上一次真挂上了什么」那一半(`lastLegMcp`),悬空时答 `200`、`lastLegMcp` 照答、`servers` 缺席、拒因在 `scenarioRefusal: { errorCode, error, scenario, source }`(与状态面整面 503 的体是同一份成员,`scenario` · `source` 是门拒的机读事实、与 `errorCode` / `error` 同层 —— 7.148.0 起,此前嵌在 `detail` 里;`servers` 缺席 = 场景这一半没答,`servers: []` = 该场景没配 MCP));`?scenario=<名>`(**7.130.0+**)= 点名场景,可见集与同场景任务 run 时是同一只解析、过同一只场景门 —— 不在册 `400 scenario_unknown`(附在册词表;空串 `?scenario=` 同此,与提交 `scenario: ""` 同答)、这位 principal 的场景白名单集外 `400 scenario_not_allowed`(附 allowlist),两者的体与提交任务时的拒逐字同形;重复给出参数 `400 request.field_invalid`(提交腿没有这一形)。场景门接缝 `scenarioGate` 是 `createHttpServer` 的必填件:自建 `ServiceDeps` 的嵌入方用包根导出的 `createScenarioGate` 铸它(`"peek"` 腿给读面),没递就是 tsc 红。老 server 忽略这个参数、回缺省场景那一份,按 `GET /v1/capabilities` 的 `version` 判。`REQUIRE_PRINCIPAL` 下无身份 `401`(先于场景门)。每一行(两条口同形)在 §G.7 的行形之外多两个可选键(**7.133.0+**):`toolNames`(这台会挂上的工具名,引擎命名空间形 `mcp__<server>__<tool>`,读数序)+ `toolNamesTruncated: true`(名单超过 200 只时只截表,`toolCount` 仍是真数;读数用 `toolCount`,别用 `toolNames.length`);该台读数里没有名单 ⇒ 两键缺席(不编 `[]`),没连上的台是在场的 `[]`;老 server 没有这两键,按缺席读。中心声明里有两台归一后挂在同一个工具命名空间前缀下(`my.srv` / `my_srv` 同挂 `mcp__my_srv__`)⇒ **7.143.0+** `409 config.mcp_server_name_collision` + 引擎原句(点名两个原拼写;此前无码 `500`),与同一份声明集在 `POST /v1/tasks` 上的答复同码、同状态、同句 —— 出路 = 改配置里的名字。自带 `.mcp.json` 的探测口是 `POST /v1/capabilities/mcp/probe` |
2324
- | `POST /v1/admin/drain` `{"actionId":"…","worker":"…","deadlineAt":…}` | **排空口**(7.121.0 起体 = settings-schema `DrainRequest`;operator-only —— principal 须在 `OPERATOR_PRINCIPALS` 内,或单用户部署声明了 `OPERATOR_SINGLE_USER=true`;两条出路都没有〔名单为空且没声明〕⇒ 501)。体 `{ actionId, worker, deadlineAt, scope?, expectedGeneration?, instanceId?, term? }`:`actionId` 幂等键(人手形自取,如 `manual:<uuid>`)、`worker` 本引擎的 worker 名(`SEMA_WEB_ADMIN_WORKER`)、`deadlineAt` 到期自停的 epoch ms、`scope` 本引擎的租户 scope(`FLEET_SCOPE`;不带 = 缺省 scope `global`,与没设 `FLEET_SCOPE` 的引擎相符 —— 多租引擎要带它自己的)、`expectedGeneration` fleet 成员的代际(`FLEET_GENERATION`;非成员不带)、`instanceId` 本进程身份(`GET /health` 读;不带则不比)。身份逐字段比(`worker` / `expectedGeneration` 缺席对缺席),不符 ⇒ `409 conflict.fleet_identity`(什么都不变);首次 ⇒ `200 {actionId, since, deadlineAt, state:"draining"}` 并**进入排空**(拒新提交、本副本可抢占的在飞腿当拍让出、活的审批卡改路耐久停驻、在飞归零或到期限即停);再来 ⇒ `200 … state:"already_draining"` 回显第一次的 `since` / `deadlineAt`,不重置。体坏 ⇒ `400 request.field_invalid`;`deadlineAt` 离本引擎此刻超过 `FLEET_LEASE_OUTAGE_MAX_MS`(缺省 24 h,一台实例活着而不服务的最长时长;7.122.0 起)⇒ 同码 400(什么都不变;拒句点名字段与上界的值;只在这一发会进入排空时判 —— 已在排空或硬停已在进行时任何重发都回 `already_draining`),已过的期限照收(进入即到期);嵌入宿主(无收尾链)⇒ `501 capability.drain_state_required`。没有 worker 身份的引擎(没配 `SEMA_WEB_ADMIN_WORKER`)恒 409 —— 用 SIGTERM。经本口进的排空之后,平台随后发的第一发 SIGTERM 被吸收、不缩短期限(第二发判据见「排空期的第二发信号」)。7.120.x 的 `{"reason":"…"}` 声明形与 `/health.drainReason` 已退役。 |
2345
+ | `POST /v1/admin/drain` `{"actionId":"…","worker":"…","deadlineAt":…}` | **排空口**(7.121.0 起体 = settings-schema `DrainRequest`;持排空权力者 —— principal 须在 `OPERATOR_PRINCIPALS`(全运维)或 `OPERATOR_DRAIN_PRINCIPALS`(排空档:只持排空一权的服务主体,7.152.0+;它的身份在其余每条路由上 403 `auth.principal_confined`)内,或单用户部署声明了 `OPERATOR_SINGLE_USER=true`;三条出路都没有〔两档名单都空且没声明〕⇒ 501)。体 `{ actionId, worker, deadlineAt, scope?, expectedGeneration?, instanceId?, term? }`:`actionId` 幂等键(人手形自取,如 `manual:<uuid>`)、`worker` 本引擎的 worker 名(`SEMA_WEB_ADMIN_WORKER`)、`deadlineAt` 到期自停的 epoch ms、`scope` 本引擎的租户 scope(`FLEET_SCOPE`;不带 = 缺省 scope `global`,与没设 `FLEET_SCOPE` 的引擎相符 —— 多租引擎要带它自己的)、`expectedGeneration` fleet 成员的代际(`FLEET_GENERATION`;非成员不带)、`instanceId` 本进程身份(`GET /health` 读;不带则不比)。身份逐字段比(`worker` / `expectedGeneration` 缺席对缺席),不符 ⇒ `409 conflict.fleet_identity`(什么都不变);首次 ⇒ `200 {actionId, since, deadlineAt, state:"draining"}` 并**进入排空**(拒新提交、本副本可抢占的在飞腿当拍让出、活的审批卡改路耐久停驻、在飞归零或到期限即停);再来 ⇒ `200 … state:"already_draining"` 回显第一次的 `since` / `deadlineAt`,不重置。体坏 ⇒ `400 request.field_invalid`;`deadlineAt` 离本引擎此刻超过 `FLEET_LEASE_OUTAGE_MAX_MS`(缺省 24 h,一台实例活着而不服务的最长时长;7.122.0 起)⇒ 同码 400(什么都不变;拒句点名字段与上界的值;只在这一发会进入排空时判 —— 已在排空或硬停已在进行时任何重发都回 `already_draining`),已过的期限照收(进入即到期);嵌入宿主(无收尾链)⇒ `501 capability.drain_state_required`。没有 worker 身份的引擎(没配 `SEMA_WEB_ADMIN_WORKER`)恒 409 —— 用 SIGTERM。经本口进的排空之后,平台随后发的第一发 SIGTERM 被吸收、不缩短期限(第二发判据见「排空期的第二发信号」)。7.120.x 的 `{"reason":"…"}` 声明形与 `/health.drainReason` 已退役。 |
2325
2346
  | `GET /v1/config/catalog` | **配置目录自描述**(operator-only —— `explicitOperatorOk`:没有运维名册〔`OPERATOR_PRINCIPALS` 为空且没有单用户声明 `OPERATOR_SINGLE_USER=true`〕⇒ 501 `capability.operator_principals_required`,有名册而调用方不是运维 ⇒ 403)。逐 env 键一行:`{name, domain, type, semantics, writeLanes, effectiveLane, envHeld, envTiming, staticDefault?/derivedDefaultNote?, effectiveValue, valueClass, danger, center?{domain,path,precedence,timing,restartSlice?,managedNow,effectiveNow}}`(🔴 7.106.0 起 `center.domainExists` 删除:座位在加载期对契约 `DOMAIN_SCHEMAS` 核过域 + 路径,核不过即拒启 ⇒ 有 `center` 块 ⇔ 中心可写;readFace 四键 / `PERMISSIONS_DISABLE_AUTO_MODE` / `MODEL_DEGRADE_ON` / `_REACTIVE` 七行的座位随之删除);顶层带 `restartSlices`(重启片闭集)与 `centerDomains`(settings-schema 现行域表)。**两轴不混**:`writeLanes`=谁能写,`effectiveLane`=现在谁在生效(env-wins 族 env 占位 ⇒ center 腿 `effectiveNow:false`)。secret 类值**永不回传**(只回在场位;opaque 回在场位+长度;URL 剥 userinfo)。完备性由仓内 AST 双向对账门执法(分母=全 `src/` 生产 env 读取点)。旋钮 `CONFIG_CATALOG_ENABLED=false`(合规部署)⇒ 501 `feature.config_catalog_disabled`(路径恒挂载)+ 能力位 `configCatalog:false`。响应 `Cache-Control: no-store`。**7.57.0 起**:`enum` 行的 `effectiveValue` **恒落在该行自己的 `enumValues` 闭集内**(仓内机器门执法)——此前 `SESSION_BACKEND` 回的是**内部标签** `tidb`(自 5.0.0 起是**退役公名**,写回去 boot 拒),现归一成公名 `mysql`;`MODEL_DEFAULT_THINKING` 补上一直被解析器接受、却漏在目录闭集外的 `xhigh`,并改为回**实算值**(未设 ⇒ `null`;7.147.x 及以前 `off` 也回 `null`,**7.148.0 起** `off` 是真档、回 `"off"`)而不是 env 原文。 |
2326
2347
  | `POST /v1/admin/config/refresh` | **配置手动刷新**(operator-only,与上一行同门:没有运维名册〔`OPERATOR_PRINCIPALS` 为空且没有单用户声明 `OPERATOR_SINGLE_USER=true`〕⇒ 501;名单非空而调用方不在名单上 ⇒ 403)。改完中心配置 / `config.d` 之后不想等 60s 轮询就调它:触发**一次既有**的 refresh 拍(在飞则汇入那一拍,绝不并发双拍),返回时该拍已落地。200 体 `{triggered:true,targetVersion,appliedVersion}` —— 两个世代号与 `/health` 的 `configTargetVersion`/`configAppliedVersion` 同源。候选被判官整拒(坏域 / 坏 prompts / 带事实的退役域 —— 7.126.0 起 / 本机 `config.d` 里读不出的文档,门控域在内)是这一拍的结论,不是失败:照样 200,`targetVersion ≠ appliedVersion`,拒因在 `config_candidate_rejected` 日志行;拍真的失败(配置源不可达 / 读不了 / 应用抛错)⇒ `502 internal.config_refresh_failed`。本部署没有配置管道(纯 env worker:既没配 `CONFIG_CENTER`、也不是 `CONFIG_PROVIDER=local`)⇒ 409 `config.refresh_unavailable` 指路,不会回一个「触发了但什么都没发生」的 200。 |
2327
2348
  | `POST /v1/adoption/<adoptionId>/sessions` `{"sessions":[{"clientSessionId":"…","entries":[…]}]}` | **收编批量导入**(**7.146.0+**;能力位 `adoptionImport`,按调用方):收编的**源主体本人**把本机会话导进那条收编的目的主体名下(operator 不能代,请求体不能点名目的地);导进来的是只读镜像,在云端接着跑先 fork。`adoptionId` 从 `GET /v1/capabilities` 的 `adoptionImport.adoptionId` 读(只对源主体本人在场,另带两只帽 `maxSessions` / `maxSessionBytes`)。云端会话 id 由 `(adoptionId, clientSessionId)` 确定性派生 ⇒ 重投安全。200 + 逐元素结局 `imported` / `already_imported` / `rejected{code: too_large \| invalid_envelope \| duplicate_client_session_id}`;信封恰好两键,多一个键(如 `cwd`)⇒ 这个元素 `invalid_envelope`。整请求拒:401 / 501 `capability.adoption_store_required` / 404 `not_found.adoption` / 403 `auth.adoption_source_only` / 409 `adoption.not_adopted` / 400(体形、元素数超帽)。帽 = `ADOPTION_IMPORT_MAX_SESSIONS`(缺省 100)/ `ADOPTION_IMPORT_MAX_SESSION_BYTES`(缺省 6 MiB)。契约 §26 |
@@ -2349,7 +2370,8 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
2349
2370
 
2350
2371
  > **operator 鉴权(审批队列与全部运维面,一条读法)**:`OPERATOR_PRINCIPALS=ops:alice,ops:bob`(CSV)= 谁能当 operator —— 列任意 owner 待办 + 决议(批/否)。非名单 principal 列待办只看自己的、且**不能决议**(防「请求方批自己的高危操作」跳过 F4 闸);
2351
2372
  > **名单为空 = 没有人是 operator**(每个调用方只看自己的待批 —— 不带主体的调用方看到的是匿名卡〔不带主体提交的任务停下的那几张,即决断口对它开放的那几张〕,`?owner=` 不生效;7.133.0 之前审批四个读面在名单为空时把每个调用方当 operator,那条读法已删)。
2352
- > **单用户部署当自己的 operator**:`OPERATOR_SINGLE_USER=true` = 「这台部署的每个调用方都是 operator」,对全部运维面生效;只在 `REQUIRE_PRINCIPAL` 未开且 `OPERATOR_PRINCIPALS` 为空时成立(与任一同在 ⇒ 拒启);
2373
+ > **排空档(7.152.0+)**:`OPERATOR_DRAIN_PRINCIPALS=svc:drain`(CSV)= 只持排空一权的服务主体 —— 名单里的验过主体只能调 `POST /v1/admin/drain`,其余每条路由整次请求 403 `auth.principal_confined`(不提交任务、不建会话、不碰运维口),`GET /health` 只拿匿名档四键;它名下登记过的设备在 `GET /v1/device/ws` 的 hello 被拒 `auth_failed`(执行器退避重试、不删钥匙;属主回到人档的那次重启后自己连上);不算运维声明;与 `OPERATOR_PRINCIPALS` 同名、或列了不是主体名的条目 ⇒ 拒启(`operator-roster-well-formed`)。
2374
+ > **单用户部署当自己的 operator**:`OPERATOR_SINGLE_USER=true` = 「这台部署的每个调用方都是 operator」,对全部运维面生效;只在 `REQUIRE_PRINCIPAL` 未开且 `OPERATOR_PRINCIPALS` 与 `OPERATOR_DRAIN_PRINCIPALS` 都为空时成立(与任一同在 ⇒ 拒启);
2353
2375
  > 声明要有一道身份边界:没配服务凭据(`SERVICE_AUTH_TOKEN` / `SERVICE_AUTH_TOKENS`)的部署上能连上端口的都是 operator,所以**声明 ∧ 没配凭据 ∧ 监听地址不只在本机回环**(缺省全接口 / `0.0.0.0` / 局域网地址 / 主机名)⇒ 服务进程起服拒
2354
2376
  > (不变量 `operator-single-user-boundary`;拒句一行给三条出路:配凭据 / `BIND_HOST=127.0.0.1` 只听本机回环 —— 端口要发布给别的主机的容器改配凭据 / 撤声明换具名 `OPERATOR_PRINCIPALS`);
2355
2377
  > `ALLOW_UNAUTHED_WRITES=true` 且没配凭据时监听地址本来就收窄到 127.0.0.1,照起。不监听的 `run-local` 不判这一条。
@@ -2357,7 +2379,7 @@ curl -N http://<host>:8090/v1/tasks/stream -H 'content-type: application/json' \
2357
2379
  > 部署形不是输入:单用户部署上 `GET /v1/diagnostics/wiring` 与 `GET /v1/outcomes` 也只认运维,具名名单在单用户部署上同样生效。
2358
2380
  > 🔴 **单用户部署必须声明谁是运维**:`REQUIRE_PRINCIPAL` 未开、`OPERATOR_PRINCIPALS` 为空、`OPERATOR_SINGLE_USER` 不是 `true` ⇒ 装配运维面时拒(服务进程起服拒;直接调 `createHttpServer` 的嵌入方同拒;不变量 `operator-declaration-required`),
2359
2381
  > stderr 一行拒句点名两条出路 —— `OPERATOR_SINGLE_USER=true`(每个调用方都是运维)或 `OPERATOR_PRINCIPALS=<主体,…>`(只有点名的主体);不止一个人调用的 worker 是多租,设 `REQUIRE_PRINCIPAL=true`。
2360
- > 没有旋钮、没有宽限、没有缺省;`OPERATOR_SINGLE_USER=false` 与没写同判。`sema-up --users=single` 按租户词渲 `OPERATOR_SINGLE_USER=true`(kube 两条 helm 路径写进 values 的 `server.operatorSingleUser`);直接 `helm` 的部署由你写显式 values 键 `server.operatorSingleUser=true`(单人)或 `server.requirePrincipal=true`(多人),图表不从租户词派生它,两键都没设渲染即拒。
2382
+ > 没有旋钮、没有宽限、没有缺省;`OPERATOR_SINGLE_USER=false` 与没写同判。`sema-up` 的 `--users` 必填、没有缺省(脚本不替部署选「几个人用」:缺了拒并给 `--users=single` / `--users=multi` 两条出路;重跑沿用 `.env` 里上一次落下的事实),`--users=single` 按租户词渲 `OPERATOR_SINGLE_USER=true`(kube 两条 helm 路径写进 values 的 `server.operatorSingleUser`);直接 `helm` 的部署由你写显式 values 键 `server.operatorSingleUser=true`(单人)或 `server.requirePrincipal=true`(多人),图表不从租户词派生它,两键都没设渲染即拒。
2361
2383
  > 不挂 HTTP 面的 `run-local` 不判这一条;运维面 501 `capability.operator_principals_required` 从此只出现在「多租 + 空名单」上。
2362
2384
  > ⚠️ **多租户形拒启**:`DURABLE_APPROVAL=true` + `REQUIRE_PRINCIPAL=true` 而 `OPERATOR_PRINCIPALS` 空 ⇒ 进程启动失败并点名修法(空名单下没人能当这台多租 worker 审批队列的 operator)。设名单,或确属单租户则不设 `REQUIRE_PRINCIPAL`。
2363
2385
  > 🔒 **停驻任务续跑上下文里的凭据是密封的(7.134.0 起)**:任务停驻时存下的提交体里,请求自带的凭据(`mcpServers[].transport.headers` / `.env` 的值、`a2aPeers[].headers` 的值、`settings.env` 的值、
@@ -3012,6 +3034,9 @@ k8s 车道每条 run 一只新 pod(pod 寿命 = 泄漏护栏,不跨 run 复用)
3012
3034
  Kata 运行时把 namespaced sysctl 设在客体内核里,作用在客体的网卡上。
3013
3035
  - **pod 的 sysctl 只有这一个属主**:嵌入方的 pod-spec 补丁带 `podSecurityContext.sysctls` ⇒ 拒(补丁是浅合并,会把这里的关闭项整只换掉)。
3014
3036
 
3037
+ **集群 API 凭据**(`REMOTE_EXEC=k8s` 的起服前提):显式 `K8S_API_URL` + `K8S_TOKEN` + `K8S_CA_CERT_B64`(实验室 `K8S_INSECURE_TLS=true`)优先、in-cluster 服务账号补缺,逐件判(不读 kubeconfig)。缺哪一件 ⇒ **起服拒**
3038
+ `config.k8s_api_credentials_missing`,拒句点名缺的那一件与两条出路(给凭据 / 改车道 `REMOTE_EXEC=none` 或别的车道);凭据缺时下面的起服自检不发。起服之后令牌失效:每条 run 铸环境之前探一次,集群答 401 ⇒ run `failed` 带 `env.sandbox_refused`(模型零调用、零 pod;同步腿 409);403 / 5xx / 探问没答不拦。
3039
+
3015
3040
  **起服自检**(`REMOTE_EXEC=k8s`,起服一次、不挡起服):服务用自己的 k8s 账号读节点表(`nodes` get / list,集群级)与沙箱 ns 的 NetworkPolicy(`networkpolicies` get / list),
3016
3041
  按「选中沙箱 pod 的 Egress 策略的放行规则并集」判每个节点的每个 InternalIP / ExternalIP。读出的缺口每个一行 warn `sandbox_egress_policy_gap`(带 `effect` 后果句)并计数
3017
3042
  `server.sandbox-egress.policy-gap`(fail-open 登记表第 135 行,记债);零缺口一行 info `sandbox_egress_policy_checked`;自检本身半途抛了一行 error `sandbox_egress_policy_check_crashed`。
@@ -3260,7 +3285,8 @@ bypass 与 auto 同样折回。7.132.x 及更早这两键被服务端丢掉:组
3260
3285
  `permissionMode`;写它的是提交入口、在飞切档(`POST /v1/runs/:id/permission-mode`)、下面的 `plan_review` 写回与续跑重放。运维 `AUTONOMY=plan` 是**提交入口的模式词地板**:受理时盖进提交体
3261
3286
  (请求不带模式词 ⇒ `plan`;带任何词 ⇒ 钳成 `plan`;bundle 的 `defaultMode` 不动,顶层赢);不按段续跑重钳 —— 停驻的 run 被续跑之前(`/decide` · `/resume` · `/wake` · `plan_review`)
3262
3287
  体上的词过一次**现**地板(与受理同一步、读此刻的配置):事后(热改或重启时)改成 `AUTONOMY=plan`,停驻 run 体上的 `bypassPermissions` / `auto` / `dontAsk` / 无词在下一次续跑
3263
- 钳成 `plan`(钳过的词随续跑写回,之后照它读;人批了它的计划再出 plan),`plan_review` 批准写在体顶层的 `default` / `acceptEdits` 原样(顶层同字的提交原词读不出是谁写的,同样原样;只在 bundle `defaultMode` 里的照钳);
3288
+ 钳成 `plan`(钳过的词随续跑写回,之后照它读;人批了它的计划再出 plan),`plan_review` 批准写在体顶层的 `default` / `acceptEdits` 原样 —— 服务端在写下那一格的同一笔里记下是谁写的(7.152.0 起),
3289
+ 所以提交或在飞切档带来的同字词照钳,只在 bundle `defaultMode` 里的照钳,7.152.0 之前写下的停驻行(没有这条记录)也照钳(已批过计划的要再批一次);
3264
3290
  那一腿的生效词看 `wiring_manifest.permissionMode`(钳制通告 `permission.entry_mode_clamped` 只在受理门发)。请求的词真被折了(带了比 `plan` 松的词)⇒ 7.129.0 起这条会话的流(`POST /v1/tasks/stream` / `POST /v1/runs` 的事件账)
3265
3291
  在开口那一拍多一帧 `engine_notice` `permission.entry_mode_clamped`,`detail: { requested, effective, ceiling, sessionId }`;没带词、带的就是 `plan` ⇒ 不发。工具表与 `default` 同面(写手、委派工具 `Agent` 都在),
3266
3292
  写与非只读调用在**调用时**由引擎的 plan 门问(有人可答)/ 拒(无人可答)—— 问是一张审批卡,人批了就写,
@@ -3873,15 +3899,18 @@ web-admin 拉取 / fleet 公告 / 凭证续签、WebSearch、对象存储、OTLP
3873
3899
  ① **运维日志** 一行 `outbound_connect_failed { targetHost, code, proxyInUse, proxyHop?, proxyReadAtMs, sinceBootMs, hint }` —— `proxyInUse` = 这一跳走的代理
3874
3900
  **变量名**(`HTTP_PROXY` / `HTTPS_PROXY`,不打值;直连 ⇒ `null`),`proxyHop` = 只在这一跳走了代理时在场的**原句**(上游错误原文,含代理 host:port,凭据位已脱敏;
3875
3901
  这是代理地址在运行时失败里唯一的去处,wire 上没有),`proxyReadAtMs` = 代理变量被读进来的时刻(= 起服那一拍),`sinceBootMs` = 进程已跑多久
3876
- —— 两者一对照就知道「起服之后 VPN / 代理是不是换过」;全进程的出网都算(模型、中心、工具 …),每条失败的请求一行(重试各算一次)。
3902
+ —— 两者一对照就知道「起服之后 VPN / 代理是不是换过」;`hint` 的出路句 = ③ 那张表的 `remedy`(属一次模型调用的直连一跳按失败类,其余是通用句);全进程的出网都算(模型、中心、工具 …),每条失败的请求一行(重试各算一次)。
3877
3903
  ② **诊断面** `GET /v1/diagnostics/wiring` 的 `outbound` 段:`{ proxy: { http, https }, noProxy, loopbackDirect, proxyReadAtMs, lastConnectFailure }` ——
3878
3904
  代理只报**名字**(哪只变量供的;`https` 在 `HTTPS_PROXY` 缺席时回落 `HTTP_PROXY`)、`NO_PROXY` 只报在不在,地址一个字节都不上本段;
3879
3905
  `lastConnectFailure` = 本进程最近一次建连失败 `{ targetHost, code, proxyInUse, atMs }`(没有过 ⇒ `null`)。
3880
3906
  ③ **用户面**:一次**模型调用**因建连失败而终态(它的最后一条请求在建连时失败;5xx / 中途断流 / 取消都不算;腿里的辅助调用 ——
3881
3907
  WebFetch 摘要、压实、提示建议、分类器 —— 同判:说的是端点够不着,不是 run 失败了)⇒ 这条 run 的流 / 账本上一帧
3882
- `engine_notice` `server.model_unreachable`,`detail = { code, targetHost, proxyInUse?, remedy: "check VPN / proxy; restart if the proxy changed since boot", sessionId }`
3883
- (契约附录 D.3;每腿每目标一次;流式 / 后台 / 续跑三条腿有,同步非流式腿没有投递口 ⇒ 只有 ① ②)。**出路**:查 VPN / 代理;代理或 VPN 在起服之后
3884
- 变过 ⇒ 重启 server(代理变量只在启动时读)。没有旋钮。**会被它打扰的部署形**:辅助端点(auto 分类器 / WebFetch 摘要 / 提示建议 / 压实)单独部署且挂掉、
3908
+ `engine_notice` `server.model_unreachable`,`detail = { code, targetHost, proxyInUse?, remedy, sessionId }`
3909
+ (契约附录 D.3;每腿每目标一次;流式 / 后台 / 续跑三条腿有,同步非流式腿没有投递口 ⇒ 只有 ① ②)。**出路按失败类分(7.152.0 起;此前对所有失败类说同一句)**:`remedy` 与正文末句取自一张闭表的同一行,按 `code` 的失败类选,**走了代理的一跳(`proxyInUse` 在场)一律说通用句**:
3910
+ · 拒连(`ECONNREFUSED`,直连)⇒ 查那个地址上有没有服务在听(本机模型服务起了没有)、模型网关地址(`MODEL_GATEWAY_BASEURL` 或该模型条目的 `baseUrl`)配得对不对 —— `remedy` = `check that a service is listening at the model endpoint address; check the model gateway address configuration`,不提 VPN;
3911
+ · 主机名解析不了(`ENOTFOUND` / `EAI_AGAIN`,直连)⇒ 地址写错,或 DNS / VPN 不通 —— `remedy` = `check the model gateway address configuration; check DNS / VPN`;
3912
+ · 建连超时 / 无路由 / 网络不可达(`UND_ERR_CONNECT_TIMEOUT` / `EHOSTUNREACH` / `ENETUNREACH`)与任何走了代理的一跳 ⇒ 通用句,逐字同 7.151.x:`remedy` = `check VPN / proxy; restart if the proxy changed since boot` —— 查 VPN / 代理;代理或 VPN 在起服之后变过 ⇒ 重启 server(代理变量只在启动时读)。
3913
+ 建议句(正文末句与 `remedy`)里没有配置值、代理地址、凭据(模型端点的 `host:port` 只在正文前半句与 `detail.targetHost`,部署自己声明的端点,与 7.151.x 同;代理只以变量名在 `detail.proxyInUse`)。按通告码与 `detail.code` / `detail.proxyInUse` 判,不要按整句匹配 `remedy` 或正文。没有旋钮。**会被它打扰的部署形**:辅助端点(auto 分类器 / WebFetch 摘要 / 提示建议 / 压实)单独部署且挂掉、
3885
3914
  主模型正常 —— 用户每腿每目标会看到一帧、出路句是泛化的;读 `detail.targetHost` 分清是哪一个端点(它是部署自己声明的模型端点,对用户是新披露的事实;
3886
3915
  代理地址不上),运维按同一目标对 `outbound_connect_failed` 对账。Bun 运行时三件都没有(它的原生 fetch 不经 undici 诊断通道)。
3887
3916
  - **Bun 运行时(发布镜像 `CMD ["bun", …]` 与 `bun build --compile` 二进制)**:Bun 的 fetch 不读 undici 的全局 dispatcher,它**自己**读这三只变量
@@ -5,8 +5,9 @@
5
5
 
6
6
  ```bash
7
7
  # npm 分发姿势(package.json bin 已挂)
8
- npx -p @sema-agent/server sema-up --profile=full-single --gateway=https://api.deepseek.com/v1 \
8
+ npx -p @sema-agent/server sema-up --profile=full-single --users=single --gateway=https://api.deepseek.com/v1 \
9
9
  --model=deepseek-v4-flash --model-key=sk-… -f site.env --yes
10
+ # --users 必填(single = 只给一个人用 | multi = 一个团队):脚本不替你选人数,缺了即拒并给两条出路
10
11
  # 或直接跑仓内脚本
11
12
  deploy/sema-up/sema-up.sh
12
13
  ```
@@ -51,7 +52,7 @@ summary 会打印手动接法。
51
52
  | flag | 默认 | 说明 |
52
53
  |---|---|---|
53
54
  | `--profile=P` | 交互问 | `full-single`/`data-center`/`local-plus`/`full-cluster` |
54
- | `--users=U` | `single` | `single`(超管 turnkey;落 `OPERATOR_SINGLE_USER=true` = 每个调用方都是运维,server 单用户形起服要这一行)/`multi`(身份隔离 REQUIRE_PRINCIPAL=true;`OPERATOR_SINGLE_USER=false`)。两根键由同一个租户词派生,重跑即按租户词重写(kube 两条 helm 路径把声明写进 values 的 `server.operatorSingleUser`,图表自己不派生,见下「运维声明」);具名运维名单(`OPERATOR_PRINCIPALS`)本脚本不渲 |
55
+ | `--users=U` | **必填,没有缺省**(装 agent 的档:`full-single` / `local-plus` / `full-cluster`;`data-center` 不装 agent,不用) | `single`(只给一个人用,超管 turnkey;落 `OPERATOR_SINGLE_USER=true` = 每个调用方都是运维,server 单用户形起服要这一行)/`multi`(一个团队,身份隔离 REQUIRE_PRINCIPAL=true;`OPERATOR_SINGLE_USER=false`)。几个人用是这台部署自己的声明,脚本不替它选(缺省 single 会让忘设人数的团队集群每个调用方都成运维,缺省 multi 同样是替单人部署选):没给旗、`-f` 配置里也没有、这台部署的 `.env` 里也没有上一次落下的事实 ⇒ 拒并给 `--users=single` / `--users=multi` 两条出路(向导里问句的缺省只有事实词,没有事实就没有缺省,空答即拒;`kube-up.sh` / `cluster-up.sh` 单独跑时同一个词是环境变量 `USERS`)。两根键由同一个租户词派生,重跑即按租户词重写(kube 两条 helm 路径把声明写进 values 的 `server.operatorSingleUser`,图表自己不派生,见下「运维声明」);具名运维名单(`OPERATOR_PRINCIPALS`)本脚本不渲 |
55
56
  | `--region=R` | `auto` | `cn`/`global`;auto=对 aliyun/npmjs 探测延迟 |
56
57
  | `--project=NAME` | `sema` | compose project 名;并行试验/多套共存用。也是工作目录 `~/.sema-up/<名>` 与自动数据根 `<盘>/sema-data/<名>` 的一段 ⇒ 只收小写字母 / 数字 / `-` / `_`,首字符是字母或数字(别的写法在建任何目录之前拒) |
57
58
  | `--dry-run` | — | 只打印执行计划,不写 .env、不起容器 |
@@ -202,8 +203,8 @@ Helm 形里,未启用 `serverV4pro`、钥匙卷是 `ReadWriteOnce` 的形(`serve
202
203
  全部凭据生成一次、落 `~/.sema-up/<project>/.env`(0600),重跑**绝不轮换**;
203
204
  summary 里管理员密码只显示一次(新生成那次),不进日志不进 git。
204
205
 
205
- 重跑时租户形与车道同一条规则:**决策 = 显式旗 ∨ 部署事实 ∨ 表缺省**。部署事实 = `.env`(与 shell / `-f` 环境)里
206
- 上一次落下的 `REQUIRE_PRINCIPAL` / `REMOTE_EXEC` —— **不带旗重跑即沿用它**(多租部署、e2b 部署都不必再带旗);显式
206
+ 重跑时租户形与车道同一条规则:**决策 = 显式旗 ∨ 部署事实 ∨ 表缺省** —— 租户形**没有表缺省**(`--users` 必填:显式旗 ∨ 部署事实,都没有 ⇒ 拒),车道才有。部署事实 = `.env`(与 shell / `-f` 环境)里
207
+ 上一次落下的 `REQUIRE_PRINCIPAL` / `REMOTE_EXEC` —— **不带旗重跑即沿用它**(多租部署、e2b 部署都不必再带旗;升级到本版的存量部署零动作,`.env` 里已有 `REQUIRE_PRINCIPAL`);显式
207
208
  `--users` / `--sandbox` 与事实不一致 ⇒ 非零退出并点名两处(不静默覆盖;要换就先清掉旧值)。`.env` 与 shell / `-f`
208
209
  两处都给了且不同 ⇒ 同样拒。读 `.env` 的这几个键(连同所选车道的必需键,如 `E2B_API_KEY`)只认 sema-up 自己写出的
209
210
  **规范形**:一行、行首顶格 `K=<值>`,值是一个词或一对同型引号包住的一个词;`export ` 前缀、键旁空格、`K: v`、
@@ -7,7 +7,7 @@
7
7
  #
8
8
  # 输入(sema-up.sh 解析 machines.yaml 后注入):
9
9
  # MACHINE_COUNT + MACHINE_<i>_{HOST,USER,SSH_KEY,NAME,ROLE,PRIVATE_IP,PUBLIC_IFACE}
10
- # WORK(状态目录,.env 在此)REGION PROJECT USERS(single|multi,缺省 single;翻成主 worker 的 REQUIRE_PRINCIPAL)
10
+ # WORK(状态目录,.env 在此)REGION PROJECT USERS(single|multi,必填、没有缺省〔重跑沿用 .env 里上一次的事实〕;翻成主 worker 的 REQUIRE_PRINCIPAL 与 OPERATOR_SINGLE_USER)
11
11
  # SEMA_SERVER_IMAGE(registry 引用)或 SEMA_SERVER_IMAGE_TAR(本地 docker save tar[.gz])二选一
12
12
  # MODEL_GATEWAY_BASEURL MODEL_ID MODEL_API_KEY
13
13
  # ALLOW_SMALL_CLUSTER=true 放行 <3 机(dev/CI 单节点验流程;生产必须 >=3 奇数)
@@ -48,16 +48,16 @@ CNPG_VERSION="${CNPG_VERSION:-1.30.0}" # ⚠ in-tree barman 1.31 移除;升版
48
48
  LONGHORN_VERSION="${LONGHORN_VERSION:-1.10.0}"
49
49
  KATA_VERSION="${KATA_VERSION:-3.21.0}"
50
50
  STACK_ENGINE="${STACK_ENGINE:-render}" # sema-stack 装配引擎:render(as-built 渲染 apply,缺省)| helm
51
- USERS_GIVEN="${USERS:+1}" # 显式给没给(缺省之前记下;决策见下方 users_decide)
52
- USERS="${USERS:-single}" # 租户形:经 principal_of_users 翻成 REQUIRE_PRINCIPAL,两条装配路径同一次翻译
51
+ USERS="${USERS:-}" # 租户形没有缺省(部署自己的声明):经 principal_of_users 翻成 REQUIRE_PRINCIPAL,两条装配路径同一次翻译
52
+ USERS_GIVEN="${USERS:+1}" # 显式给没给(决策见下方 users_decide)
53
53
  NODEPORT=30890
54
54
 
55
55
  # 取值旗的形——在对任何一台机器发起 ssh 之前。修前:REGION=eu 当成「非 cn 非 global」、STACK_ENGINE=x 当成 render。
56
56
  flag_enum "--region(REGION)" "$REGION" $FLAG_WORDS_REGION
57
57
  flag_enum "STACK_ENGINE" "$STACK_ENGINE" $FLAG_WORDS_STACK_ENGINE
58
- flag_enum "--users(USERS)" "$USERS" $FLAG_WORDS_USERS
59
- # 租户形 = 显式 USERS ∨ 上一次落下的部署事实 ∨ single(lib/flagshape.sh 的 users_decide)——在任何 ssh 之前判,
60
- # 不带旗的重跑不会把多租部署降成单用户。
58
+ if [ -n "$USERS_GIVEN" ]; then flag_enum "--users(USERS)" "$USERS" $FLAG_WORDS_USERS; fi # 没给的由 users_decide 判(事实 ∨ 拒)
59
+ # 租户形 = 显式 USERS ∨ 上一次落下的部署事实;都没有 ⇒ 拒(没有缺省;lib/flagshape.sh 的 users_decide / flag_users_required)——在任何 ssh 之前判,
60
+ # 不带旗的重跑不会把多租部署降成单用户,首装忘设人数的部署也不会被脚本替选。
61
61
  ENVF="$WORK/.env"
62
62
  users_decide "$USERS_GIVEN"
63
63
  log "租户形 users=$USERS(REQUIRE_PRINCIPAL=$(principal_of_users "$USERS"))"
@@ -51,8 +51,8 @@ PROJECT="${PROJECT:-sema}"
51
51
  REGION="${REGION:-global}"
52
52
  KUBE_MODE="${KUBE_MODE:?KUBE_MODE 未设置(existing|k3s)}"
53
53
  DRY_RUN="${DRY_RUN:-false}"
54
- USERS_GIVEN="${USERS:+1}" # 显式给没给(缺省 single 之前记下;决策见下方 users_decide)
55
- USERS="${USERS:-single}"
54
+ USERS="${USERS:-}" # 租户形没有缺省(部署自己的声明):显式 USERS ∨ 上一次落在 .env 的事实,都没有 ⇒ 拒(下方 users_decide 调 flag_users_required)
55
+ USERS_GIVEN="${USERS:+1}" # 显式给没给(决策见下方 users_decide)
56
56
  REGISTRY_ENABLED="${REGISTRY_ENABLED:-true}"
57
57
  GIT_MODE="${GIT_MODE:-none}"
58
58
  NAMESPACE="${NAMESPACE:-sema}"
@@ -64,12 +64,12 @@ K3S_CHANNEL="${K3S_CHANNEL:-stable}"
64
64
  # 修前:DRY_RUN=1 ≠ true ⇒ 当成真装;REGISTRY_ENABLED=yes 当关;USERS=team 当 single;KUBE_MODE=foo 走 existing 那一臂。
65
65
  flag_enum "--kube(KUBE_MODE)" "$KUBE_MODE" $FLAG_WORDS_KUBE_SINGLE # machines 走 cluster-up.sh,不进本脚本
66
66
  flag_bool "DRY_RUN(--dry-run)" "$DRY_RUN"
67
- flag_enum "--users(USERS)" "$USERS" $FLAG_WORDS_USERS
67
+ if [ -n "$USERS_GIVEN" ]; then flag_enum "--users(USERS)" "$USERS" $FLAG_WORDS_USERS; fi # 没给的由 users_decide 判(事实 ∨ 拒)
68
68
  flag_bool "--registry(REGISTRY_ENABLED)" "$REGISTRY_ENABLED"
69
69
  flag_enum "--git(GIT_MODE)" "$GIT_MODE" $FLAG_WORDS_GIT
70
70
  flag_enum "--region(REGION)" "$REGION" $FLAG_WORDS_REGION
71
- # 租户形 = 显式 USERS ∨ 上一次落下的部署事实 ∨ single(规则与拒句在 lib/flagshape.sh 的 users_decide)——
72
- # 在动集群之前判,不带旗的重跑不会把多租部署降成单用户。
71
+ # 租户形 = 显式 USERS ∨ 上一次落下的部署事实;都没有 ⇒ 拒(没有缺省;规则与拒句在 lib/flagshape.sh 的 users_decide / flag_users_required)——
72
+ # 在动集群之前判,不带旗的重跑不会把多租部署降成单用户,首装忘设人数的部署也不会被脚本替选。
73
73
  ENVF="$WORK/.env"
74
74
  users_decide "$USERS_GIVEN"
75
75