@sema-agent/server 7.140.0 → 7.142.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 (296) hide show
  1. package/MIGRATION.md +505 -0
  2. package/README.md +1 -1
  3. package/README.zh-CN.md +1 -1
  4. package/USAGE.md +142 -25
  5. package/dist/approval-ask-audit-store.d.ts +54 -38
  6. package/dist/approval-ask-audit-store.js +58 -39
  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 +39 -24
  10. package/dist/approval-card.js +6 -3
  11. package/dist/approval-crash-convergence.d.ts +17 -7
  12. package/dist/approval-crash-convergence.js +94 -22
  13. package/dist/approval-deny-reasons.d.ts +34 -30
  14. package/dist/approval-deny-reasons.js +21 -6
  15. package/dist/approval-policy-settlement.d.ts +1 -1
  16. package/dist/approval-reconciler.d.ts +33 -1
  17. package/dist/approval-reconciler.js +43 -40
  18. package/dist/approval.d.ts +15 -0
  19. package/dist/approval.js +10 -0
  20. package/dist/audit.d.ts +4 -2
  21. package/dist/audit.js +2 -2
  22. package/dist/auth-bridge.d.ts +9 -6
  23. package/dist/auth-bridge.js +9 -3
  24. package/dist/boot/adoption.js +2 -1
  25. package/dist/boot/budget-tracing.d.ts +16 -0
  26. package/dist/boot/budget-tracing.js +10 -6
  27. package/dist/boot/device-lane.d.ts +11 -0
  28. package/dist/boot/device-lane.js +16 -0
  29. package/dist/boot/engine-lease.d.ts +24 -3
  30. package/dist/boot/engine-lease.js +9 -2
  31. package/dist/boot/execution-env.d.ts +24 -4
  32. package/dist/boot/execution-env.js +34 -27
  33. package/dist/boot/execution-lane.js +2 -1
  34. package/dist/boot/exit-boundary.js +5 -4
  35. package/dist/boot/memory-boundary.d.ts +2 -2
  36. package/dist/boot/memory-consolidation.d.ts +1 -1
  37. package/dist/boot/memory-consolidation.js +3 -2
  38. package/dist/boot/parked-revive-gate.js +2 -1
  39. package/dist/boot/permission-rules-audit.d.ts +0 -28
  40. package/dist/boot/permission-rules-audit.js +2 -1
  41. package/dist/boot/read-deny-anchor-audit.js +2 -1
  42. package/dist/boot/reapers.d.ts +2 -2
  43. package/dist/boot/reapers.js +4 -3
  44. package/dist/boot/resolve-spec.js +18 -6
  45. package/dist/boot/retention-lane.js +5 -3
  46. package/dist/boot/runner-deps.js +16 -13
  47. package/dist/boot/runtime-caps.js +3 -2
  48. package/dist/boot/session-faces.d.ts +1 -1
  49. package/dist/boot/session-faces.js +8 -6
  50. package/dist/boot/shutdown.js +8 -2
  51. package/dist/boot/stage-04-stores.js +3 -2
  52. package/dist/boot/stage-05-execution-env.d.ts +1 -1
  53. package/dist/boot/stage-05-execution-env.js +11 -7
  54. package/dist/boot/stage-06-runners.d.ts +1 -3
  55. package/dist/boot/stage-06-runners.js +3 -2
  56. package/dist/boot/stage-07-capability-layer.d.ts +1 -2
  57. package/dist/boot/stage-07-capability-layer.js +10 -11
  58. package/dist/boot/stage-08-reapers.d.ts +1 -2
  59. package/dist/boot/stage-08-reapers.js +3 -2
  60. package/dist/boot/stage-09-leader.d.ts +1 -2
  61. package/dist/boot/stage-10-http-server.d.ts +1 -2
  62. package/dist/boot/stage-10-http-server.js +6 -4
  63. package/dist/boot/stores.js +13 -6
  64. package/dist/boot/web-admin.js +18 -17
  65. package/dist/boot/webfetch-summarize-lane.js +2 -1
  66. package/dist/boot/workflow-orchestration.js +3 -2
  67. package/dist/capabilities/builtin-tools.d.ts +2 -1
  68. package/dist/capabilities/builtin-tools.js +2 -4
  69. package/dist/capabilities/composite-members.d.ts +2 -1
  70. package/dist/capabilities/composite-members.js +2 -1
  71. package/dist/capabilities/scenarios.d.ts +1 -1
  72. package/dist/capability-facts.d.ts +33 -0
  73. package/dist/capability-facts.js +32 -0
  74. package/dist/card-void.d.ts +38 -0
  75. package/dist/card-void.js +23 -0
  76. package/dist/config-catalog.js +12 -8
  77. package/dist/config-provider.d.ts +13 -13
  78. package/dist/config-provider.js +19 -33
  79. package/dist/config-types.d.ts +39 -10
  80. package/dist/config.d.ts +6 -0
  81. package/dist/config.js +72 -49
  82. package/dist/delegated-session-owner.d.ts +21 -19
  83. package/dist/delegated-session-owner.js +1 -1
  84. package/dist/device-store.d.ts +7 -0
  85. package/dist/device-store.js +3 -0
  86. package/dist/execution-config-root.d.ts +22 -0
  87. package/dist/execution-config-root.js +35 -0
  88. package/dist/execution-lane-caps.d.ts +50 -2
  89. package/dist/execution-lane-caps.js +90 -13
  90. package/dist/execution-placement.d.ts +3 -0
  91. package/dist/execution-placement.js +7 -0
  92. package/dist/fleet/drain-entry.d.ts +3 -0
  93. package/dist/fleet/fleet-bus.d.ts +6 -3
  94. package/dist/hooks/hook-runner.d.ts +6 -0
  95. package/dist/hooks/hook-runner.js +30 -1
  96. package/dist/hosted-posture.d.ts +8 -0
  97. package/dist/hosted-posture.js +4 -0
  98. package/dist/http/admission.js +3 -3
  99. package/dist/http/caller-trust.d.ts +3 -2
  100. package/dist/http/caller-trust.js +2 -1
  101. package/dist/http/dispatch.js +8 -2
  102. package/dist/http/execution-context.d.ts +2 -2
  103. package/dist/http/idempotency.d.ts +1 -1
  104. package/dist/http/live-frame.d.ts +24 -0
  105. package/dist/http/live-frame.js +16 -0
  106. package/dist/http/principal-gate.d.ts +3 -1
  107. package/dist/http/principal-gate.js +17 -8
  108. package/dist/http/resume-legs.js +42 -29
  109. package/dist/http/route-table.js +4 -0
  110. package/dist/http/routes/approvals-assistant.js +1 -0
  111. package/dist/http/routes/attachments.js +18 -0
  112. package/dist/http/routes/capabilities.js +10 -6
  113. package/dist/http/routes/diagnostics.d.ts +0 -19
  114. package/dist/http/routes/diagnostics.js +16 -2
  115. package/dist/http/routes/memory-bundle.d.ts +2 -2
  116. package/dist/http/routes/memory-bundle.js +1 -1
  117. package/dist/http/routes/memory-compliance.js +1 -1
  118. package/dist/http/routes/memory-policy.js +27 -7
  119. package/dist/http/routes/run-permission-mode.d.ts +75 -0
  120. package/dist/http/routes/run-permission-mode.js +170 -0
  121. package/dist/http/routes/runs.js +3 -3
  122. package/dist/http/routes/session-memory-sync.d.ts +63 -0
  123. package/dist/http/routes/session-memory-sync.js +157 -0
  124. package/dist/http/routes/session-sync.d.ts +3 -6
  125. package/dist/http/routes/session-sync.js +7 -2
  126. package/dist/http/routes/sessions-list.d.ts +2 -1
  127. package/dist/http/routes/sessions.js +5 -4
  128. package/dist/http/routes/tasks.js +70 -71
  129. package/dist/http/run-read-face.d.ts +7 -0
  130. package/dist/http/run-read-face.js +10 -4
  131. package/dist/http/send.d.ts +6 -1
  132. package/dist/http/send.js +10 -3
  133. package/dist/http/server.d.ts +20 -7
  134. package/dist/http/server.js +2 -4
  135. package/dist/http/sse-log.d.ts +3 -8
  136. package/dist/http/stored-body-replay.d.ts +3 -3
  137. package/dist/http/stored-body-replay.js +3 -1
  138. package/dist/http/submit-replay.d.ts +4 -3
  139. package/dist/http/submit-replay.js +2 -2
  140. package/dist/http/sync-submit-body.d.ts +21 -4
  141. package/dist/http/sync-submit-body.js +13 -0
  142. package/dist/identity-source.d.ts +21 -2
  143. package/dist/identity-source.js +6 -0
  144. package/dist/instruction-source-notice.d.ts +7 -0
  145. package/dist/instruction-source-notice.js +6 -6
  146. package/dist/mcp-assertion-header.d.ts +4 -0
  147. package/dist/mcp-assertion-header.js +5 -0
  148. package/dist/memory-incident-sink.d.ts +19 -6
  149. package/dist/memory-incident-sink.js +8 -4
  150. package/dist/memory-posture.d.ts +1 -1
  151. package/dist/memory-scope.d.ts +4 -4
  152. package/dist/memory-scope.js +1 -1
  153. package/dist/memory-sync-client.d.ts +35 -23
  154. package/dist/memory-sync-client.js +122 -52
  155. package/dist/memory-sync-identity.d.ts +24 -0
  156. package/dist/memory-sync-identity.js +57 -0
  157. package/dist/memory-sync.d.ts +7 -1
  158. package/dist/memory-sync.js +6 -3
  159. package/dist/observability/fail-open.d.ts +57 -12
  160. package/dist/observability/fail-open.js +50 -9
  161. package/dist/observability/metrics.js +0 -1
  162. package/dist/observability/run-terminal.d.ts +93 -31
  163. package/dist/observability/run-terminal.js +175 -171
  164. package/dist/observability/secret-env-scrub.d.ts +20 -1
  165. package/dist/observability/secret-env-scrub.js +14 -0
  166. package/dist/orchestration/workflow-completion-inbox.d.ts +5 -2
  167. package/dist/orchestration/workflow-completion-inbox.js +13 -5
  168. package/dist/orchestration/workflow-notify-journal.d.ts +5 -3
  169. package/dist/orchestration/workflow-notify-journal.js +2 -1
  170. package/dist/permission-mode-admission.d.ts +68 -0
  171. package/dist/permission-mode-admission.js +23 -0
  172. package/dist/permission-rule-list.d.ts +55 -6
  173. package/dist/permission-rule-list.js +52 -5
  174. package/dist/plugins/approval-ask-store-file.d.ts +14 -4
  175. package/dist/plugins/approval-ask-store-file.js +48 -7
  176. package/dist/plugins/approval-ask-store-memory.d.ts +2 -1
  177. package/dist/plugins/approval-ask-store-memory.js +9 -8
  178. package/dist/plugins/approval-ask-store-sql.d.ts +42 -10
  179. package/dist/plugins/approval-ask-store-sql.js +49 -19
  180. package/dist/plugins/caching-session-store.d.ts +6 -0
  181. package/dist/plugins/caching-session-store.js +3 -0
  182. package/dist/plugins/checkpoint-store-sql.d.ts +65 -17
  183. package/dist/plugins/checkpoint-store-sql.js +74 -3
  184. package/dist/plugins/device-store-sql.d.ts +5 -2
  185. package/dist/plugins/device-store-sql.js +4 -1
  186. package/dist/plugins/execution-placement-sql.d.ts +11 -1
  187. package/dist/plugins/execution-placement-sql.js +12 -1
  188. package/dist/plugins/file-run-store.d.ts +7 -2
  189. package/dist/plugins/file-run-store.js +12 -2
  190. package/dist/plugins/fork-routing-session-store.d.ts +10 -32
  191. package/dist/plugins/fork-routing-session-store.js +2 -61
  192. package/dist/plugins/k8s-exec-protocol.d.ts +4 -1
  193. package/dist/plugins/k8s-exec-protocol.js +6 -1
  194. package/dist/plugins/local-checkpoint-store.d.ts +10 -1
  195. package/dist/plugins/local-checkpoint-store.js +22 -0
  196. package/dist/plugins/local-session-store.d.ts +260 -39
  197. package/dist/plugins/local-session-store.js +582 -57
  198. package/dist/plugins/local-session-window.d.ts +17 -3
  199. package/dist/plugins/local-session-window.js +7 -13
  200. package/dist/plugins/local-task-attachment-store.d.ts +12 -2
  201. package/dist/plugins/local-task-attachment-store.js +67 -43
  202. package/dist/plugins/memory-key-guards.d.ts +3 -3
  203. package/dist/plugins/memory-run-store.d.ts +7 -2
  204. package/dist/plugins/memory-run-store.js +12 -2
  205. package/dist/plugins/one-time-migrations.d.ts +68 -2
  206. package/dist/plugins/one-time-migrations.js +120 -4
  207. package/dist/plugins/pg-pool.js +3 -2
  208. package/dist/plugins/pg-session-storage.d.ts +2 -0
  209. package/dist/plugins/pg-session-storage.js +3 -2
  210. package/dist/plugins/remote-env-e2b.d.ts +47 -7
  211. package/dist/plugins/remote-env-e2b.js +242 -46
  212. package/dist/plugins/remote-env-k8s.d.ts +51 -6
  213. package/dist/plugins/remote-env-k8s.js +234 -44
  214. package/dist/plugins/remote-env-local-docker.d.ts +40 -14
  215. package/dist/plugins/remote-env-local-docker.js +127 -33
  216. package/dist/plugins/run-store-sql.d.ts +11 -3
  217. package/dist/plugins/run-store-sql.js +13 -4
  218. package/dist/plugins/sandbox-read-floor.d.ts +273 -0
  219. package/dist/plugins/sandbox-read-floor.js +608 -0
  220. package/dist/plugins/session-ownership.d.ts +2 -1
  221. package/dist/plugins/session-placement.d.ts +17 -4
  222. package/dist/plugins/session-placement.js +2 -1
  223. package/dist/plugins/session-store.d.ts +18 -20
  224. package/dist/plugins/session-store.js +4 -12
  225. package/dist/plugins/store-backend.d.ts +41 -7
  226. package/dist/plugins/store-backend.js +22 -4
  227. package/dist/plugins/store-contracts.d.ts +35 -3
  228. package/dist/plugins/store-contracts.js +3 -0
  229. package/dist/plugins/task-attachment-store.d.ts +112 -14
  230. package/dist/plugins/task-attachment-store.js +221 -28
  231. package/dist/plugins/tidb-pool.js +2 -1
  232. package/dist/plugins/tidb-session-store.d.ts +4 -1
  233. package/dist/plugins/tidb-session-store.js +3 -2
  234. package/dist/plugins/workspace-fill.d.ts +60 -0
  235. package/dist/plugins/workspace-fill.js +106 -0
  236. package/dist/project-bundle.d.ts +38 -0
  237. package/dist/project-bundle.js +609 -0
  238. package/dist/run-cancel-context.d.ts +34 -0
  239. package/dist/run-cancel-context.js +7 -0
  240. package/dist/run-local.js +2 -8
  241. package/dist/runs.d.ts +39 -12
  242. package/dist/runs.js +69 -55
  243. package/dist/sandbox-egress-policy.d.ts +4 -1
  244. package/dist/sandbox-egress-policy.js +3 -1
  245. package/dist/sandbox-git-plane.d.ts +31 -12
  246. package/dist/sandbox-git-plane.js +24 -4
  247. package/dist/security.d.ts +11 -2
  248. package/dist/security.js +8 -0
  249. package/dist/server-secret-env.d.ts +2 -1
  250. package/dist/server-secret-env.js +2 -4
  251. package/dist/session-error-wire.d.ts +46 -4
  252. package/dist/session-error-wire.js +23 -0
  253. package/dist/session-incarnation.d.ts +9 -3
  254. package/dist/session-sync-kernel.d.ts +42 -1
  255. package/dist/session-sync-kernel.js +17 -0
  256. package/dist/session-sync.js +16 -4
  257. package/dist/sql-engine-coordinates.d.ts +49 -0
  258. package/dist/sql-engine-coordinates.js +27 -0
  259. package/dist/standing-answer-grant.d.ts +31 -2
  260. package/dist/standing-answer-grant.js +14 -2
  261. package/dist/store-posture.d.ts +1 -1
  262. package/dist/task-a2a.d.ts +1 -1
  263. package/dist/task-mcp.d.ts +1 -1
  264. package/dist/task-settings.d.ts +62 -27
  265. package/dist/task-settings.js +105 -29
  266. package/dist/terminal.d.ts +97 -20
  267. package/dist/terminal.js +107 -18
  268. package/dist/tool-approval.d.ts +4 -3
  269. package/dist/tool-approval.js +12 -14
  270. package/dist/trace/core-keyset-guard.d.ts +196 -2
  271. package/dist/trace/engine-notice-wire.d.ts +8 -2
  272. package/dist/trace/engine-notice-wire.js +4 -2
  273. package/dist/trace/exit-seat.d.ts +9 -1
  274. package/dist/trace/exit-seat.js +1 -0
  275. package/dist/trace/ledger-sink.d.ts +8 -4
  276. package/dist/trace/ledger-sink.js +2 -2
  277. package/dist/trace/model-segment.d.ts +2 -2
  278. package/dist/trace/project.d.ts +18 -1
  279. package/dist/trace/project.js +16 -0
  280. package/dist/trace/redact.js +323 -90
  281. package/dist/trace/self-minted-wire.d.ts +419 -4
  282. package/dist/trace/self-minted-wire.js +145 -0
  283. package/dist/trace/wire-projection-faces.d.ts +1 -1
  284. package/dist/trace/wire-projection-faces.js +1 -1
  285. package/dist/verifier-pause-card.d.ts +8 -0
  286. package/dist/verifier-pause-card.js +19 -0
  287. package/dist/web-admin-client/apply-effective.d.ts +5 -7
  288. package/dist/web-admin-client/apply-effective.js +22 -2
  289. package/dist/web-admin-client/candidate-gate.d.ts +1 -1
  290. package/dist/web-admin-client/effective-keys.js +2 -0
  291. package/dist/web-admin-client/read-warnings.d.ts +2 -2
  292. package/dist/web-admin-client/types.d.ts +4 -0
  293. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -2
  294. package/package.json +2 -2
  295. package/wire-changes/server@7.141.0.json +367 -0
  296. package/wire-changes/server@7.142.0.json +639 -0
@@ -49,10 +49,12 @@
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";
55
+ import { type HttpHookAllowlistLayer } from "@sema-agent/settings-schema";
54
56
  import type { StoredHooksRereadM20 } from "./stored-request-hooks-m20.js";
55
- import { type EntryPermissionMode } from "./runtime-governance.js";
57
+ import type { EntryPermissionMode } from "./runtime-governance.js";
56
58
  import { type TightenOnlySeatValues } from "./tighten-only-seats.js";
57
59
  /**
58
60
  * 请求面 `permissionMode` 的闭集 = core `PERMISSION_MODES` **全集**(顶层 `permissionMode` / `settings.permissions.defaultMode` /
@@ -112,23 +114,21 @@ export declare function planReviewDecisionExitsPlan(decision: "approve" | "edit"
112
114
  /**
113
115
  * 运维 `AUTONOMY=plan` 的**入口词地板**盖进提交体 —— 此后一条 run 的模式词只有存储体顶层 `permissionMode` 一个源。
114
116
  *
115
- * 入口词 = `runtime-governance.ts` `entryPermissionMode(体的生效词, 地板).effective`(「词在地板下变成什么」的唯一一只函数;秩是 core 的)。本函数只加
116
- * 「写进体」这一半:地板缺席 ⇒ 体**原物**交回(零字节变化);地板在场 ⇒ 新体 `{ ...body, permissionMode: 入口词 }`,调用方的对象不改;bundle 的
117
- * `settings.permissions.defaultMode` 不动 —— 顶层赢过它({@link effectivePermissionMode} 的优先序),再改它就是同一语义的第二个写者。
118
- * 同时交回**这一次求值的读数**(`entry`:请求词 / 地板 / 生效词 / 折没折)—— 披露这一折的通告(`entry-mode-clamp-notice.ts`)读它,不另判一遍。
117
+ * 入口词由受理判官答(`permission-mode-admission.ts` `admitPermissionModeWord`:两口 —— 提交受理口与在飞 run 的切档口 —— 同一只;它交回的
118
+ * `entry` 就是「词在地板下变成什么」那一次求值的读数,秩是 core 的)。本函数只做「写进体」这一半:地板缺席 ⇒ 体**原物**交回(零字节变化);地板在场 ⇒
119
+ * 新体 `{ ...body, permissionMode: entry.effective }`,调用方的对象不改;bundle 的 `settings.permissions.defaultMode` 不动 —— 顶层赢过它
120
+ * ({@link effectivePermissionMode} 的优先序),再改它就是同一语义的第二个写者。披露这一折的通告(`entry-mode-clamp-notice.ts`)读的也是同一份 `entry`。
119
121
  *
120
- * 调用点一个:提交入口(`http/admission.ts` 的 `prepareSpec`:值形门全部判完之后、`resolveSpec` 之前)。那一份体就是首跑装配读的体,也是
122
+ * 调用点一个:提交入口的那一步 `permission-mode-admission.ts` `stampSubmissionEntryMode`(受理判官的入口一步;`http/admission.ts` 的 `prepareSpec` 调它:值形门全部判完之后、`resolveSpec` 之前)。那一份体就是首跑装配读的体,也是
121
123
  * `buildCheckpointCtx` 持久化的体 ⇒ 一处盖章同时覆盖首跑与每一次续跑重建;装配腿与续跑腿都不读地板(两处读 = 两个源)。
122
- * 另一个写者是 `plan_review` 写回(`http/resume-legs.ts` `drivePlanReview`,判据 {@link planReviewDecisionExitsPlan})。
123
- * 热改 `AUTONOMY` 只影响之后提交的任务:在飞的 run 保持它出生时盖下的词。地板的属主是 `runtime-governance.ts` 的 `autonomyModeFloor`。
124
+ * 另外两个写者是 `plan_review` 写回(`http/resume-legs.ts` `drivePlanReview`,判据 {@link planReviewDecisionExitsPlan})与在飞 run 的切档口写回
125
+ * (`http/routes/run-permission-mode.ts`,写的是同一只判官准入的词)。
126
+ * 热改 `AUTONOMY` 只影响之后提交的任务与之后的切档:续跑腿保持存储体里的词。地板的属主是 `runtime-governance.ts` 的 `autonomyModeFloor`。
124
127
  */
125
128
  export declare function stampEntryPermissionMode<B extends {
126
129
  permissionMode?: unknown;
127
130
  settings?: unknown;
128
- }>(body: B, floor: PermissionMode | undefined): {
129
- readonly body: B;
130
- readonly entry: EntryPermissionMode;
131
- };
131
+ }>(body: B, entry: EntryPermissionMode): B;
132
132
  /**
133
133
  * [ref] —— **一条已持久化的提交体的生效 permission mode**,单点。
134
134
  *
@@ -150,8 +150,8 @@ export declare function storedBodyPermissionMode(body: {
150
150
  * future-proof). Awareness rides for free: core composes ORCHESTRATION_AWARENESS from thinking∈{xhigh,max}, no field. */
151
151
  export declare function effectiveThinking(reasoningEffort: unknown, ultracode: boolean): ThinkingLevel | undefined;
152
152
  /**
153
- * [ref] §6-1 —— 本请求的**生效** permission mode,单点。优先序:顶层 `body.permissionMode`(本轮显式表态)赢过 bundle 的
154
- * `settings.permissions.defaultMode`;两处都没有 ⇒ `undefined` = **无表态**(调用点据此不写键,而不是替调用方选一个默认值;引擎把缺席读成 `default`)。
153
+ * [ref] §6-1 —— 本请求的**生效** permission mode,单点 = 调用方的词({@link requestedPermissionMode})经本任务 settings 的 kill-switch 折叠
154
+ * ({@link foldDisableAutoMode})。两处都没有 ⇒ `undefined` = **无表态**(调用点据此不写键,而不是替调用方选一个默认值;引擎把缺席读成 `default`)。
155
155
  *
156
156
  * 🔴 为什么必须是一只函数:同一个「生效模式」此前在 resolve-spec 里被算过两次(hooks 腿的 `permission_mode` 载荷、settings 折叠腿),
157
157
  * 两处各算各的,任何一次优先序修订都会让两面分家,而分家在这条轴上的形态是「壳发的 bundle bypass 在 A 面生效、在 B 面没生效」这种
@@ -160,6 +160,23 @@ export declare function effectiveThinking(reasoningEffort: unknown, ultracode: b
160
160
  export declare function effectivePermissionMode(body: {
161
161
  permissionMode?: unknown;
162
162
  }, settings: ParsedTaskSettings | undefined): PermissionMode | undefined;
163
+ /**
164
+ * 调用方在提交体上说出的模式词 —— 顶层 `body.permissionMode`(本轮显式表态)赢过 bundle 的 `settings.permissions.defaultMode`;两处都没有 ⇒ `undefined`。
165
+ * 读者两个:{@link effectivePermissionMode}(存储体读法)与提交口交给受理判官入口一步的那一个词(`permission-mode-admission.ts` `stampSubmissionEntryMode`)。
166
+ */
167
+ export declare function requestedPermissionMode(body: {
168
+ permissionMode?: unknown;
169
+ }, settings: ParsedTaskSettings | undefined): PermissionMode | undefined;
170
+ /**
171
+ * [ref] 件⑥([ref] §3.4,[ref]/[ref]):本任务 settings 的 kill-switch `permissions.disableAutoMode` 的**唯一**折叠(tighten-only,CC 250 `Ct`
172
+ * 棘轮形):把 auto 折 default(座不写 ⇒ 引擎不武装,与 CC「auto 不可用 ⇒ 回 default」同向)。只关 auto;缺席不动;没有放宽臂。
173
+ * 读者两个:存储体读法 {@link effectivePermissionMode} 与受理判官(`permission-mode-admission.ts`,提交口与切档口同一只)—— 切档口的
174
+ * `auto` 因此与提交口的 `auto` 在同一份 settings 下折成同一个词。`disableBypassPermissionsMode` 落点归 [ref]。
175
+ * 🔴 [ref] 放宽一档:折叠触发词是 **`"disable"` 或 `true`** —— 两套**已发布**的合法拼写(CC 250 `Ct` 的严极词 /
176
+ * `@sema-agent/sdk` 8.1.0 `SettingsPermissions.disableAutoMode?: boolean`),同义同折。显式 `false` = 「不禁」,
177
+ * 合法输入、**零效果**(不是「开 auto」的开关 —— 本键只有关的半场,放宽臂在这条轴上从来不存在)。
178
+ */
179
+ export declare function foldDisableAutoMode(word: PermissionMode | undefined, settings: ParsedTaskSettings | undefined): PermissionMode | undefined;
163
180
  /**
164
181
  * [ref] —— [ref] 技能清单预算两旋钮的**值域谓词**(一对具名单铸点)。
165
182
  *
@@ -182,9 +199,10 @@ export declare function skillListingMaxDescOk(v: unknown): v is number;
182
199
  export interface ParsedTaskSettings {
183
200
  permissions?: {
184
201
  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[];
202
+ /** 纯串 = 请求自己写的规则;`{ rule, layer }` = 端从某个设置层读来的规则(`permission-rule-list.ts` 的 {@link RuleListEntry};层只选 `/` 的锚)。 */
203
+ deny?: RuleListEntry[];
204
+ /** 同 `deny` 的条目形;编译与 `deny` 同一口({@link deriveSettingsPolicy})。 */
205
+ ask?: RuleListEntry[];
188
206
  defaultMode?: PermissionMode;
189
207
  /** [ref] 件⑥([ref] §3.4,[ref];PM 裁 [ref]):CC settings 的 kill-switch `permissions.disableAutoMode`,cli 投影
190
208
  * (settingsRulesWire.ts toWireSettings)而此前本白名单丢弃 ⇒ 用户/managed 层关 auto 到不了引擎。语义 = 只能关
@@ -225,6 +243,9 @@ export interface ParsedTaskSettings {
225
243
  * wire 位置由座位表说(三座在 `settings` 顶层,`neverAuto` 在 `settings.permissions`),解析后与位置无关。空数组 ≡ 缺席(不铸键)。
226
244
  * 去处:三座经 {@link applyTaskSettings} 落 core 任务层席;`neverAuto` 经 {@link deriveSettingsPolicy} 落请求叶策略。 */
227
245
  tightenOnly?: TightenOnlySeatValues;
246
+ /** http hook 两张名单(`settings.allowedHttpHookUrls` / `settings.httpHookAllowedEnvVars`)作为**一个设置层**:源名 {@link REQUEST_SETTINGS_SOURCE} + 两键里在场的那几把,值原样(本服务不读内容)。
247
+ * 两键都缺席 ⇒ 不铸键。唯一读者 = `hooks/hook-runner.ts` 的 http 腿(经 hooks 注册上下文的 `httpHookAllowlistLayers`)。 */
248
+ httpHookAllowlist?: HttpHookAllowlistLayer;
228
249
  }
229
250
  /** The result of parsing an untrusted `body.settings`: the applied subset + the names of present-but-deferred
230
251
  * fields (so the caller can log/advertise them — no silent drop). `null` settings ⇒ `{ settings: undefined }`. */
@@ -276,7 +297,7 @@ export interface SettingsPermissionBucketIssue {
276
297
  /**
277
298
  * 三桶的**唯一**判据(纯函数)。`perms` 不是对象 ⇒ 不判(那一形归「settings.permissions must be an object」那道门);桶缺席 / `null`
278
299
  * ⇒ 不判(与 `permissions: null` 的「容器缺席」同一约定)。逐桶按 {@link SETTINGS_PERMISSION_BUCKETS} 的序、按该桶的条目规则:不是数组 ⇒ `bad_entry`
279
- * (无 `index`);`rule_policy` 桶超帽 ⇒ `over_cap`(先判,拒体有界)、首个非串 / 空串项 ⇒ `bad_entry` + 它的下标;`allow_layer` 桶首个非串项 ⇒
300
+ * (无 `index`);`rule_policy` 桶超帽 ⇒ `over_cap`(先判,拒体有界)、首个不合条目律的项(不是非空串、也不是合形的带层条目)⇒ `bad_entry` + 它的下标;`allow_layer` 桶首个非串项 ⇒
280
301
  * `bad_entry` + 它的下标(整表判据 = `ruleEntryListShapeOk`),超帽同样 `over_cap`(帽的理由不同,见段头)。全合法 ⇒ `null`。
281
302
  */
282
303
  export declare function settingsPermissionBucketIssue(perms: unknown): SettingsPermissionBucketIssue | null;
@@ -296,8 +317,12 @@ export declare function settingsPermissionBucketIssue(perms: unknown): SettingsP
296
317
  * 🔴 两个 `skillListing*`([ref],7.77.0)= **CC settings 同名键**,值域抄 CC 的 schema 声明
297
318
  * (`skillListingBudgetFraction` 是 `gt(0).lte(1)`、`skillListingMaxDescChars` 是 `int().positive()`);
298
319
  * 消费点 = 本文件的 parse + `boot/resolve-spec.ts` 的 `TaskSpec` 透传。语义(预算怎么花、描述怎么裁、
299
- * 溢出怎么退成 name-only)整个归引擎,本仓一个字节都不渲染,也**不复刻默认值**——缺席即整键不写。 */
300
- export declare const TASK_SETTINGS_KEYS: readonly ["approvalFrameFamilies", "complianceAdditionalDenies", "env", "hooks", "model", "outputStyle", "permissions", "readDenyPatterns", "skillListingBudgetFraction", "skillListingMaxDescChars", "ultracode", "webSearch", "writeProtectedExtra"];
320
+ * 溢出怎么退成 name-only)整个归引擎,本仓一个字节都不渲染,也**不复刻默认值**——缺席即整键不写。
321
+ *
322
+ * 🔴 `allowedHttpHookUrls` / `httpHookAllowedEnvVars` = **CC settings 同名键**(契约包的 http hook 两张名单,键表属主 = 契约包 `HTTP_HOOK_ALLOWLIST_KEYS`):
323
+ * 消费点 = {@link parseTaskSettings} 把它们原样收成一层({@link requestHttpHookAllowlistLayer})→ `boot/resolve-spec.ts` 交给 hooks 注册 → `hooks/hook-runner.ts` 的 http 腿
324
+ * 在拨出之前问契约包判官。受理面只判键名(本门),不判值:名单的读法只有判官那一只(坏形由判官答 `malformed`、拦)。 */
325
+ export declare const TASK_SETTINGS_KEYS: readonly ["allowedHttpHookUrls", "approvalFrameFamilies", "complianceAdditionalDenies", "env", "hooks", "httpHookAllowedEnvVars", "model", "outputStyle", "permissions", "readDenyPatterns", "skillListingBudgetFraction", "skillListingMaxDescChars", "ultracode", "webSearch", "writeProtectedExtra"];
301
326
  /** 本 service 消费的 `settings.permissions` 子键 —— 闭集(值形校验另有既有门,本门只判键名)。 */
302
327
  export declare const TASK_SETTINGS_PERMISSION_KEYS: readonly ["allow", "ask", "defaultMode", "deny", "disableAutoMode", "neverAuto"];
303
328
  /** CC/SDK 合约里合法、本 service **不接线**的顶层键。词表外的 CC 键落 `unknown` —— 两列都是 400,标签只影响
@@ -432,6 +457,12 @@ export interface FsWriteGateWiring {
432
457
  * 运维在**下单那一刻**就知道这条规则这条车道读不了,而不是等到跑起来每次都弹一张 ask。
433
458
  */
434
459
  taskHome?: string;
460
+ /**
461
+ * **执行环境的配置根**(唯一属主 `execution-config-root.ts` 的 `executionConfigRootOf`):user 设置层里 `/` 形路径规则的根 —— 带 `layer: "userSettings"`
462
+ * 且要 `root` 的条目编进只声明 `{ root: configRoot }` 的那一集(`permission-rule-list.ts` 的 `ruleRootOf`)。缺席(沙箱车道既没转 `SEMA_CONFIG_DIR`、home 也读不出)
463
+ * ⇒ 这样的条目由 {@link rejectUnmatchableSettingsNames} 响亮拒(422 `home_unresolvable`),不猜目录。拒点与存体预判读同一只函数。
464
+ */
465
+ configRoot?: string;
435
466
  }
436
467
  /**
437
468
  * 请求的 `settings.permissions.deny` / `ask` 有永远不生效的条目 ⇒ 拒点({@link rejectUnmatchableSettingsNames})抛的错:句子(`message`)与这一遍判下来的**全部**问题(`problems`,机读位的原料)。
@@ -454,20 +485,24 @@ export declare const RULE_POLICY_BUCKETS: readonly ["ask", "deny"];
454
485
  export type RulePolicyBucket = (typeof RULE_POLICY_BUCKETS)[number];
455
486
  /**
456
487
  * 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`),所以预判什么也不声明、两腿逐字同句;
488
+ * ② 按 {@link RULE_POLICY_BUCKETS} 的序逐桶、桶内按 `RULE_ROOTS` 的序逐集(`permission-rule-list.ts` 的 `ruleSetsOf`:判的就是要编的那一集)问 `judgeUnmatchableToolNames`(名字层 + core 规则文法);
489
+ * ③ 再判「这条规则要的基本部署给不出」那一拒:`~/` 形缺 home;配置根那一集缺配置根(两者都落在执行环境的 home 上,同词 `home_unresolvable`)。答 `{ bucket, message, problems }`:`bucket` 与 `message` 是**第一处**坏处的
490
+ * (句子冻结,先撞上的那一条),`problems` 是**两段、两只桶判下来的全部**坏条目(机读位的原料,形的属主 `entry-problems.ts`;`entry` = 规则原文,带层条目也一样)。
491
+ * 入参:桶、{@link RulePolicyBases}。**home 同源**:拒点的 home 是 `resolve-spec.ts` 的 `laneHomeBase()` 经 `executionLaneHomeDir(config)` 算出的,预判传同一个函数对同一份部署配置算出的;配置根同理(`executionConfigRootOf`)。
492
+ * **任务根那一集声明给文法的基**只有拒点有(根 + home 的并集,执行车道才知道根):core 的规则文法对**没声明**的基不下判词(见 `config.ts` `uncompilableRuleGuidance`),所以预判什么也不声明、两腿逐字同句;
461
493
  * 预判不能把 home 当「半个基」声明给文法 —— core 的路径族只从根推,缺根时一个合法的 Windows 形 home 会按默认族被判 `unsupported.path_base`(V6-k)。
462
- * ③ 排在最后:一条同时文法不成立又带 `~/` 的规则,先说文法(补上 home 它仍被文法拒,先说 home 是误导)。
494
+ * **配置根那一集**两腿都声明 `{ root: 配置根 }`:两腿都算得出它,`{ root }` 自带路径族(没有「半个基」),一个用不了的配置根(转进沙箱的相对值)两腿同为引擎的 `unsupported.path_base`。
495
+ * ③ 排在最后:一条同时文法不成立又缺基的规则,先说文法(补上基它仍被文法拒,先说基是误导)。
463
496
  */
464
497
  export interface RulePolicyBases {
465
- /** 声明给 core 规则文法的路径基(这条 task 的根 + home):拒点才有;存体预判不声明。 */
498
+ /** 声明给 core 规则文法的任务根那一集的路径基(这条 task 的根 + home):拒点才有;存体预判不声明。 */
466
499
  readonly declared?: PathRuleBases;
467
500
  /** 执行环境的 home(`~/` 形规则的前提);这个部署解不出 ⇒ 缺席。 */
468
501
  readonly home?: string;
502
+ /** 执行环境的配置根(配置根那一集的根;拒点与预判都给);读不出 ⇒ 缺席。 */
503
+ readonly configRoot?: string;
469
504
  }
470
- export declare function rulePolicyBucketsRefusal(permissions: Readonly<Partial<Record<RulePolicyBucket, readonly string[]>>>, bases: RulePolicyBases): {
505
+ export declare function rulePolicyBucketsRefusal(permissions: Readonly<Partial<Record<RulePolicyBucket, readonly RuleListEntry[]>>>, bases: RulePolicyBases): {
471
506
  readonly bucket: RulePolicyBucket;
472
507
  readonly message: string;
473
508
  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 { HTTP_HOOK_ALLOWLIST_KEYS, 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
  }
@@ -156,7 +160,7 @@ export function settingsPermissionBucketIssue(perms) {
156
160
  function acceptedRuleList(v) {
157
161
  return Array.isArray(v) && v.length > 0 ? v.slice() : undefined;
158
162
  }
159
- export const TASK_SETTINGS_KEYS = ["approvalFrameFamilies", "complianceAdditionalDenies", "env", "hooks", "model", "outputStyle", "permissions", "readDenyPatterns", "skillListingBudgetFraction", "skillListingMaxDescChars", "ultracode", "webSearch", "writeProtectedExtra"];
163
+ export const TASK_SETTINGS_KEYS = ["allowedHttpHookUrls", "approvalFrameFamilies", "complianceAdditionalDenies", "env", "hooks", "httpHookAllowedEnvVars", "model", "outputStyle", "permissions", "readDenyPatterns", "skillListingBudgetFraction", "skillListingMaxDescChars", "ultracode", "webSearch", "writeProtectedExtra"];
160
164
  export const TASK_SETTINGS_PERMISSION_KEYS = ["allow", "ask", "defaultMode", "deny", "disableAutoMode", "neverAuto"];
161
165
  export const TASK_SETTINGS_UNSUPPORTED_KEYS = ["apiKeyHelper", "crossSessionInbound", "dialogExpiry", "sandbox", "statusLine"];
162
166
  export const TASK_SETTINGS_UNSUPPORTED_PERMISSION_KEYS = ["additionalDirectories", "disableBypassPermissionsMode"];
@@ -224,6 +228,16 @@ export function hooksKeyIssue(raw, config) {
224
228
  export function taskHooksHonored(config) {
225
229
  return !DEPLOYMENT_SHAPE_PREDICATES.multiTenant.holds(config);
226
230
  }
231
+ const REQUEST_SETTINGS_SOURCE = "flagSettings";
232
+ function requestHttpHookAllowlistLayer(s) {
233
+ const present = HTTP_HOOK_ALLOWLIST_KEYS.filter((key) => Object.hasOwn(s, key));
234
+ if (present.length === 0)
235
+ return undefined;
236
+ const layer = { source: REQUEST_SETTINGS_SOURCE };
237
+ for (const key of present)
238
+ layer[key] = s[key];
239
+ return layer;
240
+ }
227
241
  function tightenOnlySeatDeferredName(name) {
228
242
  return tightenOnlySeatField(name).replace(/^settings\./, "");
229
243
  }
@@ -299,6 +313,9 @@ export function parseTaskSettings(raw, seats = {}) {
299
313
  deferred.push(tightenOnlySeatDeferredName(name));
300
314
  if (Object.keys(tightenSeats.values).length > 0)
301
315
  out.tightenOnly = tightenSeats.values;
316
+ const httpHookAllowlist = requestHttpHookAllowlistLayer(s);
317
+ if (httpHookAllowlist !== undefined)
318
+ out.httpHookAllowlist = httpHookAllowlist;
302
319
  return { settings: Object.keys(out).length > 0 ? out : undefined, deferred, ...(hooksReread !== undefined ? { hooksReread } : {}) };
303
320
  }
304
321
  export function acceptAppendSystemPrompt(v, warn) {
@@ -336,8 +353,12 @@ export function createWorkflowAskPolicy(isExempt) {
336
353
  nameSets: [{ ask: WORKFLOW_ASK_NAMES }],
337
354
  };
338
355
  }
339
- function rejectUnmatchableSettingsNames(perms, pathBases) {
340
- const refused = rulePolicyBucketsRefusal(perms, { ...(pathBases !== undefined ? { declared: pathBases } : {}), ...(pathBases?.home !== undefined ? { home: pathBases.home } : {}) });
356
+ function rejectUnmatchableSettingsNames(perms, pathBases, configRoot) {
357
+ const refused = rulePolicyBucketsRefusal(perms, {
358
+ ...(pathBases !== undefined ? { declared: pathBases } : {}),
359
+ ...(pathBases?.home !== undefined ? { home: pathBases.home } : {}),
360
+ ...(configRoot !== undefined ? { configRoot } : {}),
361
+ });
341
362
  if (refused !== undefined)
342
363
  throw new SettingsRuleRefusalError(refused.message, refused.problems);
343
364
  }
@@ -354,21 +375,40 @@ export const RULE_POLICY_BUCKETS = ["ask", "deny"];
354
375
  export function rulePolicyBucketsRefusal(permissions, bases) {
355
376
  let first;
356
377
  const problems = [];
378
+ const declaredFor = (root) => {
379
+ switch (root) {
380
+ case "task":
381
+ return bases.declared;
382
+ case "configuration":
383
+ return bases.configRoot !== undefined ? { root: bases.configRoot } : undefined;
384
+ default: {
385
+ const unhandled = root;
386
+ return unhandled;
387
+ }
388
+ }
389
+ };
357
390
  for (const bucket of RULE_POLICY_BUCKETS) {
358
- const names = permissions[bucket];
359
- if (!names)
360
- continue;
361
- const judged = judgeUnmatchableToolNames(`permissions.${bucket}`, names, { consumer: "rule-policy", behavior: bucket, ...(bases.declared !== undefined ? { pathBases: bases.declared } : {}) });
362
- if (judged === undefined)
391
+ const entries = permissions[bucket];
392
+ if (!entries)
363
393
  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));
394
+ const sets = ruleSetsOf(entries, bucket);
395
+ for (const root of RULE_ROOTS) {
396
+ const names = sets[root].map(ruleTextOf);
397
+ if (names.length === 0)
398
+ continue;
399
+ const declared = declaredFor(root);
400
+ const judged = judgeUnmatchableToolNames(`permissions.${bucket}`, names, { consumer: "rule-policy", behavior: bucket, ...(declared !== undefined ? { pathBases: declared } : {}) });
401
+ if (judged === undefined)
402
+ continue;
403
+ first ??= { bucket, message: judged.message };
404
+ for (const b of judged.bad)
405
+ problems.push(entryProblem(SETTINGS_RULE_REFUSAL_CODE, `settings.permissions.${bucket}`, b.name, b.reason));
406
+ }
367
407
  }
368
408
  if (bases.home === undefined) {
369
409
  for (const bucket of RULE_POLICY_BUCKETS) {
370
410
  const field = `permissions.${bucket}`;
371
- for (const name of permissions[bucket] ?? []) {
411
+ for (const name of (permissions[bucket] ?? []).map(ruleTextOf)) {
372
412
  const parsed = parseRuleText(name, bucket);
373
413
  if ("rule" in parsed && ruleToolGrammarOf(parsed.rule.tool) === "path" && parsed.rule.command.startsWith("~/")) {
374
414
  first ??= {
@@ -382,8 +422,36 @@ export function rulePolicyBucketsRefusal(permissions, bases) {
382
422
  }
383
423
  }
384
424
  }
425
+ if (bases.configRoot === undefined) {
426
+ for (const bucket of RULE_POLICY_BUCKETS) {
427
+ const field = `permissions.${bucket}`;
428
+ for (const name of ruleSetsOf(permissions[bucket] ?? [], bucket).configuration.map(ruleTextOf)) {
429
+ first ??= {
430
+ bucket,
431
+ 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 — ` +
432
+ `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. ` +
433
+ `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.`,
434
+ };
435
+ problems.push(entryProblem(SETTINGS_RULE_REFUSAL_CODE, `settings.${field}`, name, "home_unresolvable"));
436
+ }
437
+ }
438
+ }
385
439
  return first === undefined ? undefined : { ...first, problems };
386
440
  }
441
+ function ruleSetBases(root, taskBases, configRoot) {
442
+ switch (root) {
443
+ case "task":
444
+ return taskBases;
445
+ case "configuration":
446
+ if (configRoot === undefined)
447
+ 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");
448
+ return { root: configRoot };
449
+ default: {
450
+ const unhandled = root;
451
+ return unhandled;
452
+ }
453
+ }
454
+ }
387
455
  export const REQUEST_SETTINGS_REFUSAL_PREFIX = "request settings cannot be applied as written: ";
388
456
  export function deriveSettingsPolicy(settings, bases) {
389
457
  const perms = settings.permissions;
@@ -396,11 +464,19 @@ export function deriveSettingsPolicy(settings, bases) {
396
464
  const pathBases = bases?.taskRoot !== undefined || bases?.taskHome !== undefined
397
465
  ? { ...(bases.taskRoot !== undefined ? { root: bases.taskRoot } : {}), ...(bases.taskHome !== undefined ? { home: bases.taskHome } : {}) }
398
466
  : 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));
467
+ rejectUnmatchableSettingsNames(perms, pathBases, bases?.configRoot);
468
+ for (const [bucket, entries] of [["deny", deny], ["ask", ask]]) {
469
+ if (!entries)
470
+ continue;
471
+ const sets = ruleSetsOf(entries, bucket);
472
+ for (const root of RULE_ROOTS) {
473
+ const set = sets[root];
474
+ if (set.length === 0)
475
+ continue;
476
+ const table = RULE_SOURCE_BY_BEHAVIOR[bucket];
477
+ policies.push(createRuleListPolicy(set.map((e) => ({ rule: ruleTextOf(e), source: ruleSourceOf(table, e) })), bucket, table, ruleSetBases(root, pathBases, bases?.configRoot)));
478
+ }
479
+ }
404
480
  }
405
481
  if (neverAuto !== undefined) {
406
482
  const refused = neverAutoNamesRefusal(neverAuto);
@@ -1,5 +1,6 @@
1
1
  /**
2
- * 终局因由(`TaskResult.terminal`)的 server 侧**读**面 —— 一个叶子模块,只依赖 `@sema-agent/core`。
2
+ * 终局因由(`TaskResult.terminal`)的 server 侧**读**面 —— 一个叶子模块:依赖 `@sema-agent/core`、进程级记录器(`observability/fail-open.ts`)
3
+ * 与秘密脱敏器(`trace/redact.ts`,只作码判官 {@link isCodeToken} 的一条合取);本仓的结果汇点 / 出口座反过来依赖本模块,本模块不依赖它们。
3
4
  *
4
5
  * core 7.6.0([ref] S6 D-8,BREAKING)把 `TaskResult` 上并列的八个平面键
5
6
  * (`status` / `errorCode` / `errorMessage` / `blockedReason` / `checkpointToken` / `checkpointId` /
@@ -57,13 +58,73 @@ export declare const STORE_ERROR_CODE = "internal.store_error";
57
58
  *
58
59
  * ⚠️ **辖域如实**:本臂触发条件是「码出了本仓的词法」,所以任何依赖铸出的域外码都落这里,不只 SQL 驱动。
59
60
  * 今天可观察的产出方**只有**耐久店那一层(引擎自己的传输/brain 错误在 core 侧已被分类成小写码),故码名
60
- * 取 `internal.store_error`;原码逐字进 message 与 warn 行,归因从不丢失。
61
+ * 取 `internal.store_error`;原码逐字进 message 尾注(随结果过汇点脱敏)、是码时进运维线索({@link isCodeToken}),归因从不丢失。
61
62
  */
62
63
  export declare function outwardFailureCode(code: string): string;
64
+ /**
65
+ * 一个错误的 `code` 只在它**是**码时才说出口 —— 一只判官,两条合取:
66
+ * ① **形**:短 token。驱动码 `ER_*` / SQLSTATE / `ECONNRESET` 全在这一形里;带空白、`=`、换行或超过 64 字符的就是一句话,不是码;
67
+ * ② **不是凭据形**:秘密脱敏器({@link redactSecrets})原样放过它。`sk-…` / `AKIA…` / `ghp_…` 这类秘密过得了 ①,而 wire 上同一只脱敏器
68
+ * 会把它从 message 尾注里遮掉 —— 日志行若照印,就是 wire 遮、日志露;所以「长得像码的秘密」在这里判「不是码」,日志与 wire 同读。
69
+ * 先判 ①:脱敏器只读 ≤ 64 个 token 字符(扫描代价有界);只问不出门(`observe: false`,与影子重扫同口 —— 不计「已脱」读数)。
70
+ * 读者两只,同一条判据:折码的运维线索(本文件 {@link sealResultForExit} / {@link sealStoredResult})与执行车道占位读的故障词(`boot/execution-env.ts`)。
71
+ */
72
+ export declare function isCodeToken(code: unknown): code is string;
73
+ /**
74
+ * 一个错误**说得出口**的机器码,以日志字段 / 指标标签的形交回:`err.code` 是串且 {@link isCodeToken} 判它是码 ⇒ `{ code }`(逐字);其余(没有 / 不是串 / 空串 / 非 `Error` 抛出物 / 读取时抛 / 不是码)⇒ `{}`
75
+ * —— 键缺席,不补 `null` / `none` 之类的占位。「读口 + 判官 + 字段形」的**唯一**组合:引擎交来的错上的 `code` 是别人写的串,凡把它写进运维日志行或指标标签的汇点
76
+ * (`onError` 汇点的七条行与 `runner_config_advisory_total` 的标签、记忆引擎事故席)都经这一只,不各写一份读法、也不各设一份形判。读口 {@link errorCodeOf} 不抛。
77
+ * 显式标注返回类型:推断型 `{ code?: undefined } | { code: string }` 进不了指标标签 `Record<string, string>`。
78
+ */
79
+ export declare function codeFieldOf(err: unknown): {
80
+ readonly code?: string;
81
+ };
82
+ /**
83
+ * 存量结果被**再观看**的读座(闭集):`GET /v1/runs/:id → result`(终态行与停驻行同一座)与账本 `done` 行的回放(三条耐久读腿共用)。
84
+ * 读入口 {@link sealStoredResult} 必须点名其一(类型级必填);`exit` 不在这里 —— 它只由写入口 {@link sealResultForExit} 给。
85
+ */
86
+ export type StoredResultReadSeat = "run.result" | "ev.done.result";
87
+ /**
88
+ * **一次耐久停驻在一条结果上够得着的全部座** —— 本仓**唯一**一张座表。两个属主共读它,不各抄一份:
89
+ * · 出口封口 {@link sealResultForExit} 按它**摘凭据**(`token` 键);
90
+ * · 结果汇点 `observability/run-terminal.ts` 的 `redactTerminalResult` 按它**脱门**(`gate` 键,门上有模型 / 人可控的展示文本)。
91
+ *
92
+ * 每座 = 从结果根出发的路径 + 该座上的凭据键名与门键名(引擎在两种形上给同一件事起了两套名字:因由形 `token` / `gate`,
93
+ * 验证器停驻形 `checkpointToken` / `checkpointGate`)。三座是**类型的形状**,不是三个特判:
94
+ * · `terminal` —— 本腿自己 park(`paused` 臂);
95
+ * · `terminal.nestedPause` —— 嵌套边界(verify / cascade)遇到自己驱动不了的耐久暂停,在它的边界上如实报 `failed`,
96
+ * 把那次暂停整只带在这里(正是这条路径当年让凭据漏进过账本);
97
+ * · `verification.verifierPause` —— 验证门里**验证器自己**的停驻(core `VerificationOutcome.verifierPause`,
98
+ * `unverifiedReason: "verifier_paused"`):在结果因由**之外**,宿主判官看不见它,run 照常终局。
99
+ *
100
+ * 表是否完整**不靠记性**:`trace/core-keyset-guard.ts` ⑩f 从 core 的结果类型(普通腿 / verify / cascade 三种出口结果)
101
+ * **派生**出每一条凭据型路径与门型路径,与本表逐条对账 —— core 在结果的任何一层加一个凭据座或门座 ⇒ tsc 当场红。
102
+ */
103
+ export declare const PAUSE_SEATS: readonly [{
104
+ readonly path: readonly ["terminal"];
105
+ readonly token: "token";
106
+ readonly gate: "gate";
107
+ }, {
108
+ readonly path: readonly ["terminal", "nestedPause"];
109
+ readonly token: "token";
110
+ readonly gate: "gate";
111
+ }, {
112
+ readonly path: readonly ["verification", "verifierPause"];
113
+ readonly token: "checkpointToken";
114
+ readonly gate: "checkpointGate";
115
+ }];
116
+ /** 座表的一行(类型门 ⑩f 从它派生「本仓摘 / 脱的路径」)。 */
117
+ export type PauseSeat = (typeof PAUSE_SEATS)[number];
118
+ type SeatObject = Readonly<Record<string, unknown>>;
119
+ /**
120
+ * 对一条结果上**每一座**施一次 `f`,不动的座保持同一引用(一座都没动 ⇒ 整条结果同一引用)。
121
+ * 对任何输入安全:路径上任一层缺席 / 非普通对象(含数组)⇒ 那一座跳过,不抛、不重塑(读盘面纪律:旧持久 blob / 非 core 的 Runner 实现)。
122
+ */
123
+ export declare function mapPauseSeats<T>(result: T, f: (seat: SeatObject, keys: PauseSeat) => SeatObject): T;
63
124
  /**
64
125
  * **一条结果离开引擎、进入本仓(可重放账本 / run 行 / wire)的唯一封口** —— 两件事,一个出口:
65
- * ①摘掉**耐久恢复能力凭据**(`CheckpointToken`);②把**域外的失败码**折成本仓的 `internal.store_error`
66
- * ({@link outwardFailureCode},[ref])。
126
+ * ①摘掉**耐久恢复能力凭据**(`CheckpointToken`)—— 座表 {@link PAUSE_SEATS} 上的每一座;②把**域外的失败码**折成本仓的
127
+ * `internal.store_error`({@link outwardFailureCode},[ref])—— 码座两只:因由的 `terminal.code` 与 cascade 每一级的 `attempts[].errorCode`(同一类码,见 {@link foldOutwardFailureCodes})。
67
128
  *
68
129
  * 🔴 为什么两件事合在一只函数里而不是并列两只(7.92.2 改名 `stripResumeToken` → `sealResultForExit`):
69
130
  * 它们是**同一个出口上的同一件事** —— 「这条结果可以见人了吗」。并列两只 = 六个结算点各自要记得调两次,
@@ -71,26 +132,40 @@ export declare function outwardFailureCode(code: string): string;
71
132
  * 旧名只说了摘凭据那一半,一个只做一半的名字会让下一个人在另一半上另开出口。
72
133
  *
73
134
  * ── ① 摘凭据 ──
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`)。消费端要动这次暂停,走
135
+ * token 就是 resume 凭据(token-as-auth,search [18] Q1b),**永不离开本服务**;非密的门 / `checkpointId` /
136
+ * `restoreMode` / 验证器停驻的 `status` 与 `sessionId` 有意保留(对账位;门的自由文本由结果汇点脱)。
137
+ * 凭据住在哪几座是 {@link PAUSE_SEATS} 一张表的事,本函数**不**在别处另开摘除臂:漏掉的那一座(验证器停驻,修前)
138
+ * 正是「座表只写了两座」造成的,修法是座表加一行,不是另起第二处摘字。
139
+ *
140
+ * wire 上的形:每一座**缺席**凭据键(core 类型里因由的 `token` 是必填座,那是**引擎内**的契约;本服务的 wire
141
+ * 契约逐字声明这一座不出服务边界 —— 见 `docs/ASSISTANT-WIRE-CONTRACT.md`)。消费端要动一次停驻,走
87
142
  * `/v1/approvals` 决策端点,凭 `checkpointId` 对账,从来不是拿 token 直连引擎。
88
143
  *
89
- * 对任何输入安全(非普通对象 / 无 `terminal` / 因由不带 token ⇒ **同一引用**原样返回,不重塑)。
144
+ * **出口全集都经这一口**:三家 run 店的行写口与账本店的 `done` 行写口在店入口调它(`observability/run-terminal.ts`),
145
+ * 同步 200 体 / 流 done 帧 / 续跑决定腿在铸体处调它;`GET /v1/runs/:id{,/events}` 等读面在回放前过同一套封口的**读入口**
146
+ * {@link sealStoredResult} —— 升级前落盘、还带着凭据的存量行在读面上同样摘掉。两件事都**幂等**,重复过口是同形。
147
+ *
148
+ * ── 折码的运维线索:一句定形字段,两只入口各说一只词 ──
149
+ * 字段 `original=<码 | <not-a-code>> outward=<对外词> messageBytes=<n> at=<观看点>`,只有闭集词、数字与码({@link isCodeToken}):
150
+ * 失败句只报字节数,**永不**报原文 —— 失败句是自由文本(驱动句里可以有连接串 / 口令),结果里的那份由结果汇点脱敏,运维线索没有汇点,
151
+ * 所以压根不带它;码形座里装着文字的、长得像码的秘密同理只报 `<not-a-code>`。`at` 说这是哪一次观看,**一个入口一只词**(类型给定,不靠记得传):
152
+ * · 本函数 = 结果**第一次**离开引擎(店的写口 / 出口铸体)⇒ `at=exit`,每折一次一行 `[terminal] outward_failure_code_folded <字段>` —— 每次都是一件新的失败;
153
+ * · {@link sealStoredResult} = 存量行的**再观看**(读座必填)⇒ 走 fail-open 纪律(登记 tag `server.terminal.stored-result-code-folded`):
154
+ * 逐读计数 + 逐读探针行 + 每进程一次 warn,detail 即上面那句字段。读面每次轮询 / 每个观看者 / 每次重连都重放同一批行 ——
155
+ * 计数让「旧失败被读了多少次」看得见,一次 warn 让日志不随读次放大;去重归记录器一只,不另造表。
156
+ *
157
+ * 对任何输入安全(非普通对象 / 无可摘的座 / 码在词法内 ⇒ **同一引用**原样返回,不重塑)。
90
158
  */
91
159
  export declare function sealResultForExit<T extends {
92
- terminal: TerminalCause;
160
+ readonly terminal?: unknown;
93
161
  }>(result: T): T;
162
+ /**
163
+ * **存量结果被再观看时的封口** —— 与 {@link sealResultForExit} 同一套封口(摘凭据 + 折码,同判同形),只差运维线索的观看点:
164
+ * 读座({@link StoredResultReadSeat})**必填**,于是新开一条读腿忘了点名读座是 tsc 红,不是悄悄落成 `at=exit`、按读次放大。
165
+ */
166
+ export declare function sealStoredResult<T extends {
167
+ readonly terminal?: unknown;
168
+ }>(result: T, seat: StoredResultReadSeat): T;
94
169
  /**
95
170
  * **投影出口的唯一属主**([ref])—— core 的 `terminalProjection` + 对外码归一,一次。
96
171
  *
@@ -146,7 +221,8 @@ export declare function pausedCauseOf(terminal: TerminalCause): PausedCause | un
146
221
  *
147
222
  * 前提:结果出自 core 的组合器(可信产者)—— `failed.nestedPause` 只由引擎的嵌套边界包停驻铸,`paused` 只在卡行写入确认之后返回。
148
223
  * 判官**不**验证那张卡在不在、是不是这一张、此刻什么态;会话相等不是对卡的校验。验证器**自己**的停驻
149
- * (`verification.verifierPause`)不在结果因由里,本函数看不见它 —— 那一形照常终局。
224
+ * (`verification.verifierPause`)不在结果因由里,本函数看不见它 —— 那一形照常终局;它那张卡本服务兑现不了,由结算点当场作废
225
+ * (`verifier-pause-card.ts`),凭据由封口按座表摘。
150
226
  *
151
227
  * 纯函数,不读店、不抛。凭据随提升上来,摘除仍归 {@link sealResultForExit}(调用方照旧先 graft 后封口)。
152
228
  * 判官只答「该不该停驻」;认领保不保得住是停驻写口**写成功**与否的事(行写口对 `running` / `suspended` 行 CAS)。
@@ -178,4 +254,5 @@ export declare function persistedPlaneOf(result: unknown): {
178
254
  errorMessage?: string;
179
255
  blockedReason?: string;
180
256
  };
257
+ export {};
181
258
  //# sourceMappingURL=terminal.d.ts.map