@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
@@ -24,7 +24,8 @@
24
24
  */
25
25
  import { type CompliancePosture, type EngineNotice, type LockedKey, type McpInjectionDropReason, type McpServerSpec } from "@sema-agent/core";
26
26
  import { type McpIdentityHeadersIssue, type RequestMcpServerSpec } from "./mcp-assertion-header.js";
27
- import { type DeclarationRefusal } from "./request-declaration-refusal.js";
27
+ import { type McpDraft, type PeerDesk, type PeerLease } from "./peer-seat.js";
28
+ import { type DeclarationKeyIssue, type DeclarationRefusal } from "./request-declaration-refusal.js";
28
29
  import type { StoredToolAxesRereadM19 } from "./stored-request-tool-axes-m19.js";
29
30
  /** Bound on caller-supplied servers (anti-DoS; a real local `.mcp.json` has a handful). */
30
31
  export declare const MAX_REQUEST_MCP_SERVERS = 32;
@@ -52,11 +53,11 @@ export declare const MAX_REQUEST_MCP_DROP_NAMES = 32;
52
53
  * 再写一份就是一份手抄投影([ref]:任何仓第一排查点),而两份哪天分岔,窄的那一份会把**合法**的键当成
53
54
  * 拼写错误 400 掉。
54
55
  *
55
- * ## 🔴 两向钉在 core 的 `McpServerSpec` 上(不是手抄)
56
- * 下面六道编译期门:型上多一个键而表没跟 ⇒ 红;表里写一个型上没有的词(改名 / 拼错 / 删键后的残条目)
57
- * ⇒ 也红。于是这三张表是从 core 的型**派生**出来的数据,而不是誰记得去同步的清单。
58
- * (唯一一格不在 core 型上的键 = http 传输的 `assertionHeader`:引擎尚无座,本仓承载 —— 那一支的门钉在
59
- * `mcp-assertion-header.ts` 的 {@link RequestMcpServerSpec} 上,引擎一旦长出同名座,那里的退役门当场红。)
56
+ * ## 🔴 两向钉在请求面条目形上(不是手抄)
57
+ * 条目层:下面四道编译期门两向钉在请求面条目形 {@link RequestMcpServerSpec}(条目层 = 引擎 `McpServerSpec` 的条目层)上:型上多一个键而表没跟 ⇒ 红;表里写一个型上没有的词
58
+ * (改名 / 拼错 / 删键后的残条目)⇒ 也红。传输层:两张键表**就是**两臂处置表的键(`mcp-assertion-header.ts` 的 {@link MCP_STDIO_TRANSPORT_DISPOSITION} /
59
+ * {@link MCP_HTTP_TRANSPORT_DISPOSITION}),处置表对契约包的请求传输形两向钉、对引擎的传输形再钉两道 —— 一张表同时说「收哪些键」与「每个键去哪」。
60
+ * 于是这几张表是从型**派生**出来的数据,而不是谁记得去同步的清单。
60
61
  *
61
62
  * ## 一个回答者,两扇门;读器另算
62
63
  * 「这份声明能不能原样用」只有 {@link requestMcpDeclarationRefusal} 一只回答者:词表外的键(条目层 / 本形 `transport` 层)⇒ `400 request.body_shape` 带 `unknownKeys`,
@@ -66,12 +67,11 @@ export declare const MAX_REQUEST_MCP_DROP_NAMES = 32;
66
67
  * ## 受理键的判官表也钉在这张表上
67
68
  * 逐键判官表 {@link ENTRY_KEY_JUDGES} 是 `{ [K in 本表的键]: 判官 }` 的映射型 —— 本表多一个键而没写判官 ⇒ 编译红,不会出现「这个键写坏了却被悄悄受理」的新键。
68
69
  */
69
- export declare const MCP_SERVER_ENTRY_KEYS: readonly ["allowTools", "contentOrigin", "elicitation", "name", "source", "startupTimeoutMs", "toolAxes", "toolFaces", "transport"];
70
- /** `transport.kind:"stdio"` 的键闭集(两向钉在 core 那一支上)。 */
71
- export declare const STDIO_MCP_TRANSPORT_KEYS: readonly ["args", "command", "env", "kind"];
72
- /** `transport.kind:"http"` 的键闭集(同上)。`assertionHeader` 是本仓承载、引擎尚无座的一格(见 {@link RequestMcpServerSpec}):
73
- * 表两向钉在**请求面的 http 传输型**上 = 引擎的键 ∪ 这一格;引擎一旦长出同名座,`mcp-assertion-header.ts` 的退役门当场红。 */
74
- export declare const HTTP_MCP_TRANSPORT_KEYS: readonly ["assertionHeader", "headers", "kind", "principalHeader", "url"];
70
+ export declare const MCP_SERVER_ENTRY_KEYS: readonly ["allowTools", "contentOrigin", "elicitation", "name", "sessionMetaKey", "source", "startupTimeoutMs", "toolAxes", "toolFaces", "transport"];
71
+ /** `transport.kind:"stdio"` 的键闭集 = stdio 臂处置表的键(`env` 是件,其余进声明)。 */
72
+ export declare const STDIO_MCP_TRANSPORT_KEYS: readonly ("kind" | "command" | "args" | "env")[];
73
+ /** `transport.kind:"http"` 的键闭集 = http 臂处置表的键(`headers` 是件,`assertionHeader` 承载不发送,其余进声明)。 */
74
+ export declare const HTTP_MCP_TRANSPORT_KEYS: readonly ("kind" | "url" | "principalHeader" | "headers" | "assertionHeader")[];
75
75
  /**
76
76
  * Does THIS deployment honor caller-supplied per-request MCP servers? TRUE for a SINGLE-USER deployment
77
77
  * (deployment-shape row `multiTenant` does not hold) — the requester is the super-admin of their own worker, so their `.mcp.json` is
@@ -157,9 +157,10 @@ export declare function requestEntryIdentityIssue(entry: unknown): McpIdentityHe
157
157
  * `startupTimeoutMs` = 引擎根导出的 `mcpStartupTimeoutMs`(坏值抛 `config.mcp_startup_timeout_invalid`,本门把那一句作原因句);`contentOrigin` = `isContentOriginWord`;`source` = `isMcpSourceLabel`;
158
158
  * `name` = `isUsableWireName`;`allowTools` / `transport.args` = {@link isStringArray};`transport.env` / `headers` = 契约包读器 {@link readRequestMcpTransport} 的纯数据记录读法;`elicitation` = {@link isElicitationFlag};
159
159
  * `transport` 的硬字段(`kind` / `command` / `url`)与软字段(`args` / `env` / `headers` / `principalHeader`)= {@link readRequestMcpTransport}(读器投影 transport 的那一只函数,门读它报的 `lost`;读器在契约包,本仓只据 `lost[0]` 拒、据 `lost` 在重放腿逐处点名);
160
- * 读器交回的 http `url` 再问「带不带凭据」= 契约包根的 `findUrlCredential`([ref];拒体另带机读 `field` + `reason`,见 {@link transportUrlCredentialIssue})。
160
+ * 读器交回的 http `url` 再问「带不带凭据」= 契约包根的 `findUrlCredential`([ref];拒体另带机读 `field` + `reason`,见 {@link transportUrlCredentialIssue});
161
+ * http `principalHeader` 再问「这个名有没有别的属主」= 引擎包根的 `headerOwnershipCollision`(见 {@link principalHeaderOwnershipIssue})。
161
162
  * 于是判官受理的条目,读器({@link normalizeServer})交回的就是申报本身(只差判官自己的合法归一:空容器 / `skillScopeEligible:false` 折成缺席、`toolAxes` 取引擎的拷贝)——
162
- * 探测口拨的那一份因此就是提交腿会挂的那一份,不另比一遍(另比就是第二只回答者)。唯一按门分的后果是「声明了 `assertionHeader`」这一个事实(引擎尚无座,
163
+ * 探测口拨的那一份因此就是提交腿会挂的那一份,不另比一遍(另比就是第二只回答者)。唯一按门分的后果是「声明了 `assertionHeader`」这一个事实(本部署不发送断言,
163
164
  * 承载、不发送):新提交照挂 + 运维 info,探测口整条拒 —— 判据是同一只谓词 `specWithholdsMcpAssertionHeader`(开关 `MCP_ASSERTION_HEADER_SENT` 假时「声明了」即「被扣下」),不是第二份判法(见 `readMcpProbeBody`)。
164
165
  *
165
166
  * ## 射程
@@ -184,22 +185,32 @@ export declare function requestMcpDeclarationRefusal(list: unknown): Declaration
184
185
  * 交出去的剔掉 `__proto__` 名成员),server 照挂,运维 warn `mcp_transport_members_dropped` 逐处点名 —— 续跑重放腿不静默丢声明。
185
186
  */
186
187
  export declare function assertRequestMcpEntries(bodyMcp: unknown): void;
188
+ /**
189
+ * `principalHeader` 的**名**有没有别的属主:判官是引擎包根的 `headerOwnershipCollision`(拨号时判同一条的那一只;这里只问身份头这一个名,件头名由引擎在发送时判)——
190
+ * 名(任何大小写)是这一协议的传输自己写的头(MCP:`mcp-session-id` · `mcp-protocol-version` · `content-type` · `accept` · `last-event-id`;A2A 没有),或是引擎的断言头
191
+ * (`connector-assertion`)⇒ 这一台拨号时必被拒,新提交在门上按名拒。原因句只点名**属主的拼写**(引擎 / 协议的词),不回显调用方写的串。
192
+ * 新提交腿上按名 400(MCP 门表的 `transport`、A2A 判官表的 `principalHeader`)。续跑重放腿不 4xx:MCP 的读器不问它 —— 存量体里这一形照挂,由引擎在拨号时拒那一台、兄弟照挂;
193
+ * A2A 的读器问同一张判官表 —— 这一条对端整条不进、`a2a_injection_dropped` 点名原因(引擎物化时同样拒那一台;结局同,原因说在本仓这一行)。
194
+ */
195
+ export declare function principalHeaderOwnershipIssue(principalHeader: string | undefined, protocol: "mcp" | "a2a", path: string): DeclarationKeyIssue | undefined;
187
196
  /**
188
197
  * The ONE predicate for "this MCP server name belongs to the deployment": the scenario baseline (what mounted for
189
198
  * this scenario) ∪ every name the deployment DECLARED (`CenterMcp.reservedNames` — mounted or not, any scenario).
190
199
  * A caller can ADD servers but can never SHADOW or REFILL a deployment-declared one: a center entry that was skipped
191
200
  * (disabled / missing env ref / malformed `toolFaces`) still occupies its name, otherwise "skipped" would silently
192
201
  * mean "free for the caller to remount without the operator's `allowTools` / `toolFaces`" (车CF codex r2 [high]).
193
- * Same posture as `mergeUserSkills`.
202
+ * Same posture as `buildRunSkills`.
194
203
  */
195
204
  export declare function deploymentClosedMcpNames(baseline: McpServerSpec[] | undefined, reservedNames: readonly string[]): ReadonlySet<string>;
196
205
  /**
197
- * Merge GATED per-request MCP servers OVER the deployment baseline (center/config). Names closed by the deployment
198
- * ({@link deploymentClosedMcpNames}) are dropped from `gated`. `reservedNames` has NO default: a security predicate's
199
- * input is never implied — a caller with no deployment declaration passes `[]` and says so. Returns the baseline unchanged
200
- * when nothing is added.
206
+ * Merge GATED per-request MCP drafts OVER the deployment baseline (center/config): **select first, then mint**. Names closed by the deployment
207
+ * ({@link deploymentClosedMcpNames}) are dropped from `drafts` before anything is registered — a draft that loses to the deployment never
208
+ * reaches the desk; each winner is minted once on `desk` under `lease` (`peer-seat.ts` `mintDeclaration`: the reference on its transport, zero
209
+ * piece values). `desk` / `lease` = the process's peer desk and this leg's host lease (`boot/resolve-spec.ts` passes `peerDesk` and `opts.lease`).
210
+ * `reservedNames` has NO default: a security predicate's input is never implied — a caller with no deployment declaration passes `[]` and says so.
211
+ * Returns the baseline unchanged when nothing is added.
201
212
  */
202
- export declare function mergeRequestMcp(baseline: McpServerSpec[] | undefined, gated: McpServerSpec[], reservedNames: readonly string[]): McpServerSpec[] | undefined;
213
+ export declare function mergeRequestMcp(baseline: McpServerSpec[] | undefined, drafts: readonly McpDraft[], reservedNames: readonly string[], desk: Pick<PeerDesk, "declare">, lease: PeerLease): McpServerSpec[] | undefined;
203
214
  /**
204
215
  * The single entry point the spec-builder calls: compute the effective `TaskSpec.mcp` = deployment baseline + the
205
216
  * caller's gated per-request servers. Off the honored lane (or with no caller servers) returns the baseline
@@ -216,7 +227,9 @@ export declare function resolveRequestMcp(baseline: McpServerSpec[] | undefined,
216
227
  mcpServers?: {
217
228
  reservedNames: readonly string[];
218
229
  };
219
- }, logger?: {
230
+ },
231
+ /** 对端座的 desk 与本腿的宿主租约(必填):胜出草稿在它下面铸成带引用的声明 —— 一条腿交给引擎的请求面声明只经它带引用。 */
232
+ desk: Pick<PeerDesk, "declare">, lease: PeerLease, logger?: {
220
233
  info?(m: string, meta?: unknown): void;
221
234
  warn(m: string, meta?: unknown): void;
222
235
  },
package/dist/task-mcp.js CHANGED
@@ -1,19 +1,21 @@
1
- import { mcpInjectionDroppedNotice, mcpStartupTimeoutMs, readToolFaces, resolveComplianceDenies } from "@sema-agent/core";
1
+ import { MCP_ASSERTION_HEADER, headerOwnershipCollision, judgeSessionMetaKey, mcpInjectionDroppedNotice, mcpStartupTimeoutMs, readToolFaces, resolveComplianceDenies } from "@sema-agent/core";
2
2
  import { MCP_CONTENT_ORIGINS, describeBadContentOrigin, describeNotStringArray, describeValueShape, findUrlCredential, isContentOriginWord, isStringArray, isUsableWireName, readRequestMcpTransport, stripControlAndBidi, } from "@sema-agent/settings-schema";
3
3
  import { HttpError } from "./security.js";
4
4
  import { GOVERNANCE_HTTP_STATUS } from "./governance-http-status.js";
5
5
  import { DEPLOYMENT_SHAPE_PREDICATES } from "./deployment-shape.js";
6
6
  import { MCP_SOURCE_LABEL_MAX, isMcpSourceLabel, readMcpSourceLabel } from "./mcp-source-label.js";
7
- import { describeMcpAssertionHeaderNotAString, describeMcpIdentityHeadersConflict, mcpIdentityHeadersIssue, noteMcpAssertionHeaderIgnored, noteMcpAssertionHeaderNotSent, specWithholdsMcpAssertionHeader, toCoreMcpServerSpec } from "./mcp-assertion-header.js";
7
+ import { MCP_HTTP_TRANSPORT_DISPOSITION, MCP_STDIO_TRANSPORT_DISPOSITION, describeMcpAssertionHeaderNotAString, describeMcpIdentityHeadersConflict, mcpIdentityHeadersIssue, noteMcpAssertionHeaderIgnored, noteMcpAssertionHeaderNotSent, specWithholdsMcpAssertionHeader, toMcpDraft, } from "./mcp-assertion-header.js";
8
+ import { declarationKeys } from "./plain-string-record.js";
9
+ import { mintDeclaration, peerSentence } from "./peer-seat.js";
8
10
  import { MAX_KEY_ECHO, echoKeyName, keyClosureClause, scanKeyClosure } from "./request-key-closure.js";
9
11
  import { readRequestToolAxes } from "./request-tool-axes.js";
10
12
  import { urlCredentialHeaderExit, urlCredentialRefusalReason } from "./url-credential-refusal.js";
11
13
  import { MALFORMED_NAME, declarationFieldRefusal, declarationNameIssue, declaredName, judgeDeclarationKeys, requestDeclarationRefusal, throwDeclarationRefusal, } from "./request-declaration-refusal.js";
12
14
  export const MAX_REQUEST_MCP_SERVERS = 32;
13
15
  export const MAX_REQUEST_MCP_DROP_NAMES = MAX_REQUEST_MCP_SERVERS;
14
- export const MCP_SERVER_ENTRY_KEYS = ["allowTools", "contentOrigin", "elicitation", "name", "source", "startupTimeoutMs", "toolAxes", "toolFaces", "transport"];
15
- export const STDIO_MCP_TRANSPORT_KEYS = ["args", "command", "env", "kind"];
16
- export const HTTP_MCP_TRANSPORT_KEYS = ["assertionHeader", "headers", "kind", "principalHeader", "url"];
16
+ export const MCP_SERVER_ENTRY_KEYS = ["allowTools", "contentOrigin", "elicitation", "name", "sessionMetaKey", "source", "startupTimeoutMs", "toolAxes", "toolFaces", "transport"];
17
+ export const STDIO_MCP_TRANSPORT_KEYS = declarationKeys(MCP_STDIO_TRANSPORT_DISPOSITION);
18
+ export const HTTP_MCP_TRANSPORT_KEYS = declarationKeys(MCP_HTTP_TRANSPORT_DISPOSITION);
17
19
  export function mcpInjectionHonored(config) {
18
20
  if (config.lockedConfigKeys?.includes("mcp") === true)
19
21
  return false;
@@ -52,7 +54,7 @@ function normalizeServer(e, noteBadOrigin, storedToolAxes) {
52
54
  const again = readRequestToolAxes(storedToolAxes.mcp(o.toolAxes), "mcp");
53
55
  if (again.ok) {
54
56
  judged = again;
55
- toolAxesReread = stripControlAndBidi(axes.problem);
57
+ toolAxesReread = peerSentence(stripControlAndBidi(axes.problem));
56
58
  }
57
59
  }
58
60
  if (!judged.ok)
@@ -73,6 +75,11 @@ function normalizeServer(e, noteBadOrigin, storedToolAxes) {
73
75
  return bad("toolFaces");
74
76
  if (faces.faces !== undefined)
75
77
  spec.toolFaces = faces.faces;
78
+ const metaKey = judgeSessionMetaKey(o.sessionMetaKey);
79
+ if ("problem" in metaKey)
80
+ return bad("sessionMetaKey");
81
+ if (metaKey.key !== undefined)
82
+ spec.sessionMetaKey = metaKey.key;
76
83
  return { ok: true, spec, ...(toolAxesReread !== undefined ? { toolAxesReread } : {}), transportLost: read.lost };
77
84
  }
78
85
  export function validateRequestMcp(raw) {
@@ -92,7 +99,7 @@ function validateRequestMcpEntries(raw, storedToolAxes) {
92
99
  if (v.ok) {
93
100
  ok.push(v.spec);
94
101
  if (v.toolAxesReread !== undefined)
95
- toolAxesReread.push({ server: v.spec.name, problem: v.toolAxesReread });
102
+ toolAxesReread.push({ server: v.spec.name, sentence: v.toolAxesReread });
96
103
  if (v.transportLost.length > 0)
97
104
  transportLost.push({ server: v.spec.name, lost: v.transportLost.map((i) => i.path) });
98
105
  }
@@ -164,6 +171,17 @@ function transportUrlCredentialIssue(url) {
164
171
  return undefined;
165
172
  return { path: "transport.url", reason: urlCredentialRefusalReason(found, urlCredentialHeaderExit("transport.headers")), credential: found };
166
173
  }
174
+ export function principalHeaderOwnershipIssue(principalHeader, protocol, path) {
175
+ if (principalHeader === undefined)
176
+ return undefined;
177
+ const collision = headerOwnershipCollision({ principalHeader }, protocol);
178
+ if (collision === undefined)
179
+ return undefined;
180
+ return {
181
+ path,
182
+ reason: `names the header "${collision.first}" (header names are case-insensitive), which is not the principal header's to own — it is ${protocol === "mcp" && collision.first !== MCP_ASSERTION_HEADER ? "a header the MCP transport writes itself" : "the engine's connector assertion header"}; name the principal header something of its own`,
183
+ };
184
+ }
167
185
  const ENTRY_KEY_JUDGES = {
168
186
  allowTools: (o) => o.allowTools === undefined || isStringArray(o.allowTools)
169
187
  ? undefined
@@ -177,6 +195,10 @@ const ENTRY_KEY_JUDGES = {
177
195
  },
178
196
  elicitation: (o) => (o.elicitation === undefined || isElicitationFlag(o.elicitation) ? undefined : { path: "elicitation", reason: `must be a boolean — got ${describeValueShape(o.elicitation)}` }),
179
197
  name: declarationNameIssue,
198
+ sessionMetaKey: (o) => {
199
+ const read = judgeSessionMetaKey(o.sessionMetaKey);
200
+ return "problem" in read ? { path: "sessionMetaKey", reason: peerSentence(read.problem) } : undefined;
201
+ },
180
202
  source: (o) => o.source === undefined || isMcpSourceLabel(o.source)
181
203
  ? undefined
182
204
  : { path: "source", reason: `must be a non-empty string of at most ${MCP_SOURCE_LABEL_MAX} characters (a display label) — got ${describeValueShape(o.source)}` },
@@ -190,16 +212,16 @@ const ENTRY_KEY_JUDGES = {
190
212
  catch (e) {
191
213
  if (e.code !== "config.mcp_startup_timeout_invalid")
192
214
  throw e;
193
- return { path: "startupTimeoutMs", reason: e instanceof Error ? e.message : String(e) };
215
+ return { path: "startupTimeoutMs", reason: peerSentence(e instanceof Error ? e.message : String(e)) };
194
216
  }
195
217
  },
196
218
  toolAxes: (o) => {
197
219
  const read = readRequestToolAxes(o.toolAxes, "mcp");
198
- return read.ok ? undefined : { path: "toolAxes", reason: read.problem };
220
+ return read.ok ? undefined : { path: "toolAxes", reason: peerSentence(read.problem) };
199
221
  },
200
222
  toolFaces: (o) => {
201
223
  const read = readToolFaces(o.toolFaces);
202
- return read.ok ? undefined : { path: "toolFaces", reason: read.problem };
224
+ return read.ok ? undefined : { path: "toolFaces", reason: peerSentence(read.problem) };
203
225
  },
204
226
  transport: (o) => {
205
227
  const read = readRequestMcpTransport(o.transport);
@@ -207,7 +229,9 @@ const ENTRY_KEY_JUDGES = {
207
229
  return read.issue;
208
230
  if (read.lost[0] !== undefined)
209
231
  return read.lost[0];
210
- return read.transport.kind === "http" ? transportUrlCredentialIssue(read.transport.url) : undefined;
232
+ if (read.transport.kind !== "http")
233
+ return undefined;
234
+ return transportUrlCredentialIssue(read.transport.url) ?? principalHeaderOwnershipIssue(read.transport.principalHeader, "mcp", "transport.principalHeader");
211
235
  },
212
236
  };
213
237
  function requestEntryRefusal(entry, at) {
@@ -229,27 +253,27 @@ function requestEntryRefusal(entry, at) {
229
253
  export function deploymentClosedMcpNames(baseline, reservedNames) {
230
254
  return new Set([...(baseline ?? []).map((s) => s.name), ...reservedNames]);
231
255
  }
232
- export function mergeRequestMcp(baseline, gated, reservedNames) {
233
- if (gated.length === 0)
256
+ export function mergeRequestMcp(baseline, drafts, reservedNames, desk, lease) {
257
+ if (drafts.length === 0)
234
258
  return baseline;
235
259
  const closed = deploymentClosedMcpNames(baseline, reservedNames);
236
- const add = gated.filter((s) => !closed.has(s.name));
237
- return add.length === 0 ? baseline : [...(baseline ?? []), ...add];
260
+ const winners = drafts.filter((d) => !closed.has(d.spec.name));
261
+ return winners.length === 0 ? baseline : [...(baseline ?? []), ...winners.map((d) => mintDeclaration(desk, d, lease))];
238
262
  }
239
- export function resolveRequestMcp(baseline, bodyMcp, config, logger, notice, storedToolAxes) {
263
+ export function resolveRequestMcp(baseline, bodyMcp, config, desk, lease, logger, notice, storedToolAxes) {
240
264
  if (bodyMcp === undefined || bodyMcp === null)
241
265
  return baseline;
242
- const { mcp, drops } = classifyRequestMcp(baseline, bodyMcp, config, logger, storedToolAxes);
266
+ const { mcp, drops } = classifyRequestMcp(baseline, bodyMcp, config, desk, lease, logger, storedToolAxes);
243
267
  mintInjectionDropNotices(drops, notice);
244
268
  return mcp;
245
269
  }
246
- function classifyRequestMcp(baseline, bodyMcp, config, logger, storedToolAxes) {
270
+ function classifyRequestMcp(baseline, bodyMcp, config, desk, lease, logger, storedToolAxes) {
247
271
  if (bodyMcp !== undefined && bodyMcp !== null && !Array.isArray(bodyMcp)) {
248
272
  logger?.warn("mcp_injection_ignored", { reason: "mcpServers not an array" });
249
273
  return { mcp: baseline, drops: [] };
250
274
  }
251
275
  const entries = Array.isArray(bodyMcp) ? bodyMcp : [];
252
- const outcome = mcpInjectionHonored(config) ? classifyHonored(baseline, entries, config, logger, storedToolAxes) : classifyGateClosed(baseline, entries);
276
+ const outcome = mcpInjectionHonored(config) ? classifyHonored(baseline, entries, config, desk, lease, logger, storedToolAxes) : classifyGateClosed(baseline, entries);
253
277
  if (outcome.drops.length > 0)
254
278
  logger?.warn("mcp_injection_dropped", { dropped: outcome.drops, droppedTotal: outcome.droppedTotal });
255
279
  return { mcp: outcome.mcp, drops: outcome.drops };
@@ -261,7 +285,7 @@ function classifyGateClosed(baseline, entries) {
261
285
  droppedTotal: entries.length,
262
286
  };
263
287
  }
264
- function classifyHonored(baseline, entries, config, logger, storedToolAxes) {
288
+ function classifyHonored(baseline, entries, config, desk, lease, logger, storedToolAxes) {
265
289
  const v = validateRequestMcpEntries(entries, storedToolAxes);
266
290
  if (v.badContentOrigin.length > 0) {
267
291
  logger?.warn("mcp_content_class_dropped", {
@@ -288,7 +312,7 @@ function classifyHonored(baseline, entries, config, logger, storedToolAxes) {
288
312
  logger?.warn("mcp_tool_axes_read_as_before_upgrade", {
289
313
  server: r.server,
290
314
  migration: "M19",
291
- problem: r.problem,
315
+ problem: r.sentence,
292
316
  note: "this replayed request was stored before the upgrade and its toolAxes declaration does not pass the engine's reader; while one-time migration M19 is open it is read the way the pre-upgrade server read it (members that did not fit are dropped, a declaration that is not an object counts as none) and the server stays mounted — once the window closes the same request leaves this server unmounted",
293
317
  });
294
318
  }
@@ -305,7 +329,7 @@ function classifyHonored(baseline, entries, config, logger, storedToolAxes) {
305
329
  });
306
330
  }
307
331
  return {
308
- mcp: mergeRequestMcp(baseline, v.ok.map(toCoreMcpServerSpec), reservedNames),
332
+ mcp: mergeRequestMcp(baseline, v.ok.map(toMcpDraft), reservedNames, desk, lease),
309
333
  drops: [...v.dropped, ...blocked.map((server) => ({ server, reason: "name_reserved_by_deployment" }))],
310
334
  droppedTotal: v.droppedTotal + blocked.length,
311
335
  };
@@ -73,7 +73,7 @@ export declare const PERMISSION_MODE_WORDS: string;
73
73
  * through verbatim (`dontAsk` included: folding the auto-deny mode to `default`, the asking mode, would turn a refusal
74
74
  * into a card). An UNKNOWN string coerces to `default` (fail-safe: the most-asking mode).
75
75
  * [ref]-②: fresh submits can no longer REACH the unknown-word arm — the HTTP gate 400s a non-member value
76
- * (admission.ts, sibling of promptProfile) and the wire type is a closed enum. This lenient fold stays for the one
76
+ * (admission.ts, sibling of toolMaterializeStrategy) and the wire type is a closed enum. This lenient fold stays for the one
77
77
  * population the gate deliberately does not cover: RESUME replay of a persisted pre-gate body (outputStyle/model
78
78
  * two-layer posture — bricking a resume over a value that was legal at submit time is worse than the quiet fold).
79
79
  */
@@ -477,7 +477,7 @@ export declare const REQUEST_SETTINGS_REFUSAL_PREFIX = "request settings cannot
477
477
  /**
478
478
  * Build the per-request permission ToolPolicy from the parsed settings: the two **rule lists** (`deny` / `ask`) through ONE compiler
479
479
  * ({@link createRuleListPolicy} → core `createPermissionRulePolicy`) — a `deny` rule hard-blocks, an `ask` rule forces an approval
480
- * gate, `deny` > `ask` on overlap. 两表的 **covering 语义同源**([ref]):底下是 core 的同一只 `namespacedRuleNameCovers`,本仓不持第二套 glob。
480
+ * gate, `deny` > `ask` on overlap. 两表的 **covering 语义同源**([ref]):底下是 core 的同一只 `ruleNameCovers`,本仓不持第二套 glob。
481
481
  * The `allow` bucket is not here: a policy says deny / ask only (core 7.42.0); the list rides into `TaskSpec.allowedTools` at
482
482
  * {@link applyTaskSettings}, the one write point.
483
483
  *
@@ -10,8 +10,9 @@ import { type AskRowEndingCause, type SettleCause, type SettleOutcome } from "./
10
10
  import type { ParkCommitWatch } from "./plugins/park-commit-watch.js";
11
11
  import { type GovernanceAskMarks } from "./governance-ask-marks.js";
12
12
  import { type GovernancePosture } from "./runtime-governance.js";
13
- import { type GrantBinding, type StandingAnswerCall } from "./standing-answer-grant.js";
13
+ import { type GrantBinding, type StandingAnswerCard } from "./standing-answer-grant.js";
14
14
  import { type ApprovalExemptionStore } from "./plugins/approval-exemption-store.js";
15
+ import { type ExtensionRequesterFace } from "./trace/extension-call-face.js";
15
16
  /** {@link ToolApprovalCoordinator.adoptOpenAsks} 的读数(装配点进启动日志)。 */
16
17
  interface OpenAskAdoption {
17
18
  /** 本次接管进活集合的问数。 */
@@ -213,11 +214,14 @@ export declare function carriesDeploymentListQuestion(deploymentListAsks: boolea
213
214
  * 本仓**策略层自己交出去的一问**的 {@link StandingAnswerFacts}。读者两个:部署名单门(`approval.ts`,`deploymentListQuestion: true`)与 workflow 问句门
214
215
  * (`task-settings.ts`,`false`)—— 它们在交出这一问之前先读按工具名记的耐久豁免店(「本会话不再询问」在本层兑现,继承链上的同一问才够得着;审批席依约不退役
215
216
  * 继承问),而**能不能读**与审批席 / 回决写侧是同一只判据({@link standingAnswerDurablyRecordable}):记得下的才代答,记不下的(带授予绑定的问 ……)不代答,
216
- * 存量行也不例外 —— 一事一读。事实取自**这一问本身**:类集经 core 包根唯一的类派生 `classifyAsk`({@link NO_GATE_FACTS});`requiresRealApproval` /
217
- * `mandated` 读这一问自报的成员;治理位恒假(本层不是治理层);授予绑定读**调用**(`callGrantBindingOf` —— 本层手里还没有引擎铸的卡,core 7.51.0 不在
218
- * `ToolCallRequest` 上盖绑定词,[ref] 载体问题见 `standing-answer-grant.ts` 头注)。
217
+ * 存量行也不例外 —— 一事一读。事实取自**这一问本身**:类集经 core 包根唯一的类派生 `classifyAsk`({@link noGateFacts},调用坐标取 `req`);`requiresRealApproval` /
218
+ * `mandated` 读这一问自报的成员;治理位恒假(本层不是治理层);授予绑定读**请求上引擎的词**(core 7.59.0 起 Runner 交给策略的 `ToolCallRequest`
219
+ * 带与卡同一组词;宿主不经 Runner 自建的请求两者都不说 ⇒ `unreadable` ⇒ 不读店,fail-closed —— 见 `standing-answer-grant.ts` 头注)。
219
220
  */
220
- export declare function policyAskStandingAnswerFacts(req: StandingAnswerCall, ask: Extract<PermissionResult, {
221
+ export declare function policyAskStandingAnswerFacts(req: StandingAnswerCard & {
222
+ readonly toolCallId: string;
223
+ readonly toolName: string;
224
+ }, ask: Extract<PermissionResult, {
221
225
  action: "ask";
222
226
  }>, deploymentListQuestion: boolean): StandingAnswerFacts;
223
227
  /**
@@ -239,8 +243,8 @@ export interface StandingAnswerFacts {
239
243
  readonly mandated: boolean;
240
244
  /** 这一问的应答**授不授予**这一次调用之外的东西、授予的话绑定是什么(批准引擎的 `Skill` = 激活技能 + 本 run 内清单免问)。
241
245
  * `bound` ⇒ 站着的答案按「工具 + 绑定」记、只代答绑定相同的问(core 7.43.0 [ref] / [ref]:活的问与停驻行同带 `boundGrantDigest`);
242
- * `unreadable` ⇒ 不可记、不被代答(fail-closed)。读者一律经 `standing-answer-grant.ts` 折这一位:载体由问的属主定(`questionGrantBindingOf`)——
243
- * 引擎铸的问读卡上引擎的词(`grantBindingOf`),部署名单自己的问与策略层的调用读 `callGrantBindingOf`([ref])。 */
246
+ * `unreadable` ⇒ 不可记、不被代答(fail-closed)。读者一律经 `standing-answer-grant.ts` 的唯一读口 `grantBindingOf` 折这一位:只读载体上引擎的词,
247
+ * 载体不论(卡、停驻行、队列行、策略层的请求)。 */
244
248
  readonly grantBinding: GrantBinding;
245
249
  /** 引擎盖在这一问上的**类集**(`AskRequest.classes` / 停驻行孪生座;经 core 的屏 `screenAskClassMembers` 读)。`undefined` = 卡上没有类
246
250
  * (老引擎 / 不盖类的站)或屏拒 —— 读不出的类不是「无类」。 */
@@ -471,6 +475,14 @@ export interface ToolApprovalFrame {
471
475
  * ask」,治理策略产的与普通策略产的在这一个词上同形 ⇒ 顶替不了(逐字论证见 `governance-ask-marks.ts` 顶注)。
472
476
  */
473
477
  origin?: string;
478
+ /**
479
+ * **ADDITIVE,`"tool_approval"` only** —— 这一问的调用是**一张扩展注册自己发起**的(`$.tool.call`,core 7.56.0 `AskRequest.requestedBy`):
480
+ * `{ kind: "extension", name }`,卡上「扩展 X 想跑 …」那一行的出处。模型消息带来的调用上缺席。display 事实,不是裁决输入。
481
+ * 读法与 `tool_start` / `tool_end` 上的同名键是同一只(`trace/extension-call-face.ts`:注册名过注册方文本的出口纪律,与 `wiring_manifest.mods[]` 的 `name`
482
+ * 逐字节同;形不对 ⇒ 丢 + 计一次)。呈卡腿上它是 `approval_request` 顶层的卡外座之一(`APPROVAL_REQUEST_LIVE_FACT_KEYS`,同一只帧对象的值);
483
+ * ⚠️ `card_json` 卡面不带本键(卡的键集属主在契约包),断连重放帧上它恒缺席(与其余卡外座同)。
484
+ */
485
+ requestedBy?: ExtensionRequesterFace;
474
486
  /**
475
487
  * **[ref] ②(core 7.37.0 [ref];ADDITIVE,`"tool_approval"` only)** —— 这一问的**类**:`classes` 的首员(core `ASK_CLASSES` 序),
476
488
  * `MODE_ARBITRATION` 的行键(权限模式读的就是它 / 它们)。引擎在每一个铸 `origin` 的站盖章(策略自报的值在盖章处被替换),本仓经
@@ -496,7 +508,7 @@ export interface ToolApprovalFrame {
496
508
  /** [ref](core 7.51.0 [ref],**ADDITIVE**,`"tool_approval"` only):这张卡为什么没有 {@link boundGrantDigest} —— 引擎铸的每张卡恰带两者之一:
497
509
  * `not_bindable`(这一问不是引擎 `Skill` 工具的激活,不绑任何授予 ⇒ 应答可按工具记)/ `unreadable`(绑着却读不出;活卡上引擎恒给摘要,这一词
498
510
  * 只出现在读存量行的面上)。闭集经 `readClosedWordSeat(req, "boundGrantAbsence")` 窄读(筛 = core `BOUND_GRANT_ABSENCES`),集外 ⇒ 缺席 + 计一次。
499
- * 与停驻行 / 运维队列行 / inbox 行的同名位同值;站着的答案的授予绑定(引擎铸的问那一家;部署名单自己的问读调用口)读它(`standing-answer-grant.ts` `grantBindingOf`)。display。 */
511
+ * 与停驻行 / 运维队列行 / inbox 行的同名位同值;站着的答案的授予绑定读它(`standing-answer-grant.ts` `grantBindingOf`,问是谁的都同读)。display。 */
500
512
  boundGrantAbsence?: BoundGrantAbsence;
501
513
  /** core 7.43.0 [ref]:分类器没给判词的成因词(`error` / `timeout` / `parse_error`;只在类 `classifier_no_ruling` 的卡上)。闭集经 core 的
502
514
  * `readClosedWordSeat(req, "classifierNoRuling")` 窄读(筛 = core `isAutoModeFallbackCause`),集外 ⇒ 缺席 + 计一次。display:本仓不按它裁决。 */
@@ -1027,6 +1039,13 @@ export type UnattendedAskOutcome = Extract<AskOutcome, "unavailable"> | Unattend
1027
1039
  /** [ref] 候裁 16:耐久会话授权店(`src/plugins/approval-exemption-store.ts` 的同一只店):审批席读它(引擎的问),活卡上部署名单那一问的
1028
1040
  * 「本会话放行」写它(名单门在出问之前读同一行)。 */
1029
1041
  export type SessionGrantStore = Pick<ApprovalExemptionStore, "has" | "grant">;
1042
+ /** redactDeep + size-bound the UNTRUSTED tool args before they are streamed. Over the cap / unserializable ⇒
1043
+ * undefined (frame rides argsOmitted — see MAX_APPROVAL_ARGS_BYTES). */
1044
+ /**
1045
+ * 给审批人看的一份入参 —— 卡上 `args` 的**同一种读法**({@link boundArgs}:脱敏 + 字节帽;超帽 / 序列化不出 ⇒ `undefined`)。
1046
+ * 读者:卡帧 / 卡面(经 `boundArgs` 本体)与续跑拒体的 `acceptableForm`(core 7.56.0 [ref]:编辑复判只收改写后的那一形,人在决定口看到它、批准它)。
1047
+ */
1048
+ export declare function approverFacingArgs(args: unknown): unknown;
1030
1049
  /**
1031
1050
  * [ref] 车3 刀 3b —— 「流内审批协议到底上不上场」的**单一谓词**(设计稿 §2.3 注入 / §2.4 能力面 /
1032
1051
  * §8.4 park 设施自检,三处同一份判据)。
@@ -1591,7 +1610,7 @@ export declare class ToolApprovalCoordinator {
1591
1610
  * ⇒ 截一个字就恒 `rule_not_offered`,所以**绝不截**;真超尺(= core 放宽了自己的 512 或本仓与 core
1592
1611
  * 版本不同步)⇒ **整条车道撤回**(返回 `undefined`),与本文件既有的治理撤回 / 漂移撤回逐字同形:
1593
1612
  * 少渲一格是安全方向,渲一格按不动的才是 wire 谎言。今天这条臂不可达(每个座都是解析器产物),
1594
- * 它守的是版本不同步那一形 —— 与 `isSupportedRuleMatch` 的运行期 `default` 同族。
1613
+ * 它守的是版本不同步那一形 —— 与 `isCommandRuleMatch` 的运行期 `default` 同族。
1595
1614
  *
1596
1615
  * 位置在**素材铸点**(与基数帽同一处):两族帧 + `card_json` + 回决口的等式左边从此拿的是**同一份**
1597
1616
  * 已截素材,而不是两处各截一遍。
@@ -1601,7 +1620,22 @@ export declare class ToolApprovalCoordinator {
1601
1620
  private static ruleOfferTextsOverSize;
1602
1621
  /** codex r1 [high]([ref]):offer 的自称座与 `parseRuleText(rule, "allow")` 派生的三元组逐字一致?(理由见调用点) */
1603
1622
  private static ruleOfferDerivedFactsAgree;
1623
+ /**
1624
+ * 一只 single 候选的工具与它的匹配词对得上吗(卡上的 single 只有两族):命令形(exact / prefix / wildcard / pattern)⇒ 规则是 shell 的;
1625
+ * 整工具(`tool`,core 7.57.0:没有规则词替它说话的工具的卡铸「这只工具不再问」)⇒ 工具必须是一键整工具同意落得下的那一族
1626
+ * ({@link cardTakesWholeToolConsent}:没有自己的规则文法、不在 shell 面上 —— 一张 shell 卡上的整工具候选 = 一键放行每一个程序,引擎不铸,兑付时也兑不动)。
1627
+ * 其余匹配词(路径 / 名字 / 主机)不是本服务卡面的 single 候选 ⇒ 丢(那些卡今天没有本服务的规则车道)。穷举 `switch`:core 给匹配词加员时这里编译红。
1628
+ */
1629
+ private static singleOfferToolAgrees;
1604
1630
  private static boundRuleOfferTexts;
1631
+ /**
1632
+ * 这张卡的**绑定值**(`prepareCardApproval` 的 `command`,也是耐久行的 `rule_command`)= 引擎为这张卡铸的候选所说的那一形(core 只按卡的规则形铸候选):
1633
+ * · 候选全是整工具候选(`match: "tool"`,core 7.57.0;至少一只)⇒ `""` —— 整只工具不绑任何值;core 恰在 `command` 是 `""` 时为这类工具铸卡;
1634
+ * · 其余(命令形候选,或读者把候选丢光了)⇒ 被裁决的命令原字节 `args.command`(非空串;读不出 ⇒ 没有素材)。空候选表不说「整工具」:
1635
+ * 它什么形都没说,绑定值回到调用自己的命令(编辑面兑的就是它)。
1636
+ * 两形混在一张卡上 = 不是 core 铸得出的卡 ⇒ 没有素材(整条车道撤回,与本文件其余撤回臂同形)。`req.args` 窄读,禁裸 as-cast。
1637
+ */
1638
+ private static cardBindingOf;
1605
1639
  private buildRuleLaneMaterial;
1606
1640
  /** [ref]([ref]/[ref] 双属主裁定):把某 wire run 的**流内未决 ask** 立即转 durable park——断连支专用。
1607
1641
  * 匹配键=`ctxTaskId`(wire run id)。该字段的顶注说它「不是清扫判据」——那是因为清扫要判**连接**的
@@ -3,6 +3,7 @@ import { heldAskOf, liveSettledBy, readDecidedBy, retiredDecisionKeyFacts, DECID
3
3
  import { AsyncLocalStorage } from "node:async_hooks";
4
4
  import { uuidv7, classifyAsk, foldArbitrationRow, policyCannotAnswer, screenAskClassMembers, MAX_RULE_TEXT_CHARS, READ_ROOT_CANDIDATE_DIR_MAX, parseRuleText, pathConfinableWriteToolNames } from "@sema-agent/core";
5
5
  import { precheckCardRuleText } from "./rules-consent.js";
6
+ import { cardTakesWholeToolConsent } from "./permission-rule-vocab.js";
6
7
  import { redactDeep, redactSecrets, redactSecretsWithArms } from "./trace/redact.js";
7
8
  import { seatText } from "./trace/exit-seat.js";
8
9
  import { hasBidiControls } from "./text-bidi.js";
@@ -18,10 +19,12 @@ import { APPROVAL_ENDING_CAUSES, SETTLE_OUTCOME, causeOfAskRow } from "./approva
18
19
  import { settleByPolicy } from "./approval-policy-settlement.js";
19
20
  import { governanceAskMarksFor, runWithGovernanceAskScope } from "./governance-ask-marks.js";
20
21
  import { governanceOriginOf } from "./runtime-governance.js";
21
- import { callGrantBindingOf, questionGrantBindingOf } from "./standing-answer-grant.js";
22
+ import { grantBindingOf } from "./standing-answer-grant.js";
22
23
  import { operatorAsk } from "./operator-ask.js";
23
24
  import { APPROVAL_EXEMPTED_EVENT } from "./plugins/approval-exemption-store.js";
24
25
  import { refusalBody } from "./http/refusal-body.js";
26
+ import { withoutSettlementEvidence } from "./gate-settlement.js";
27
+ import { extensionRequesterOf } from "./trace/extension-call-face.js";
25
28
  const defaultLogger = createLogger();
26
29
  const MAX_APPROVAL_ARGS_BYTES = 16384;
27
30
  const DEFAULT_APPROVAL_TTL_MS = 5 * 60_000;
@@ -118,13 +121,15 @@ function askStandingAnswerFacts(req, governanceForced, deploymentListAsks) {
118
121
  governanceForced,
119
122
  requiresRealApproval: req.requiresRealApproval === true,
120
123
  mandated: req.mandated === true,
121
- grantBinding: questionGrantBindingOf(req, deploymentListQuestion),
124
+ grantBinding: grantBindingOf(req),
122
125
  classes,
123
126
  deploymentListQuestion,
124
127
  };
125
128
  }
126
- const NO_GATE_FACTS = { contentQuestion: false, markedUnresolvable: false, org: undefined, tightened: undefined, mandateMarks: {} };
127
- const DEPLOYMENT_LIST_ASK_CLASSES = new Set(classifyAsk(operatorAsk("APPROVAL_REQUIRE", ""), NO_GATE_FACTS).classes);
129
+ function noGateFacts(call) {
130
+ return { call, contentQuestion: false, markedUnresolvable: false, org: undefined, tightened: undefined, mandateMarks: {} };
131
+ }
132
+ const DEPLOYMENT_LIST_ASK_CLASSES = new Set(classifyAsk(operatorAsk("APPROVAL_REQUIRE", ""), noGateFacts({ toolCallId: "", toolName: "" })).classes);
128
133
  export function carriesDeploymentListQuestion(deploymentListAsks, classes) {
129
134
  return deploymentListAsks && classes !== undefined && classes.some((c) => DEPLOYMENT_LIST_ASK_CLASSES.has(c));
130
135
  }
@@ -133,8 +138,8 @@ export function policyAskStandingAnswerFacts(req, ask, deploymentListQuestion) {
133
138
  governanceForced: false,
134
139
  requiresRealApproval: ask.requiresRealApproval === true,
135
140
  mandated: ask.mandated === true,
136
- grantBinding: callGrantBindingOf(req),
137
- classes: [...classifyAsk(ask, NO_GATE_FACTS).classes],
141
+ grantBinding: grantBindingOf(req),
142
+ classes: [...classifyAsk(ask, noGateFacts({ toolCallId: req.toolCallId, toolName: req.toolName })).classes],
138
143
  deploymentListQuestion,
139
144
  };
140
145
  }
@@ -170,7 +175,7 @@ export function parkRowStandingAnswerFacts(stored, gate, governance, deploymentL
170
175
  governanceForced: governanceOriginOf({ riskDescriptor }, governance) === true,
171
176
  requiresRealApproval: readParkRowTrueSeat(pending, "requiresRealApproval"),
172
177
  mandated: readParkRowTrueSeat(pending, "mandated") || mandatedFromSeat(readRuleOffersAbsenceSeat(pending)),
173
- grantBinding: questionGrantBindingOf(pending, deploymentListQuestion),
178
+ grantBinding: grantBindingOf(pending),
174
179
  classes,
175
180
  deploymentListQuestion,
176
181
  };
@@ -335,6 +340,9 @@ function unattendedRefusalReason(site) {
335
340
  function isChildAsk(req) {
336
341
  return req.fromSubagent === true;
337
342
  }
343
+ export function approverFacingArgs(args) {
344
+ return boundArgs(args).args;
345
+ }
338
346
  function boundArgs(args) {
339
347
  const redacted = redactDeep(args);
340
348
  let hasBidi = false;
@@ -745,7 +753,7 @@ export class ToolApprovalCoordinator {
745
753
  };
746
754
  if (offer.kind === "single") {
747
755
  const d = derive(offer.rule);
748
- return d !== undefined && d.rule === offer.rule && d.tool === "Bash" && d.match === offer.match && d.command === offer.command;
756
+ return d !== undefined && d.rule === offer.rule && d.match === offer.match && d.command === offer.command && ToolApprovalCoordinator.singleOfferToolAgrees(d.match, d.tool);
749
757
  }
750
758
  return offer.rules.every((m) => {
751
759
  const d = derive(m.rule);
@@ -761,6 +769,29 @@ export class ToolApprovalCoordinator {
761
769
  }
762
770
  });
763
771
  }
772
+ static singleOfferToolAgrees(match, tool) {
773
+ switch (match) {
774
+ case "exact":
775
+ case "prefix":
776
+ case "wildcard":
777
+ case "pattern":
778
+ return tool === "Bash";
779
+ case "tool":
780
+ return cardTakesWholeToolConsent(tool);
781
+ case "subpath":
782
+ case "path":
783
+ case "name":
784
+ case "name_prefix":
785
+ case "host":
786
+ case "subdomain":
787
+ return false;
788
+ default: {
789
+ const unknown = match;
790
+ void unknown;
791
+ return false;
792
+ }
793
+ }
794
+ }
764
795
  static boundRuleOfferTexts(offers) {
765
796
  const over = (s) => s.length > MAX_RULE_TEXT_CHARS;
766
797
  const clipSegment = (s) => (over(s) ? s.slice(0, MAX_RULE_TEXT_CHARS) : s);
@@ -807,6 +838,17 @@ export class ToolApprovalCoordinator {
807
838
  }
808
839
  return out;
809
840
  }
841
+ static cardBindingOf(args, offers) {
842
+ const isWholeTool = (o) => o.kind === "single" && o.match === "tool";
843
+ if (offers.length > 0 && offers.every(isWholeTool))
844
+ return "";
845
+ if (offers.some(isWholeTool))
846
+ return undefined;
847
+ if (args === null || typeof args !== "object" || Array.isArray(args))
848
+ return undefined;
849
+ const command = args.command;
850
+ return typeof command === "string" && command !== "" ? command : undefined;
851
+ }
810
852
  buildRuleLaneMaterial(req, owner, governanceForced, sessionId) {
811
853
  if (this.ruleConsent === undefined)
812
854
  return undefined;
@@ -821,11 +863,8 @@ export class ToolApprovalCoordinator {
821
863
  const offers = ToolApprovalCoordinator.boundRuleOfferTexts(capped);
822
864
  if (offers === undefined)
823
865
  return undefined;
824
- const args = req.args;
825
- if (args === null || typeof args !== "object" || Array.isArray(args))
826
- return undefined;
827
- const command = args.command;
828
- if (typeof command !== "string" || command === "")
866
+ const command = ToolApprovalCoordinator.cardBindingOf(req.args, offers);
867
+ if (command === undefined)
829
868
  return undefined;
830
869
  const boundInputHash = readBoundInputHash(req);
831
870
  const scopeRoot = this.ruleScopeRootFor?.(sessionId);
@@ -1290,6 +1329,7 @@ export class ToolApprovalCoordinator {
1290
1329
  const cardWord = readApprovalCard(req.approvalCard);
1291
1330
  const originWord = typeof req.origin === "string" && req.origin.length > 0 ? req.origin : undefined;
1292
1331
  const askClassPair = readAskClassPair(req);
1332
+ const requester = extensionRequesterOf(req.requestedBy, "tool_approval");
1293
1333
  const frame = {
1294
1334
  type: "tool_approval",
1295
1335
  approvalId: id,
@@ -1323,6 +1363,7 @@ export class ToolApprovalCoordinator {
1323
1363
  ...(mandateWord !== undefined ? { mandate: mandateWord } : {}),
1324
1364
  ...(originWord !== undefined ? { origin: originWord } : {}),
1325
1365
  ...(askClassPair ?? {}),
1366
+ ...(requester !== undefined ? { requestedBy: requester } : {}),
1326
1367
  ...((d) => (d !== undefined ? { allowRuleShadowed: d } : {}))(readAllowRuleShadowed(req)),
1327
1368
  ...(ruleLaneMaterial !== undefined ? { ruleOffers: ruleLaneMaterial.offers } : {}),
1328
1369
  ...((a) => (a !== undefined ? { ruleOffersAbsence: a } : {}))(readRuleOffersAbsence(req)),
@@ -1856,7 +1897,7 @@ export class ToolApprovalCoordinator {
1856
1897
  }
1857
1898
  const entryClasses = askClassPair?.classes;
1858
1899
  const entryDeploymentListQuestion = carriesDeploymentListQuestion(deploymentListAsks, entryClasses);
1859
- const entryGrantBinding = questionGrantBindingOf(req, entryDeploymentListQuestion);
1900
+ const entryGrantBinding = grantBindingOf(req);
1860
1901
  const rememberRefused = standingAnswerHome(pendingEntryStandingAnswerFacts({
1861
1902
  ...(effectiveGovernanceForced ? { governanceForced: true } : {}),
1862
1903
  ...(req.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
@@ -2581,10 +2622,11 @@ export class ToolApprovalCoordinator {
2581
2622
  if (fresh?.terminal === true && memo.redeemed !== undefined)
2582
2623
  memo.redeemed.rule = fresh.body;
2583
2624
  }
2625
+ const sameDecision = (parsed.value === "deny") === (receipt.decision === "deny");
2584
2626
  return {
2585
2627
  status: 200,
2586
2628
  body: {
2587
- ...receipt.body,
2629
+ ...(sameDecision ? receipt.body : withoutSettlementEvidence(receipt.body)),
2588
2630
  approvalId: id,
2589
2631
  askId: row.askId,
2590
2632
  accepted: "parked_redeemed",
@@ -22,6 +22,11 @@
22
22
  * 7.53.x 及以前,`MyShell` 的 `egress: true` / `irreversibility: "always"` 会让引擎 `Bash` 的调用多问一次(收紧),它的恒可逆探针会让引擎 `Bash` 的
23
23
  * `touch` 不问就跑(放宽);7.54.0 起两者都不再作用于引擎 shell。要收紧引擎 shell:给**正名**叫 `Bash` 的工具声明标记(同名标记仍作用),或请求
24
24
  * `TaskSpec.shellGate: "always"`(本仓的入口 `MANUAL_MODE_SHELL_GATE` / 请求 `shellGate`)。
25
+ * ⚠️ **反方向(core 7.56.0 [ref])**:一次调用的安全轴只按它答得上的名字读,别名不带副本;两个名字的层级**平局时归 wire 名**(真正跑的那只工具)。
26
+ * 于是 `MyShell{irreversibility:"maybe", aliases:["Bash"]}` 在引擎 shell 被选择器卸下的腿上按 `Bash` 被调时,判它的是 **`MyShell` 自己的探针**
27
+ * (7.55.x 及以前是引擎 shell 席的形状探针:`ls` 不问就跑、`MyShell` 的探针从未被问)。⇒ 嵌入方给这类工具**照实声明**判它自己命令的探针,
28
+ * 或不声明层级、或 `irreversibility: "always"`;不声明层级时引擎 shell 席的层级仍够得着它(引擎侧候设计车,问的模式下按 shell 探针问,
29
+ * `bypassPermissions` 下照放)。本仓自身不声明带别名的工具,零码;这一形只有经 `resolveSpec` 席挂别名工具的嵌入方走得到。
25
30
  *
26
31
  * 返回**命中的那个名字**(调用答得上的名字里第一个被规则够到的,拼写在先)—— 规则归因(`matchedAskRule`)写运维名单里的那个名,
27
32
  * 不写一个名单里查不到的 wire 名;够不到 ⇒ `undefined`。判定式(精确集 / covering 规则)由调用方给,本函数只管「读哪些名字」。