@sema-agent/server 7.139.0 → 7.141.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 (328) hide show
  1. package/MIGRATION.md +471 -0
  2. package/README.md +1 -1
  3. package/README.zh-CN.md +1 -1
  4. package/USAGE.md +94 -26
  5. package/dist/adoption/runner.js +1 -1
  6. package/dist/adoption/sql.js +1 -1
  7. package/dist/approval-batch-census.d.ts +27 -0
  8. package/dist/approval-batch-census.js +27 -0
  9. package/dist/approval-card.d.ts +37 -22
  10. package/dist/approval-card.js +6 -3
  11. package/dist/approval.d.ts +15 -0
  12. package/dist/approval.js +10 -0
  13. package/dist/audit.d.ts +4 -2
  14. package/dist/audit.js +2 -2
  15. package/dist/bench/s1/live-deps.js +3 -2
  16. package/dist/bench/s1/row.d.ts +5 -2
  17. package/dist/bench/s1/row.js +2 -22
  18. package/dist/boot/adoption.js +1 -1
  19. package/dist/boot/device-lane.d.ts +11 -0
  20. package/dist/boot/device-lane.js +16 -0
  21. package/dist/boot/engine-lease.d.ts +24 -3
  22. package/dist/boot/engine-lease.js +9 -2
  23. package/dist/boot/execution-env.js +18 -12
  24. package/dist/boot/execution-lane.d.ts +8 -0
  25. package/dist/boot/execution-lane.js +6 -1
  26. package/dist/boot/memory-boundary.d.ts +16 -53
  27. package/dist/boot/memory-boundary.js +10 -20
  28. package/dist/boot/memory-consolidation.d.ts +16 -12
  29. package/dist/boot/memory-consolidation.js +24 -13
  30. package/dist/boot/reapers.d.ts +2 -2
  31. package/dist/boot/resolve-spec.d.ts +3 -5
  32. package/dist/boot/resolve-spec.js +9 -8
  33. package/dist/boot/runner-deps.d.ts +3 -3
  34. package/dist/boot/runner-deps.js +1 -0
  35. package/dist/boot/session-faces.d.ts +1 -1
  36. package/dist/boot/session-faces.js +4 -3
  37. package/dist/boot/shutdown.js +5 -0
  38. package/dist/boot/stage-03-store-backend.d.ts +2 -6
  39. package/dist/boot/stage-03-store-backend.js +2 -3
  40. package/dist/boot/stage-04-stores.d.ts +2 -6
  41. package/dist/boot/stage-04-stores.js +5 -5
  42. package/dist/boot/stage-05-execution-env.d.ts +2 -6
  43. package/dist/boot/stage-05-execution-env.js +3 -1
  44. package/dist/boot/stage-06-runners.d.ts +2 -8
  45. package/dist/boot/stage-06-runners.js +3 -3
  46. package/dist/boot/stage-07-capability-layer.d.ts +2 -7
  47. package/dist/boot/stage-07-capability-layer.js +8 -10
  48. package/dist/boot/stage-08-reapers.d.ts +2 -7
  49. package/dist/boot/stage-09-leader.d.ts +2 -7
  50. package/dist/boot/stage-10-http-server.d.ts +2 -7
  51. package/dist/boot/stage-10-http-server.js +6 -4
  52. package/dist/boot/stores.d.ts +3 -6
  53. package/dist/boot/stores.js +37 -44
  54. package/dist/boot/task-list-lane.d.ts +1 -1
  55. package/dist/boot/task-list-lane.js +1 -1
  56. package/dist/boot/web-admin.js +36 -9
  57. package/dist/capabilities/execution-lane.d.ts +2 -2
  58. package/dist/capabilities/scenarios.d.ts +1 -1
  59. package/dist/capability-facts.d.ts +9 -0
  60. package/dist/capability-facts.js +9 -0
  61. package/dist/card-void.d.ts +38 -0
  62. package/dist/card-void.js +23 -0
  63. package/dist/config-catalog.js +12 -8
  64. package/dist/config-provider.d.ts +18 -17
  65. package/dist/config-provider.js +28 -36
  66. package/dist/config-types.d.ts +36 -9
  67. package/dist/config.js +56 -46
  68. package/dist/delegated-session-owner.d.ts +23 -20
  69. package/dist/delegated-session-owner.js +2 -2
  70. package/dist/deployment-shape.d.ts +9 -8
  71. package/dist/deployment-shape.js +4 -4
  72. package/dist/device-store.d.ts +7 -0
  73. package/dist/device-store.js +3 -0
  74. package/dist/execution-config-root.d.ts +22 -0
  75. package/dist/execution-config-root.js +35 -0
  76. package/dist/execution-lane-caps.d.ts +145 -107
  77. package/dist/execution-lane-caps.js +144 -83
  78. package/dist/execution-placement.d.ts +3 -0
  79. package/dist/execution-placement.js +7 -0
  80. package/dist/fleet/drain-entry.d.ts +3 -0
  81. package/dist/hosted-posture.d.ts +8 -0
  82. package/dist/hosted-posture.js +4 -0
  83. package/dist/http/admission.js +3 -3
  84. package/dist/http/caller-trust.d.ts +3 -2
  85. package/dist/http/caller-trust.js +2 -1
  86. package/dist/http/dispatch.js +4 -0
  87. package/dist/http/live-frame.d.ts +24 -0
  88. package/dist/http/live-frame.js +16 -0
  89. package/dist/http/memory-compliance-failure-reply.d.ts +3 -1
  90. package/dist/http/memory-compliance-failure-reply.js +5 -0
  91. package/dist/http/memory-layer-reply.d.ts +30 -0
  92. package/dist/http/memory-layer-reply.js +24 -0
  93. package/dist/http/resume-legs.js +49 -35
  94. package/dist/http/retired-service-deps.d.ts +1 -1
  95. package/dist/http/retired-service-deps.js +1 -1
  96. package/dist/http/route-table.js +2 -0
  97. package/dist/http/routes/adoption.js +1 -1
  98. package/dist/http/routes/approvals-assistant.js +1 -0
  99. package/dist/http/routes/capabilities.js +5 -4
  100. package/dist/http/routes/diagnostics.d.ts +0 -19
  101. package/dist/http/routes/diagnostics.js +16 -2
  102. package/dist/http/routes/memory-bundle.d.ts +2 -2
  103. package/dist/http/routes/memory-bundle.js +4 -1
  104. package/dist/http/routes/memory-compliance.js +9 -2
  105. package/dist/http/routes/memory-consolidation.js +12 -1
  106. package/dist/http/routes/memory-origin.js +14 -2
  107. package/dist/http/routes/memory-policy.js +7 -2
  108. package/dist/http/routes/run-permission-mode.d.ts +75 -0
  109. package/dist/http/routes/run-permission-mode.js +170 -0
  110. package/dist/http/routes/session-sync.d.ts +3 -6
  111. package/dist/http/routes/session-sync.js +7 -2
  112. package/dist/http/routes/sessions.js +33 -15
  113. package/dist/http/routes/tasks.js +67 -68
  114. package/dist/http/run-read-face.js +3 -3
  115. package/dist/http/send.d.ts +1 -1
  116. package/dist/http/send.js +2 -2
  117. package/dist/http/server.d.ts +14 -27
  118. package/dist/http/server.js +2 -2
  119. package/dist/http/sse-log.d.ts +3 -8
  120. package/dist/http/stored-body-replay.d.ts +3 -3
  121. package/dist/http/stored-body-replay.js +3 -1
  122. package/dist/identity-source.d.ts +1 -1
  123. package/dist/identity-source.js +1 -1
  124. package/dist/instruction-source-notice.d.ts +7 -0
  125. package/dist/instruction-source-notice.js +6 -6
  126. package/dist/lane-home-probe.d.ts +68 -42
  127. package/dist/lane-home-probe.js +53 -30
  128. package/dist/leader/wire.js +5 -4
  129. package/dist/mcp-assertion-header.d.ts +4 -0
  130. package/dist/mcp-assertion-header.js +5 -0
  131. package/dist/memory-bundle-engine.d.ts +16 -9
  132. package/dist/memory-bundle-engine.js +29 -6
  133. package/dist/memory-operator-faces.d.ts +85 -30
  134. package/dist/memory-operator-faces.js +136 -30
  135. package/dist/memory-posture.d.ts +1 -1
  136. package/dist/memory-project-layer.d.ts +3 -21
  137. package/dist/memory-project-layer.js +11 -8
  138. package/dist/memory-scope-stores.d.ts +73 -0
  139. package/dist/memory-scope-stores.js +86 -0
  140. package/dist/memory-scope.d.ts +33 -9
  141. package/dist/memory-scope.js +17 -5
  142. package/dist/memory-sync-client.d.ts +34 -9
  143. package/dist/memory-sync-client.js +108 -28
  144. package/dist/observability/fail-open.d.ts +30 -2
  145. package/dist/observability/fail-open.js +30 -2
  146. package/dist/observability/metrics.js +0 -1
  147. package/dist/observability/run-terminal.d.ts +6 -2
  148. package/dist/observability/run-terminal.js +25 -26
  149. package/dist/observability/secret-env-scrub.d.ts +20 -1
  150. package/dist/observability/secret-env-scrub.js +14 -0
  151. package/dist/orchestration/workflow-completion-inbox.d.ts +5 -2
  152. package/dist/orchestration/workflow-completion-inbox.js +13 -5
  153. package/dist/orchestration/workflow-notify-journal.d.ts +5 -3
  154. package/dist/orchestration/workflow-notify-journal.js +2 -1
  155. package/dist/permission-mode-admission.d.ts +68 -0
  156. package/dist/permission-mode-admission.js +23 -0
  157. package/dist/permission-rule-list.d.ts +55 -6
  158. package/dist/permission-rule-list.js +52 -5
  159. package/dist/plugins/adoption-log-sql.js +4 -4
  160. package/dist/plugins/approval-ask-store-sql.js +14 -14
  161. package/dist/plugins/approval-nonce-store.js +4 -4
  162. package/dist/plugins/blob-backend.d.ts +4 -4
  163. package/dist/plugins/blob-backend.js +7 -7
  164. package/dist/plugins/breaker-state-sql.js +2 -2
  165. package/dist/plugins/caching-session-store.d.ts +6 -0
  166. package/dist/plugins/caching-session-store.js +3 -0
  167. package/dist/plugins/checkpoint-index-columns.js +2 -2
  168. package/dist/plugins/checkpoint-store-sql.d.ts +65 -17
  169. package/dist/plugins/checkpoint-store-sql.js +90 -19
  170. package/dist/plugins/device-store-sql.d.ts +5 -2
  171. package/dist/plugins/device-store-sql.js +23 -20
  172. package/dist/plugins/ensure-index.d.ts +1 -2
  173. package/dist/plugins/execution-placement-sql.d.ts +19 -9
  174. package/dist/plugins/execution-placement-sql.js +20 -9
  175. package/dist/plugins/file-history-store-sql.d.ts +2 -2
  176. package/dist/plugins/file-history-store-sql.js +4 -4
  177. package/dist/plugins/file-run-store.d.ts +2 -2
  178. package/dist/plugins/file-run-store.js +1 -1
  179. package/dist/plugins/fork-routing-session-store.d.ts +10 -32
  180. package/dist/plugins/fork-routing-session-store.js +2 -61
  181. package/dist/plugins/image-bake-store-sql.js +9 -9
  182. package/dist/plugins/image-index-sql.js +11 -11
  183. package/dist/plugins/k8s-exec-protocol.d.ts +4 -1
  184. package/dist/plugins/k8s-exec-protocol.js +6 -1
  185. package/dist/plugins/leader-run-store-sql.js +4 -4
  186. package/dist/plugins/local-checkpoint-store.d.ts +10 -1
  187. package/dist/plugins/local-checkpoint-store.js +22 -0
  188. package/dist/plugins/local-session-store.d.ts +227 -35
  189. package/dist/plugins/local-session-store.js +518 -37
  190. package/dist/plugins/local-session-window.d.ts +17 -3
  191. package/dist/plugins/local-session-window.js +7 -13
  192. package/dist/plugins/mailbox-store-sql.d.ts +2 -1
  193. package/dist/plugins/mailbox-store-sql.js +4 -4
  194. package/dist/plugins/memory-engine-tidb.js +5 -5
  195. package/dist/plugins/memory-optout-grant-store-sql.js +1 -1
  196. package/dist/plugins/memory-run-store.d.ts +2 -2
  197. package/dist/plugins/memory-run-store.js +1 -1
  198. package/dist/plugins/memory-sync-store-tidb.js +3 -3
  199. package/dist/plugins/one-time-migrations.d.ts +52 -11
  200. package/dist/plugins/one-time-migrations.js +101 -39
  201. package/dist/plugins/org-delivery-quota-store.js +2 -2
  202. package/dist/plugins/outcome-ledger-sql.js +2 -2
  203. package/dist/plugins/permission-rule-store-sql.d.ts +4 -4
  204. package/dist/plugins/permission-rule-store-sql.js +16 -16
  205. package/dist/plugins/pg-pool.js +3 -2
  206. package/dist/plugins/pg-session-storage.d.ts +2 -0
  207. package/dist/plugins/pg-session-storage.js +3 -2
  208. package/dist/plugins/remote-env-adb.d.ts +7 -24
  209. package/dist/plugins/remote-env-adb.js +22 -5
  210. package/dist/plugins/remote-env-device.d.ts +2 -28
  211. package/dist/plugins/remote-env-device.js +2 -1
  212. package/dist/plugins/remote-env-e2b.d.ts +40 -27
  213. package/dist/plugins/remote-env-e2b.js +180 -20
  214. package/dist/plugins/remote-env-file-error.d.ts +1 -1
  215. package/dist/plugins/remote-env-file-error.js +8 -2
  216. package/dist/plugins/remote-env-host.d.ts +19 -35
  217. package/dist/plugins/remote-env-host.js +26 -3
  218. package/dist/plugins/remote-env-k8s.d.ts +22 -14
  219. package/dist/plugins/remote-env-k8s.js +150 -26
  220. package/dist/plugins/remote-env-local-docker.d.ts +27 -24
  221. package/dist/plugins/remote-env-local-docker.js +91 -24
  222. package/dist/plugins/remote-env-ssh.d.ts +4 -24
  223. package/dist/plugins/remote-env-ssh.js +4 -3
  224. package/dist/plugins/resume-anchor-store-sql.js +2 -2
  225. package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
  226. package/dist/plugins/retention-lane-store-sql.js +2 -2
  227. package/dist/plugins/retention-store-sql.d.ts +1 -1
  228. package/dist/plugins/retention-store-sql.js +6 -6
  229. package/dist/plugins/run-store-sql.d.ts +3 -3
  230. package/dist/plugins/run-store-sql.js +19 -18
  231. package/dist/plugins/sandbox-read-floor.d.ts +273 -0
  232. package/dist/plugins/sandbox-read-floor.js +608 -0
  233. package/dist/plugins/schema-consistency.d.ts +6 -6
  234. package/dist/plugins/schema-consistency.js +1 -1
  235. package/dist/plugins/schema-ensure-legs.d.ts +2 -3
  236. package/dist/plugins/schema-ensure-legs.js +2 -2
  237. package/dist/plugins/schema-live-columns.d.ts +1 -1
  238. package/dist/plugins/session-capture-record-store-sql.js +1 -1
  239. package/dist/plugins/session-ownership.d.ts +2 -1
  240. package/dist/plugins/session-placement.d.ts +17 -4
  241. package/dist/plugins/session-placement.js +2 -1
  242. package/dist/plugins/session-rules-column.d.ts +1 -1
  243. package/dist/plugins/session-rules-column.js +1 -1
  244. package/dist/plugins/session-store.d.ts +18 -20
  245. package/dist/plugins/session-store.js +4 -12
  246. package/dist/plugins/session-workspace-cell.d.ts +1 -1
  247. package/dist/plugins/session-workspace-cell.js +1 -1
  248. package/dist/plugins/shared-memory-store-sql.js +2 -2
  249. package/dist/plugins/sql-driver.d.ts +10 -5
  250. package/dist/plugins/sql-driver.js +6 -6
  251. package/dist/plugins/sql-errors.js +1 -1
  252. package/dist/plugins/store-backend.d.ts +35 -6
  253. package/dist/plugins/store-backend.js +27 -10
  254. package/dist/plugins/store-contracts.d.ts +9 -2
  255. package/dist/plugins/task-list-store-sql.js +11 -11
  256. package/dist/plugins/tidb-pool.js +7 -6
  257. package/dist/plugins/tidb-session-storage.js +4 -4
  258. package/dist/plugins/tidb-session-store.d.ts +4 -1
  259. package/dist/plugins/tidb-session-store.js +16 -15
  260. package/dist/plugins/tool-result-store-sql.d.ts +2 -2
  261. package/dist/plugins/tool-result-store-sql.js +3 -3
  262. package/dist/plugins/user-input-ledger-sql.js +2 -2
  263. package/dist/plugins/workflow-journal-store-sql.js +5 -5
  264. package/dist/plugins/workflow-run-store-sql.js +19 -19
  265. package/dist/plugins/write-behind-counter.d.ts +2 -3
  266. package/dist/plugins/write-behind-counter.js +5 -5
  267. package/dist/resource-suspend.d.ts +14 -12
  268. package/dist/resource-suspend.js +1 -1
  269. package/dist/resource-window.d.ts +4 -3
  270. package/dist/run-cancel-context.d.ts +34 -0
  271. package/dist/run-cancel-context.js +7 -0
  272. package/dist/run-local.js +14 -19
  273. package/dist/runs.d.ts +47 -16
  274. package/dist/runs.js +112 -113
  275. package/dist/security.d.ts +2 -2
  276. package/dist/server-secret-env.d.ts +2 -1
  277. package/dist/server-secret-env.js +2 -4
  278. package/dist/session-error-wire.d.ts +22 -4
  279. package/dist/session-error-wire.js +16 -0
  280. package/dist/session-incarnation.d.ts +10 -4
  281. package/dist/session-incarnation.js +1 -1
  282. package/dist/session-policy-wire.d.ts +1 -1
  283. package/dist/session-sync-kernel.d.ts +42 -1
  284. package/dist/session-sync-kernel.js +17 -0
  285. package/dist/session-sync.js +16 -4
  286. package/dist/sql-engine-coordinates.d.ts +49 -0
  287. package/dist/sql-engine-coordinates.js +27 -0
  288. package/dist/sql-engine-posture.d.ts +1 -1
  289. package/dist/sql-engine-posture.js +1 -1
  290. package/dist/standing-answer-grant.d.ts +31 -2
  291. package/dist/standing-answer-grant.js +14 -2
  292. package/dist/store-posture.d.ts +1 -1
  293. package/dist/{plugins → stores-sql}/background-agent-store-sql.d.ts +2 -2
  294. package/dist/{plugins → stores-sql}/background-agent-store-sql.js +8 -8
  295. package/dist/{plugins → stores-sql}/session-policy-store-sql.d.ts +7 -5
  296. package/dist/{plugins → stores-sql}/session-policy-store-sql.js +12 -12
  297. package/dist/{plugins → stores-sql}/usage-window-store-sql.d.ts +1 -1
  298. package/dist/{plugins → stores-sql}/usage-window-store-sql.js +3 -3
  299. package/dist/task-a2a.d.ts +1 -1
  300. package/dist/task-a2a.js +5 -6
  301. package/dist/task-mcp.d.ts +21 -8
  302. package/dist/task-mcp.js +17 -9
  303. package/dist/task-settings.d.ts +52 -25
  304. package/dist/task-settings.js +91 -28
  305. package/dist/terminal.d.ts +103 -19
  306. package/dist/terminal.js +72 -20
  307. package/dist/trace/core-keyset-guard.d.ts +79 -2
  308. package/dist/trace/exit-seat.d.ts +7 -0
  309. package/dist/trace/exit-seat.js +1 -0
  310. package/dist/trace/model-segment.d.ts +2 -2
  311. package/dist/trace/redact.d.ts +3 -1
  312. package/dist/trace/redact.js +395 -110
  313. package/dist/trace/self-minted-wire.d.ts +226 -2
  314. package/dist/trace/self-minted-wire.js +79 -0
  315. package/dist/verifier-pause-card.d.ts +8 -0
  316. package/dist/verifier-pause-card.js +19 -0
  317. package/dist/web-admin-client/apply-effective.d.ts +5 -7
  318. package/dist/web-admin-client/apply-effective.js +2 -2
  319. package/dist/web-admin-client/candidate-gate.d.ts +11 -7
  320. package/dist/web-admin-client/candidate-gate.js +1 -1
  321. package/dist/web-admin-client/http-client.d.ts +5 -4
  322. package/dist/web-admin-client/http-client.js +2 -2
  323. package/dist/web-admin-client/read-warnings.d.ts +7 -17
  324. package/dist/web-admin-client/read-warnings.js +5 -9
  325. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -2
  326. package/package.json +2 -2
  327. package/wire-changes/server@7.140.0.json +184 -0
  328. package/wire-changes/server@7.141.0.json +367 -0
@@ -49,10 +49,11 @@
49
49
  */
50
50
  import { type PathRuleBases, type TaskSpec, type ThinkingLevel, type ToolPolicy, type PermissionMode } from "@sema-agent/core";
51
51
  import { type EntryProblem } from "./entry-problems.js";
52
+ import { type RuleListEntry } from "./permission-rule-list.js";
52
53
  import { type KeyClosureIssue } from "./request-key-closure.js";
53
54
  import { type RunnableHooksConfig } from "./hooks/hook-runner.js";
54
55
  import type { StoredHooksRereadM20 } from "./stored-request-hooks-m20.js";
55
- import { type EntryPermissionMode } from "./runtime-governance.js";
56
+ import type { EntryPermissionMode } from "./runtime-governance.js";
56
57
  import { type TightenOnlySeatValues } from "./tighten-only-seats.js";
57
58
  /**
58
59
  * 请求面 `permissionMode` 的闭集 = core `PERMISSION_MODES` **全集**(顶层 `permissionMode` / `settings.permissions.defaultMode` /
@@ -112,23 +113,21 @@ export declare function planReviewDecisionExitsPlan(decision: "approve" | "edit"
112
113
  /**
113
114
  * 运维 `AUTONOMY=plan` 的**入口词地板**盖进提交体 —— 此后一条 run 的模式词只有存储体顶层 `permissionMode` 一个源。
114
115
  *
115
- * 入口词 = `runtime-governance.ts` `entryPermissionMode(体的生效词, 地板).effective`(「词在地板下变成什么」的唯一一只函数;秩是 core 的)。本函数只加
116
- * 「写进体」这一半:地板缺席 ⇒ 体**原物**交回(零字节变化);地板在场 ⇒ 新体 `{ ...body, permissionMode: 入口词 }`,调用方的对象不改;bundle 的
117
- * `settings.permissions.defaultMode` 不动 —— 顶层赢过它({@link effectivePermissionMode} 的优先序),再改它就是同一语义的第二个写者。
118
- * 同时交回**这一次求值的读数**(`entry`:请求词 / 地板 / 生效词 / 折没折)—— 披露这一折的通告(`entry-mode-clamp-notice.ts`)读它,不另判一遍。
116
+ * 入口词由受理判官答(`permission-mode-admission.ts` `admitPermissionModeWord`:两口 —— 提交受理口与在飞 run 的切档口 —— 同一只;它交回的
117
+ * `entry` 就是「词在地板下变成什么」那一次求值的读数,秩是 core 的)。本函数只做「写进体」这一半:地板缺席 ⇒ 体**原物**交回(零字节变化);地板在场 ⇒
118
+ * 新体 `{ ...body, permissionMode: entry.effective }`,调用方的对象不改;bundle 的 `settings.permissions.defaultMode` 不动 —— 顶层赢过它
119
+ * ({@link effectivePermissionMode} 的优先序),再改它就是同一语义的第二个写者。披露这一折的通告(`entry-mode-clamp-notice.ts`)读的也是同一份 `entry`。
119
120
  *
120
- * 调用点一个:提交入口(`http/admission.ts` 的 `prepareSpec`:值形门全部判完之后、`resolveSpec` 之前)。那一份体就是首跑装配读的体,也是
121
+ * 调用点一个:提交入口的那一步 `permission-mode-admission.ts` `stampSubmissionEntryMode`(受理判官的入口一步;`http/admission.ts` 的 `prepareSpec` 调它:值形门全部判完之后、`resolveSpec` 之前)。那一份体就是首跑装配读的体,也是
121
122
  * `buildCheckpointCtx` 持久化的体 ⇒ 一处盖章同时覆盖首跑与每一次续跑重建;装配腿与续跑腿都不读地板(两处读 = 两个源)。
122
- * 另一个写者是 `plan_review` 写回(`http/resume-legs.ts` `drivePlanReview`,判据 {@link planReviewDecisionExitsPlan})。
123
- * 热改 `AUTONOMY` 只影响之后提交的任务:在飞的 run 保持它出生时盖下的词。地板的属主是 `runtime-governance.ts` 的 `autonomyModeFloor`。
123
+ * 另外两个写者是 `plan_review` 写回(`http/resume-legs.ts` `drivePlanReview`,判据 {@link planReviewDecisionExitsPlan})与在飞 run 的切档口写回
124
+ * (`http/routes/run-permission-mode.ts`,写的是同一只判官准入的词)。
125
+ * 热改 `AUTONOMY` 只影响之后提交的任务与之后的切档:续跑腿保持存储体里的词。地板的属主是 `runtime-governance.ts` 的 `autonomyModeFloor`。
124
126
  */
125
127
  export declare function stampEntryPermissionMode<B extends {
126
128
  permissionMode?: unknown;
127
129
  settings?: unknown;
128
- }>(body: B, floor: PermissionMode | undefined): {
129
- readonly body: B;
130
- readonly entry: EntryPermissionMode;
131
- };
130
+ }>(body: B, entry: EntryPermissionMode): B;
132
131
  /**
133
132
  * [ref] —— **一条已持久化的提交体的生效 permission mode**,单点。
134
133
  *
@@ -150,8 +149,8 @@ export declare function storedBodyPermissionMode(body: {
150
149
  * future-proof). Awareness rides for free: core composes ORCHESTRATION_AWARENESS from thinking∈{xhigh,max}, no field. */
151
150
  export declare function effectiveThinking(reasoningEffort: unknown, ultracode: boolean): ThinkingLevel | undefined;
152
151
  /**
153
- * [ref] §6-1 —— 本请求的**生效** permission mode,单点。优先序:顶层 `body.permissionMode`(本轮显式表态)赢过 bundle 的
154
- * `settings.permissions.defaultMode`;两处都没有 ⇒ `undefined` = **无表态**(调用点据此不写键,而不是替调用方选一个默认值;引擎把缺席读成 `default`)。
152
+ * [ref] §6-1 —— 本请求的**生效** permission mode,单点 = 调用方的词({@link requestedPermissionMode})经本任务 settings 的 kill-switch 折叠
153
+ * ({@link foldDisableAutoMode})。两处都没有 ⇒ `undefined` = **无表态**(调用点据此不写键,而不是替调用方选一个默认值;引擎把缺席读成 `default`)。
155
154
  *
156
155
  * 🔴 为什么必须是一只函数:同一个「生效模式」此前在 resolve-spec 里被算过两次(hooks 腿的 `permission_mode` 载荷、settings 折叠腿),
157
156
  * 两处各算各的,任何一次优先序修订都会让两面分家,而分家在这条轴上的形态是「壳发的 bundle bypass 在 A 面生效、在 B 面没生效」这种
@@ -160,6 +159,23 @@ export declare function effectiveThinking(reasoningEffort: unknown, ultracode: b
160
159
  export declare function effectivePermissionMode(body: {
161
160
  permissionMode?: unknown;
162
161
  }, settings: ParsedTaskSettings | undefined): PermissionMode | undefined;
162
+ /**
163
+ * 调用方在提交体上说出的模式词 —— 顶层 `body.permissionMode`(本轮显式表态)赢过 bundle 的 `settings.permissions.defaultMode`;两处都没有 ⇒ `undefined`。
164
+ * 读者两个:{@link effectivePermissionMode}(存储体读法)与提交口交给受理判官入口一步的那一个词(`permission-mode-admission.ts` `stampSubmissionEntryMode`)。
165
+ */
166
+ export declare function requestedPermissionMode(body: {
167
+ permissionMode?: unknown;
168
+ }, settings: ParsedTaskSettings | undefined): PermissionMode | undefined;
169
+ /**
170
+ * [ref] 件⑥([ref] §3.4,[ref]/[ref]):本任务 settings 的 kill-switch `permissions.disableAutoMode` 的**唯一**折叠(tighten-only,CC 250 `Ct`
171
+ * 棘轮形):把 auto 折 default(座不写 ⇒ 引擎不武装,与 CC「auto 不可用 ⇒ 回 default」同向)。只关 auto;缺席不动;没有放宽臂。
172
+ * 读者两个:存储体读法 {@link effectivePermissionMode} 与受理判官(`permission-mode-admission.ts`,提交口与切档口同一只)—— 切档口的
173
+ * `auto` 因此与提交口的 `auto` 在同一份 settings 下折成同一个词。`disableBypassPermissionsMode` 落点归 [ref]。
174
+ * 🔴 [ref] 放宽一档:折叠触发词是 **`"disable"` 或 `true`** —— 两套**已发布**的合法拼写(CC 250 `Ct` 的严极词 /
175
+ * `@sema-agent/sdk` 8.1.0 `SettingsPermissions.disableAutoMode?: boolean`),同义同折。显式 `false` = 「不禁」,
176
+ * 合法输入、**零效果**(不是「开 auto」的开关 —— 本键只有关的半场,放宽臂在这条轴上从来不存在)。
177
+ */
178
+ export declare function foldDisableAutoMode(word: PermissionMode | undefined, settings: ParsedTaskSettings | undefined): PermissionMode | undefined;
163
179
  /**
164
180
  * [ref] —— [ref] 技能清单预算两旋钮的**值域谓词**(一对具名单铸点)。
165
181
  *
@@ -182,9 +198,10 @@ export declare function skillListingMaxDescOk(v: unknown): v is number;
182
198
  export interface ParsedTaskSettings {
183
199
  permissions?: {
184
200
  allow?: string[];
185
- deny?: string[];
186
- /** Carried for completeness; v1 folds `ask` into the gate via the deny-wins base (see {@link applyTaskSettings}). */
187
- ask?: string[];
201
+ /** 纯串 = 请求自己写的规则;`{ rule, layer }` = 端从某个设置层读来的规则(`permission-rule-list.ts` 的 {@link RuleListEntry};层只选 `/` 的锚)。 */
202
+ deny?: RuleListEntry[];
203
+ /** 同 `deny` 的条目形;编译与 `deny` 同一口({@link deriveSettingsPolicy})。 */
204
+ ask?: RuleListEntry[];
188
205
  defaultMode?: PermissionMode;
189
206
  /** [ref] 件⑥([ref] §3.4,[ref];PM 裁 [ref]):CC settings 的 kill-switch `permissions.disableAutoMode`,cli 投影
190
207
  * (settingsRulesWire.ts toWireSettings)而此前本白名单丢弃 ⇒ 用户/managed 层关 auto 到不了引擎。语义 = 只能关
@@ -276,7 +293,7 @@ export interface SettingsPermissionBucketIssue {
276
293
  /**
277
294
  * 三桶的**唯一**判据(纯函数)。`perms` 不是对象 ⇒ 不判(那一形归「settings.permissions must be an object」那道门);桶缺席 / `null`
278
295
  * ⇒ 不判(与 `permissions: null` 的「容器缺席」同一约定)。逐桶按 {@link SETTINGS_PERMISSION_BUCKETS} 的序、按该桶的条目规则:不是数组 ⇒ `bad_entry`
279
- * (无 `index`);`rule_policy` 桶超帽 ⇒ `over_cap`(先判,拒体有界)、首个非串 / 空串项 ⇒ `bad_entry` + 它的下标;`allow_layer` 桶首个非串项 ⇒
296
+ * (无 `index`);`rule_policy` 桶超帽 ⇒ `over_cap`(先判,拒体有界)、首个不合条目律的项(不是非空串、也不是合形的带层条目)⇒ `bad_entry` + 它的下标;`allow_layer` 桶首个非串项 ⇒
280
297
  * `bad_entry` + 它的下标(整表判据 = `ruleEntryListShapeOk`),超帽同样 `over_cap`(帽的理由不同,见段头)。全合法 ⇒ `null`。
281
298
  */
282
299
  export declare function settingsPermissionBucketIssue(perms: unknown): SettingsPermissionBucketIssue | null;
@@ -432,6 +449,12 @@ export interface FsWriteGateWiring {
432
449
  * 运维在**下单那一刻**就知道这条规则这条车道读不了,而不是等到跑起来每次都弹一张 ask。
433
450
  */
434
451
  taskHome?: string;
452
+ /**
453
+ * **执行环境的配置根**(唯一属主 `execution-config-root.ts` 的 `executionConfigRootOf`):user 设置层里 `/` 形路径规则的根 —— 带 `layer: "userSettings"`
454
+ * 且要 `root` 的条目编进只声明 `{ root: configRoot }` 的那一集(`permission-rule-list.ts` 的 `ruleRootOf`)。缺席(沙箱车道既没转 `SEMA_CONFIG_DIR`、home 也读不出)
455
+ * ⇒ 这样的条目由 {@link rejectUnmatchableSettingsNames} 响亮拒(422 `home_unresolvable`),不猜目录。拒点与存体预判读同一只函数。
456
+ */
457
+ configRoot?: string;
435
458
  }
436
459
  /**
437
460
  * 请求的 `settings.permissions.deny` / `ask` 有永远不生效的条目 ⇒ 拒点({@link rejectUnmatchableSettingsNames})抛的错:句子(`message`)与这一遍判下来的**全部**问题(`problems`,机读位的原料)。
@@ -454,20 +477,24 @@ export declare const RULE_POLICY_BUCKETS: readonly ["ask", "deny"];
454
477
  export type RulePolicyBucket = (typeof RULE_POLICY_BUCKETS)[number];
455
478
  /**
456
479
  * deny / ask 两桶的**完整规则判**,唯一一只(拒点 {@link rejectUnmatchableSettingsNames} 与存体预判 `http/stored-body-replay.ts` 同调):
457
- * ② 按 {@link RULE_POLICY_BUCKETS} 的序逐桶问 `judgeUnmatchableToolNames`(名字层 + core 规则文法);③ 再逐桶判 `~/` 形要 home 的那一拒。答 `{ bucket, message, problems }`:`bucket` 与 `message` 是**第一处**坏处的
458
- * (句子冻结,先撞上的那一条),`problems` 是**两段、两只桶判下来的全部**坏条目(机读位的原料,形的属主 `entry-problems.ts`)。
459
- * 入参:桶、{@link RulePolicyBases}。**home 同源**:拒点的 home 是 `resolve-spec.ts` 的 `laneHomeBase()` 经 `executionLaneHomeDir(config)` 算出的,预判传同一个函数对同一份部署配置算出的。
460
- * **声明给文法的基**只有拒点有(根 + home 的并集,执行车道才知道根):core 的规则文法对**没声明**的基不下判词(见 `config.ts` `uncompilableRuleGuidance`),所以预判什么也不声明、两腿逐字同句;
480
+ * ② 按 {@link RULE_POLICY_BUCKETS} 的序逐桶、桶内按 `RULE_ROOTS` 的序逐集(`permission-rule-list.ts` 的 `ruleSetsOf`:判的就是要编的那一集)问 `judgeUnmatchableToolNames`(名字层 + core 规则文法);
481
+ * ③ 再判「这条规则要的基本部署给不出」那一拒:`~/` 形缺 home;配置根那一集缺配置根(两者都落在执行环境的 home 上,同词 `home_unresolvable`)。答 `{ bucket, message, problems }`:`bucket` 与 `message` 是**第一处**坏处的
482
+ * (句子冻结,先撞上的那一条),`problems` 是**两段、两只桶判下来的全部**坏条目(机读位的原料,形的属主 `entry-problems.ts`;`entry` = 规则原文,带层条目也一样)。
483
+ * 入参:桶、{@link RulePolicyBases}。**home 同源**:拒点的 home 是 `resolve-spec.ts` 的 `laneHomeBase()` 经 `executionLaneHomeDir(config)` 算出的,预判传同一个函数对同一份部署配置算出的;配置根同理(`executionConfigRootOf`)。
484
+ * **任务根那一集声明给文法的基**只有拒点有(根 + home 的并集,执行车道才知道根):core 的规则文法对**没声明**的基不下判词(见 `config.ts` `uncompilableRuleGuidance`),所以预判什么也不声明、两腿逐字同句;
461
485
  * 预判不能把 home 当「半个基」声明给文法 —— core 的路径族只从根推,缺根时一个合法的 Windows 形 home 会按默认族被判 `unsupported.path_base`(V6-k)。
462
- * ③ 排在最后:一条同时文法不成立又带 `~/` 的规则,先说文法(补上 home 它仍被文法拒,先说 home 是误导)。
486
+ * **配置根那一集**两腿都声明 `{ root: 配置根 }`:两腿都算得出它,`{ root }` 自带路径族(没有「半个基」),一个用不了的配置根(转进沙箱的相对值)两腿同为引擎的 `unsupported.path_base`。
487
+ * ③ 排在最后:一条同时文法不成立又缺基的规则,先说文法(补上基它仍被文法拒,先说基是误导)。
463
488
  */
464
489
  export interface RulePolicyBases {
465
- /** 声明给 core 规则文法的路径基(这条 task 的根 + home):拒点才有;存体预判不声明。 */
490
+ /** 声明给 core 规则文法的任务根那一集的路径基(这条 task 的根 + home):拒点才有;存体预判不声明。 */
466
491
  readonly declared?: PathRuleBases;
467
492
  /** 执行环境的 home(`~/` 形规则的前提);这个部署解不出 ⇒ 缺席。 */
468
493
  readonly home?: string;
494
+ /** 执行环境的配置根(配置根那一集的根;拒点与预判都给);读不出 ⇒ 缺席。 */
495
+ readonly configRoot?: string;
469
496
  }
470
- export declare function rulePolicyBucketsRefusal(permissions: Readonly<Partial<Record<RulePolicyBucket, readonly string[]>>>, bases: RulePolicyBases): {
497
+ export declare function rulePolicyBucketsRefusal(permissions: Readonly<Partial<Record<RulePolicyBucket, readonly RuleListEntry[]>>>, bases: RulePolicyBases): {
471
498
  readonly bucket: RulePolicyBucket;
472
499
  readonly message: string;
473
500
  readonly problems: readonly EntryProblem[];
@@ -2,14 +2,14 @@ import { combinePolicies, parseRuleText, resolveReasoningProfile, rankOf, isThin
2
2
  import { judgeUnmatchableToolNames } from "./config.js";
3
3
  import { NAME_TABLE_BEHAVIOR } from "./namespaced-rule-name.js";
4
4
  import { entryProblem } from "./entry-problems.js";
5
- import { MAX_PERMISSION_RULES_PER_LIST, createRuleListPolicy } from "./permission-rule-list.js";
5
+ import { MAX_PERMISSION_RULES_PER_LIST, RULE_ROOTS, createRuleListPolicy, ruleListEntryOk, ruleSetsOf, ruleSourceOf, ruleTextOf } from "./permission-rule-list.js";
6
6
  import { allowLayerEntryCountIssue, describeAllowLayerOverCap, ruleEntryListShapeOk } from "./rule-entry-list.js";
7
7
  import { limitRefusal } from "./limit-refusal.js";
8
8
  import { tightenGoverned } from "./governance-overrides.js";
9
9
  import { MAX_KEY_ECHO, keyClosureClause, scanKeyClosure } from "./request-key-closure.js";
10
10
  import { hooksCoordinate, parseHooksConfig } from "./hooks/hook-runner.js";
11
+ import { SETTINGS_SOURCES } from "@sema-agent/settings-schema";
11
12
  import { heldRuleName } from "./tool-rule-names.js";
12
- import { entryPermissionMode } from "./runtime-governance.js";
13
13
  import { policyAskStandingAnswerFacts, standingAnswerDurablyRecordable } from "./tool-approval.js";
14
14
  import { createRequestNeverAutoPolicy } from "./approval.js";
15
15
  import { readTightenOnlySeats, tightenOnlySeatField, tightenOnlySpecSeats } from "./tighten-only-seats.js";
@@ -39,9 +39,8 @@ export function planReviewDecisionExitsPlan(decision) {
39
39
  return false;
40
40
  }
41
41
  }
42
- export function stampEntryPermissionMode(body, floor) {
43
- const entry = entryPermissionMode(storedBodyPermissionMode(body), floor);
44
- return { body: entry.floor === undefined ? body : { ...body, permissionMode: entry.effective }, entry };
42
+ export function stampEntryPermissionMode(body, entry) {
43
+ return entry.floor === undefined ? body : { ...body, permissionMode: entry.effective };
45
44
  }
46
45
  export function storedBodyPermissionMode(body) {
47
46
  return effectivePermissionMode(body, parseTaskSettings(body.settings).settings);
@@ -54,11 +53,16 @@ export function effectiveThinking(reasoningEffort, ultracode) {
54
53
  return explicit && rankOf(explicit) > rankOf(ultra) ? explicit : ultra;
55
54
  }
56
55
  export function effectivePermissionMode(body, settings) {
57
- const requested = coercePermissionMode(body.permissionMode) ?? settings?.permissions?.defaultMode;
56
+ return foldDisableAutoMode(requestedPermissionMode(body, settings), settings);
57
+ }
58
+ export function requestedPermissionMode(body, settings) {
59
+ return coercePermissionMode(body.permissionMode) ?? settings?.permissions?.defaultMode;
60
+ }
61
+ export function foldDisableAutoMode(word, settings) {
58
62
  const kill = settings?.permissions?.disableAutoMode;
59
- if (requested === "auto" && (kill === "disable" || kill === true))
63
+ if (word === "auto" && (kill === "disable" || kill === true))
60
64
  return "default";
61
- return requested;
65
+ return word;
62
66
  }
63
67
  export function skillListingBudgetFractionOk(v) {
64
68
  return typeof v === "number" && Number.isFinite(v) && v > 0 && v <= 1;
@@ -105,12 +109,12 @@ function bucketIssueMessage(f, rule) {
105
109
  switch (rule) {
106
110
  case "rule_policy":
107
111
  return f.index === undefined
108
- ? `${f.field} must be an array of non-empty rule strings — refused rather than ignored, because an ignored list reads as "in effect" to the caller`
109
- : `${f.field}[${f.index}] is not a non-empty rule string — every entry must be one. Refused rather than dropped, because a dropped entry reads as "in effect" to the caller`;
112
+ ? `${f.field} must be an array of rule entries (a non-empty rule string, or { "rule": <rule>, "layer": <settings source> }) — refused rather than ignored, because an ignored list reads as "in effect" to the caller`
113
+ : `${f.field}[${f.index}] is neither a non-empty rule string nor a { "rule", "layer" } entry (exactly those two keys; rule = a non-empty rule string; layer = one of ${SETTINGS_SOURCES.join(", ")}) — every entry must be one. Refused rather than dropped, because a dropped entry reads as "in effect" to the caller`;
110
114
  case "allow_layer":
111
115
  return f.index === undefined
112
116
  ? `${f.field} must be an array of permission-rule strings — refused rather than ignored, because an ignored list reads as "in effect" to the caller`
113
- : `${f.field}[${f.index}] is not a string — every entry must be a permission-rule string. Refused rather than dropped, because a dropped entry reads as "in effect" to the caller`;
117
+ : `${f.field}[${f.index}] is not a string — every entry must be a permission-rule string (a settings layer's { "rule", "layer" } entry is read in deny / ask only). Refused rather than dropped, because a dropped entry reads as "in effect" to the caller`;
114
118
  }
115
119
  }
116
120
  }
@@ -132,7 +136,7 @@ export function settingsPermissionBucketIssue(perms) {
132
136
  if (v.length > MAX_PERMISSION_RULES_PER_LIST)
133
137
  fields = { field, reason: "over_cap", count: v.length, cap: MAX_PERMISSION_RULES_PER_LIST };
134
138
  else {
135
- const index = v.findIndex((x) => typeof x !== "string" || x.length === 0);
139
+ const index = v.findIndex((x) => !ruleListEntryOk(x));
136
140
  if (index >= 0)
137
141
  fields = { field, reason: "bad_entry", index };
138
142
  }
@@ -336,8 +340,12 @@ export function createWorkflowAskPolicy(isExempt) {
336
340
  nameSets: [{ ask: WORKFLOW_ASK_NAMES }],
337
341
  };
338
342
  }
339
- function rejectUnmatchableSettingsNames(perms, pathBases) {
340
- const refused = rulePolicyBucketsRefusal(perms, { ...(pathBases !== undefined ? { declared: pathBases } : {}), ...(pathBases?.home !== undefined ? { home: pathBases.home } : {}) });
343
+ function rejectUnmatchableSettingsNames(perms, pathBases, configRoot) {
344
+ const refused = rulePolicyBucketsRefusal(perms, {
345
+ ...(pathBases !== undefined ? { declared: pathBases } : {}),
346
+ ...(pathBases?.home !== undefined ? { home: pathBases.home } : {}),
347
+ ...(configRoot !== undefined ? { configRoot } : {}),
348
+ });
341
349
  if (refused !== undefined)
342
350
  throw new SettingsRuleRefusalError(refused.message, refused.problems);
343
351
  }
@@ -354,21 +362,40 @@ export const RULE_POLICY_BUCKETS = ["ask", "deny"];
354
362
  export function rulePolicyBucketsRefusal(permissions, bases) {
355
363
  let first;
356
364
  const problems = [];
365
+ const declaredFor = (root) => {
366
+ switch (root) {
367
+ case "task":
368
+ return bases.declared;
369
+ case "configuration":
370
+ return bases.configRoot !== undefined ? { root: bases.configRoot } : undefined;
371
+ default: {
372
+ const unhandled = root;
373
+ return unhandled;
374
+ }
375
+ }
376
+ };
357
377
  for (const bucket of RULE_POLICY_BUCKETS) {
358
- const names = permissions[bucket];
359
- if (!names)
378
+ const entries = permissions[bucket];
379
+ if (!entries)
360
380
  continue;
361
- const judged = judgeUnmatchableToolNames(`permissions.${bucket}`, names, { consumer: "rule-policy", behavior: bucket, ...(bases.declared !== undefined ? { pathBases: bases.declared } : {}) });
362
- if (judged === undefined)
363
- continue;
364
- first ??= { bucket, message: judged.message };
365
- for (const b of judged.bad)
366
- problems.push(entryProblem(SETTINGS_RULE_REFUSAL_CODE, `settings.permissions.${bucket}`, b.name, b.reason));
381
+ const sets = ruleSetsOf(entries, bucket);
382
+ for (const root of RULE_ROOTS) {
383
+ const names = sets[root].map(ruleTextOf);
384
+ if (names.length === 0)
385
+ continue;
386
+ const declared = declaredFor(root);
387
+ const judged = judgeUnmatchableToolNames(`permissions.${bucket}`, names, { consumer: "rule-policy", behavior: bucket, ...(declared !== undefined ? { pathBases: declared } : {}) });
388
+ if (judged === undefined)
389
+ continue;
390
+ first ??= { bucket, message: judged.message };
391
+ for (const b of judged.bad)
392
+ problems.push(entryProblem(SETTINGS_RULE_REFUSAL_CODE, `settings.permissions.${bucket}`, b.name, b.reason));
393
+ }
367
394
  }
368
395
  if (bases.home === undefined) {
369
396
  for (const bucket of RULE_POLICY_BUCKETS) {
370
397
  const field = `permissions.${bucket}`;
371
- for (const name of permissions[bucket] ?? []) {
398
+ for (const name of (permissions[bucket] ?? []).map(ruleTextOf)) {
372
399
  const parsed = parseRuleText(name, bucket);
373
400
  if ("rule" in parsed && ruleToolGrammarOf(parsed.rule.tool) === "path" && parsed.rule.command.startsWith("~/")) {
374
401
  first ??= {
@@ -382,8 +409,36 @@ export function rulePolicyBucketsRefusal(permissions, bases) {
382
409
  }
383
410
  }
384
411
  }
412
+ if (bases.configRoot === undefined) {
413
+ for (const bucket of RULE_POLICY_BUCKETS) {
414
+ const field = `permissions.${bucket}`;
415
+ for (const name of ruleSetsOf(permissions[bucket] ?? [], bucket).configuration.map(ruleTextOf)) {
416
+ first ??= {
417
+ bucket,
418
+ message: `${field} has a user-settings path rule rooted at the configuration directory (${name}) but this deployment cannot name the EXECUTION environment's configuration root — ` +
419
+ `it is the SEMA_CONFIG_DIR forwarded into the sandbox, else .sema under the execution environment's home, and this lane's home is unknown; the engine never guesses one. ` +
420
+ `Spell the path absolutely (${"Read(//abs/path/**)"} form), forward SEMA_CONFIG_DIR into the sandbox env, declare the lane's home with REMOTE_EXEC_HOME_DIR, or run the task on a lane whose home this worker knows.`,
421
+ };
422
+ problems.push(entryProblem(SETTINGS_RULE_REFUSAL_CODE, `settings.${field}`, name, "home_unresolvable"));
423
+ }
424
+ }
425
+ }
385
426
  return first === undefined ? undefined : { ...first, problems };
386
427
  }
428
+ function ruleSetBases(root, taskBases, configRoot) {
429
+ switch (root) {
430
+ case "task":
431
+ return taskBases;
432
+ case "configuration":
433
+ if (configRoot === undefined)
434
+ throw new Error("a user-settings path rule reached the compiler without a configuration root — the admission judgement refuses it first (home_unresolvable); refusing rather than anchoring it at the task root");
435
+ return { root: configRoot };
436
+ default: {
437
+ const unhandled = root;
438
+ return unhandled;
439
+ }
440
+ }
441
+ }
387
442
  export const REQUEST_SETTINGS_REFUSAL_PREFIX = "request settings cannot be applied as written: ";
388
443
  export function deriveSettingsPolicy(settings, bases) {
389
444
  const perms = settings.permissions;
@@ -396,11 +451,19 @@ export function deriveSettingsPolicy(settings, bases) {
396
451
  const pathBases = bases?.taskRoot !== undefined || bases?.taskHome !== undefined
397
452
  ? { ...(bases.taskRoot !== undefined ? { root: bases.taskRoot } : {}), ...(bases.taskHome !== undefined ? { home: bases.taskHome } : {}) }
398
453
  : undefined;
399
- rejectUnmatchableSettingsNames(perms, pathBases);
400
- if (deny)
401
- policies.push(createRuleListPolicy(deny, "deny", RULE_SOURCE_BY_BEHAVIOR.deny, pathBases));
402
- if (ask)
403
- policies.push(createRuleListPolicy(ask, "ask", RULE_SOURCE_BY_BEHAVIOR.ask, pathBases));
454
+ rejectUnmatchableSettingsNames(perms, pathBases, bases?.configRoot);
455
+ for (const [bucket, entries] of [["deny", deny], ["ask", ask]]) {
456
+ if (!entries)
457
+ continue;
458
+ const sets = ruleSetsOf(entries, bucket);
459
+ for (const root of RULE_ROOTS) {
460
+ const set = sets[root];
461
+ if (set.length === 0)
462
+ continue;
463
+ const table = RULE_SOURCE_BY_BEHAVIOR[bucket];
464
+ policies.push(createRuleListPolicy(set.map((e) => ({ rule: ruleTextOf(e), source: ruleSourceOf(table, e) })), bucket, table, ruleSetBases(root, pathBases, bases?.configRoot)));
465
+ }
466
+ }
404
467
  }
405
468
  if (neverAuto !== undefined) {
406
469
  const refused = neverAutoNamesRefusal(neverAuto);
@@ -57,13 +57,61 @@ export declare const STORE_ERROR_CODE = "internal.store_error";
57
57
  *
58
58
  * ⚠️ **辖域如实**:本臂触发条件是「码出了本仓的词法」,所以任何依赖铸出的域外码都落这里,不只 SQL 驱动。
59
59
  * 今天可观察的产出方**只有**耐久店那一层(引擎自己的传输/brain 错误在 core 侧已被分类成小写码),故码名
60
- * 取 `internal.store_error`;原码逐字进 message 与 warn 行,归因从不丢失。
60
+ * 取 `internal.store_error`;原码逐字进 message 尾注(随结果过汇点脱敏)、是码 token 时进运维 warn 行({@link isCodeToken}),归因从不丢失。
61
61
  */
62
62
  export declare function outwardFailureCode(code: string): string;
63
+ /**
64
+ * 一个错误的 `code` 只在它**是**码(短 token)时才说出口 —— 码形座里装着文字的,不是码(驱动码 `ER_*` / SQLSTATE / `ECONNRESET`
65
+ * 全在这一形里;带空白、`=`、换行或超过 64 字符的就是一句话)。读者两只,同一条判据:折码的运维 warn 行(本文件 {@link sealResultForExit})
66
+ * 与执行车道占位读的故障词(`boot/execution-env.ts`)。
67
+ */
68
+ export declare function isCodeToken(code: unknown): code is string;
69
+ /**
70
+ * 存量结果被**再观看**的读座(闭集):`GET /v1/runs/:id → result`(终态行与停驻行同一座)与账本 `done` 行的回放(三条耐久读腿共用)。
71
+ * 封口在读座上折码时,运维行按 (读座, 原码) 只报一次 —— 见 {@link sealResultForExit}。
72
+ */
73
+ export type StoredResultReadSeat = "run.result" | "ev.done.result";
74
+ /**
75
+ * **一次耐久停驻在一条结果上够得着的全部座** —— 本仓**唯一**一张座表。两个属主共读它,不各抄一份:
76
+ * · 出口封口 {@link sealResultForExit} 按它**摘凭据**(`token` 键);
77
+ * · 结果汇点 `observability/run-terminal.ts` 的 `redactTerminalResult` 按它**脱门**(`gate` 键,门上有模型 / 人可控的展示文本)。
78
+ *
79
+ * 每座 = 从结果根出发的路径 + 该座上的凭据键名与门键名(引擎在两种形上给同一件事起了两套名字:因由形 `token` / `gate`,
80
+ * 验证器停驻形 `checkpointToken` / `checkpointGate`)。三座是**类型的形状**,不是三个特判:
81
+ * · `terminal` —— 本腿自己 park(`paused` 臂);
82
+ * · `terminal.nestedPause` —— 嵌套边界(verify / cascade)遇到自己驱动不了的耐久暂停,在它的边界上如实报 `failed`,
83
+ * 把那次暂停整只带在这里(正是这条路径当年让凭据漏进过账本);
84
+ * · `verification.verifierPause` —— 验证门里**验证器自己**的停驻(core `VerificationOutcome.verifierPause`,
85
+ * `unverifiedReason: "verifier_paused"`):在结果因由**之外**,宿主判官看不见它,run 照常终局。
86
+ *
87
+ * 表是否完整**不靠记性**:`trace/core-keyset-guard.ts` ⑩f 从 core 的结果类型(普通腿 / verify / cascade 三种出口结果)
88
+ * **派生**出每一条凭据型路径与门型路径,与本表逐条对账 —— core 在结果的任何一层加一个凭据座或门座 ⇒ tsc 当场红。
89
+ */
90
+ export declare const PAUSE_SEATS: readonly [{
91
+ readonly path: readonly ["terminal"];
92
+ readonly token: "token";
93
+ readonly gate: "gate";
94
+ }, {
95
+ readonly path: readonly ["terminal", "nestedPause"];
96
+ readonly token: "token";
97
+ readonly gate: "gate";
98
+ }, {
99
+ readonly path: readonly ["verification", "verifierPause"];
100
+ readonly token: "checkpointToken";
101
+ readonly gate: "checkpointGate";
102
+ }];
103
+ /** 座表的一行(类型门 ⑩f 从它派生「本仓摘 / 脱的路径」)。 */
104
+ export type PauseSeat = (typeof PAUSE_SEATS)[number];
105
+ type SeatObject = Readonly<Record<string, unknown>>;
106
+ /**
107
+ * 对一条结果上**每一座**施一次 `f`,不动的座保持同一引用(一座都没动 ⇒ 整条结果同一引用)。
108
+ * 对任何输入安全:路径上任一层缺席 / 非普通对象(含数组)⇒ 那一座跳过,不抛、不重塑(读盘面纪律:旧持久 blob / 非 core 的 Runner 实现)。
109
+ */
110
+ export declare function mapPauseSeats<T>(result: T, f: (seat: SeatObject, keys: PauseSeat) => SeatObject): T;
63
111
  /**
64
112
  * **一条结果离开引擎、进入本仓(可重放账本 / run 行 / wire)的唯一封口** —— 两件事,一个出口:
65
- * ①摘掉**耐久恢复能力凭据**(`CheckpointToken`);②把**域外的失败码**折成本仓的 `internal.store_error`
66
- * ({@link outwardFailureCode},[ref])。
113
+ * ①摘掉**耐久恢复能力凭据**(`CheckpointToken`)—— 座表 {@link PAUSE_SEATS} 上的每一座;②把**域外的失败码**折成本仓的
114
+ * `internal.store_error`({@link outwardFailureCode},[ref])。
67
115
  *
68
116
  * 🔴 为什么两件事合在一只函数里而不是并列两只(7.92.2 改名 `stripResumeToken` → `sealResultForExit`):
69
117
  * 它们是**同一个出口上的同一件事** —— 「这条结果可以见人了吗」。并列两只 = 六个结算点各自要记得调两次,
@@ -71,26 +119,33 @@ export declare function outwardFailureCode(code: string): string;
71
119
  * 旧名只说了摘凭据那一半,一个只做一半的名字会让下一个人在另一半上另开出口。
72
120
  *
73
121
  * ── ① 摘凭据 ──
74
- * token 就是 resume 凭据(token-as-auth,search [18] Q1b),**永不离开本服务**;非密的 `gate` /
75
- * `checkpointId` / `restoreMode` 有意保留(对账位)。
76
- *
77
- * 🔴 core 7.6.0 起凭据换了住址:它不再是 `TaskResult` 顶层的 `checkpointToken`,而是因由里
78
- * `paused` 那一臂的 `token`。而**一条结果上够得着 paused 因由的位置有两处**,这是类型的形状,不是特判:
79
- * · `terminal`(kind `paused`)—— 本腿自己 park 了;
80
- * · `terminal.nestedPause`(kind `failed`)—— 嵌套边界(verify / cascade)遇到自己驱动不了的耐久暂停,
81
- * 在**它的**边界上如实报 `failed`,而把那次暂停的因由整只带在这里(顶层调用方据此仍能恢复)。
82
- * 正是这条路径当年让 `checkpointToken` 漏进过账本 —— 摘除腿从来不是「理论上的对称」。
83
- * 两处走**同一条**摘除规则({@link stripPausedToken}),不是两条。
84
- *
85
- * wire 上的形:paused 因由**缺席 `token`**(core 类型里它是必填座,那是**引擎内**的契约;本服务的 wire
86
- * 契约逐字声明这一座不出服务边界 —— 见 `docs/ASSISTANT-WIRE-CONTRACT.md`)。消费端要动这次暂停,走
122
+ * token 就是 resume 凭据(token-as-auth,search [18] Q1b),**永不离开本服务**;非密的门 / `checkpointId` /
123
+ * `restoreMode` / 验证器停驻的 `status` 与 `sessionId` 有意保留(对账位;门的自由文本由结果汇点脱)。
124
+ * 凭据住在哪几座是 {@link PAUSE_SEATS} 一张表的事,本函数**不**在别处另开摘除臂:漏掉的那一座(验证器停驻,修前)
125
+ * 正是「座表只写了两座」造成的,修法是座表加一行,不是另起第二处摘字。
126
+ *
127
+ * wire 上的形:每一座**缺席**凭据键(core 类型里因由的 `token` 是必填座,那是**引擎内**的契约;本服务的 wire
128
+ * 契约逐字声明这一座不出服务边界 —— 见 `docs/ASSISTANT-WIRE-CONTRACT.md`)。消费端要动一次停驻,走
87
129
  * `/v1/approvals` 决策端点,凭 `checkpointId` 对账,从来不是拿 token 直连引擎。
88
130
  *
89
- * 对任何输入安全(非普通对象 / 无 `terminal` / 因由不带 token ⇒ **同一引用**原样返回,不重塑)。
131
+ * **出口全集都经这一口**:三家 run 店的行写口与账本店的 `done` 行写口在店入口调它(`observability/run-terminal.ts`),
132
+ * 同步 200 体 / 流 done 帧 / 续跑决定腿在铸体处调它,`GET /v1/runs/:id{,/events}` 等读面在回放前再调一次 ——
133
+ * 升级前落盘、还带着凭据的存量行在读面上同样摘掉。两件事都**幂等**,重复过口是同形。
134
+ *
135
+ * ── 折码的运维线索:一行定形句,两个观看点同一句 ──
136
+ * `[terminal] outward_failure_code_folded original=<码 | <not-a-code>> outward=<对外词> messageBytes=<n> at=<exit | 读座>`。
137
+ * 字段只有闭集词、数字与码 token({@link isCodeToken}):失败句只报字节数,**永不**报原文 —— 失败句是自由文本(驱动句里可以有连接串 / 口令),
138
+ * 结果里的那份由结果汇点脱敏,日志行没有汇点,所以日志行压根不带它;码形座里装着文字的同理只报 `<not-a-code>`。`at` 说这是哪一次观看:
139
+ * · 省略 `readSeat` = 结果**第一次**离开引擎(店的写口 / 出口铸体)⇒ 每折一次报一次 —— 每次都是一件新的失败;
140
+ * · 给出读座({@link StoredResultReadSeat})= 存量行的**再观看** ⇒ 同一读座同一原码(按它在行上的样子)只报一次,表有界(先进先出)——
141
+ * 读面每次轮询 / 每个观看者 / 每次重连都重放同一批行,逐读逐报会把一次旧失败放大成读次那么多行,而它们说的是同一件事。
142
+ * 折**本身**照旧逐读做(wire 上永远是对外词),去重的只是日志行。
143
+ *
144
+ * 对任何输入安全(非普通对象 / 无可摘的座 / 码在词法内 ⇒ **同一引用**原样返回,不重塑)。
90
145
  */
91
146
  export declare function sealResultForExit<T extends {
92
- terminal: TerminalCause;
93
- }>(result: T): T;
147
+ readonly terminal?: unknown;
148
+ }>(result: T, readSeat?: StoredResultReadSeat): T;
94
149
  /**
95
150
  * **投影出口的唯一属主**([ref])—— core 的 `terminalProjection` + 对外码归一,一次。
96
151
  *
@@ -129,6 +184,34 @@ export declare function nestedPauseOf(terminal: TerminalCause): PausedCause | un
129
184
  * status 恒 `"failed"`)。因由形下两条路径合成一句话,规则从二变一。
130
185
  */
131
186
  export declare function pausedCauseOf(terminal: TerminalCause): PausedCause | undefined;
187
+ /**
188
+ * **宿主边界上的诚实终局** —— 宿主 run 行停不停、停在哪个词上,只问这一只判官。
189
+ *
190
+ * · 本腿自己的 `paused` ⇒ 原样(它就是宿主腿的停驻);
191
+ * · `failed.nestedPause` 且 `result.sessionId === hostSessionId` ⇒ 把那次停驻**整只**提成本腿的终局因由
192
+ * (门 / 凭据 / `checkpointId` 同址;结果的其余格 —— `stats` / `verification` / … —— 原样);
193
+ * · 其余 ⇒ **同一引用**原样(真失败;或嵌套停驻的卡不在宿主会话上)。
194
+ *
195
+ * 为什么判据是「停驻的那条腿跑在宿主会话上」:宿主的续跑链按会话把一张卡连回 run —— 决定腿按卡的会话找行
196
+ * (`getActiveTaskId`)与续跑输入(`getCtx`),取消按 run 的会话找待决卡,过期收割按会话相等连行与卡。卡在宿主会话上,
197
+ * 这条 run 才兑现得了它;判官只把兑现得了的停驻判成停驻。verify 的实现腿与修复轮跑在宿主会话上 ⇒ 提;cascade
198
+ * 每级被引擎剥掉会话、级上的卡落在级自己现铸的会话上 ⇒ 不提(照常终局)。这不是按组合器分的特例:引擎让级上的卡落到
199
+ * 宿主可连的会话上的那一天,答案随事实翻,本函数一个字不改。读 `result.sessionId` 而不去店里读卡:它与卡行的会话是同一次
200
+ * `runTask` 的同一个量,零 I/O。
201
+ *
202
+ * 前提:结果出自 core 的组合器(可信产者)—— `failed.nestedPause` 只由引擎的嵌套边界包停驻铸,`paused` 只在卡行写入确认之后返回。
203
+ * 判官**不**验证那张卡在不在、是不是这一张、此刻什么态;会话相等不是对卡的校验。验证器**自己**的停驻
204
+ * (`verification.verifierPause`)不在结果因由里,本函数看不见它 —— 那一形照常终局;它那张卡本服务兑现不了,由结算点当场作废
205
+ * (`verifier-pause-card.ts`),凭据由封口按座表摘。
206
+ *
207
+ * 纯函数,不读店、不抛。凭据随提升上来,摘除仍归 {@link sealResultForExit}(调用方照旧先 graft 后封口)。
208
+ * 判官只答「该不该停驻」;认领保不保得住是停驻写口**写成功**与否的事(行写口对 `running` / `suspended` 行 CAS)。
209
+ * `hostSessionId` 缺席 = 没有宿主会话 ⇒ 没有续跑链兑现得了嵌套停驻 ⇒ 原样(相等判自然为假,不另设臂)。
210
+ */
211
+ export declare function atHostBoundary<T extends {
212
+ readonly terminal: TerminalCause;
213
+ readonly sessionId: string;
214
+ }>(result: T, hostSessionId: string | undefined): T;
132
215
  /**
133
216
  * **持久 blob 的平面读面** —— 把一条**任何年代**的持久终局结果读成五词 `status` + 三个平面文本格。
134
217
  *
@@ -151,4 +234,5 @@ export declare function persistedPlaneOf(result: unknown): {
151
234
  errorMessage?: string;
152
235
  blockedReason?: string;
153
236
  };
237
+ export {};
154
238
  //# sourceMappingURL=terminal.d.ts.map
package/dist/terminal.js CHANGED
@@ -8,25 +8,75 @@ export const STORE_ERROR_CODE = "internal.store_error";
8
8
  export function outwardFailureCode(code) {
9
9
  return OUTWARD_CODE_SHAPE.test(code) ? code : STORE_ERROR_CODE;
10
10
  }
11
- export function sealResultForExit(result) {
12
- const t = result?.terminal;
13
- if (t === null || typeof t !== "object")
11
+ export function isCodeToken(code) {
12
+ return typeof code === "string" && CODE_TOKEN_SHAPE.test(code);
13
+ }
14
+ const CODE_TOKEN_SHAPE = /^[A-Za-z0-9_.:-]{1,64}$/;
15
+ export const PAUSE_SEATS = [
16
+ { path: ["terminal"], token: "token", gate: "gate" },
17
+ { path: ["terminal", "nestedPause"], token: "token", gate: "gate" },
18
+ { path: ["verification", "verifierPause"], token: "checkpointToken", gate: "checkpointGate" },
19
+ ];
20
+ export function mapPauseSeats(result, f) {
21
+ let out = result;
22
+ for (const seat of PAUSE_SEATS)
23
+ out = mapSeatAt(out, seat.path, 0, (o) => f(o, seat));
24
+ return out;
25
+ }
26
+ function mapSeatAt(node, path, depth, f) {
27
+ if (node === null || typeof node !== "object" || Array.isArray(node))
28
+ return node;
29
+ if (depth === path.length)
30
+ return f(node);
31
+ const key = path[depth];
32
+ if (!Object.hasOwn(node, key))
33
+ return node;
34
+ const child = node[key];
35
+ const next = mapSeatAt(child, path, depth + 1, f);
36
+ return Object.is(next, child) ? node : { ...node, [key]: next };
37
+ }
38
+ function dropCapability(seat, keys) {
39
+ if (!Object.hasOwn(seat, keys.token))
40
+ return seat;
41
+ const { [keys.token]: _drop, ...rest } = seat;
42
+ return rest;
43
+ }
44
+ export function sealResultForExit(result, readSeat) {
45
+ if (result === null || typeof result !== "object")
14
46
  return result;
15
- if (t.kind === "paused")
16
- return { ...result, terminal: stripPausedToken(t) };
17
- if (t.kind !== "failed")
47
+ return foldOutwardFailureCode(mapPauseSeats(result, dropCapability), readSeat);
48
+ }
49
+ function foldOutwardFailureCode(result, readSeat) {
50
+ const t = failedCauseWithCode(result.terminal);
51
+ if (t === undefined)
18
52
  return result;
19
- let terminal = t;
20
- if (t.nestedPause !== undefined)
21
- terminal = { ...terminal, nestedPause: stripPausedToken(t.nestedPause) };
22
- if (t.code !== undefined) {
23
- const outward = outwardFailureCode(t.code);
24
- if (outward !== t.code) {
25
- console.warn(`[terminal] outward_failure_code_folded original=${t.code} outward=${outward}`, { message: t.message });
26
- terminal = { ...terminal, code: outward, message: `${t.message ?? "the durable store failed"} [original code: ${t.code}]` };
27
- }
53
+ const outward = outwardFailureCode(t.code);
54
+ if (outward === t.code)
55
+ return result;
56
+ reportFold(t.code, outward, t.message, readSeat);
57
+ const message = typeof t.message === "string" ? t.message : "the durable store failed";
58
+ return { ...result, terminal: { ...t, code: outward, message: `${message} [original code: ${t.code}]` } };
59
+ }
60
+ const READ_FOLD_LATCH_CAP = 1024;
61
+ const readFoldLatch = new Set();
62
+ function reportFold(code, outward, message, readSeat) {
63
+ const original = isCodeToken(code) ? code : "<not-a-code>";
64
+ if (readSeat !== undefined) {
65
+ const key = `${readSeat} ${original}`;
66
+ if (readFoldLatch.has(key))
67
+ return;
68
+ if (readFoldLatch.size >= READ_FOLD_LATCH_CAP)
69
+ readFoldLatch.delete(readFoldLatch.values().next().value);
70
+ readFoldLatch.add(key);
28
71
  }
29
- return terminal === t ? result : { ...result, terminal };
72
+ const messageBytes = typeof message === "string" ? Buffer.byteLength(message, "utf8") : 0;
73
+ console.warn(`[terminal] outward_failure_code_folded original=${original} outward=${outward} messageBytes=${messageBytes} at=${readSeat ?? "exit"}`);
74
+ }
75
+ function failedCauseWithCode(t) {
76
+ if (t === null || typeof t !== "object")
77
+ return undefined;
78
+ const { kind, code } = t;
79
+ return kind === "failed" && typeof code === "string" ? { ...t, kind, code } : undefined;
30
80
  }
31
81
  export function outwardPlane(terminal) {
32
82
  const plane = terminalProjection(terminal);
@@ -35,16 +85,18 @@ export function outwardPlane(terminal) {
35
85
  const outward = outwardFailureCode(plane.errorCode);
36
86
  return outward === plane.errorCode ? plane : { ...plane, errorCode: outward };
37
87
  }
38
- function stripPausedToken(paused) {
39
- const { token: _drop, ...rest } = paused;
40
- return rest;
41
- }
42
88
  export function nestedPauseOf(terminal) {
43
89
  return terminal.kind === "failed" ? terminal.nestedPause : undefined;
44
90
  }
45
91
  export function pausedCauseOf(terminal) {
46
92
  return terminal.kind === "paused" ? terminal : nestedPauseOf(terminal);
47
93
  }
94
+ export function atHostBoundary(result, hostSessionId) {
95
+ const parked = nestedPauseOf(result.terminal);
96
+ if (parked === undefined || result.sessionId !== hostSessionId)
97
+ return result;
98
+ return { ...result, terminal: parked };
99
+ }
48
100
  export function persistedPlaneOf(result) {
49
101
  if (result === null || typeof result !== "object")
50
102
  return {};