@sema-agent/server 7.136.0 → 7.138.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 (327) hide show
  1. package/MIGRATION.md +501 -139
  2. package/README.md +1 -0
  3. package/README.zh-CN.md +1 -0
  4. package/USAGE.md +149 -34
  5. package/deploy/sema-up/README.md +14 -0
  6. package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +7 -1
  7. package/deploy/sema-up/stack/sema-stack-cluster.yaml +7 -1
  8. package/dist/approval-card.d.ts +21 -14
  9. package/dist/approval-card.js +1 -0
  10. package/dist/approval-content-kind.d.ts +6 -0
  11. package/dist/approval-content-kind.js +7 -0
  12. package/dist/approval-gate-currency.d.ts +48 -6
  13. package/dist/approval-gate-currency.js +16 -9
  14. package/dist/approval-hmac.d.ts +13 -1
  15. package/dist/approval-hmac.js +1 -1
  16. package/dist/approval.js +3 -3
  17. package/dist/audit.js +1 -0
  18. package/dist/auth-bridge.js +2 -2
  19. package/dist/background-agent-name.d.ts +5 -0
  20. package/dist/background-agent-name.js +5 -0
  21. package/dist/boot/budget-tracing.d.ts +1 -12
  22. package/dist/boot/deployment-mods.d.ts +46 -16
  23. package/dist/boot/deployment-mods.js +82 -6
  24. package/dist/boot/execution-env.d.ts +24 -1
  25. package/dist/boot/execution-env.js +69 -6
  26. package/dist/boot/governance-seams.d.ts +10 -17
  27. package/dist/boot/governance-seams.js +5 -17
  28. package/dist/boot/leader.d.ts +3 -0
  29. package/dist/boot/leader.js +1 -0
  30. package/dist/boot/org-delivery.js +15 -2
  31. package/dist/boot/reapers.d.ts +3 -0
  32. package/dist/boot/reapers.js +15 -1
  33. package/dist/boot/resolve-spec.d.ts +7 -0
  34. package/dist/boot/resolve-spec.js +36 -26
  35. package/dist/boot/retention-lane.d.ts +11 -5
  36. package/dist/boot/retention-lane.js +2 -2
  37. package/dist/boot/runner-deps.d.ts +7 -3
  38. package/dist/boot/runner-deps.js +1 -0
  39. package/dist/boot/stage-03-store-backend.d.ts +1 -1
  40. package/dist/boot/stage-04-stores.d.ts +2 -13
  41. package/dist/boot/stage-05-execution-env.d.ts +6 -13
  42. package/dist/boot/stage-05-execution-env.js +17 -5
  43. package/dist/boot/stage-06-runners.d.ts +6 -13
  44. package/dist/boot/stage-06-runners.js +10 -3
  45. package/dist/boot/stage-07-capability-layer.d.ts +7 -13
  46. package/dist/boot/stage-07-capability-layer.js +3 -3
  47. package/dist/boot/stage-08-reapers.d.ts +7 -13
  48. package/dist/boot/stage-08-reapers.js +2 -1
  49. package/dist/boot/stage-09-leader.d.ts +7 -13
  50. package/dist/boot/stage-09-leader.js +2 -2
  51. package/dist/boot/stage-10-http-server.d.ts +7 -13
  52. package/dist/boot/stage-10-http-server.js +6 -3
  53. package/dist/boot/stores.d.ts +1 -1
  54. package/dist/boot/web-admin.d.ts +5 -2
  55. package/dist/boot/web-admin.js +3 -3
  56. package/dist/brain.js +5 -5
  57. package/dist/broker/broker.d.ts +89 -0
  58. package/dist/broker/broker.js +287 -0
  59. package/dist/broker/constants.d.ts +39 -0
  60. package/dist/broker/constants.js +15 -0
  61. package/dist/broker/http1.d.ts +111 -0
  62. package/dist/broker/http1.js +256 -0
  63. package/dist/broker/lease-book.d.ts +87 -0
  64. package/dist/broker/lease-book.js +142 -0
  65. package/dist/broker/lease-file.d.ts +60 -0
  66. package/dist/broker/lease-file.js +127 -0
  67. package/dist/broker/main.d.ts +2 -0
  68. package/dist/broker/main.js +20 -0
  69. package/dist/budget.d.ts +17 -15
  70. package/dist/budget.js +2 -2
  71. package/dist/capabilities/composite-members.d.ts +2 -1
  72. package/dist/capabilities/composite-members.js +2 -1
  73. package/dist/capabilities/engine-capabilities.js +1 -0
  74. package/dist/capabilities/mcp-probe.d.ts +36 -11
  75. package/dist/capabilities/mcp-probe.js +44 -5
  76. package/dist/capabilities/scenarios.d.ts +23 -8
  77. package/dist/capabilities/scenarios.js +13 -8
  78. package/dist/capabilities/skills.d.ts +28 -5
  79. package/dist/capabilities/skills.js +8 -0
  80. package/dist/capability-facts.d.ts +6 -0
  81. package/dist/capability-facts.js +5 -0
  82. package/dist/center-credential-renewer.js +5 -1
  83. package/dist/checkpoint-ctx-seal-m28.d.ts +3 -10
  84. package/dist/checkpoint-ctx-seal-m28.js +1 -1
  85. package/dist/checkpoint-identity.d.ts +13 -6
  86. package/dist/config-catalog.js +16 -7
  87. package/dist/config-types.d.ts +24 -7
  88. package/dist/config.d.ts +1 -1
  89. package/dist/config.js +42 -13
  90. package/dist/deployment-governance.d.ts +2 -2
  91. package/dist/deployment-governance.js +7 -9
  92. package/dist/deployment-shape.d.ts +6 -6
  93. package/dist/device-ws-protocol.d.ts +1 -0
  94. package/dist/device-ws-protocol.js +2 -1
  95. package/dist/file-history-disclosure.d.ts +4 -2
  96. package/dist/file-history-disclosure.js +2 -0
  97. package/dist/gate-settlement.d.ts +71 -0
  98. package/dist/gate-settlement.js +40 -0
  99. package/dist/hooks/classic-stations.d.ts +12 -54
  100. package/dist/hooks/classic-stations.js +1 -0
  101. package/dist/hooks/command-spawn.d.ts +2 -2
  102. package/dist/hooks/hook-runner.js +14 -14
  103. package/dist/http/admission.js +14 -9
  104. package/dist/http/approval-sweeps.js +4 -3
  105. package/dist/http/caller-trust.d.ts +2 -1
  106. package/dist/http/caller-trust.js +1 -3
  107. package/dist/http/checkpoint-ctx.d.ts +48 -7
  108. package/dist/http/checkpoint-ctx.js +40 -0
  109. package/dist/http/dispatch.d.ts +4 -0
  110. package/dist/http/dispatch.js +18 -10
  111. package/dist/http/durable-run-leg.d.ts +5 -1
  112. package/dist/http/durable-run-leg.js +5 -3
  113. package/dist/http/health-self-report.d.ts +2 -2
  114. package/dist/http/memory-compliance-failure-reply.js +3 -0
  115. package/dist/http/principal-gate.d.ts +15 -10
  116. package/dist/http/principal-gate.js +48 -9
  117. package/dist/http/resume-legs.d.ts +7 -31
  118. package/dist/http/resume-legs.js +185 -126
  119. package/dist/http/route-ctx.d.ts +38 -53
  120. package/dist/http/route-ctx.js +2 -5
  121. package/dist/http/routes/a2a-serve.js +10 -4
  122. package/dist/http/routes/agents-roster.d.ts +1 -1
  123. package/dist/http/routes/agents-roster.js +5 -1
  124. package/dist/http/routes/approvals-assistant.js +24 -10
  125. package/dist/http/routes/capabilities.js +23 -11
  126. package/dist/http/routes/mcp-reconnect.d.ts +3 -2
  127. package/dist/http/routes/mcp-reconnect.js +7 -7
  128. package/dist/http/routes/memory-consolidation.d.ts +1 -1
  129. package/dist/http/routes/memory-origin.d.ts +1 -1
  130. package/dist/http/routes/rules.js +3 -3
  131. package/dist/http/routes/runs.js +13 -8
  132. package/dist/http/routes/session-sync.js +42 -6
  133. package/dist/http/routes/sessions.js +6 -3
  134. package/dist/http/routes/side-query.js +2 -3
  135. package/dist/http/routes/tasks.js +794 -780
  136. package/dist/http/server.d.ts +35 -25
  137. package/dist/http/stored-body-replay.d.ts +3 -3
  138. package/dist/http/stored-body-replay.js +6 -1
  139. package/dist/http/wire-types.d.ts +66 -33
  140. package/dist/identity-source.d.ts +65 -0
  141. package/dist/identity-source.js +45 -0
  142. package/dist/index.d.ts +2 -0
  143. package/dist/index.js +1 -0
  144. package/dist/leader/wire.d.ts +7 -0
  145. package/dist/leader/wire.js +5 -4
  146. package/dist/mcp-assertion-header.d.ts +51 -40
  147. package/dist/mcp-assertion-header.js +10 -11
  148. package/dist/mcp-authorization-face.d.ts +27 -0
  149. package/dist/mcp-authorization-face.js +15 -0
  150. package/dist/memory-operator-faces.d.ts +13 -4
  151. package/dist/memory-operator-faces.js +1 -17
  152. package/dist/memory-store-refusal.d.ts +3 -3
  153. package/dist/memory-sync-client.js +72 -57
  154. package/dist/memory-sync.d.ts +1 -1
  155. package/dist/model-entry-refusal.d.ts +2 -0
  156. package/dist/model-entry-refusal.js +11 -0
  157. package/dist/model-route-gap.d.ts +6 -22
  158. package/dist/model-route-gap.js +7 -5
  159. package/dist/model-select.d.ts +6 -17
  160. package/dist/model-select.js +0 -3
  161. package/dist/mods-declaration.d.ts +110 -0
  162. package/dist/mods-declaration.js +109 -0
  163. package/dist/namespaced-rule-name.d.ts +1 -1
  164. package/dist/observability/err-text.d.ts +5 -0
  165. package/dist/observability/err-text.js +9 -0
  166. package/dist/observability/fail-open.d.ts +17 -1
  167. package/dist/observability/fail-open.js +17 -1
  168. package/dist/observability/metrics.js +1 -1
  169. package/dist/observability/run-terminal.js +1 -0
  170. package/dist/observability/secret-env-scrub.d.ts +1 -1
  171. package/dist/observability/secret-env-scrub.js +1 -1
  172. package/dist/operator-ask.d.ts +2 -2
  173. package/dist/org-delivery-gate.js +1 -1
  174. package/dist/org-memory-admission.d.ts +2 -2
  175. package/dist/org-memory-admission.js +39 -2
  176. package/dist/parked-decide.d.ts +15 -2
  177. package/dist/parked-decide.js +17 -10
  178. package/dist/peer-directory.js +2 -0
  179. package/dist/peer-seat.d.ts +150 -0
  180. package/dist/peer-seat.js +239 -0
  181. package/dist/permission-rule-list.d.ts +2 -2
  182. package/dist/permission-rule-vocab.d.ts +7 -7
  183. package/dist/permission-rule-vocab.js +3 -3
  184. package/dist/plain-string-record.d.ts +38 -0
  185. package/dist/plain-string-record.js +38 -0
  186. package/dist/plugins/background-agent-store-sql.js +6 -3
  187. package/dist/plugins/caching-session-store.d.ts +5 -0
  188. package/dist/plugins/caching-session-store.js +6 -0
  189. package/dist/plugins/checkpoint-store-sql.d.ts +34 -29
  190. package/dist/plugins/checkpoint-store-sql.js +14 -14
  191. package/dist/plugins/file-history-store-sql.js +10 -16
  192. package/dist/plugins/file-run-store.js +8 -2
  193. package/dist/plugins/k8s-exec-protocol.d.ts +69 -13
  194. package/dist/plugins/k8s-exec-protocol.js +116 -28
  195. package/dist/plugins/k8s-pod-delete-failure.d.ts +25 -0
  196. package/dist/plugins/k8s-pod-delete-failure.js +17 -0
  197. package/dist/plugins/k8s-pod-reaper.d.ts +106 -0
  198. package/dist/plugins/k8s-pod-reaper.js +122 -0
  199. package/dist/plugins/local-checkpoint-store.d.ts +4 -17
  200. package/dist/plugins/local-checkpoint-store.js +3 -4
  201. package/dist/plugins/local-session-store.d.ts +66 -27
  202. package/dist/plugins/local-session-store.js +60 -28
  203. package/dist/plugins/mailbox-store-sql.d.ts +11 -3
  204. package/dist/plugins/mailbox-store-sql.js +103 -15
  205. package/dist/plugins/memory-run-store.js +2 -0
  206. package/dist/plugins/memory-sync-store-pg.d.ts +16 -5
  207. package/dist/plugins/memory-sync-store-tidb.d.ts +3 -3
  208. package/dist/plugins/one-time-migrations.d.ts +34 -12
  209. package/dist/plugins/one-time-migrations.js +51 -9
  210. package/dist/plugins/permission-rule-store-file.js +2 -2
  211. package/dist/plugins/permission-rule-store-sql.d.ts +17 -17
  212. package/dist/plugins/permission-rule-store-sql.js +37 -30
  213. package/dist/plugins/pg-pool.js +4 -0
  214. package/dist/plugins/pg-session-storage.d.ts +10 -2
  215. package/dist/plugins/pg-session-storage.js +20 -7
  216. package/dist/plugins/remote-env-adb.d.ts +6 -1
  217. package/dist/plugins/remote-env-adb.js +2 -1
  218. package/dist/plugins/remote-env-device.d.ts +6 -1
  219. package/dist/plugins/remote-env-device.js +5 -1
  220. package/dist/plugins/remote-env-e2b.d.ts +5 -0
  221. package/dist/plugins/remote-env-e2b.js +2 -1
  222. package/dist/plugins/remote-env-file-error.d.ts +8 -1
  223. package/dist/plugins/remote-env-file-error.js +7 -1
  224. package/dist/plugins/remote-env-host.d.ts +10 -2
  225. package/dist/plugins/remote-env-host.js +8 -2
  226. package/dist/plugins/remote-env-k8s.d.ts +140 -11
  227. package/dist/plugins/remote-env-k8s.js +277 -15
  228. package/dist/plugins/remote-env-local-docker.d.ts +5 -0
  229. package/dist/plugins/remote-env-local-docker.js +2 -1
  230. package/dist/plugins/remote-env-ssh.d.ts +6 -1
  231. package/dist/plugins/remote-env-ssh.js +2 -1
  232. package/dist/plugins/retention-store-sql.d.ts +14 -2
  233. package/dist/plugins/retention-store-sql.js +6 -0
  234. package/dist/plugins/run-store-sql.js +10 -4
  235. package/dist/plugins/session-fork.d.ts +10 -13
  236. package/dist/plugins/session-fork.js +5 -25
  237. package/dist/plugins/session-ownership.d.ts +24 -3
  238. package/dist/plugins/session-ownership.js +24 -5
  239. package/dist/plugins/session-workspace-cell.d.ts +17 -0
  240. package/dist/plugins/session-workspace-cell.js +38 -0
  241. package/dist/plugins/sql-errors.d.ts +23 -2
  242. package/dist/plugins/sql-errors.js +15 -0
  243. package/dist/plugins/store-backend.js +7 -7
  244. package/dist/plugins/store-contracts.d.ts +10 -1
  245. package/dist/plugins/store-fault.d.ts +2 -2
  246. package/dist/plugins/store-fault.js +4 -2
  247. package/dist/plugins/tidb-pool.js +13 -0
  248. package/dist/plugins/tidb-session-store.d.ts +17 -2
  249. package/dist/plugins/tidb-session-store.js +18 -7
  250. package/dist/plugins/tool-result-store-sql.d.ts +13 -6
  251. package/dist/plugins/tool-result-store-sql.js +8 -7
  252. package/dist/plugins/worktree-isolation.d.ts +3 -2
  253. package/dist/plugins/worktree-isolation.js +1 -7
  254. package/dist/project-identity.d.ts +1 -14
  255. package/dist/project-identity.js +0 -4
  256. package/dist/request-key-closure.js +1 -1
  257. package/dist/request-value-closure.d.ts +31 -2
  258. package/dist/request-value-closure.js +7 -1
  259. package/dist/rule-entry-list.d.ts +3 -2
  260. package/dist/rule-entry-list.js +2 -2
  261. package/dist/rules-consent.js +12 -7
  262. package/dist/run-local.d.ts +2 -2
  263. package/dist/run-local.js +12 -6
  264. package/dist/runs.d.ts +7 -1
  265. package/dist/runs.js +8 -2
  266. package/dist/runtime-governance.d.ts +5 -5
  267. package/dist/sandbox-git-lease.d.ts +49 -0
  268. package/dist/sandbox-git-lease.js +60 -0
  269. package/dist/sandbox-git-plane.d.ts +84 -0
  270. package/dist/sandbox-git-plane.js +105 -0
  271. package/dist/security.d.ts +16 -4
  272. package/dist/security.js +8 -3
  273. package/dist/server-secret-env.d.ts +12 -3
  274. package/dist/server-secret-env.js +18 -2
  275. package/dist/session-error-wire.d.ts +14 -6
  276. package/dist/session-error-wire.js +2 -1
  277. package/dist/session-incarnation.d.ts +4 -5
  278. package/dist/session-incarnation.js +2 -2
  279. package/dist/session-policy-wire.d.ts +1 -1
  280. package/dist/session-policy-wire.js +2 -2
  281. package/dist/session-sync.d.ts +1 -1
  282. package/dist/session-workspace.d.ts +130 -0
  283. package/dist/session-workspace.js +83 -0
  284. package/dist/spec-fields.d.ts +44 -14
  285. package/dist/spec-fields.js +33 -12
  286. package/dist/standing-answer-grant.d.ts +2 -13
  287. package/dist/standing-answer-grant.js +0 -7
  288. package/dist/stored-request-prompt-profile-m30.d.ts +49 -0
  289. package/dist/stored-request-prompt-profile-m30.js +25 -0
  290. package/dist/task-a2a.d.ts +44 -14
  291. package/dist/task-a2a.js +46 -20
  292. package/dist/task-mcp.d.ts +34 -21
  293. package/dist/task-mcp.js +46 -22
  294. package/dist/task-settings.d.ts +2 -2
  295. package/dist/tool-approval.d.ts +43 -9
  296. package/dist/tool-approval.js +57 -15
  297. package/dist/tool-rule-names.d.ts +5 -0
  298. package/dist/trace/core-keyset-guard.d.ts +34 -19
  299. package/dist/trace/engine-notice-wire.d.ts +1 -1
  300. package/dist/trace/engine-notice-wire.js +4 -0
  301. package/dist/trace/exit-seat.d.ts +20 -1
  302. package/dist/trace/exit-seat.js +5 -2
  303. package/dist/trace/extension-call-face.d.ts +33 -0
  304. package/dist/trace/extension-call-face.js +39 -0
  305. package/dist/trace/ledger-sink.d.ts +4 -5
  306. package/dist/trace/ledger-sink.js +3 -7
  307. package/dist/trace/project.d.ts +62 -10
  308. package/dist/trace/project.js +49 -9
  309. package/dist/trace/redact.d.ts +18 -6
  310. package/dist/trace/redact.js +154 -60
  311. package/dist/trace/self-minted-wire.d.ts +500 -7
  312. package/dist/trace/self-minted-wire.js +182 -1
  313. package/dist/trace/wire-projection-faces.d.ts +7 -5
  314. package/dist/trace/wire-projection-faces.js +9 -4
  315. package/dist/user-authored-text.d.ts +2 -2
  316. package/dist/user-authored-text.js +16 -1
  317. package/dist/web-admin-client/apply-effective.js +0 -1
  318. package/dist/web-admin-client/center-request.d.ts +23 -4
  319. package/dist/web-admin-client/center-request.js +42 -8
  320. package/dist/web-admin-client/http-client.js +6 -6
  321. package/dist/web-admin-client/skills-mcp.d.ts +8 -4
  322. package/dist/web-admin-client/skills-mcp.js +31 -18
  323. package/dist/web-admin-client/types.d.ts +10 -5
  324. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +11 -5
  325. package/package.json +3 -3
  326. package/wire-changes/server@7.137.0.json +825 -0
  327. package/wire-changes/server@7.138.0.json +446 -0
@@ -7,6 +7,7 @@ import { WORKSPACE_CACHE_DEFAULT, WORKSPACE_CACHE_MOUNT_DEFAULT, WORKSPACE_CACHE
7
7
  import { SESSION_SNAPSHOT_BUCKET_DEFAULT, WORKSPACE_SNAPSHOT_BUCKET_DEFAULT } from "./object-store-buckets.js";
8
8
  import { WORKSPACE_PERSIST_DEFAULT, WORKSPACE_PERSIST_WORDS, WORKSPACE_SNAPSHOT_KEEP_DEFAULT, WORKSPACE_SNAPSHOT_KEEP_RANGE, WORKSPACE_SNAPSHOT_PREFIX_DEFAULT, WORKSPACE_SNAPSHOT_TIMEOUT_MS_DEFAULT, WORKSPACE_SNAPSHOT_TIMEOUT_MS_RANGE } from "./workspace-space.js";
9
9
  import { APPROVAL_ASK_RETENTION_DEFAULT_MS, APPROVAL_ASK_RETENTION_FLOOR_MS } from "./approval-ask-retention.js";
10
+ import { SANDBOX_SECRET_SEAT_VERBS } from "./plugins/k8s-exec-protocol.js";
10
11
  import { LIVENESS_MS_DEFAULT } from "./exec-bounds.js";
11
12
  import { THINKING_FORMATS } from "./model-compat.js";
12
13
  import { AUTO_MODE_CLASSIFIER_TIMEOUT_BOUNDS, CENTER_BASE_URL_RULE, CONFIG_PROVIDER_WORDS, DRAIN_SECOND_SIGNAL_MIN_GAP_BOUNDS, DRAIN_SECOND_SIGNAL_MIN_GAP_DEFAULT_MS, DEVICE_DELIVERY_TIMEOUT_BOUNDS, DEVICE_EXEC_TIMEOUT_BOUNDS, FIRST_BYTE_TIMEOUT_BOUNDS, FLEET_LEASE_OUTAGE_MAX_DEFAULT_MS, RESILIENCE_DEFAULTS, TASK_WRITE_FACE_ESCALATIONS } from "./config-types.js";
@@ -125,6 +126,7 @@ export const CONFIG_CATALOG = [
125
126
  r("DEVICE_MAX_INFLIGHT_PER_DEVICE", "orchestration", "number", `per-device 在途上限(超额云侧排队;原值进 helloAck.maxInflight;须为 ≥ ${DEVICE_HELLO_ACK_BOUNDS.maxInflight.min} 的规范十进制整数,其余拒启 —— 0 会让每次派发都 device.busy)`, { derivedDefaultNote: "缺席 ⇒ 4(§11-O3 裁定值)" }),
126
127
  r("DEVICE_MAX_PREAUTH_PER_REMOTE", "orchestration", "number", "未鉴权握手的 per-remote 并发帽(正整数或字面 off;XFF/NAT 形用 off+全局帽)", { danger: SEC, derivedDefaultNote: "缺席 ⇒ 8" }),
127
128
  r("DEVICE_RECONNECT_GRACE_MS", "orchestration", "number", `device 断连后在途不判丢的窗(ms;同 epoch 重连续接;原值进 helloAck.reconnectGraceMs;须为 [${DEVICE_HELLO_ACK_BOUNDS.reconnectGraceMs.min}, ${DEVICE_HELLO_ACK_BOUNDS.reconnectGraceMs.max}] 内规范十进制整数,其余拒启)`, { derivedDefaultNote: "缺席 ⇒ adapter 默认 120s" }),
129
+ r("DEVICE_RELAY_URL", "orchestration", "url", "设备直达入口:设备连不到本引擎自己的地址(引擎在中继 / 网关之后)时拨的那一个 ws(s) URL,逐字上 /v1/capabilities 的 deviceExecutor.relayUrl;须为无 userinfo / query / fragment、不带凭据形的 ws:// 或 wss:// 绝对地址,其余拒启。不从 FLEET_ADVERTISE_ADDRESS 派生(那是对中心公告的地址,受众不是设备)", { derivedDefaultNote: "缺席 ⇒ 能力面不带 relayUrl,设备按引擎地址 + deviceExecutor.wsPath 拨号" }),
128
130
  r("DEVICE_SUPERSEDE", "orchestration", "enum", "同 deviceId 二连的顶替策略", { enumValues: ["stale_only", "deny"], staticDefault: "stale_only", danger: SEC }),
129
131
  r("DIRECT_APPROVAL_DOOR", "approval", "boolean", "直连 /decide 门(无 BFF 的桌面/CLI 客户端;开 ⇒ 要求签名 JWT+HMAC 信封,principal 头降级不可信)", { staticDefault: "false", danger: SEC, configKey: "directApprovalDoor" }),
130
132
  r("DOCKER_COMMAND_TIMEOUT_MS", "orchestration", "number", "local-docker 车道单命令超时(ms)"),
@@ -163,9 +165,13 @@ export const CONFIG_CATALOG = [
163
165
  r("FLEET_GENERATION", "integrations", "string", "fleet 成员的代际(配置渲染器写;引擎回声到心跳与 /health 的 generation)。缺席 ⇔ 非 fleet 成员(不续服务租约、不受围栏);在场必须是规范十进制串(\"0\" / \"7\";空串、前导零、符号、空白拒启)", { danger: SEC, configKey: "fleetGeneration" }),
164
166
  r("FLEET_LEASE_FAIL_CLOSED", "integrations", "boolean", "租约连接级失败(中心鉴权拒 / 凭证不可读 / 不可达 / 5xx / 超时 / 坏体)时计费入口拒 503 fleet.lease_unavailable(true)还是放行(false);409 not-lease 与耗尽 429 不受它管;词表外拒启", { danger: SEC, derivedDefaultNote: "缺席 ⇒ 按部署姿态派生:REQUIRE_PRINCIPAL=true ⇒ true(多租:额度是安全轴);单用户 ⇒ false(AP 姿态)。显式值压过派生。生效值见 caps centerWiring.failMode", configKey: "fleetLeaseFailClosed" }),
165
167
  r("FLEET_LEASE_OUTAGE_MAX_MS", "integrations", "number", "一台实例「活着而不服务」的最长时长 T_max(ms),两处读它:① 服务租约中心不可达的容忍上限 —— 租约过期满这么久 ⇒ 新计费提交 503 state.lease_expired(取消 / HITL 应答 / 续跑 / 读面照放行,在飞不动,/readyz 不翻),只在 fleet 成员 ∧ 中心宣告 servingLease 能力位时生效;② 排空口 POST /v1/admin/drain 的期限上界 —— deadlineAt 离此刻超过它 ⇒ 400 request.field_invalid,成员与非成员同读;< 60000(租约 TTL 地板)或非整数拒启", { danger: SEC, staticDefault: String(FLEET_LEASE_OUTAGE_MAX_DEFAULT_MS), configKey: "fleetLeaseOutageMaxMs" }),
166
- r("FLEET_SCOPE", "integrations", "string", "本引擎所在的租户 scope(配置渲染器为多租引擎写;唯一读者 = 排空口 POST /v1/admin/drain 的身份比对,请求不带 scope 也读成缺省 scope)。在场必须是 scope id 形(小写字母 / 数字 / 内部连字符,1–64;空串拒启);与 FLEET_GENERATION 不联动(成员缺席 ⇒ 缺省 scope)。续租体与心跳不带它(scope 由中心凭证绑定)", { danger: SEC, staticDefault: GLOBAL_SCOPE, configKey: "fleetScope" }),
168
+ r("FLEET_SCOPE", "integrations", "string", "本引擎所在的租户 scope(配置渲染器对每台受管引擎写,缺省 scope 也写;手接的引擎缺席 = 缺省 scope;唯一读者 = 排空口 POST /v1/admin/drain 的身份比对,请求不带 scope 也读成缺省 scope)。在场必须是 scope id 形(小写字母 / 数字 / 内部连字符,1–64;空串拒启);与 FLEET_GENERATION 不联动(成员缺席 ⇒ 缺省 scope)。续租体与心跳不带它(scope 由中心凭证绑定)", { danger: SEC, staticDefault: GLOBAL_SCOPE, configKey: "fleetScope" }),
167
169
  r("FORK_ENABLED", "orchestration", "boolean", "CC /fork(core Fork 工具)装配开关", { derivedDefaultNote: "posture 三态:缺席 = 单用户 ⇒ on / 多租户 ⇒ off(per-principal entitlement 另判)", configKey: "forkEnabled" }),
168
- r("GATEWAY_MAX_RETRIES", "modelPlane", "number", "openai 兼容腿每次调用重试上限", { derivedDefaultNote: "缺席 ⇒ 不传引擎,引擎默认当家" }),
170
+ r("GATEWAY_MAX_RETRIES", "modelPlane", "number", "openai 兼容腿每次调用重试上限", {
171
+ derivedDefaultNote: "缺席 ⇒ 按外层拓扑分两态(启动日志 `brain` 字段的 `gatewayRetryCap` 回显哪一态):配了备用网关(`MODEL_GATEWAY_FALLBACK_URLS`)/ 开了断路器开关(`MODEL_CIRCUIT_BREAKER`,单路网关上也算)/ 开了反应式降级(配着 `MODEL_DEGRADE_TO` 且 `MODEL_DEGRADE_REACTIVE` 开)" +
172
+ "⇒ 网关各路 server 传 2(`fast-fail:2`),引擎的 `SEMA_MAX_RETRIES` 被遮蔽(降级兜底腿不吃这条收窄:不设时它不传,引擎默认当家);没有这些快切层 ⇒ 不传(`engine-default`),引擎默认当家(读 `SEMA_MAX_RETRIES`,没设是 10)。" +
173
+ "设了恒赢(`env:<n>`):原样传给引擎(降级兜底腿也吃显式值)并遮蔽 `SEMA_MAX_RETRIES`",
174
+ }),
169
175
  r("GIT_API_BASEURL", "integrations", "url", "repo 工具的 Git host API base", { configKey: "gitApiBaseUrl" }),
170
176
  r("GIT_API_KIND", "integrations", "enum", "Git host API 方言", { enumValues: ["gitea", "github"], staticDefault: "gitea", configKey: "gitApiKind" }),
171
177
  r("GIT_API_TOKEN", "integrations", "secret", "repo 工具的 Git host 凭据", { danger: SEC }),
@@ -204,7 +210,8 @@ export const CONFIG_CATALOG = [
204
210
  r("K8S_MOUNT_PATH", "orchestration", "path", "k8s 工作区挂载路径"),
205
211
  r("K8S_NAMESPACE", "orchestration", "string", "沙箱 pod namespace"),
206
212
  r("K8S_RUNTIME_CLASS", "orchestration", "string", "沙箱 pod runtimeClass(Kata 等)"),
207
- r("K8S_SANDBOX_ENV", "orchestration", "secret", "转发进 k8s 沙箱 pod 容器 env 的本进程 env 点名表(NAME,NAME;点到的键从 worker env 原样取,值只进 pod env —— 不进 spec 的其它位置 / 工作区 / 快照 / center / 事件日志;与 LSP_ENV_ALLOW / E2B_SANDBOX_ENV / DOCKER_SANDBOX_ENV 同一只解析器与「点名放行」谓词:形错 / 点名名单型旋钮拒启且不回显值;本进程环境里没有的名按序号 warn、不转发;与包源派生 env 撞名时显式名单胜;只在本车道被选中时求值;原文按凭据类对待:只回在场位、不进任何子进程)", { danger: SEC }),
213
+ r("K8S_SANDBOX_BROKER_IMAGE", "orchestration", "string", "成员 git 凭据代理容器的镜像(k8s 沙箱 pod 的 containers[1],只挂在成员 run 的 pod 上;建议填本引擎自身镜像 —— 代理是本包里固定路径的一只程序,命令固定);不设 ⇒ 成员 git 凭据平面关(GET /v1/capabilities 的 credentialPlane.sandboxGit = broker_image_unset),任何 pod 都不挂代理,pod 形逐字节同不设时;只在 REMOTE_EXEC=k8s 被选中时求值"),
214
+ r("K8S_SANDBOX_ENV", "orchestration", "secret", `转发进 k8s 沙箱 pod 容器 env 的本进程 env 点名表(NAME,NAME;点到的键从 worker env 原样取,值只进该 pod 自己的 Secret —— pod spec 的容器 env 只带 secretKeyRef 引用、不带值,Secret 随 pod 由集群回收;不进工作区 / 快照 / center / 事件日志;沙箱命名空间的 Role 要 secrets ${SANDBOX_SECRET_SEAT_VERBS.env.join(" / ")},起服问不许即拒启;与 LSP_ENV_ALLOW / E2B_SANDBOX_ENV / DOCKER_SANDBOX_ENV 同一只解析器与「点名放行」谓词:形错 / 点名名单型旋钮拒启且不回显值;本进程环境里没有的名按序号 warn、不转发;与包源派生 env 撞名时显式名单胜;只在本车道被选中时求值;原文按凭据类对待:只回在场位、不进任何子进程)`, { danger: SEC }),
208
215
  r("K8S_SANDBOX_IMAGE", "orchestration", "string", "k8s 车道沙箱镜像(REMOTE_EXEC=k8s 必需件)"),
209
216
  r("K8S_SANDBOX_IPV6", "orchestration", "enum", "k8s 沙箱 pod 的 IPv6:disabled = pod 级 sysctl 关 IPv6(net.ipv6.conf.all / default.disable_ipv6=1;两只都不在 k8s 的 safe sysctl 表里 ⇒ 每个节点 kubelet 要 --allowed-unsafe-sysctls 点名,否则 pod 起不来;沙箱 ns 不能 enforce PodSecurity baseline / restricted);enabled = 不动 pod(pod 保留平台给的 IPv6,只执行 IPv4 的出网策略管不到它,起服自检每次一行 sandbox_egress_policy_gap{kind: ipv6_enabled});词表外拒启", { enumValues: SANDBOX_IPV6_WORDS, staticDefault: SANDBOX_IPV6_DEFAULT, danger: SEC }),
210
217
  r("K8S_TIMEOUT_MS", "orchestration", "number", "k8s 沙箱 pod 寿命(activeDeadlineSeconds,ms;整只 pod 的泄漏护栏,后台任务超时也被它钳;各条命令的超时见工具超时)"),
@@ -290,9 +297,9 @@ export const CONFIG_CATALOG = [
290
297
  r("MODEL_CHEAP_ID", "modelPlane", "string", "cheap 槽模型 id(subagent/summarize 两 role 走它;未设或与 MODEL_ID 同 id=全落主模型,同 id 不铸 cheap 槽)", { danger: BILL, center: cModels("models[].id") }),
291
298
  r("MODEL_CHEAP_MAX_TOKENS", "modelPlane", "number", "cheap 槽输出上限", { derivedDefaultNote: "缺席 ⇒ 继承主模型", center: cModels("models[].maxTokens") }),
292
299
  r("MODEL_CHEAP_REASONING", "modelPlane", "boolean", "cheap 槽 reasoning 声明(三态:缺席继承主模型)", { derivedDefaultNote: "缺席 ⇒ 继承主模型" }),
293
- r("MODEL_CHEAP_VISION", "modelPlane", "boolean", "cheap 槽 vision 声明(三态:缺席继承主模型;text-only cheap 必设 false 让预检真拦)", { derivedDefaultNote: "缺席 ⇒ 继承主模型" }),
300
+ r("MODEL_CHEAP_VISION", "modelPlane", "boolean", "cheap 槽 vision 声明(三态:缺席继承主模型;text-only cheap 必设 false,引擎才会在替换图片时于会话流发 `model.capability_downgraded`)", { derivedDefaultNote: "缺席 ⇒ 继承主模型" }),
294
301
  r("MODEL_CIRCUIT_BREAKER", "modelPlane", "boolean", "模型腿熔断开关", { staticDefault: String(RESILIENCE_DEFAULTS.circuitBreaker.value) }),
295
- r("MODEL_CODE_ROLES", "modelPlane", "csv", "给点名 role 附编码 persona(role 表内名字才生效)"),
302
+ r("MODEL_CODE_ROLES", "modelPlane", "csv", "给点名 role 按词点名引擎的编码角色基底(`promptRole: \"code\"`;role 表内名字才生效)"),
296
303
  r("MODEL_CONTEXT_WINDOW", "modelPlane", "number", "主模型上下文窗声明", { staticDefault: "262144", configKey: "model", resolve: (c) => c.model.contextWindow ?? null, center: cModels("models[].contextWindow") }),
297
304
  r("MODEL_COST_CACHE_READ", "modelPlane", "number", "主模型 cache-read 单价(per-1M USD;显式 0=声明免费,不是「没配」)", { derivedDefaultNote: "四键全缺席 ⇒ 未定价(不铸 cost 键,成本读面缺席);任一设 ⇒ 四键在场,未设的按 0 显式", danger: BILL, resolve: (c) => c.model.cost?.cacheRead ?? null, center: cModels("models[].cost.cacheRead") }),
298
305
  r("MODEL_COST_CACHE_WRITE", "modelPlane", "number", "主模型 cache-write 单价", { derivedDefaultNote: "四键全缺席 ⇒ 未定价(不铸 cost 键,成本读面缺席);任一设 ⇒ 四键在场,未设的按 0 显式", danger: BILL, resolve: (c) => c.model.cost?.cacheWrite ?? null, center: cModels("models[].cost.cacheWrite") }),
@@ -341,14 +348,16 @@ export const CONFIG_CATALOG = [
341
348
  },
342
349
  }),
343
350
  r("MODEL_VERIFIER", "modelPlane", "string", "verify 门的异构 verifier(须命中目录;typo 回 default 并 boot warn=自评自批)", { derivedDefaultNote: "缺席/不在目录 ⇒ default(=implementer,去相关性失效警告)" }),
344
- r("MODEL_VISION", "modelPlane", "boolean", "主模型 vision 声明(三态:true / false / 缺席 = 未声明;false = 明说不收图:带图请求的图被换成占位句交给模型,提交时计数并告警)", {
345
- derivedDefaultNote: "缺席 ⇒ 不铸 `Model.input`(没有 true 缺省);引擎与带图预检都按「收」读(图原样交给模型,不出信号);`GET /v1/models` 该行不带 `vision` 键。要让预检对纯文本模型出信号写 MODEL_VISION=false",
351
+ r("MODEL_VISION", "modelPlane", "boolean", "主模型 vision 声明(三态:true / false / 缺席 = 未声明;false = 明说不收图:带图请求的图被换成占位句交给模型,引擎在替换的那一刻于会话流发 `model.capability_downgraded`,每会话每模型一次)", {
352
+ derivedDefaultNote: "缺席 ⇒ 不铸 `Model.input`(没有 true 缺省);引擎按「收」读(图原样交给模型,不发 `model.capability_downgraded`);`GET /v1/models` 该行不带 `vision` 键。要让引擎对纯文本模型发该信号写 MODEL_VISION=false",
346
353
  resolve: (c) => {
347
354
  const v = modelImageInput(c.model);
348
355
  return v === "undeclared" ? null : v === "yes";
349
356
  },
350
357
  center: cModels("models[].vision"),
351
358
  }),
359
+ r("MODS_PACKAGES", "integrations", "opaque", "部署座装载的 mod 包声明(JSON 数组,每项闭集 { name, dir, tier, options? }:name = 包自己的名、逐字同;dir 绝对路径、真实路径须在 MODS_ROOT 之下且是目录;tier ∈ prepend / user / append 宿主必填;options 原样交包的 register)。读包只有引擎的读包者,本服务不读包内文件;形错 / 越根 / 读包者拒 / 名不同 / 引擎准入拒 ⇒ 拒启,一行拒句点名哪一席或哪只包(本服务写的句子不回显声明的值;读包者 / 准入交回的话原样透传、凭据形换记号,可能点名包目录);起服装配一次,改声明或换包要重启;本目录只回在场位与长度(不回显声明);options 不是凭据通道(本服务不替包扣凭据:宿主车道 inheritEnv \"all\" 下子进程照样继承这根旋钮与包自己读的变量)", { danger: SEC, derivedDefaultNote: "缺席 / 空串 = 不声明 ⇒ 部署座只有本服务自铸的注册,/health 与 /v1/capabilities 逐字节同没有这根旋钮的版本" }),
360
+ r("MODS_ROOT", "integrations", "path", "声明的 mod 包允许所在的根(绝对路径;包的真实路径按路径分量严格落在它的真实路径之下 —— 符号链接出根、前缀相似目录、.. 遍历、根本身都拒启;建议只读物料树)。声明了包而没设它 ⇒ 拒启;单设它而不声明包 = 无事(形照判)", { danger: SEC, configKey: "modsDeclaration", derivedDefaultNote: "缺席 ⇒ 不许声明任何包", resolve: (c) => c.modsDeclaration.root ?? null }),
352
361
  r("MYSQL_DATABASE", "store", "string", "MySQL 协议库名(选中该引擎时必填)", { danger: DATA }),
353
362
  r("MYSQL_HOST", "store", "string", "MySQL 协议主机(在场即算 SQL 信号,参与 DB_BACKEND 默认推导)", { danger: DATA }),
354
363
  r("MYSQL_PASSWORD", "store", "secret", "MySQL 协议口令", { danger: SEC }),
@@ -23,6 +23,7 @@ import type { CustomPkgSource } from "./sandbox-pkg-source.js";
23
23
  import type { MemoryEngineBackend, StoreBackend as StoreBackendWord } from "@sema-agent/settings-schema/execution-lanes";
24
24
  import { DRAIN_GRACE_FLOOR_MS } from "@sema-agent/settings-schema/fleet";
25
25
  import type { AtMention } from "@sema-agent/settings-schema";
26
+ import type { ModsDeclaration } from "./mods-declaration.js";
26
27
  /** [ref]:记忆向量面的 embedder 坐标(env `MEMORY_EMBEDDER_*` 解析结果;单一属主 = 本类型,
27
28
  * `plugins/memory-embedder.ts` 的工厂直接消费它,不复制形状)。语义与三问见
28
29
  * {@link ServiceConfigFlat.memoryEmbedder}。纯数据 —— 端点原文照存(拼 `/embeddings` 是 transport
@@ -130,7 +131,7 @@ export declare const DRAIN_SECOND_SIGNAL_MIN_GAP_DEFAULT_MS = 250;
130
131
  * 住在本叶(而不在 `fleet/drain-entry.ts`):`config.ts` 读它,而 drain-entry 值引 `plugins/store-contracts.ts`,config 不值引 plugins 层。
131
132
  */
132
133
  export declare function fleetScopeOf(raw: string | undefined): string;
133
- /** 中心基址三席(`SEMA_WEB_ADMIN_URL` / `SEMA_WEB_ADMIN_LIVE_URL` / `SEMA_WEB_ADMIN_WEB_URL`)与公告地址的形句 —— 判官 `web-admin-client/center-request.ts` `pathBaseUrlRefusal` 一只(形判 = settings-schema `httpUrlFlaws`),配置目录四行同读这一句。 */
134
+ /** 中心基址三席(`SEMA_WEB_ADMIN_URL` / `SEMA_WEB_ADMIN_LIVE_URL` / `SEMA_WEB_ADMIN_WEB_URL`)与公告地址的形句 —— 判官 `web-admin-client/center-request.ts` `pathBaseUrlRefusal` 一只(形判 = settings-schema `pathBaseUrlFlaws`,14.7.0 起),配置目录四行同读这一句。 */
134
135
  export declare const CENTER_BASE_URL_RULE = "\u8BBE\u4E86\u5C31\u5FC5\u987B\u662F\u53EF\u89E3\u6790\u7684 http(s) URL,\u4E14\u4E0D\u5E26\u51ED\u636E / query / fragment(\u57FA\u5740\u540E\u9762\u8981\u62FC\u8DEF\u5F84);\u574F\u5F62\u62D2\u542F(\u6CA1\u63A5\u4E2D\u5FC3\u4E5F\u62D2),\u62D2\u53E5\u53EA\u70B9\u540D\u89C4\u5219,\u4E0D\u56DE\u663E scheme";
135
136
  /** 编排器片 2:`FLEET_LEASE_OUTAGE_MAX_MS` 的缺省(24 h)—— 中心 / 库故障、中心降级在今天都靠 LKG 无限期容忍,10 分钟级的窗会把它们变成数据面停服。 */
136
137
  export declare const FLEET_LEASE_OUTAGE_MAX_DEFAULT_MS = 86400000;
@@ -266,7 +267,7 @@ export interface MemoryConsolidationDriverConfig {
266
267
  * ①恰好一条时 = 两个 admin 口省略 `scope` 的**唯一**缺省(两条及以上恒不猜——在两个库之间猜一个
267
268
  * 去花钱是本设计要消灭的形);②周期腿落地时的 scope 源(设计稿 D2)。
268
269
  * 🔴 **不是授权边界**:本仓今天没有任何授权源能对一个 operator 收窄 scope(理由与亲读依据逐字见
269
- * `src/memory-operator-faces.ts` 的 `erasureEnvelopeRefusal` 头注),在本层拿它当白名单就是**发明
270
+ * `src/memory-operator-faces.ts` `MemoryComplianceFaces.erase` 的「授权座只有一只」段),在本层拿它当白名单就是**发明
270
271
  * 第二套授权语义**。显式传的 scope 照跑,表只管缺省。 */
271
272
  scopes: readonly string[];
272
273
  /**
@@ -297,7 +298,8 @@ export interface MemoryConsolidationDriverConfig {
297
298
  */
298
299
  autoRunOnRecommendation?: boolean;
299
300
  }
300
- /** A web-admin MCP server resolved to a core spec (env-NAME refs already → real values) plus the
301
+ /** A web-admin MCP server resolved to a core spec (env-NAME refs already read and registered on the peer desk under the
302
+ * roster lease: the spec carries the minted reference, never the values) plus the
301
303
  * scenarios it applies to (empty = all). `resolveSpec` filters by scenario and passes `spec` to core. */
302
304
  export interface ScopedMcpServer {
303
305
  scenarios: string[];
@@ -312,7 +314,8 @@ export interface CenterMcp {
312
314
  servers: ScopedMcpServer[];
313
315
  reservedNames: readonly string[];
314
316
  }
315
- /** DESIGN-269 §2.1 —— an A2A PEER resolved to a core spec (header-NAME refs already → real values) plus
317
+ /** DESIGN-269 §2.1 —— an A2A PEER resolved to a core spec (header-NAME refs already read and registered on the peer desk
318
+ * under the roster lease: the spec carries the minted reference, never the values) plus
316
319
  * the scenarios it applies to (empty = all). Exact sibling of {@link ScopedMcpServer}: `resolveSpec`
317
320
  * filters by scenario (`a2aForScenario`) and passes `spec` to core as `TaskSpec.a2a`.
318
321
  *
@@ -510,8 +513,10 @@ export interface ServiceConfigFlat {
510
513
  gatewayFallbackUrls: string[];
511
514
  /** `GATEWAY_MAX_RETRIES` —— openai 兼容腿的**每次调用**重试上限。
512
515
  *
513
- * 🔴 **缺席 = 不传给引擎**(不是"传个默认值"):此前这里是硬编码 `maxRetries: 2`,而显式传参**压过**
514
- * 引擎默认 ⇒ core 抬默认对 server 部署一点效果都没有。缺席不传之后,引擎默认当家、core 抬默认自动继承。
516
+ * 🔴 **配置层缺席 = 本字段 `undefined`**(不是"传个默认值"):此前这里是硬编码 `maxRetries: 2`,而显式传参**压过**
517
+ * 引擎默认 ⇒ core 抬默认对 server 部署一点效果都没有。缺席之后由 `brain.ts` 按**外层拓扑**分两态(启动日志 `gatewayRetryCap` 回显):
518
+ * 外层有快切层(备用网关 / 断路器开关 / 反应式降级 —— 后者须配着 `MODEL_DEGRADE_TO`)⇒ 网关各路传 `FAST_FAIL_MAX_RETRIES`(2)并遮蔽引擎的 `SEMA_MAX_RETRIES`;
519
+ * 没有快切层 ⇒ 不传,引擎默认当家、core 抬默认自动继承。降级兜底腿不吃这条收窄(只吃显式值)。设了恒赢(原样传、同样遮蔽 `SEMA_MAX_RETRIES`)。
515
520
  * (五报清查 R1① 的 server 半场,黑板 [ref]/[ref];anthropic 腿早有 `ANTHROPIC_MAX_RETRIES` 出口,
516
521
  * 这条只是把同样的出口补给限流 provider 真正走的那条腿。) */
517
522
  gatewayMaxRetries?: number;
@@ -944,6 +949,10 @@ export interface ServiceConfigFlat {
944
949
  /** `K8S_SANDBOX_IPV6`(闭集与缺省的属主 `sandbox-egress-policy.ts`):`disabled` ⇒ 沙箱 pod 级 sysctl 关 IPv6;`enabled` ⇒ pod spec 不带 sysctl。
945
950
  * loader 恒铸;可选只为手搓配置。 */
946
951
  sandboxIpv6?: import("./sandbox-egress-policy.js").SandboxIpv6;
952
+ /** `K8S_SANDBOX_BROKER_IMAGE` — the member git-credential broker container's image (`containers[1]` of a member's sandbox pod; this
953
+ * engine's own image is the recommended value — the broker is a program of this package at a fixed path, `broker/constants.ts`). Absent /
954
+ * empty ⇒ the plane is off (`credentialPlane.sandboxGit` = `broker_image_unset`) and no pod carries a broker. */
955
+ brokerImage?: string;
947
956
  /** `K8S_SANDBOX_ENV=NAME,NAME` 点到、且本进程环境里真有的键 ⇒ 原值(与 e2b 的 `sandboxEnv` 同名同形、同一只解析器、同一只谓词 `allowlistedEnv`,
948
957
  * 空串值照带)。只进 pod 容器 env;装配点与包源派生 env 合并一处,显式名单胜。键缺席 = 旋钮未设 / 空串(零转发)。 */
949
958
  sandboxEnv?: Record<string, string>;
@@ -1014,6 +1023,11 @@ export interface ServiceConfigFlat {
1014
1023
  * 域 = `[1, 86400]` 的十进制正整数,坏值**拒启**([ref] A 档;解析器 `parseDeviceEnrollTokenTtlEnv`)。
1015
1024
  * 缺席 ⇒ 签发点的属主默认 900(不在 config 层抄第二份默认,与本族其余旋钮同姿势)。 */
1016
1025
  enrollTokenTtlSec?: number;
1026
+ /** `DEVICE_RELAY_URL` = 设备的**直达入口**:设备连不到本引擎自己的地址(引擎在中继 / 网关之后)时该拨的那一个 ws(s) URL,运维声明、**逐字**上能力面
1027
+ * `deviceExecutor.relayUrl`(唯一消费点 `http/routes/capabilities.ts`)。缺席 ⇒ 键缺席 = 设备按本引擎地址 + `deviceExecutor.wsPath` 拨号(既有形)。
1028
+ * **不派生**:`FLEET_ADVERTISE_ADDRESS` 是对中心公告的地址(内网 pod IP / 容器别名),受众不是用户机器上的设备;从它拼出来的入口对设备多半不可达。
1029
+ * 形门 = ws / wss 绝对地址,无 userinfo / query / fragment、不带凭据形(值逐字上能力面),坏值**拒启**(解析器 `parseDeviceRelayUrlEnv`)。 */
1030
+ relayUrl?: string;
1017
1031
  /** `DEVICE_SUPERSEDE`(默认 `stale_only`)= 同 deviceId 二连的顶替策略(§5.2)。 */
1018
1032
  supersede?: "stale_only" | "deny";
1019
1033
  } | {
@@ -2196,6 +2210,9 @@ export interface ServiceConfigFlat {
2196
2210
  defaultScenario: string;
2197
2211
  /** Directory of baked-in skill Markdown files, loaded once at startup. */
2198
2212
  skillsDir: string;
2213
+ /** 运维声明的 mod 包(`MODS_ROOT` + `MODS_PACKAGES`;`loadConfig` 读一次,唯一读法 `mods-declaration.ts` 的 `parseModsDeclaration`)。两只起服入口的部署座装配
2214
+ * (`boot/deployment-mods.ts` 的 `assembleDeploymentMods`)都读这一份;没声明 = 没有 `packages` 这一席(`{}` / 单设根时 `{ root }`;部署座只有本仓自铸的注册)。起服装配一次,配置刷新不重载。 */
2215
+ modsDeclaration: ModsDeclaration;
2199
2216
  /** Central web-admin (web-admin). When set, the service pulls the effective config
2200
2217
  * on boot (and refreshes) and applies it OVER the env defaults — env is the fallback. Logical config
2201
2218
  * only (model roster/roles/teams); secrets/gateways stay in this service's env.
@@ -2285,7 +2302,7 @@ export type ServiceLimitsHttpConfig = Pick<ServiceConfigFlat, "port" | "attachme
2285
2302
  /** 组:observability(可观测)。 */
2286
2303
  export type ServiceObservabilityConfig = Pick<ServiceConfigFlat, "metricsToken" | "traceToken" | "toolTrace" | "traceThinking" | "logLevel" | "otel">;
2287
2304
  /** 组:integrations(外部集成)。`mcpServers` / `a2aPeers` 由 web-admin 适配器填(无 env 腿),归本组。 */
2288
- export type ServiceIntegrationsConfig = Pick<ServiceConfigFlat, "pluginsAllowHosts" | "mcpServers" | "a2aPeers" | "a2aServe" | "configBootFetchBudgetMs" | "gitApiBaseUrl" | "gitApiToken" | "gitApiKind" | "defaultScenario" | "skillsDir" | "centerCredentialRenewLeadMs" | "webAdmin" | "fleetLeaseFailClosed" | "fleetGeneration" | "fleetScope" | "fleetAdvertiseAddress" | "fleetLeaseOutageMaxMs" | "configProvider" | "configLocalDir" | "configLocalOwnerUid">;
2305
+ export type ServiceIntegrationsConfig = Pick<ServiceConfigFlat, "pluginsAllowHosts" | "mcpServers" | "a2aPeers" | "a2aServe" | "configBootFetchBudgetMs" | "gitApiBaseUrl" | "gitApiToken" | "gitApiKind" | "defaultScenario" | "skillsDir" | "modsDeclaration" | "centerCredentialRenewLeadMs" | "webAdmin" | "fleetLeaseFailClosed" | "fleetGeneration" | "fleetScope" | "fleetAdvertiseAddress" | "fleetLeaseOutageMaxMs" | "configProvider" | "configLocalDir" | "configLocalOwnerUid">;
2289
2306
  /** 九个组槽。每组恒在场(`loadConfig` / `attachConfigGroups` 装好才交出配置),故不可选——新代码写
2290
2307
  * `config.modelPlane.model` 不需要 `?.`(可选组会把 `Model` 污染成 `Model | undefined`)。 */
2291
2308
  export interface ServiceConfigGroups {
package/dist/config.d.ts CHANGED
@@ -274,7 +274,7 @@ export declare function isRosterGateArmed(webAdmin: ServiceConfig["webAdmin"]):
274
274
  * core 的每一只 name-keyed policy(`createAllowDenyPolicy` / `createPermissionRulePolicy` /
275
275
  * `createApprovalPolicy`)都解 covering 拼法,而 operator 腿的消费者 `createDurableAskPolicy`
276
276
  * (`src/approval.ts`)自 core 5.60.1 的谓词根导出到货后,deny/neverAuto/require 三集合也改成
277
- * 「精确 ∪ covering」(同一只 `namespacedRuleNameCovers`)。于是本函数放行的
277
+ * 「精确 ∪ covering」(同一只 `ruleNameCovers`)。于是本函数放行的
278
278
  * `<ns>__<server>__*` / `<ns>__<server>__<glob>` 在**每一条**腿上都是**真判**,不再是
279
279
  * 「放行进 no-op」—— 而那正是 [ref] 病形(`APPROVAL_DENY=mcp__figma__*` 判 allow)的根。
280
280
  *
package/dist/config.js CHANGED
@@ -3,7 +3,7 @@ import { DEFAULT_AT_MENTION, DEFAULT_MODEL_REF, mintCapabilityKeys } from "./mod
3
3
  import { homedir } from "node:os";
4
4
  import { isAbsolute, join, posix as posixPath } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
- import { compileAllowList, inheritEnvPolicyProblem, BUILTIN_COMPLIANCE_DENIES, COMPLIANCE_CAPABILITIES, CROSS_SESSION_INBOUND_SETTINGS, CROSS_SESSION_DIALOG_EXPIRY_VALUES, compileReadDeny, resolveReadDenyBuiltins, CODE_AGENT_PROMPT, isModelPriced, PROTOCOL_TABLE, RECOMMENDED_SENSITIVE_PATTERNS, judgeRuleToolName, resolveFileHistoryRetention, resolveLockedKeys, validatePermissionRules, WRITE_PROTECTED_DEFAULT_TABLE, resolveWriteProtectedTable, principalInboxConsumerOf } from "@sema-agent/core";
6
+ import { compileAllowList, inheritEnvPolicyProblem, BUILTIN_COMPLIANCE_DENIES, COMPLIANCE_CAPABILITIES, CROSS_SESSION_INBOUND_SETTINGS, CROSS_SESSION_DIALOG_EXPIRY_VALUES, compileReadDeny, resolveReadDenyBuiltins, isModelPriced, PROTOCOL_TABLE, RECOMMENDED_SENSITIVE_PATTERNS, judgeRuleToolName, resolveFileHistoryRetention, resolveLockedKeys, validatePermissionRules, WRITE_PROTECTED_DEFAULT_TABLE, resolveWriteProtectedTable, principalInboxConsumerOf } from "@sema-agent/core";
7
7
  import { ROSTER_PRIMARY_ROLES, ROSTER_CHEAP_ROLES, WEB_ADMIN_ENV, retiredEnvRefusal, stripControlAndBidi } from "@sema-agent/settings-schema";
8
8
  import { parseApprovalHmacKeys, parsePrincipalJwks } from "./auth-keys.js";
9
9
  import { DEFAULT_ELICITATION_THROTTLE } from "./elicitation.js";
@@ -15,7 +15,7 @@ import { DEFAULT_QUESTION_THROTTLE } from "./question.js";
15
15
  import { GIT_API_KINDS, isGitApiKind } from "./git-api-kind.js";
16
16
  import { CONFIG_PROVIDER_WORDS, RETENTION_MODES, MEMORY_CONSOLIDATION_MIN_INTERVAL_SEC, RESILIENCE_DEFAULTS, TASK_WRITE_FACE_ESCALATIONS, MAX_TIMER_DELAY_MS, AUTO_MODE_CLASSIFIER_TIMEOUT_BOUNDS, DRAIN_GRACE_FLOOR_MS, DRAIN_SECOND_SIGNAL_MIN_GAP_BOUNDS, DRAIN_SECOND_SIGNAL_MIN_GAP_DEFAULT_MS, FIRST_BYTE_TIMEOUT_BOUNDS, FLEET_LEASE_OUTAGE_MAX_DEFAULT_MS, fleetScopeOf, DEVICE_DELIVERY_TIMEOUT_BOUNDS, DEVICE_EXEC_TIMEOUT_BOUNDS, inWholeNumberBounds, wholeNumberBoundsRule } from "./config-types.js";
17
17
  import { DEVICE_HELLO_ACK_BOUNDS } from "./device-ws-protocol.js";
18
- import { FLEET_GENERATION_ENV, FLEET_SCOPE_ENV, SERVING_LEASE_TTL_FLOOR_MS, isCanonicalGeneration } from "@sema-agent/settings-schema/fleet";
18
+ import { FLEET_ADVERTISE_ADDRESS_ENV, FLEET_GENERATION_ENV, FLEET_SCOPE_ENV, SERVING_LEASE_TTL_FLOOR_MS, isCanonicalGeneration } from "@sema-agent/settings-schema/fleet";
19
19
  import { ScopeId } from "@sema-agent/settings-schema/api/scopes";
20
20
  import { centerLaneConfigured, pathBaseUrlRefusal } from "./web-admin-client/center-request.js";
21
21
  import { resolveLocalDataRoot } from "./local-data-root.js";
@@ -45,9 +45,9 @@ import { parseCenterCredentialRenewLeadMs } from "./center-credential-renewer.js
45
45
  import { modelProviderRefusal } from "./model-provider.js";
46
46
  import { modelRouteUrlRefusal } from "./model-route-url.js";
47
47
  import { httpUrlFlaws } from "@sema-agent/settings-schema";
48
- import { buildHttpUrlRefusal } from "./url-refusal.js";
48
+ import { buildHttpUrlRefusal, buildRefusalShownUrl } from "./url-refusal.js";
49
49
  import { customPkgSourceFromEnv } from "./sandbox-pkg-source.js";
50
- import { anthropicCredentialRefusal, csvOf, envLaneTable, mintedCheapId, readModelRouteEnv } from "./model-route-gap.js";
50
+ import { anthropicCredentialRefusal, csvOf, declaredCredential, envLaneTable, mintedCheapId, readModelRouteEnv } from "./model-route-gap.js";
51
51
  import { displayListEntry } from "./list-entry-display.js";
52
52
  import { echoCallerText } from "./caller-text-echo.js";
53
53
  import { buildDisplaySafeUrl, redactSecrets } from "./trace/redact.js";
@@ -55,6 +55,7 @@ import { parseConfigLocalOwnerUid } from "./config-dir-trust.js";
55
55
  import { allowlistedEnv } from "./server-secret-env.js";
56
56
  import { ENV_NAME_ALLOWLIST_KNOBS, ENV_NAME_ALLOWLIST_KNOB_NAMES, describeAllowlistPostures } from "./env-name-allowlist-knobs.js";
57
57
  import { APPROVAL_ASK_RETENTION_DEFAULT_MS, APPROVAL_ASK_RETENTION_FLOOR_MS } from "./approval-ask-retention.js";
58
+ import { parseModsDeclaration } from "./mods-declaration.js";
58
59
  export { RETENTION_MODES, MEMORY_CONSOLIDATION_MIN_INTERVAL_SEC } from "./config-types.js";
59
60
  function csv(name) {
60
61
  return csvOf(process.env[name]);
@@ -372,6 +373,30 @@ function parseDeviceEnrollTokenTtlEnv() {
372
373
  }
373
374
  return Number(raw);
374
375
  }
376
+ function credentialLawRewriteOf(href) {
377
+ const scanned = redactSecrets(href);
378
+ return scanned === href ? undefined : scanned;
379
+ }
380
+ function parseDeviceRelayUrlEnv() {
381
+ const raw = process.env.DEVICE_RELAY_URL;
382
+ if (raw === undefined || raw === "")
383
+ return undefined;
384
+ const scheme = /^wss?:\/\//i.exec(raw)?.[0];
385
+ const asHttp = scheme === undefined ? undefined : `${scheme.length === "wss://".length ? "https" : "http"}://${raw.slice(scheme.length)}`;
386
+ const flaws = asHttp === undefined ? ["not-ws-url"] : httpUrlFlaws(asHttp, ["userinfo", "query", "fragment"]).map((f) => (f === "not-http-url" ? "not-ws-url" : f));
387
+ if (flaws.length > 0) {
388
+ throw new Error(`env DEVICE_RELAY_URL must be an absolute ws:// or wss:// URL with no userinfo, query or fragment (ws(s)://host[:port][/path]) — found: ${flaws.join(", ")}; got ${JSON.stringify(buildRefusalShownUrl(raw))}. ` +
389
+ `It is published verbatim on GET /v1/capabilities (deviceExecutor.relayUrl) as the address devices dial, so it has no room for credentials (devices authenticate by signature). ` +
390
+ `Leave it unset when devices reach this engine at its own address — they then dial it at deviceExecutor.wsPath.`);
391
+ }
392
+ const href = new URL(raw).href;
393
+ const credentialScan = credentialLawRewriteOf(href);
394
+ if (credentialScan !== undefined) {
395
+ throw new Error(`env DEVICE_RELAY_URL carries something the credential-redaction law recognizes (${credentialScan}) — it is published verbatim on GET /v1/capabilities (deviceExecutor.relayUrl), ` +
396
+ `so it must not carry a token-shaped path segment; keep it to ws(s)://host[:port][/path] (devices authenticate by signature, not by the URL).`);
397
+ }
398
+ return raw;
399
+ }
375
400
  function parseDevicePreAuthPerRemoteEnv() {
376
401
  const raw = process.env.DEVICE_MAX_PREAUTH_PER_REMOTE;
377
402
  if (raw === undefined || raw === "")
@@ -1079,7 +1104,7 @@ function parseModelDomain() {
1079
1104
  }
1080
1105
  const models = envLaneTable(modelId, model, cheapModel);
1081
1106
  const gatewayApiKey = route.gatewayApiKey;
1082
- if (gatewayBaseUrl === undefined && gatewayApiKey !== undefined && gatewayApiKey !== "") {
1107
+ if (gatewayBaseUrl === undefined && declaredCredential(gatewayApiKey) !== undefined) {
1083
1108
  CONFIG_NOTICES.push({
1084
1109
  level: "warn",
1085
1110
  event: "model_gateway_key_unpaired",
@@ -1090,7 +1115,7 @@ function parseModelDomain() {
1090
1115
  }
1091
1116
  const cheapRef = cheapModel ? cheapModel.id : DEFAULT_MODEL_REF;
1092
1117
  const verifierEnv = process.env.MODEL_VERIFIER;
1093
- const verifierRef = verifierEnv && models[verifierEnv] ? verifierEnv : DEFAULT_MODEL_REF;
1118
+ const verifierRef = verifierEnv && Object.hasOwn(models, verifierEnv) ? verifierEnv : DEFAULT_MODEL_REF;
1094
1119
  const roles = {
1095
1120
  verifier: verifierRef,
1096
1121
  };
@@ -1105,8 +1130,8 @@ function parseModelDomain() {
1105
1130
  continue;
1106
1131
  roles[role] =
1107
1132
  typeof cur === "string"
1108
- ? { model: cur, systemPrompt: CODE_AGENT_PROMPT }
1109
- : { ...cur, systemPrompt: CODE_AGENT_PROMPT };
1133
+ ? { model: cur, promptRole: "code" }
1134
+ : { ...cur, promptRole: "code" };
1110
1135
  }
1111
1136
  const degradeOn = parseDegradeOn(csv("MODEL_DEGRADE_ON"));
1112
1137
  return {
@@ -1942,6 +1967,7 @@ function parseOrchestrationDomain(ctx) {
1942
1967
  ...(process.env.K8S_MEMORY ? { memory: process.env.K8S_MEMORY } : {}),
1943
1968
  ...(process.env.K8S_CPU ? { cpu: process.env.K8S_CPU } : {}),
1944
1969
  sandboxIpv6: enumEnv("K8S_SANDBOX_IPV6", SANDBOX_IPV6_DEFAULT, SANDBOX_IPV6_WORDS),
1970
+ ...(process.env.K8S_SANDBOX_BROKER_IMAGE ? { brokerImage: process.env.K8S_SANDBOX_BROKER_IMAGE } : {}),
1945
1971
  ...((names) => (names !== undefined ? { sandboxEnv: allowlistedEnv(names, process.env) } : {}))(parseEnvNameAllowlist("K8S_SANDBOX_ENV", singleUserTurnkey)),
1946
1972
  ...k8sWorkspaceStore(),
1947
1973
  ...((c) => (c !== undefined ? { workspaceCache: c } : {}))(k8sWorkspaceCache()),
@@ -2000,6 +2026,7 @@ function parseOrchestrationDomain(ctx) {
2000
2026
  ...((n) => (n !== undefined ? { maxInflightPerDevice: n } : {}))(parseDeviceBoundedIntEnv("DEVICE_MAX_INFLIGHT_PER_DEVICE", process.env.DEVICE_MAX_INFLIGHT_PER_DEVICE, DEVICE_HELLO_ACK_BOUNDS.maxInflight)),
2001
2027
  ...((n) => (n !== undefined ? { maxPreAuthPerRemote: n } : {}))(parseDevicePreAuthPerRemoteEnv()),
2002
2028
  ...((n) => (n !== undefined ? { enrollTokenTtlSec: n } : {}))(parseDeviceEnrollTokenTtlEnv()),
2029
+ ...((n) => (n !== undefined ? { relayUrl: n } : {}))(parseDeviceRelayUrlEnv()),
2003
2030
  ...(process.env.DEVICE_SUPERSEDE ? { supersede: enumEnv("DEVICE_SUPERSEDE", "stale_only", ["stale_only", "deny"]) } : {}),
2004
2031
  }
2005
2032
  :
@@ -2202,6 +2229,7 @@ function parseIntegrationsDomain(ctx) {
2202
2229
  gitApiKind: parseGitApiKind(process.env.GIT_API_KIND, process.env.GIT_API_BASEURL),
2203
2230
  defaultScenario: env("DEFAULT_SCENARIO", "code"),
2204
2231
  skillsDir: env("SKILLS_DIR", defaultSkillsDir()),
2232
+ modsDeclaration: parseModsDeclaration(process.env.MODS_ROOT, process.env.MODS_PACKAGES),
2205
2233
  centerCredentialRenewLeadMs: parseCenterCredentialRenewLeadMs(process.env.CENTER_CREDENTIAL_RENEW_LEAD_MS),
2206
2234
  webAdmin,
2207
2235
  fleetLeaseFailClosed: parseFleetLeaseFailClosed(process.env.FLEET_LEASE_FAIL_CLOSED, DEPLOYMENT_SHAPE_PREDICATES.multiTenant.holds({ requirePrincipal })),
@@ -2241,15 +2269,16 @@ function parseFleetScope(raw) {
2241
2269
  if (raw !== undefined && !ScopeId.safeParse(raw).success) {
2242
2270
  const shown = raw.length > 40 ? `${raw.slice(0, 40)}…(${raw.length} chars)` : raw;
2243
2271
  throw fleetEnvRefusal("scope_invalid", `env ${name}=${JSON.stringify(shown)} is not a scope id: it must be a lowercase slug of letters, digits and inner dashes, 1–64 characters, ` +
2244
- `not empty. The config renderer sets it on engines that serve a tenant scope; unset it for the default scope ("${fleetScopeOf(undefined)}"). ` +
2272
+ `not empty. The config renderer sets it on every engine it manages (the default scope included); on a hand-wired engine leave it unset for the default scope ("${fleetScopeOf(undefined)}"). ` +
2245
2273
  `Refusing to boot rather than answering drain requests under the wrong identity.`);
2246
2274
  }
2247
2275
  return fleetScopeOf(raw);
2248
2276
  }
2249
2277
  function parseFleetAdvertiseAddress(raw) {
2278
+ const name = "FLEET_ADVERTISE_ADDRESS";
2250
2279
  if (raw === undefined || raw === "")
2251
2280
  return {};
2252
- const refusal = pathBaseUrlRefusal("env FLEET_ADVERTISE_ADDRESS", raw);
2281
+ const refusal = pathBaseUrlRefusal(`env ${name}`, raw);
2253
2282
  if (refusal !== undefined) {
2254
2283
  throw fleetEnvRefusal("advertise_address_invalid", `${refusal} — it must be an http(s) base URL such as "http://10.0.0.5:8090": the fleet center rejects any other shape on every announce. ` +
2255
2284
  `Unset it if this engine does not announce itself.`);
@@ -2330,8 +2359,8 @@ function parseA2aServe() {
2330
2359
  `inbound authentication on this endpoint is header-shaped (a service credential), not URL-embedded.`);
2331
2360
  }
2332
2361
  const parsed = new URL(rawUrl);
2333
- const a2aCredentialScan = redactSecrets(parsed.href);
2334
- if (a2aCredentialScan !== parsed.href) {
2362
+ const a2aCredentialScan = credentialLawRewriteOf(parsed.href);
2363
+ if (a2aCredentialScan !== undefined) {
2335
2364
  throw new Error(`A2A_SERVE_URL carries something the credential-redaction law recognizes (${a2aCredentialScan}) — this address is published VERBATIM on the ANONYMOUSLY readable agent card (the card's url is a functional address: peers fetch it, so it is never rewritten for display), ` +
2336
2365
  `so it must not carry credentials of ANY shape: no userinfo, no credential-named query parameter (api_key/token/…), no token-shaped path segment or parameter value. ` +
2337
2366
  `Keep A2A_SERVE_URL to scheme://host[:port][/path] and move the secret into the caller's headers.`);
@@ -2434,7 +2463,7 @@ const LIMITS_HTTP_GROUP_KEYS = [
2434
2463
  const OBSERVABILITY_GROUP_KEYS = ["metricsToken", "traceToken", "toolTrace", "traceThinking", "logLevel", "otel"];
2435
2464
  const INTEGRATIONS_GROUP_KEYS = [
2436
2465
  "pluginsAllowHosts", "mcpServers", "a2aPeers", "a2aServe", "configBootFetchBudgetMs", "gitApiBaseUrl", "gitApiToken", "gitApiKind", "defaultScenario",
2437
- "skillsDir", "centerCredentialRenewLeadMs", "webAdmin", "fleetLeaseFailClosed", "fleetGeneration", "fleetScope", "fleetAdvertiseAddress", "fleetLeaseOutageMaxMs", "configProvider", "configLocalDir", "configLocalOwnerUid",
2466
+ "skillsDir", "modsDeclaration", "centerCredentialRenewLeadMs", "webAdmin", "fleetLeaseFailClosed", "fleetGeneration", "fleetScope", "fleetAdvertiseAddress", "fleetLeaseOutageMaxMs", "configProvider", "configLocalDir", "configLocalOwnerUid",
2438
2467
  ];
2439
2468
  export const CONFIG_GROUP_KEYS = {
2440
2469
  store: STORE_GROUP_KEYS,
@@ -103,8 +103,8 @@ export interface AbsentApprovalSeat {
103
103
  * durable resume 驱动、断连后的 detach 腿)ask——durable park 原语义逐字保留([ref] 后无活流腿放行执行
104
104
  * 也不会产出空答:coordinator 无 ALS ctx ⇒ 冻结 `{kind:"unavailable"}`(src/question.ts),永不悬挂;
105
105
  * park 仍是把问题送到人面前的唯一那条腿,正当性不变、只是反事实前提换了)。
106
- * 工具名取 `approval-content-kind.ts` 的单一属主(它 re-export core 根导出的 `ASK_USER_QUESTION_TOOL_NAME`);
107
- * server.ts 的 pre-CAS 守卫读同一枚([ref]①:此处旧注称该常量「core 未根导出」= 失实,曾据此手抄 13 处)。
106
+ * 「是不是引擎的问题工具」不在本层判:core 7.56.0 [ref] 起本门**组合** core 原形的答(它读调用的脸 —— 引擎挂载点 + 目录契约 id,不读名字;
107
+ * 它答 `ask` ⇒ 是;本层只在那之上按活流分腿)。决断口的 pre-CAS 守卫(「批准问答必须带答」)仍按保留名读(core 同版「Not changed here」那一条)。
108
108
  * ⚠️ **单一属主**(复审 A2):AskUserQuestion 的 durable 判决只有这一处。任何需要「同参重建」这条判决的
109
109
  * 地方(main.ts 的 parkedReviveInheritedGate 父约束链)必须调本工厂,不得自折 core 原形——两份拷贝里
110
110
  * 只改一份正是本条 finding 的成因。**登记豁免一处**:leader worker 腿(src/leader/wire.ts provisionWorker)
@@ -1,18 +1,16 @@
1
1
  import { join, posix } from "node:path";
2
2
  import { FileError, StubExecutionEnv, combinePolicies, createAllowDenyPolicy, createDurableQuestionPolicy, createSensitivePathPolicy, createTranscriptIntegrityPolicy, err, isAbsoluteForFamily, isProtectedWrite, ok, pathTargetValue, protectivePathTargetOf, } from "@sema-agent/core";
3
3
  import { createDurableAskPolicy, hasApprovalListIntent } from "./approval.js";
4
- import { ASK_USER_QUESTION_TOOL_NAME } from "./approval-content-kind.js";
5
- import { heldRuleName } from "./tool-rule-names.js";
6
4
  export function createDurableQuestionGate(live) {
5
+ const durable = createDurableQuestionPolicy();
7
6
  if (live === undefined)
8
- return createDurableQuestionPolicy();
7
+ return durable;
9
8
  return {
10
- check(req) {
11
- if (heldRuleName(req, (n) => n === ASK_USER_QUESTION_TOOL_NAME) === undefined)
12
- return { action: "allow" };
13
- return live.hasLiveContext()
14
- ? { action: "allow" }
15
- : { action: "ask", message: "AskUserQuestion: awaiting a human answer (durable)" };
9
+ async check(req) {
10
+ const answer = await durable.check(req);
11
+ if (answer.action !== "ask")
12
+ return answer;
13
+ return live.hasLiveContext() ? { action: "allow" } : answer;
16
14
  },
17
15
  };
18
16
  }
@@ -93,23 +93,23 @@ export declare const DEPLOYMENT_SHAPE_PREDICATES: Readonly<{
93
93
  */
94
94
  multiTenant: DeploymentShapePredicate<"principal">;
95
95
  /** 读能力表 `callerCwd` 位(host = 单主才开;device = 路径落在发起者自己的设备上,归属由 `device_session` 绑定验过,不要求单主)。 */
96
- callerCwd: DeploymentShapePredicate<"principal" | "lane">;
96
+ callerCwd: DeploymentShapePredicate<"lane" | "principal">;
97
97
  /** 调用方给的宿主绝对路径(附加目录 / 项目上下文 / 本机 SendUserFile / 指令文件探针)只在「手就在本 worker 的盘上 ∧ 单主」时指向调用方自己的东西。 */
98
- callerOwnsWorkerDisk: DeploymentShapePredicate<"principal" | "lane">;
98
+ callerOwnsWorkerDisk: DeploymentShapePredicate<"lane" | "principal">;
99
99
  /** wire `executionLane.toolsOnThisHost` 与出厂 / 中心技能的 `baseDir`:只问文件系统同一性,与租户无关(出厂内容每个租户拿到同一份)。 */
100
100
  toolsOnThisHost: DeploymentShapePredicate<"lane">;
101
101
  /** `REMOTE_EXEC` 未设的进程内形(没有手)且单主:进程级消费者(hooks 命令、项目上下文本机读、卡批规则 root)看到的就是引擎自己的进程 cwd / env。 */
102
- inProcessSingleUser: DeploymentShapePredicate<"principal" | "lane">;
102
+ inProcessSingleUser: DeploymentShapePredicate<"lane" | "principal">;
103
103
  /** 任务层替部署声明宿主姿态(bypassPermissions 开写面 / host shell 继承整份环境)的射程:device 车道上跨租户宿主穿越结构性不存在(clay 裁定③)。 */
104
- taskHostPostureDeclarations: DeploymentShapePredicate<"principal" | "lane">;
104
+ taskHostPostureDeclarations: DeploymentShapePredicate<"lane" | "principal">;
105
105
  /** 写门走 [ref] 代理裁决(文件平面不在 worker 本机);认不出的词 ⇒ 走代理(代理 slot 空 ⇒ 写门 fail-closed)。 */
106
106
  sandboxPathAdjudication: DeploymentShapePredicate<"lane">;
107
107
  /** 读能力表 `sandboxSendUserFile` 位(e2b / k8s 每任务一只沙箱 ⇒ 任何租户;ssh 共享一台真机 ⇒ 只单主)。 */
108
- sandboxSendUserFile: DeploymentShapePredicate<"principal" | "lane">;
108
+ sandboxSendUserFile: DeploymentShapePredicate<"lane" | "principal">;
109
109
  /** 读能力表 `remoteScratchpad` 位(`/tmp/scratchpad/<sessionId>` 远端约定)。 */
110
110
  remoteScratchpad: DeploymentShapePredicate<"lane">;
111
111
  /** 读能力表 `tenantSeparation` 位(值来自 settings-schema 车道词表正本);唯一读者 = 跨域不变量 `multi-tenant-shared-lane`。 */
112
- laneSeparatesTenants: DeploymentShapePredicate<"principal" | "lane">;
112
+ laneSeparatesTenants: DeploymentShapePredicate<"lane" | "principal">;
113
113
  /**
114
114
  * [ref] —— **VM 级**隔离强度(独立内核:e2b 的 Firecracker microVM;k8s 只在 Kata / gVisor 运行时类上)。与能力表 `isolation` 位
115
115
  * (OS 级边界,容器即算;wire `executionLane.isolated` 的属主 `laneIsolated`)是两个问题,所以不叫「isolated」。读者:A 值路由。
@@ -74,6 +74,7 @@ export declare function asInstructionKind(v: string): DeviceInstructionKind | nu
74
74
  export declare const DEVICE_OUTCOME_ERROR_CODES_BY_REVISION: {
75
75
  readonly 1: readonly ["aborted", "already_exists", "auth_failed", "callback_error", "invalid", "is_directory", "not_directory", "not_found", "not_supported", "outcome_unknown", "permission_denied", "precondition_failed", "shell_unavailable", "spawn_error", "suspended", "target_unavailable", "timeout", "transport_lost", "unknown"];
76
76
  readonly 2: readonly ["liveness_no_output"];
77
+ readonly 3: readonly ["name_too_long"];
77
78
  };
78
79
  /** 本包认得的协议修订号(= 增量表的行键)。 */
79
80
  export type DeviceProtocolRevision = keyof typeof DEVICE_OUTCOME_ERROR_CODES_BY_REVISION;
@@ -1,7 +1,7 @@
1
1
  import { createPublicKey, randomBytes, randomInt, verify as edVerify } from "node:crypto";
2
2
  import { MAX_TIMER_DELAY_MS, inWholeNumberBounds, wholeNumberBoundsRule } from "./config-types.js";
3
3
  export const DEVICE_WS_PATH = "/v1/device/ws";
4
- export const DEVICE_PROTOCOL_VERSION = 2;
4
+ export const DEVICE_PROTOCOL_VERSION = 3;
5
5
  export const DEVICE_MIN_PROTOCOL_VERSION = 1;
6
6
  export const DEVICE_HELLO_DOMAIN_TAG = "sema.device.hello.v1";
7
7
  export const DEVICE_WS_LIMITS = {
@@ -66,6 +66,7 @@ export const DEVICE_OUTCOME_ERROR_CODES_BY_REVISION = {
66
66
  "unknown",
67
67
  ],
68
68
  2: ["liveness_no_output"],
69
+ 3: ["name_too_long"],
69
70
  };
70
71
  export function deviceOutcomeErrorCodesAt(revision) {
71
72
  if (!Number.isInteger(revision) || revision < 1 || revision > DEVICE_PROTOCOL_VERSION) {
@@ -25,7 +25,8 @@
25
25
  * · **判断面**(同一只函数的 `fault`,core 7.55.0 `FileHistoryError.permanence`):永久码表里的 typed 码 ⇒
26
26
  * `{ permanence: "terminal" }`;**连接类故障**(到库的连接坏了 / 库此刻不接 —— 判官是整仓唯一一只
27
27
  * `plugins/store-fault.ts` 的 `storeFaultClassOf`,识别表在 `plugins/sql-errors.ts`,本文件不再抄第二份)⇒
28
- * `{ permanence: "transient" }`;其余 ⇒ `{}`(没判 —— 引擎照旧给重试引导语)。`terminal` 只对永久码。
28
+ * `{ permanence: "transient" }`;**部署错配类**(缺表 / 权限拒:连接是好的、重试注定失败,修表 / 授权之前什么都不会变 —— 同一只判官的 `deployment` 一词,表在 `plugins/sql-errors.ts`)⇒
29
+ * `{ permanence: "terminal" }`;其余 ⇒ `{}`(没判 —— 引擎照旧给重试引导语)。`terminal` 只对永久码与部署错配类。
29
30
  * 折点把它摊进店答的 `FileHistoryError`;引擎的拒句引导语、rewind 披露、`terminal.detail` 都读这一格,**不读散文**。
30
31
  * · **机器面**(同一只函数的副作用):码取自 typed 错**对象**、对闭集词表判定,**绝不从散文里抠**。
31
32
  * 🔴 这一条是 codex R1-[high] 验真后的定形:文本通道**天然可伪造** —— 一个名叫
@@ -54,7 +55,8 @@ export interface CaughtFailure {
54
55
  * `text` = 散文形:带 typed `code` 的前缀一个括号词(`(blob_too_large_for_sql) blob of … exceeds …`),
55
56
  * 不带的**逐字**是原 message(transient 失败的文案因此与本批之前一字不差)。
56
57
  * `fault` = 判断形(core 7.55.0 `FileHistoryError.permanence`):永久码表里的码 ⇒ `{ permanence: "terminal" }`;连接类故障 ⇒ `{ permanence: "transient" }`
57
- * (什么都不改稍后再试可能成);其余 ⇒ `{}`(店不知道 —— 认不出的驱动 / 库错是不是会自己好,不归本店判)。永久码先于连接类判(一只错不会两个都是)。
58
+ * (什么都不改稍后再试可能成);部署错配类故障 ⇒ `{ permanence: "terminal" }`(修表 / 授权之前重试注定失败);其余 ⇒ `{}`(店不知道 —— 认不出的驱动 / 库错是不是会自己好,不归本店判)。
59
+ * 永久码先于分类判(一只错不会两个都是);分类词只读判官 `storeFaultClassOf`,本文件不写码。
58
60
  * 副作用 = 机器形:码取自错误对象,逐次计数 + 每进程每码一条 warn。观测绝不抛。
59
61
  */
60
62
  export declare function foldCaughtFailure(err: unknown): CaughtFailure;
@@ -46,6 +46,8 @@ function faultOfClass(cls) {
46
46
  switch (cls) {
47
47
  case "connection":
48
48
  return { permanence: "transient" };
49
+ case "deployment":
50
+ return { permanence: "terminal" };
49
51
  case "corrupt":
50
52
  case "unclassified":
51
53
  return {};
@@ -0,0 +1,71 @@
1
+ /**
2
+ * 回决门族 200 体上的**结清证据**(`decisionCommitted` / `checkpointId`)—— 唯一铸点与唯一读法。
3
+ *
4
+ * ## 律(一条)
5
+ * `decisionCommitted: true` = 「本请求点名的那道门,已以本请求的决定被耐久地结清过」的**历史证据**。它只在下面五个执行 / 回放边界之一
6
+ * 被跨过的那一拍、由 {@link buildSettlementEvidence} 铸(闭集 {@link SettlementCrossing},`switch` 穷尽):
7
+ * ① `engine_accepted` —— 引擎续跑腿的受理点(`resumeStream` 解析 = 引擎的 pre-CAS 守卫与 checkpoint resolve CAS 都过了);
8
+ * ② `engine_terminal` —— 引擎续跑腿交回结果之后的终局体(同步长调用形 / verify 臂;含「已提交、又重开同一张卡」的 `retriable:true` 那一形);
9
+ * ③ `replay_matched` —— `/decide` 幂等回放比对通过(同一决定早已与 resolve 原子地落在行上;回放不起续跑);
10
+ * ④ `agent_revived` —— 停驻后台子代复活成功(引擎以选中的 token 续跑、attach 之后才交回);
11
+ * ⑤ `card_voided` —— 复活不了的停驻子代被 deny 作废成功(这道门以本 deny **经作废结清**:卡 pending → expired;没有 resolve、没有 deny 结果行)。
12
+ * 调用方对一条引擎续跑腿的分类({@link GateCommitment})只说「这条腿到边界时能证什么」,**从不单独铸位** —— 位要有边界作证。
13
+ * 不铸的出口(都在调用点成文):`checkpoint.resume_aborted` 取消(拿不到 resolve 的返回值 ⇒ 证不出)、抛出臂的取消(受理之后也可能走到 ⇒
14
+ * **刻意不带**证据,fail-closed)、4xx、交给宿主 / 新铸一跑 / 同一决定重投的回执(`delivered`,门由别人结清或仍待决)、停驻子代认领到的
15
+ * token 不是选中的那一张(⇒ `409 decide.gate_moved`,不复活)。证据只随**同一次决定**的回放走:停驻赎回席的反向重试回放摘掉它
16
+ * ({@link withoutSettlementEvidence})。
17
+ *
18
+ * ## 读法(三句,契约 §4a「结清证据」同句)
19
+ * · 位在场 ⇒ 门已以此决定结清过(历史证据;与 `errorCode:"cancelled"` 或 `retriable:true` 并存时同样成立 —— 它不证明卡的**当前**状态、
20
+ * 不证明批过的动作执行了);
21
+ * · 位缺席 ⇒ 证不出或刻意不带,按「没提交」读;
22
+ * · `checkpointId` = 执行**前**从选中的那一行捕获、过信任边界(`checkpoint-identity.ts` 的 `durableRowIdentity`,face `decision-receipt`)
23
+ * 的门身份,只随位出现;缺席 = 身份不可用(老行 / 没过边界)或不适用(回放的投影不带门身份),不推断任何事。
24
+ *
25
+ * ## 读者
26
+ * 本仓两个:`/decide` 的 [ref] 迟到结算留痕与 remember 豁免落店,都经 {@link decisionCommittedOf} 读体上的位(一处读法;不读 `errorCode`)。
27
+ * 纯数据、零运行时依赖(停驻子代腿与 HTTP 层共用)。
28
+ */
29
+ import type { GateIdentity } from "./checkpoint-identity.js";
30
+ /**
31
+ * 一条**引擎续跑腿**到达边界时能证什么(调用方声明;闭集)。`DriveResumeArgs` 的必填位 —— 同一条驱动路径既续本请求点名的门,也替宿主投递。
32
+ * · `gate_resolved` —— 这条腿 resolve 的就是本请求点名的那道门(`/decide` 引擎臂、`/resume`、`/plan_review`);`checkpointId` = 执行前捕获的身份片段。
33
+ * · `delivered` —— 这条腿 resolve 的是宿主的门(wake;含 `/decide` workflow 车道的 wake 投递腿):本请求点名的那道门由别人结清或仍待决 ⇒ 到任何边界都不铸。
34
+ */
35
+ export type GateCommitment = {
36
+ readonly kind: "gate_resolved";
37
+ readonly checkpointId?: string;
38
+ } | {
39
+ readonly kind: "delivered";
40
+ };
41
+ /** 五个边界(铸点穷尽)。引擎两边界按腿的分类铸;回放不带身份;停驻子代两边界的门恒是本请求点名的那道,身份取执行前捕获的那一份。 */
42
+ export type SettlementCrossing = {
43
+ readonly at: "engine_accepted" | "engine_terminal";
44
+ readonly commitment: GateCommitment;
45
+ } | {
46
+ readonly at: "replay_matched";
47
+ } | {
48
+ readonly at: "agent_revived" | "card_voided";
49
+ readonly gate: GateIdentity;
50
+ };
51
+ /** 体上的结清证据(两键都可缺席;wire 只加)。由调用方原样展开进它自己的体形(各体形保持各自的键集,不并进一张受理形)。 */
52
+ export interface GateSettlementEvidence {
53
+ readonly decisionCommitted?: true;
54
+ readonly checkpointId?: string;
55
+ }
56
+ /** 没有跨过任何边界的回执(`/decide` workflow 车道的宿主新一跑、同一决定重投)带的证据:无。 */
57
+ export declare const NO_SETTLEMENT_EVIDENCE: GateSettlementEvidence;
58
+ /** **唯一铸点**:一次边界跨越 → 体上的证据片段。 */
59
+ export declare function buildSettlementEvidence(crossing: SettlementCrossing): GateSettlementEvidence;
60
+ /**
61
+ * 一张已带证据的回执被拿去答**另一个**决定(停驻赎回席的反向重试:回放首次受理的那一向、不改判)⇒ 证据两键都摘掉 ——
62
+ * 证据说的是当初被结清的那次决定,这一次请求的决定从没被结清。同向重试是同一次请求的回放,体原样(证据随它)。
63
+ * 只摘、不铸:本模块之外没有第二个写这两键的地方。
64
+ */
65
+ export declare function withoutSettlementEvidence<B extends Record<string, unknown>>(body: B): Omit<B, keyof GateSettlementEvidence>;
66
+ /** **唯一读法**:这张回决应答说「门已以此决定结清过」吗(`200` 且体上的位为真)。两个读者同调这一只。 */
67
+ export declare function decisionCommittedOf(out: {
68
+ readonly status: number;
69
+ readonly body: object;
70
+ }): boolean;
71
+ //# sourceMappingURL=gate-settlement.d.ts.map